diff --git a/.gitignore b/.gitignore index e78248369..6f03e0944 100644 --- a/.gitignore +++ b/.gitignore @@ -33,7 +33,6 @@ *.idb # Kernel Module Compile Results -*.mod* *.cmd modules.order Module.symvers @@ -50,10 +49,6 @@ dkms.conf # Compiled Dynamic libraries -# Fortran module files -*.mod -*.smod - # Compiled Static libraries *.lai @@ -165,16 +160,6 @@ Temporary Items # Visual Studio 2015 database file *.VC.db -# Compiled Object files - -# Precompiled Headers - -# Compiled Dynamic libraries - -# Fortran module files - -# Compiled Static libraries - # Executables *.ipa @@ -187,6 +172,8 @@ Temporary Items *.sdf *.VC.opendb +*.vsconfig + # Precompiled Assets SourceArt/**/*.png SourceArt/**/*.tga @@ -547,10 +534,12 @@ NakamaBlueprintsTest/DerivedDataCache NakamaBlueprintsTest/Saved NakamaBlueprintsTest/Binaries -NakamaTest/Plugins/Nakama -NakamaTest/Intermediate -NakamaTest/Saved -NakamaTest/Binaries +IntegrationTests/Plugins/Nakama +IntegrationTests/Plugins/Satori +IntegrationTests/Intermediate +IntegrationTests/DerivedDataCache +IntegrationTests/Saved +IntegrationTests/Binaries Nakama/Intermediate Nakama/Saved @@ -570,3 +559,16 @@ IntegrationTests/DerivedDataCache vcpkg_installed/ /NakamaBlueprintsTest/Content/Developers /NakamaBlueprintsTest/Content/Collections +.env +proto/ +Reports/IntegrationTests/index.html +Reports/IntegrationTests/index.json +Reports/NakamaProfile/ +/IntegrationTests/Reports +NakamaTest/Binaries +NakamaTest/DerivedDataCache +NakamaTest/Intermediate/ +NakamaTest/Saved/ +Reports +IntegrationTests/satori/ +IntegrationTests/dashboards/ diff --git a/CHANGELOG.md b/CHANGELOG.md index ed2d21e9c..1c5009ab3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,27 @@ All notable changes to this project are documented below. The format is based on [keep a changelog](http://keepachangelog.com/) and this project uses [semantic versioning](http://semver.org/). +### [3.0.0] - 2026-05-11 +### Added +- Async Future-based API. +- Missing Server-to-server methods. +- Auto-generation from Nakama/Satori schema. +- Extensive Integration Test suite. + +### Changed +- Function signatures (please refer to documentation). +- Component lifetimes are less coupled to Unreal Engine Garbage Collector. + +### Fixed +- Improved consistency across the SDK. +- Revamped real-time websocket code. + +This is a major release with a larger list of changes. +For more information on the current version and the differences to previous versions, +please refer to [documentation](https://heroiclabs.com/docs/nakama/client-libraries/unreal/index.html) +and the [migration guide](https://heroiclabs.com/docs/nakama/client-libraries/unreal/migration-guide). + + ### [2.11.2] - 2026-04-27 ### Added - Server-side Satori events: #165 diff --git a/IntegrationTests/.editorconfig b/IntegrationTests/.editorconfig new file mode 100644 index 000000000..fbec8216a --- /dev/null +++ b/IntegrationTests/.editorconfig @@ -0,0 +1,91 @@ +[*.{cpp,h}] + +# Naming convention rules (note: currently need to be ordered from more to less specific) + +cpp_naming_rule.aactor_prefixed.symbols = aactor_class +cpp_naming_rule.aactor_prefixed.style = aactor_style + +cpp_naming_rule.swidget_prefixed.symbols = swidget_class +cpp_naming_rule.swidget_prefixed.style = swidget_style + +cpp_naming_rule.uobject_prefixed.symbols = uobject_class +cpp_naming_rule.uobject_prefixed.style = uobject_style + +cpp_naming_rule.booleans_prefixed.symbols = boolean_vars +cpp_naming_rule.booleans_prefixed.style = boolean_style + +cpp_naming_rule.structs_prefixed.symbols = structs +cpp_naming_rule.structs_prefixed.style = unreal_engine_structs + +cpp_naming_rule.enums_prefixed.symbols = enums +cpp_naming_rule.enums_prefixed.style = unreal_engine_enums + +cpp_naming_rule.templates_prefixed.symbols = templates +cpp_naming_rule.templates_prefixed.style = unreal_engine_templates + +cpp_naming_rule.general_names.symbols = all_symbols +cpp_naming_rule.general_names.style = unreal_engine_default + +# Naming convention symbols + +cpp_naming_symbols.aactor_class.applicable_kinds = class +cpp_naming_symbols.aactor_class.applicable_type = AActor + +cpp_naming_symbols.swidget_class.applicable_kinds = class +cpp_naming_symbols.swidget_class.applicable_type = SWidget + +cpp_naming_symbols.uobject_class.applicable_kinds = class +cpp_naming_symbols.uobject_class.applicable_type = UObject + +cpp_naming_symbols.boolean_vars.applicable_kinds = local,parameter,field +cpp_naming_symbols.boolean_vars.applicable_type = bool + +cpp_naming_symbols.enums.applicable_kinds = enum + +cpp_naming_symbols.templates.applicable_kinds = template_class + +cpp_naming_symbols.structs.applicable_kinds = struct + +cpp_naming_symbols.all_symbols.applicable_kinds = * + +# Naming convention styles + +cpp_naming_style.unreal_engine_default.capitalization = pascal_case +cpp_naming_style.unreal_engine_default.required_prefix = +cpp_naming_style.unreal_engine_default.required_suffix = +cpp_naming_style.unreal_engine_default.word_separator = + +cpp_naming_style.unreal_engine_enums.capitalization = pascal_case +cpp_naming_style.unreal_engine_enums.required_prefix = E +cpp_naming_style.unreal_engine_enums.required_suffix = +cpp_naming_style.unreal_engine_enums.word_separator = + +cpp_naming_style.unreal_engine_templates.capitalization = pascal_case +cpp_naming_style.unreal_engine_templates.required_prefix = T +cpp_naming_style.unreal_engine_templates.required_suffix = +cpp_naming_style.unreal_engine_templates.word_separator = + +cpp_naming_style.unreal_engine_structs.capitalization = pascal_case +cpp_naming_style.unreal_engine_structs.required_prefix = F +cpp_naming_style.unreal_engine_structs.required_suffix = +cpp_naming_style.unreal_engine_structs.word_separator = + +cpp_naming_style.uobject_style.capitalization = pascal_case +cpp_naming_style.uobject_style.required_prefix = U +cpp_naming_style.uobject_style.required_suffix = +cpp_naming_style.uobject_style.word_separator = + +cpp_naming_style.aactor_style.capitalization = pascal_case +cpp_naming_style.aactor_style.required_prefix = A +cpp_naming_style.aactor_style.required_suffix = +cpp_naming_style.aactor_style.word_separator = + +cpp_naming_style.swidget_style.capitalization = pascal_case +cpp_naming_style.swidget_style.required_prefix = S +cpp_naming_style.swidget_style.required_suffix = +cpp_naming_style.swidget_style.word_separator = + +cpp_naming_style.boolean_style.capitalization = pascal_case +cpp_naming_style.boolean_style.required_prefix = b +cpp_naming_style.boolean_style.required_suffix = +cpp_naming_style.boolean_style.word_separator = \ No newline at end of file diff --git a/IntegrationTests/.vsconfig b/IntegrationTests/.vsconfig deleted file mode 100644 index b981b2e51..000000000 --- a/IntegrationTests/.vsconfig +++ /dev/null @@ -1,19 +0,0 @@ -{ - "version": "1.0", - "components": [ - "Component.Unreal.Debugger", - "Component.Unreal.Ide", - "Microsoft.Net.Component.4.6.2.TargetingPack", - "Microsoft.VisualStudio.Component.VC.14.38.17.8.ATL", - "Microsoft.VisualStudio.Component.VC.14.38.17.8.x86.x64", - "Microsoft.VisualStudio.Component.VC.14.44.17.14.ATL", - "Microsoft.VisualStudio.Component.VC.14.44.17.14.x86.x64", - "Microsoft.VisualStudio.Component.VC.Llvm.Clang", - "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", - "Microsoft.VisualStudio.Component.Windows11SDK.22621", - "Microsoft.VisualStudio.Workload.CoreEditor", - "Microsoft.VisualStudio.Workload.ManagedDesktop", - "Microsoft.VisualStudio.Workload.NativeDesktop", - "Microsoft.VisualStudio.Workload.NativeGame" - ] -} diff --git a/IntegrationTests/Config/DefaultEngine.ini b/IntegrationTests/Config/DefaultEngine.ini index 69e857948..3a979df17 100644 --- a/IntegrationTests/Config/DefaultEngine.ini +++ b/IntegrationTests/Config/DefaultEngine.ini @@ -2,8 +2,8 @@ [/Script/EngineSettings.GameMapsSettings] GameInstanceClass=/Script/Engine.GameInstance -EditorStartupMap=/Game/Maps/EmptyMap -GameDefaultMap=/Game/Maps/EmptyMap +EditorStartupMap=/Game/Maps/menu.menu +GameDefaultMap=/Game/Maps/menu.menu [/Script/HardwareTargeting.HardwareTargetingSettings] TargetedHardwareClass=Desktop diff --git a/IntegrationTests/Content/Maps/menu.umap b/IntegrationTests/Content/Maps/menu.umap new file mode 100644 index 000000000..ecb170ea6 Binary files /dev/null and b/IntegrationTests/Content/Maps/menu.umap differ diff --git a/IntegrationTests/Content/Maps/testnakama.uasset b/IntegrationTests/Content/Maps/testnakama.uasset new file mode 100644 index 000000000..f87a89ff5 Binary files /dev/null and b/IntegrationTests/Content/Maps/testnakama.uasset differ diff --git a/IntegrationTests/IntegrationTests.uproject b/IntegrationTests/IntegrationTests.uproject index d67946d5a..c69099115 100644 --- a/IntegrationTests/IntegrationTests.uproject +++ b/IntegrationTests/IntegrationTests.uproject @@ -1,6 +1,6 @@ { "FileVersion": 3, - "EngineAssociation": "{A3A5E785-4C17-39B0-8BE8-0F814BEA5348}", + "EngineAssociation": "{7BBEE08C-471E-1AB2-A872-B7AFBBC9FC79}", "Category": "", "Description": "", "Modules": [ diff --git a/IntegrationTests/Source/IntegrationTests/IntegrationTests.Build.cs b/IntegrationTests/Source/IntegrationTests/IntegrationTests.Build.cs index 447a20087..1b2b3b602 100644 --- a/IntegrationTests/Source/IntegrationTests/IntegrationTests.Build.cs +++ b/IntegrationTests/Source/IntegrationTests/IntegrationTests.Build.cs @@ -9,7 +9,6 @@ public class IntegrationTests : ModuleRules public IntegrationTests(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - CppStandard = CppStandardVersion.Cpp20; - PrivateDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "FieldNotification", "InputCore", "Json", "HTTP", "NakamaUnreal", "SatoriUnreal", "WebSockets", "Slate", "SlateCore", "UMG", "ModelViewViewModel" }); + PrivateDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "FieldNotification", "InputCore", "Json", "HTTP", "NakamaApi", "Nakama", "NakamaBlueprints", "SatoriApi", "Satori", "SatoriBlueprints", "WebSockets", "Slate", "SlateCore", "UMG", "ModelViewViewModel" }); } } diff --git a/IntegrationTests/Source/IntegrationTests/Tests/FutureThreadingTests.cpp b/IntegrationTests/Source/IntegrationTests/Tests/FutureThreadingTests.cpp new file mode 100644 index 000000000..c6bf82eef --- /dev/null +++ b/IntegrationTests/Source/IntegrationTests/Tests/FutureThreadingTests.cpp @@ -0,0 +1,121 @@ +/** + * Future Threading Tests + * + * Demonstrates bug: TSatoriFuture terminal .Next() callbacks ran on the UE + * background task thread pool instead of the game thread. + * + * Root cause (Satori.h, old TSatoriFuture:169-179): the terminal Next() overload + * passed the user callback directly to UE::Tasks::Launch without any + * AsyncTask(ENamedThreads::GameThread, ...) dispatch. Callers that touched + * UObject*, fired delegates, or updated UI would therefore hit non-thread-safe + * access and intermittent crashes. + * + * Fix: both TNakamaFuture and TSatoriFuture are now aliases for the shared + * TAsyncFuture<> template (AsyncFuture.h / NakamaApi module), which dispatches + * every user-visible callback — chaining and terminal — to the game thread. + * + * The Satori test below verifies the fix. The Nakama control test was already + * passing before the consolidation. + */ + +#include +#include "Misc/AutomationTest.h" +#include "AsyncFuture.h" + +// ============================================================================ +// Satori: terminal .Next() must run on the game thread +// ============================================================================ + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FSatoriTerminalNextOnGameThreadTest, + "IntegrationTests.Threading.Satori.TerminalNextMustRunOnGameThread", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter +) + +struct FSatoriThreadingTestResult +{ + bool bIsError = false; +}; + +bool FSatoriTerminalNextOnGameThreadTest::RunTest(const FString& Parameters) +{ + TSharedRef> bCallbackRanOnGameThread = MakeShared>(false); + TSharedRef> bCallbackInvoked = MakeShared>(false); + + MakeCompletedAsyncFuture(FSatoriThreadingTestResult{}).Next( + [bCallbackRanOnGameThread, bCallbackInvoked](FSatoriThreadingTestResult) + { + bCallbackRanOnGameThread->store(IsInGameThread()); + bCallbackInvoked->store(true); + }); + + // Spin-wait up to 5 s, pumping the game-thread task queue each iteration + // so the AsyncTask dispatch can actually execute. + constexpr double TimeoutSeconds = 5.0; + const double Deadline = FPlatformTime::Seconds() + TimeoutSeconds; + while (!bCallbackInvoked->load()) + { + FTaskGraphInterface::Get().ProcessThreadUntilIdle(ENamedThreads::GameThread); + if (FPlatformTime::Seconds() > Deadline) + { + AddError(TEXT("Timed out waiting for TSatoriFuture terminal .Next() callback")); + return false; + } + FPlatformProcess::Sleep(0.01f); + } + + TestTrue( + TEXT("TSatoriFuture terminal .Next() callback must run on the game thread"), + bCallbackRanOnGameThread->load() + ); + + return true; +} + +// ============================================================================ +// Nakama: same guarantee, kept as a control / regression test +// ============================================================================ + +struct FNakamaThreadingTestResult +{ + bool bIsError = false; +}; + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FNakamaTerminalNextOnGameThreadTest, + "IntegrationTests.Threading.Nakama.TerminalNextMustRunOnGameThread", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter +) + +bool FNakamaTerminalNextOnGameThreadTest::RunTest(const FString& Parameters) +{ + TSharedRef> bCallbackRanOnGameThread = MakeShared>(false); + TSharedRef> bCallbackInvoked = MakeShared>(false); + + MakeCompletedAsyncFuture(FNakamaThreadingTestResult{}).Next( + [bCallbackRanOnGameThread, bCallbackInvoked](FNakamaThreadingTestResult) + { + bCallbackRanOnGameThread->store(IsInGameThread()); + bCallbackInvoked->store(true); + }); + + constexpr double TimeoutSeconds = 5.0; + const double Deadline = FPlatformTime::Seconds() + TimeoutSeconds; + while (!bCallbackInvoked->load()) + { + FTaskGraphInterface::Get().ProcessThreadUntilIdle(ENamedThreads::GameThread); + if (FPlatformTime::Seconds() > Deadline) + { + AddError(TEXT("Timed out waiting for TNakamaFuture terminal .Next() callback")); + return false; + } + FPlatformProcess::Sleep(0.01f); + } + + TestTrue( + TEXT("TNakamaFuture terminal .Next() callback must run on the game thread"), + bCallbackRanOnGameThread->load() + ); + + return true; +} diff --git a/IntegrationTests/Source/IntegrationTests/Tests/NakamaBlueprintTests.cpp b/IntegrationTests/Source/IntegrationTests/Tests/NakamaBlueprintTests.cpp new file mode 100644 index 000000000..a7aafacb5 --- /dev/null +++ b/IntegrationTests/Source/IntegrationTests/Tests/NakamaBlueprintTests.cpp @@ -0,0 +1,2225 @@ +/** + * Blueprint Async Action Test Suite + * + * Tests for NakamaBlueprints module UBlueprintAsyncActionBase subclasses. + * Each test fires a BP async action, then verifies the server-side effect + * through the C++ client (which supports lambda callbacks natively). + * + * This avoids creating any UObject helpers for delegate binding. + */ + +#include "Misc/AutomationTest.h" +#include "NakamaApi.h" +#include "NakamaClientBlueprintLibrary.h" +#include "Misc/Guid.h" +#include "Containers/Ticker.h" + +// Helper: keep the BP action alive for the duration of the async call. Tests pass +// GetTransientPackage() as WorldContextObject — this satisfies UE 5.7's requirement +// that NewObject<> receives a valid packaged outer, while RegisterWithGameInstance +// remains effectively a no-op (transient package has no GameInstance). +// The base class constructor sets RF_StrongRefOnFrame, and SetReadyToDestroy() +// clears it. We AddToRoot() to prevent GC across frames, then poll until +// RF_StrongRefOnFrame is cleared (signalling the action called SetReadyToDestroy). +static void VerifyWhenComplete(UBlueprintAsyncActionBase* Action, TFunction VerifyFunc) +{ + Action->AddToRoot(); + + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([Action, VerifyFunc = MoveTemp(VerifyFunc)](float) -> bool + { + if (Action->HasAnyFlags(RF_StrongRefOnFrame)) + { + return true; // keep ticking — action hasn't called SetReadyToDestroy yet + } + Action->RemoveFromRoot(); + VerifyFunc(); + return false; + }), + 0.0f + ); +} + +// ============================================================================ +// HEALTHCHECK TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaBPHealthcheckSpec, "IntegrationTests.NakamaBlueprintTests.Healthcheck", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + + FNakamaSession Session; + FNakamaClientConfig Client; + + static const FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7350; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaBPHealthcheckSpec) + +const FString FNakamaBPHealthcheckSpec::ServerKey = TEXT("defaultkey"); +const FString FNakamaBPHealthcheckSpec::Host = TEXT("127.0.0.1"); + +void FNakamaBPHealthcheckSpec::Define() +{ + BeforeEach([this]() + { + Client = FNakamaClientConfig{ServerKey, Host, Port, false}; + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + NakamaApi::AuthenticateCustom(Client, Account, true, TEXT(""), + [this, Done](const FNakamaSession& Result) + { + Session = Result; + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("Setup auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + NakamaApi::DeleteAccount(Client, Session, + [Done]() { Done.Execute(); }, + [Done](const FNakamaError&) { Done.Execute(); } + ); + }); + + Describe("Healthcheck", [this]() + { + LatentIt("should pass healthcheck", [this](const FDoneDelegate& Done) + { + auto* Action = UNakamaClientHealthcheck::Healthcheck(GetTransientPackage(), Client, Session); + Action->Activate(); + + VerifyWhenComplete(Action, [this, Done]() + { + NakamaApi::Healthcheck(Client, Session, + [Done]() { Done.Execute(); }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("Healthcheck failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); +} + +// ============================================================================ +// AUTHENTICATION TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaBPAuthSpec, "IntegrationTests.NakamaBlueprintTests.Auth", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + + FNakamaClientConfig Client; + FNakamaSession Session; + + static const FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7350; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateShortId() { return FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaBPAuthSpec) + +const FString FNakamaBPAuthSpec::ServerKey = TEXT("defaultkey"); +const FString FNakamaBPAuthSpec::Host = TEXT("127.0.0.1"); + +void FNakamaBPAuthSpec::Define() +{ + BeforeEach([this]() + { + Client = FNakamaClientConfig{ServerKey, Host, Port, false}; + Session = FNakamaSession(); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) + { + Done.Execute(); + return; + } + NakamaApi::DeleteAccount(Client, Session, + [Done]() { Done.Execute(); }, + [Done](const FNakamaError&) { Done.Execute(); } + ); + }); + + Describe("AuthenticateCustom", [this]() + { + LatentIt("should authenticate with custom ID", [this](const FDoneDelegate& Done) + { + FString CustomId = GenerateId(); + + FNakamaAccountCustom Account; + Account.Id = CustomId; + auto* Action = UNakamaClientAuthenticateCustom::AuthenticateCustom( + GetTransientPackage(), Client, Account, true, TEXT("")); + Action->Activate(); + + // Verify: re-auth with create=false should succeed (account was created) + VerifyWhenComplete(Action, [this, Done, CustomId]() + { + FNakamaAccountCustom Account; + Account.Id = CustomId; + NakamaApi::AuthenticateCustom(Client, Account, false, TEXT(""), + [this, Done](const FNakamaSession& Result) + { + Session = Result; + TestTrue("Session has token", !Result.Token.IsEmpty()); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("Re-auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); + + Describe("AuthenticateEmail", [this]() + { + LatentIt("should authenticate with email", [this](const FDoneDelegate& Done) + { + FString Email = FString::Printf(TEXT("test_%s@example.com"), *GenerateShortId()); + FString Password = TEXT("password123!"); + + FNakamaAccountEmail Account; + Account.Email = Email; + Account.Password = Password; + auto* Action = UNakamaClientAuthenticateEmail::AuthenticateEmail( + GetTransientPackage(), Client, Account, true, TEXT("")); + Action->Activate(); + + // Verify: re-auth with create=false should succeed + VerifyWhenComplete(Action, [this, Done, Email, Password]() + { + FNakamaAccountEmail Account; + Account.Email = Email; + Account.Password = Password; + NakamaApi::AuthenticateEmail(Client, Account, false, TEXT(""), + [this, Done](const FNakamaSession& Result) + { + Session = Result; + TestTrue("Session has token", !Result.Token.IsEmpty()); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("Re-auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); + + Describe("AuthenticateDevice", [this]() + { + LatentIt("should authenticate with device ID", [this](const FDoneDelegate& Done) + { + FString DeviceId = GenerateId(); + FNakamaAccountDevice Account; + Account.Id = DeviceId; + auto* Action = UNakamaClientAuthenticateDevice::AuthenticateDevice( + GetTransientPackage(), Client, Account, true, TEXT("")); + Action->Activate(); + + // Verify: re-auth with create=false should succeed + VerifyWhenComplete(Action, [this, Done, DeviceId]() + { + FNakamaAccountDevice Account; + Account.Id = DeviceId; + NakamaApi::AuthenticateDevice(Client, Account, false, TEXT(""), + [this, Done](const FNakamaSession& Result) + { + Session = Result; + TestTrue("Session has token", !Result.Token.IsEmpty()); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("Re-auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); +} + +// ============================================================================ +// SESSION TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaBPSessionSpec, "IntegrationTests.NakamaBlueprintTests.Session", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + + FNakamaClientConfig Client; + FNakamaSession Session; + + static const FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7350; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaBPSessionSpec) + +const FString FNakamaBPSessionSpec::ServerKey = TEXT("defaultkey"); +const FString FNakamaBPSessionSpec::Host = TEXT("127.0.0.1"); + +void FNakamaBPSessionSpec::Define() +{ + BeforeEach([this]() + { + Client = FNakamaClientConfig{ServerKey, Host, Port, false}; + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + NakamaApi::AuthenticateCustom(Client, Account, true, TEXT(""), + [this, Done](const FNakamaSession& Result) + { + Session = Result; + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("Setup auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + NakamaApi::DeleteAccount(Client, Session, + [Done]() { Done.Execute(); }, + [Done](const FNakamaError&) { Done.Execute(); } + ); + }); + + Describe("SessionRefresh", [this]() + { + LatentIt("should refresh session", [this](const FDoneDelegate& Done) + { + auto* Action = UNakamaClientSessionRefresh::SessionRefresh( + GetTransientPackage(), Client, Session.RefreshToken, TMap()); + Action->Activate(); + + // Verify: original session token still works for API call + VerifyWhenComplete(Action, [this, Done]() + { + NakamaApi::GetAccount(Client, Session, + [this, Done](const FNakamaAccount& Result) + { + TestTrue("Account retrieved", !Result.User.Id.IsEmpty()); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("GetAccount failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); + + Describe("SessionLogout", [this]() + { + LatentIt("should logout session", [this](const FDoneDelegate& Done) + { + auto* Action = UNakamaClientSessionLogout::SessionLogout( + GetTransientPackage(), Client, Session, Session.Token, Session.RefreshToken); + Action->Activate(); + + // Verify: action completed without crash; server is still responsive + VerifyWhenComplete(Action, [this, Done]() + { + NakamaApi::Healthcheck(Client, Session, + [Done]() { Done.Execute(); }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("Healthcheck failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); +} + +// ============================================================================ +// ACCOUNT TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaBPAccountSpec, "IntegrationTests.NakamaBlueprintTests.Account", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + + FNakamaClientConfig Client; + FNakamaSession Session; + FString UserId; + + static const FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7350; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaBPAccountSpec) + +const FString FNakamaBPAccountSpec::ServerKey = TEXT("defaultkey"); +const FString FNakamaBPAccountSpec::Host = TEXT("127.0.0.1"); + +void FNakamaBPAccountSpec::Define() +{ + BeforeEach([this]() + { + Client = FNakamaClientConfig{ServerKey, Host, Port, false}; + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + NakamaApi::AuthenticateCustom(Client, Account, true, TEXT(""), + [this, Done](const FNakamaSession& Result) + { + Session = Result; + NakamaApi::GetAccount(Client, Session, + [this, Done](const FNakamaAccount& AccResult) + { + UserId = AccResult.User.Id; + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("GetAccount failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("Setup auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + NakamaApi::DeleteAccount(Client, Session, + [Done]() { Done.Execute(); }, + [Done](const FNakamaError&) { Done.Execute(); } + ); + }); + + Describe("GetAccount", [this]() + { + LatentIt("should get account", [this](const FDoneDelegate& Done) + { + auto* Action = UNakamaClientGetAccount::GetAccount(GetTransientPackage(), Client, Session); + Action->Activate(); + + // Verify via C++ GetAccount + VerifyWhenComplete(Action, [this, Done]() + { + NakamaApi::GetAccount(Client, Session, + [this, Done](const FNakamaAccount& Result) + { + TestEqual("User ID matches", Result.User.Id, UserId); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("GetAccount failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); + + Describe("UpdateAccount", [this]() + { + LatentIt("should update and verify display name", [this](const FDoneDelegate& Done) + { + FString NewDisplayName = TEXT("BPTestUser"); + + auto* Action = UNakamaClientUpdateAccount::UpdateAccount( + GetTransientPackage(), Client, Session, + TEXT(""), NewDisplayName, TEXT(""), TEXT(""), TEXT(""), TEXT("")); + Action->Activate(); + + // Verify via C++ GetAccount + VerifyWhenComplete(Action, [this, Done, NewDisplayName]() + { + NakamaApi::GetAccount(Client, Session, + [this, Done, NewDisplayName](const FNakamaAccount& Result) + { + TestEqual("Display name updated", Result.User.DisplayName, NewDisplayName); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("GetAccount failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); + + Describe("GetUsers", [this]() + { + LatentIt("should get users by ID", [this](const FDoneDelegate& Done) + { + TArray Ids = { UserId }; + TArray Usernames; + TArray FacebookIds; + + auto* Action = UNakamaClientGetUsers::GetUsers( + GetTransientPackage(), Client, Session, Ids, Usernames, FacebookIds); + Action->Activate(); + + // Verify via C++ GetUsers + VerifyWhenComplete(Action, [this, Done]() + { + NakamaApi::GetUsers(Client, Session, { UserId }, {}, {}, + [this, Done](const FNakamaUsers& Result) + { + TestTrue("Got at least one user", Result.Users.Num() > 0); + if (Result.Users.Num() > 0) + { + TestEqual("User ID matches", Result.Users[0].Id, UserId); + } + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("GetUsers failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); +} + +// ============================================================================ +// FRIENDS TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaBPFriendsSpec, "IntegrationTests.NakamaBlueprintTests.Friends", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + + FNakamaClientConfig Client; + FNakamaSession Session; + FString UserId; + + FNakamaClientConfig FriendClient; + FNakamaSession FriendSession; + FString FriendUserId; + + static const FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7350; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaBPFriendsSpec) + +const FString FNakamaBPFriendsSpec::ServerKey = TEXT("defaultkey"); +const FString FNakamaBPFriendsSpec::Host = TEXT("127.0.0.1"); + +void FNakamaBPFriendsSpec::Define() +{ + BeforeEach([this]() + { + Client = FNakamaClientConfig{ServerKey, Host, Port, false}; + + FriendClient = FNakamaClientConfig{ServerKey, Host, Port, false}; + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + NakamaApi::AuthenticateCustom(Client, Account, true, TEXT(""), + [this, Done](const FNakamaSession& Result) + { + Session = Result; + NakamaApi::GetAccount(Client, Session, + [this, Done](const FNakamaAccount& AccResult) + { + UserId = AccResult.User.Id; + + FNakamaAccountCustom FriendAccount; + FriendAccount.Id = GenerateId(); + + NakamaApi::AuthenticateCustom(FriendClient, FriendAccount, true, TEXT(""), + [this, Done](const FNakamaSession& FResult) + { + FriendSession = FResult; + NakamaApi::GetAccount(FriendClient, FriendSession, + [this, Done](const FNakamaAccount& FAccResult) + { + FriendUserId = FAccResult.User.Id; + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("Friend GetAccount failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("Friend auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("GetAccount failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("Setup auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + NakamaApi::DeleteAccount(Client, Session, + [this, Done]() + { + NakamaApi::DeleteAccount(Client, FriendSession, + [Done]() { Done.Execute(); }, + [Done](const FNakamaError&) { Done.Execute(); } + ); + }, + [this, Done](const FNakamaError&) + { + NakamaApi::DeleteAccount(Client, FriendSession, + [Done]() { Done.Execute(); }, + [Done](const FNakamaError&) { Done.Execute(); } + ); + } + ); + }); + + Describe("AddFriends", [this]() + { + LatentIt("should add a friend and verify via list", [this](const FDoneDelegate& Done) + { + TArray Ids = { FriendUserId }; + TArray Usernames; + + auto* Action = UNakamaClientAddFriends::AddFriends( + GetTransientPackage(), Client, Session, Ids, Usernames, TEXT("")); + Action->Activate(); + + // Verify via C++ ListFriends + VerifyWhenComplete(Action, [this, Done]() + { + NakamaApi::ListFriends(Client, Session, 10, {}, TEXT(""), + [this, Done](const FNakamaFriendList& Result) + { + TestTrue("Friend list is not empty", Result.Friends.Num() > 0); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("ListFriends failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); + + Describe("ListFriends", [this]() + { + LatentIt("should list friends after adding one", [this](const FDoneDelegate& Done) + { + // Setup: add friend via C++ + NakamaApi::AddFriends(Client, Session, { FriendUserId }, {}, TEXT(""), + [this, Done]() + { + // Fire BP ListFriends + auto* Action = UNakamaClientListFriends::ListFriends( + GetTransientPackage(), Client, Session, 10, {}, TEXT("")); + Action->Activate(); + + // Verify via C++ + VerifyWhenComplete(Action, [this, Done]() + { + NakamaApi::ListFriends(Client, Session, 10, {}, TEXT(""), + [this, Done](const FNakamaFriendList& Result) + { + TestTrue("Friend list has entries", Result.Friends.Num() > 0); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("ListFriends failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("AddFriends setup failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + + Describe("BlockFriends", [this]() + { + LatentIt("should block a friend", [this](const FDoneDelegate& Done) + { + TArray Ids = { FriendUserId }; + TArray Usernames; + + auto* Action = UNakamaClientBlockFriends::BlockFriends( + GetTransientPackage(), Client, Session, Ids, Usernames); + Action->Activate(); + + // Verify: blocked friend appears in friend list with state=3 (blocked) + VerifyWhenComplete(Action, [this, Done]() + { + NakamaApi::ListFriends(Client, Session, 10, 3, TEXT(""), + [this, Done](const FNakamaFriendList& Result) + { + TestTrue("Blocked friend list has entries", Result.Friends.Num() > 0); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("ListFriends failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); + + Describe("DeleteFriends", [this]() + { + LatentIt("should delete a friend", [this](const FDoneDelegate& Done) + { + // Setup: add friend via C++ + NakamaApi::AddFriends(Client, Session, { FriendUserId }, {}, TEXT(""), + [this, Done]() + { + // Fire BP DeleteFriends + TArray Ids = { FriendUserId }; + TArray Usernames; + + auto* Action = UNakamaClientDeleteFriends::DeleteFriends( + GetTransientPackage(), Client, Session, Ids, Usernames); + Action->Activate(); + + // Verify: friend list is now empty + VerifyWhenComplete(Action, [this, Done]() + { + NakamaApi::ListFriends(Client, Session, 10, 0, TEXT(""), + [this, Done](const FNakamaFriendList& Result) + { + TestEqual("Friend list is empty", Result.Friends.Num(), 0); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("ListFriends failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("AddFriends setup failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); +} + +// ============================================================================ +// GROUPS TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaBPGroupsSpec, "IntegrationTests.NakamaBlueprintTests.Groups", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + + FNakamaClientConfig Client; + FNakamaSession Session; + FString UserId; + + + FNakamaSession Session2; + FString UserId2; + + FString GroupId; + FString GroupName; + + static const FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7350; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateShortId() { return FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaBPGroupsSpec) + +const FString FNakamaBPGroupsSpec::ServerKey = TEXT("defaultkey"); +const FString FNakamaBPGroupsSpec::Host = TEXT("127.0.0.1"); + +void FNakamaBPGroupsSpec::Define() +{ + BeforeEach([this]() + { + Client = FNakamaClientConfig{ServerKey, Host, Port, false}; + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account1; + Account1.Id = GenerateId(); + + NakamaApi::AuthenticateCustom(Client, Account1, true, TEXT(""), + [this, Done](const FNakamaSession& Result) + { + Session = Result; + NakamaApi::GetAccount(Client, Session, + [this, Done](const FNakamaAccount& AccResult) + { + UserId = AccResult.User.Id; + + FNakamaAccountCustom Account2; + Account2.Id = GenerateId(); + + NakamaApi::AuthenticateCustom(Client, Account2, true, TEXT(""), + [this, Done](const FNakamaSession& Result2) + { + Session2 = Result2; + NakamaApi::GetAccount(Client, Session2, + [this, Done](const FNakamaAccount& AccResult2) + { + UserId2 = AccResult2.User.Id; + + // Create a group as user1 + GroupName = FString::Printf(TEXT("bp_grp_%s"), *GenerateShortId()); + NakamaApi::CreateGroup(Client, Session, GroupName, TEXT("Test group"), + TEXT("en"), TEXT(""), true, 100, + [this, Done](const FNakamaGroup& Group) + { + GroupId = Group.Id; + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("CreateGroup failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("GetAccount2 failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("Auth2 failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("GetAccount failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("Auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + NakamaApi::DeleteGroup(Client, Session, GroupId, + [this, Done]() + { + NakamaApi::DeleteAccount(Client, Session, + [this, Done]() + { + NakamaApi::DeleteAccount(Client, Session2, + [Done]() { Done.Execute(); }, + [Done](const FNakamaError&) { Done.Execute(); } + ); + }, + [this, Done](const FNakamaError&) + { + NakamaApi::DeleteAccount(Client, Session2, + [Done]() { Done.Execute(); }, + [Done](const FNakamaError&) { Done.Execute(); } + ); + } + ); + }, + [this, Done](const FNakamaError&) + { + NakamaApi::DeleteAccount(Client, Session, + [this, Done]() + { + NakamaApi::DeleteAccount(Client, Session2, + [Done]() { Done.Execute(); }, + [Done](const FNakamaError&) { Done.Execute(); } + ); + }, + [this, Done](const FNakamaError&) + { + NakamaApi::DeleteAccount(Client, Session2, + [Done]() { Done.Execute(); }, + [Done](const FNakamaError&) { Done.Execute(); } + ); + } + ); + } + ); + }); + + Describe("CreateGroup", [this]() + { + LatentIt("should create a group", [this](const FDoneDelegate& Done) + { + FString NewGroupName = FString::Printf(TEXT("bp_new_%s"), *GenerateShortId()); + + auto* Action = UNakamaClientCreateGroup::CreateGroup( + GetTransientPackage(), Client, Session, + NewGroupName, TEXT("Test group"), TEXT("en"), TEXT(""), true, 100); + Action->Activate(); + + // Verify via C++ ListUserGroups (ListGroups always sends open filter which conflicts with name) + VerifyWhenComplete(Action, [this, Done, NewGroupName]() + { + NakamaApi::ListUserGroups(Client, Session, UserId, 100, 0, TEXT(""), + [this, Done, NewGroupName](const FNakamaUserGroupList& Result) + { + FString NewGroupId; + bool Found = false; + for (const auto& UG : Result.UserGroups) + { + if (UG.Group.Name == NewGroupName) + { + Found = true; + NewGroupId = UG.Group.Id; + break; + } + } + TestTrue("Created group found in user groups", Found); + if (!NewGroupId.IsEmpty()) + { + NakamaApi::DeleteGroup(Client, Session, NewGroupId, + [Done]() { Done.Execute(); }, + [Done](const FNakamaError&) { Done.Execute(); } + ); + } + else + { + Done.Execute(); + } + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("ListUserGroups failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); + + Describe("UpdateGroup", [this]() + { + LatentIt("should update a group", [this](const FDoneDelegate& Done) + { + FString NewDesc = TEXT("Updated description"); + + auto* Action = UNakamaClientUpdateGroup::UpdateGroup( + GetTransientPackage(), Client, Session, + GroupId, GroupName, NewDesc, TEXT("en"), TEXT(""), true); + Action->Activate(); + + // Verify: group still exists and is accessible + VerifyWhenComplete(Action, [this, Done]() + { + NakamaApi::ListGroupUsers(Client, Session, GroupId, 10, {}, TEXT(""), + [this, Done](const FNakamaGroupUserList& Result) + { + TestTrue("Group still has users after update", Result.GroupUsers.Num() > 0); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("ListGroupUsers failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); + + Describe("DeleteGroup", [this]() + { + LatentIt("should delete a group", [this](const FDoneDelegate& Done) + { + auto* Action = UNakamaClientDeleteGroup::DeleteGroup( + GetTransientPackage(), Client, Session, GroupId); + Action->Activate(); + + // Verify: group no longer in user's groups + VerifyWhenComplete(Action, [this, Done]() + { + NakamaApi::ListUserGroups(Client, Session, UserId, 100, 0, TEXT(""), + [this, Done](const FNakamaUserGroupList& Result) + { + bool Found = false; + for (const auto& UG : Result.UserGroups) + { + if (UG.Group.Id == GroupId) + { + Found = true; + break; + } + } + TestFalse("Deleted group not in user groups", Found); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("ListUserGroups failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); + + Describe("ListGroups", [this]() + { + LatentIt("should list groups", [this](const FDoneDelegate& Done) + { + auto* Action = UNakamaClientListGroups::ListGroups( + GetTransientPackage(), Client, Session, + TEXT(""), TEXT(""), 100, TEXT(""), {}, true); + Action->Activate(); + + // Verify via C++ ListGroups + VerifyWhenComplete(Action, [this, Done]() + { + NakamaApi::ListGroups(Client, Session, TEXT(""), TEXT(""), 100, TEXT(""), {}, true, + [this, Done](const FNakamaGroupList& Result) + { + TestTrue("Group list has entries", Result.Groups.Num() > 0); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("ListGroups failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); + + Describe("JoinGroup", [this]() + { + LatentIt("should join a group", [this](const FDoneDelegate& Done) + { + // User2 fires BP JoinGroup on user1's open group + auto* Action = UNakamaClientJoinGroup::JoinGroup( + GetTransientPackage(), Client, Session2, GroupId); + Action->Activate(); + + // Verify via C++ ListGroupUsers + VerifyWhenComplete(Action, [this, Done]() + { + NakamaApi::ListGroupUsers(Client, Session, GroupId, 10, {}, TEXT(""), + [this, Done](const FNakamaGroupUserList& Result) + { + TestTrue("Group has at least 2 users", Result.GroupUsers.Num() >= 2); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("ListGroupUsers failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); + + Describe("AddGroupUsers", [this]() + { + LatentIt("should add users to a group", [this](const FDoneDelegate& Done) + { + TArray UserIds = { UserId2 }; + + auto* Action = UNakamaClientAddGroupUsers::AddGroupUsers( + GetTransientPackage(), Client, Session, GroupId, UserIds); + Action->Activate(); + + // Verify via C++ ListGroupUsers + VerifyWhenComplete(Action, [this, Done]() + { + NakamaApi::ListGroupUsers(Client, Session, GroupId, 10, {}, TEXT(""), + [this, Done](const FNakamaGroupUserList& Result) + { + TestTrue("Group has at least 2 users", Result.GroupUsers.Num() >= 2); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("ListGroupUsers failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); + + Describe("KickGroupUsers", [this]() + { + LatentIt("should kick users from a group", [this](const FDoneDelegate& Done) + { + // Setup: add user2 to group via C++ + NakamaApi::AddGroupUsers(Client, Session, GroupId, { UserId2 }, + [this, Done]() + { + // Fire BP KickGroupUsers + TArray UserIds = { UserId2 }; + + auto* Action = UNakamaClientKickGroupUsers::KickGroupUsers( + GetTransientPackage(), Client, Session, GroupId, UserIds); + Action->Activate(); + + // Verify user2 is no longer in group + VerifyWhenComplete(Action, [this, Done]() + { + NakamaApi::ListGroupUsers(Client, Session, GroupId, 10, {}, TEXT(""), + [this, Done](const FNakamaGroupUserList& Result) + { + bool Found = false; + for (const auto& GU : Result.GroupUsers) + { + if (GU.User.Id == UserId2) + { + Found = true; + break; + } + } + TestFalse("Kicked user not in group", Found); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("ListGroupUsers failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("AddGroupUsers setup failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + + Describe("PromoteGroupUsers", [this]() + { + LatentIt("should promote users in a group", [this](const FDoneDelegate& Done) + { + // Setup: add user2 to group via C++ + NakamaApi::AddGroupUsers(Client, Session, GroupId, { UserId2 }, + [this, Done]() + { + // Fire BP PromoteGroupUsers + TArray UserIds = { UserId2 }; + + auto* Action = UNakamaClientPromoteGroupUsers::PromoteGroupUsers( + GetTransientPackage(), Client, Session, GroupId, UserIds); + Action->Activate(); + + // Verify: user2 is still in group (promotion doesn't remove) + VerifyWhenComplete(Action, [this, Done]() + { + NakamaApi::ListGroupUsers(Client, Session, GroupId, 10, {}, TEXT(""), + [this, Done](const FNakamaGroupUserList& Result) + { + bool Found = false; + for (const auto& GU : Result.GroupUsers) + { + if (GU.User.Id == UserId2) + { + Found = true; + break; + } + } + TestTrue("Promoted user still in group", Found); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("ListGroupUsers failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("AddGroupUsers setup failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + + Describe("LeaveGroup", [this]() + { + LatentIt("should leave a group", [this](const FDoneDelegate& Done) + { + // Setup: user2 joins group via C++ + NakamaApi::JoinGroup(Client, Session2, GroupId, + [this, Done]() + { + // Fire BP LeaveGroup as user2 + auto* Action = UNakamaClientLeaveGroup::LeaveGroup( + GetTransientPackage(), Client, Session2, GroupId); + Action->Activate(); + + // Verify user2 no longer in group + VerifyWhenComplete(Action, [this, Done]() + { + NakamaApi::ListGroupUsers(Client, Session, GroupId, 10, {}, TEXT(""), + [this, Done](const FNakamaGroupUserList& Result) + { + bool Found = false; + for (const auto& GU : Result.GroupUsers) + { + if (GU.User.Id == UserId2) + { + Found = true; + break; + } + } + TestFalse("User2 left the group", Found); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("ListGroupUsers failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("JoinGroup setup failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + + Describe("ListGroupUsers", [this]() + { + LatentIt("should list group users", [this](const FDoneDelegate& Done) + { + auto* Action = UNakamaClientListGroupUsers::ListGroupUsers( + GetTransientPackage(), Client, Session, GroupId, 10, 0, TEXT("")); + Action->Activate(); + + // Verify via C++ + VerifyWhenComplete(Action, [this, Done]() + { + NakamaApi::ListGroupUsers(Client, Session, GroupId, 10, {}, TEXT(""), + [this, Done](const FNakamaGroupUserList& Result) + { + TestTrue("Group has at least one user (owner)", Result.GroupUsers.Num() > 0); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("ListGroupUsers failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); + + Describe("ListUserGroups", [this]() + { + LatentIt("should list user groups", [this](const FDoneDelegate& Done) + { + auto* Action = UNakamaClientListUserGroups::ListUserGroups( + GetTransientPackage(), Client, Session, UserId, 10, 0, TEXT("")); + Action->Activate(); + + // Verify via C++ + VerifyWhenComplete(Action, [this, Done]() + { + NakamaApi::ListUserGroups(Client, Session, UserId, 10, 0, TEXT(""), + [this, Done](const FNakamaUserGroupList& Result) + { + TestTrue("User has at least one group", Result.UserGroups.Num() > 0); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("ListUserGroups failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); +} + +// ============================================================================ +// STORAGE TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaBPStorageSpec, "IntegrationTests.NakamaBlueprintTests.Storage", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + + FNakamaClientConfig Client; + FNakamaSession Session; + FString UserId; + + static const FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7350; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaBPStorageSpec) + +const FString FNakamaBPStorageSpec::ServerKey = TEXT("defaultkey"); +const FString FNakamaBPStorageSpec::Host = TEXT("127.0.0.1"); + +void FNakamaBPStorageSpec::Define() +{ + BeforeEach([this]() + { + Client = FNakamaClientConfig{ServerKey, Host, Port, false}; + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + NakamaApi::AuthenticateCustom(Client, Account, true, TEXT(""), + [this, Done](const FNakamaSession& Result) + { + Session = Result; + NakamaApi::GetAccount(Client, Session, + [this, Done](const FNakamaAccount& AccResult) + { + UserId = AccResult.User.Id; + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("GetAccount failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("Setup auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + NakamaApi::DeleteAccount(Client, Session, + [Done]() { Done.Execute(); }, + [Done](const FNakamaError&) { Done.Execute(); } + ); + }); + + Describe("WriteStorageObjects", [this]() + { + LatentIt("should write and read back storage objects", [this](const FDoneDelegate& Done) + { + FString Key = FString::Printf(TEXT("key_%s"), *GenerateId()); + + FNakamaWriteStorageObject Obj; + Obj.Collection = TEXT("bp_test"); + Obj.Key = Key; + Obj.Value = TEXT("{\"score\":42}"); + Obj.PermissionRead = 1; + Obj.PermissionWrite = 1; + + TArray Objects = { Obj }; + + auto* Action = UNakamaClientWriteStorageObjects::WriteStorageObjects( + GetTransientPackage(), Client, Session, Objects); + Action->Activate(); + + // Verify via C++ ReadStorageObjects + VerifyWhenComplete(Action, [this, Done, Key]() + { + FNakamaReadStorageObjectId ReadId; + ReadId.Collection = TEXT("bp_test"); + ReadId.Key = Key; + ReadId.UserId = UserId; + + NakamaApi::ReadStorageObjects(Client, Session, { ReadId }, + [this, Done](const FNakamaStorageObjects& Result) + { + TestTrue("Got at least one object", Result.Objects.Num() > 0); + if (Result.Objects.Num() > 0) + { + TestTrue("Value contains score", Result.Objects[0].Value.Contains(TEXT("42"))); + } + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("ReadStorage failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + + LatentIt("should write then verify value content", [this](const FDoneDelegate& Done) + { + FString Key = FString::Printf(TEXT("key_%s"), *GenerateId()); + + FNakamaWriteStorageObject Obj; + Obj.Collection = TEXT("bp_test"); + Obj.Key = Key; + Obj.Value = TEXT("{\"level\":99,\"name\":\"hero\"}"); + Obj.PermissionRead = 1; + Obj.PermissionWrite = 1; + + TArray Objects = { Obj }; + + auto* Action = UNakamaClientWriteStorageObjects::WriteStorageObjects( + GetTransientPackage(), Client, Session, Objects); + Action->Activate(); + + // Verify via C++ ReadStorageObjects + VerifyWhenComplete(Action, [this, Done, Key]() + { + FNakamaReadStorageObjectId ReadId; + ReadId.Collection = TEXT("bp_test"); + ReadId.Key = Key; + ReadId.UserId = UserId; + + NakamaApi::ReadStorageObjects(Client, Session, { ReadId }, + [this, Done](const FNakamaStorageObjects& Result) + { + TestTrue("Got at least one object", Result.Objects.Num() > 0); + if (Result.Objects.Num() > 0) + { + TestTrue("Value contains level", Result.Objects[0].Value.Contains(TEXT("99"))); + TestTrue("Value contains name", Result.Objects[0].Value.Contains(TEXT("hero"))); + } + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("ReadStorage failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); + + Describe("ReadStorageObjects", [this]() + { + LatentIt("should read storage objects", [this](const FDoneDelegate& Done) + { + FString Key = FString::Printf(TEXT("key_%s"), *GenerateId()); + + // Setup: write via C++ + FNakamaWriteStorageObject Obj; + Obj.Collection = TEXT("bp_test"); + Obj.Key = Key; + Obj.Value = TEXT("{\"data\":true}"); + Obj.PermissionRead = 1; + Obj.PermissionWrite = 1; + + NakamaApi::WriteStorageObjects(Client, Session, { Obj }, + [this, Done, Key](const FNakamaStorageObjectAcks& Acks) + { + // Fire BP ReadStorageObjects + FNakamaReadStorageObjectId ReadId; + ReadId.Collection = TEXT("bp_test"); + ReadId.Key = Key; + ReadId.UserId = UserId; + + auto* Action = UNakamaClientReadStorageObjects::ReadStorageObjects( + GetTransientPackage(), Client, Session, { ReadId }); + Action->Activate(); + + // Verify via C++ that object exists + VerifyWhenComplete(Action, [this, Done, Key]() + { + FNakamaReadStorageObjectId VerifyId; + VerifyId.Collection = TEXT("bp_test"); + VerifyId.Key = Key; + VerifyId.UserId = UserId; + + NakamaApi::ReadStorageObjects(Client, Session, { VerifyId }, + [this, Done](const FNakamaStorageObjects& Result) + { + TestTrue("Object still readable", Result.Objects.Num() > 0); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("ReadStorage failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("WriteStorage setup failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + + Describe("ListStorageObjects", [this]() + { + LatentIt("should list storage objects", [this](const FDoneDelegate& Done) + { + // Setup: write an object via C++ + FNakamaWriteStorageObject Obj; + Obj.Collection = TEXT("bp_list_test"); + Obj.Key = FString::Printf(TEXT("key_%s"), *GenerateId()); + Obj.Value = TEXT("{\"x\":1}"); + Obj.PermissionRead = 1; + Obj.PermissionWrite = 1; + + NakamaApi::WriteStorageObjects(Client, Session, { Obj }, + [this, Done](const FNakamaStorageObjectAcks& Acks) + { + // Fire BP ListStorageObjects + auto* Action = UNakamaClientListStorageObjects::ListStorageObjects( + GetTransientPackage(), Client, Session, UserId, TEXT("bp_list_test"), 10, TEXT("")); + Action->Activate(); + + // Verify via C++ + VerifyWhenComplete(Action, [this, Done]() + { + NakamaApi::ListStorageObjects(Client, Session, UserId, TEXT("bp_list_test"), 10, TEXT(""), + [this, Done](const FNakamaStorageObjectList& Result) + { + TestTrue("Listed at least one object", Result.Objects.Num() > 0); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("ListStorage failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("WriteStorage setup failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + + Describe("DeleteStorageObjects", [this]() + { + LatentIt("should delete storage objects", [this](const FDoneDelegate& Done) + { + FString Key = FString::Printf(TEXT("key_%s"), *GenerateId()); + + // Setup: write an object via C++ + FNakamaWriteStorageObject Obj; + Obj.Collection = TEXT("bp_del_test"); + Obj.Key = Key; + Obj.Value = TEXT("{\"del\":true}"); + Obj.PermissionRead = 1; + Obj.PermissionWrite = 1; + + NakamaApi::WriteStorageObjects(Client, Session, { Obj }, + [this, Done, Key](const FNakamaStorageObjectAcks& Acks) + { + // Fire BP DeleteStorageObjects + FNakamaDeleteStorageObjectId DelId; + DelId.Collection = TEXT("bp_del_test"); + DelId.Key = Key; + DelId.Version = Acks.Acks.Num() > 0 ? Acks.Acks[0].Version : TEXT(""); + + auto* Action = UNakamaClientDeleteStorageObjects::DeleteStorageObjects( + GetTransientPackage(), Client, Session, { DelId }); + Action->Activate(); + + // Verify: object is gone + VerifyWhenComplete(Action, [this, Done, Key]() + { + FNakamaReadStorageObjectId ReadId; + ReadId.Collection = TEXT("bp_del_test"); + ReadId.Key = Key; + ReadId.UserId = UserId; + + NakamaApi::ReadStorageObjects(Client, Session, { ReadId }, + [this, Done](const FNakamaStorageObjects& Result) + { + TestEqual("Object deleted", Result.Objects.Num(), 0); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("ReadStorage failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("WriteStorage setup failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); +} + +// ============================================================================ +// LINK / UNLINK TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaBPLinkSpec, "IntegrationTests.NakamaBlueprintTests.Link", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + + FNakamaClientConfig Client; + FNakamaSession Session; + + static const FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7350; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateShortId() { return FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaBPLinkSpec) + +const FString FNakamaBPLinkSpec::ServerKey = TEXT("defaultkey"); +const FString FNakamaBPLinkSpec::Host = TEXT("127.0.0.1"); + +void FNakamaBPLinkSpec::Define() +{ + BeforeEach([this]() + { + Client = FNakamaClientConfig{ServerKey, Host, Port, false}; + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + NakamaApi::AuthenticateCustom(Client, Account, true, TEXT(""), + [this, Done](const FNakamaSession& Result) + { + Session = Result; + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("Setup auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + NakamaApi::DeleteAccount(Client, Session, + [Done]() { Done.Execute(); }, + [Done](const FNakamaError&) { Done.Execute(); } + ); + }); + + Describe("LinkCustom", [this]() + { + LatentIt("should link a custom ID", [this](const FDoneDelegate& Done) + { + FString NewId = GenerateId(); + + auto* Action = UNakamaClientLinkCustom::LinkCustom( + GetTransientPackage(), Client, Session, NewId, TMap()); + Action->Activate(); + + // Verify: can authenticate with the linked ID + VerifyWhenComplete(Action, [this, Done, NewId]() + { + FNakamaAccountCustom LinkedAccount; + LinkedAccount.Id = NewId; + + NakamaApi::AuthenticateCustom(Client, LinkedAccount, false, TEXT(""), + [this, Done](const FNakamaSession& Result) + { + TestTrue("Linked custom auth succeeded", !Result.Token.IsEmpty()); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("Linked auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); + + Describe("UnlinkCustom", [this]() + { + LatentIt("should unlink a custom ID", [this](const FDoneDelegate& Done) + { + FString ExtraId = GenerateId(); + FString DeviceId = GenerateId(); + + // Setup: link a device first (so there's a fallback auth method), + // then link an extra custom ID (replaces original) + NakamaApi::LinkDevice(Client, Session, DeviceId, TMap(), + [this, Done, ExtraId]() + { + NakamaApi::LinkCustom(Client, Session, ExtraId, TMap(), + [this, Done, ExtraId]() + { + // Fire BP UnlinkCustom + auto* Action = UNakamaClientUnlinkCustom::UnlinkCustom( + GetTransientPackage(), Client, Session, ExtraId, TMap()); + Action->Activate(); + + // Verify: can no longer auth with the unlinked ID + VerifyWhenComplete(Action, [this, Done, ExtraId]() + { + FNakamaAccountCustom UnlinkedAccount; + UnlinkedAccount.Id = ExtraId; + + NakamaApi::AuthenticateCustom(Client, UnlinkedAccount, false, TEXT(""), + [this, Done](const FNakamaSession& Result) + { + AddError(TEXT("Auth should have failed for unlinked ID")); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + // Expected: auth fails because ID is unlinked + TestTrue("Unlinked ID auth rejected", true); + Done.Execute(); + } + ); + }); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("LinkCustom setup failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("LinkDevice setup failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + + Describe("LinkDevice", [this]() + { + LatentIt("should link a device ID", [this](const FDoneDelegate& Done) + { + FString DeviceId = GenerateId(); + + auto* Action = UNakamaClientLinkDevice::LinkDevice( + GetTransientPackage(), Client, Session, DeviceId, TMap()); + Action->Activate(); + + // Verify: can authenticate with the linked device ID + VerifyWhenComplete(Action, [this, Done, DeviceId]() + { + FNakamaAccountDevice LinkedDevice; + LinkedDevice.Id = DeviceId; + + NakamaApi::AuthenticateDevice(Client, LinkedDevice, false, TEXT(""), + [this, Done](const FNakamaSession& Result) + { + TestTrue("Linked device auth succeeded", !Result.Token.IsEmpty()); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("Linked device auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); + + Describe("UnlinkDevice", [this]() + { + LatentIt("should unlink a device ID", [this](const FDoneDelegate& Done) + { + FString DeviceId = GenerateId(); + + // Setup: link device via C++ + NakamaApi::LinkDevice(Client, Session, DeviceId, TMap(), + [this, Done, DeviceId]() + { + // Fire BP UnlinkDevice + auto* Action = UNakamaClientUnlinkDevice::UnlinkDevice( + GetTransientPackage(), Client, Session, DeviceId, TMap()); + Action->Activate(); + + // Verify: can no longer auth with the unlinked device + VerifyWhenComplete(Action, [this, Done, DeviceId]() + { + FNakamaAccountDevice UnlinkedDevice; + UnlinkedDevice.Id = DeviceId; + + NakamaApi::AuthenticateDevice(Client, UnlinkedDevice, false, TEXT(""), + [this, Done](const FNakamaSession& Result) + { + AddError(TEXT("Auth should have failed for unlinked device")); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + TestTrue("Unlinked device auth rejected", true); + Done.Execute(); + } + ); + }); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("LinkDevice setup failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + + Describe("LinkEmail", [this]() + { + LatentIt("should link an email", [this](const FDoneDelegate& Done) + { + FString Email = FString::Printf(TEXT("link_%s@example.com"), *GenerateShortId()); + FString Password = TEXT("password123!"); + + auto* Action = UNakamaClientLinkEmail::LinkEmail( + GetTransientPackage(), Client, Session, Email, Password, TMap()); + Action->Activate(); + + // Verify: can authenticate with the linked email + VerifyWhenComplete(Action, [this, Done, Email, Password]() + { + FNakamaAccountEmail LinkedEmail; + LinkedEmail.Email = Email; + LinkedEmail.Password = Password; + + NakamaApi::AuthenticateEmail(Client, LinkedEmail, false, TEXT(""), + [this, Done](const FNakamaSession& Result) + { + TestTrue("Linked email auth succeeded", !Result.Token.IsEmpty()); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("Linked email auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); + + Describe("UnlinkEmail", [this]() + { + LatentIt("should unlink an email", [this](const FDoneDelegate& Done) + { + FString Email = FString::Printf(TEXT("unlink_%s@example.com"), *GenerateShortId()); + FString Password = TEXT("password123!"); + + // Setup: link email via C++ + NakamaApi::LinkEmail(Client, Session, Email, Password, TMap(), + [this, Done, Email, Password]() + { + // Fire BP UnlinkEmail + auto* Action = UNakamaClientUnlinkEmail::UnlinkEmail( + GetTransientPackage(), Client, Session, Email, Password, TMap()); + Action->Activate(); + + // Verify: can no longer auth with the unlinked email + VerifyWhenComplete(Action, [this, Done, Email, Password]() + { + FNakamaAccountEmail UnlinkedEmail; + UnlinkedEmail.Email = Email; + UnlinkedEmail.Password = Password; + + NakamaApi::AuthenticateEmail(Client, UnlinkedEmail, false, TEXT(""), + [this, Done](const FNakamaSession& Result) + { + AddError(TEXT("Auth should have failed for unlinked email")); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + TestTrue("Unlinked email auth rejected", true); + Done.Execute(); + } + ); + }); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("LinkEmail setup failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); +} + +// ============================================================================ +// NOTIFICATIONS TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaBPNotificationsSpec, "IntegrationTests.NakamaBlueprintTests.Notifications", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + + FNakamaClientConfig Client; + FNakamaSession Session; + + static const FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7350; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaBPNotificationsSpec) + +const FString FNakamaBPNotificationsSpec::ServerKey = TEXT("defaultkey"); +const FString FNakamaBPNotificationsSpec::Host = TEXT("127.0.0.1"); + +void FNakamaBPNotificationsSpec::Define() +{ + BeforeEach([this]() + { + Client = FNakamaClientConfig{ServerKey, Host, Port, false}; + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + NakamaApi::AuthenticateCustom(Client, Account, true, TEXT(""), + [this, Done](const FNakamaSession& Result) + { + Session = Result; + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("Setup auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + NakamaApi::DeleteAccount(Client, Session, + [Done]() { Done.Execute(); }, + [Done](const FNakamaError&) { Done.Execute(); } + ); + }); + + Describe("ListNotifications", [this]() + { + LatentIt("should list notifications", [this](const FDoneDelegate& Done) + { + auto* Action = UNakamaClientListNotifications::ListNotifications( + GetTransientPackage(), Client, Session, 100, TEXT("")); + Action->Activate(); + + // Verify via C++ + VerifyWhenComplete(Action, [this, Done]() + { + NakamaApi::ListNotifications(Client, Session, 100, TEXT(""), + [this, Done](const FNakamaNotificationList& Result) + { + // Fresh account has no notifications; just verify no error + TestTrue("Notification list call succeeded", true); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("ListNotifications failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); +} + +// ============================================================================ +// MATCHES TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaBPMatchesSpec, "IntegrationTests.NakamaBlueprintTests.Matches", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + + FNakamaClientConfig Client; + FNakamaSession Session; + + static const FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7350; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaBPMatchesSpec) + +const FString FNakamaBPMatchesSpec::ServerKey = TEXT("defaultkey"); +const FString FNakamaBPMatchesSpec::Host = TEXT("127.0.0.1"); + +void FNakamaBPMatchesSpec::Define() +{ + BeforeEach([this]() + { + Client = FNakamaClientConfig{ServerKey, Host, Port, false}; + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + NakamaApi::AuthenticateCustom(Client, Account, true, TEXT(""), + [this, Done](const FNakamaSession& Result) + { + Session = Result; + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("Setup auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + NakamaApi::DeleteAccount(Client, Session, + [Done]() { Done.Execute(); }, + [Done](const FNakamaError&) { Done.Execute(); } + ); + }); + + Describe("ListMatches", [this]() + { + LatentIt("should list matches", [this](const FDoneDelegate& Done) + { + auto* Action = UNakamaClientListMatches::ListMatches( + GetTransientPackage(), Client, Session, 10, false, TEXT(""), 0, 100, TEXT("")); + Action->Activate(); + + // Verify via C++ + VerifyWhenComplete(Action, [this, Done]() + { + NakamaApi::ListMatches(Client, Session, 10, false, TEXT(""), 0, 100, TEXT(""), + [this, Done](const FNakamaMatchList& Result) + { + TestTrue("ListMatches call succeeded", true); + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("ListMatches failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); +} + +// ============================================================================ +// EVENTS TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaBPEventsSpec, "IntegrationTests.NakamaBlueprintTests.Events", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + + FNakamaClientConfig Client; + FNakamaSession Session; + + static const FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7350; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaBPEventsSpec) + +const FString FNakamaBPEventsSpec::ServerKey = TEXT("defaultkey"); +const FString FNakamaBPEventsSpec::Host = TEXT("127.0.0.1"); + +void FNakamaBPEventsSpec::Define() +{ + BeforeEach([this]() + { + Client = FNakamaClientConfig{ServerKey, Host, Port, false}; + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + NakamaApi::AuthenticateCustom(Client, Account, true, TEXT(""), + [this, Done](const FNakamaSession& Result) + { + Session = Result; + Done.Execute(); + }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("Setup auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + NakamaApi::DeleteAccount(Client, Session, + [Done]() { Done.Execute(); }, + [Done](const FNakamaError&) { Done.Execute(); } + ); + }); + + Describe("Event", [this]() + { + LatentIt("should send an event", [this](const FDoneDelegate& Done) + { + TMap Properties; + Properties.Add(TEXT("key1"), TEXT("value1")); + + auto* Action = UNakamaClientEvent::Event( + GetTransientPackage(), Client, Session, TEXT("bp_test_event"), FDateTime(), false, Properties); + Action->Activate(); + + // Verify: server is still healthy after event + VerifyWhenComplete(Action, [this, Done]() + { + NakamaApi::Healthcheck(Client, Session, + [Done]() { Done.Execute(); }, + [this, Done](const FNakamaError& Error) + { + AddError(FString::Printf(TEXT("Healthcheck failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); +} diff --git a/IntegrationTests/Source/IntegrationTests/Tests/NakamaRtConnectionTests.cpp b/IntegrationTests/Source/IntegrationTests/Tests/NakamaRtConnectionTests.cpp new file mode 100644 index 000000000..604cd081a --- /dev/null +++ b/IntegrationTests/Source/IntegrationTests/Tests/NakamaRtConnectionTests.cpp @@ -0,0 +1,1107 @@ +/** + * Nakama Realtime (WebSocket) Integration Test Suite + * + * Tests FNakamaRtConnection connection lifecycle, request/response round-trips, + * delegate notifications, reconnection, error paths, and concurrent requests. + * + * Requires a running Nakama server (IntegrationTests/server/docker-compose.yml). + * + * Run with: + * -ExecCmds="Automation RunTests IntegrationTests.NakamaRtConnection" + */ + +#include +#include "Misc/AutomationTest.h" +#include "Misc/Guid.h" +#include "Dom/JsonObject.h" +#include "Nakama.h" +#include "NakamaRtConnection.h" + +// --------------------------------------------------------------------------- +// Shared constants (file-local) +// --------------------------------------------------------------------------- + +namespace +{ + static const FString RtServerKey = TEXT("defaultkey"); + static const FString RtHost = TEXT("127.0.0.1"); + static constexpr int32 RtPort = 7350; + + /** Build connection params with a long ping interval so auto-pings don't + * pollute test assertions. */ + FNakamaWebSocketConnectionParams MakeConnParams(const FNakamaSession& Session, + int32 OverridePort = RtPort) + { + FNakamaWebSocketConnectionParams P; + P.Host = RtHost; + P.Port = OverridePort; + P.Token = Session.Token; + P.PingIntervalSeconds = 60.0f; + P.bUseSSL = false; + return P; + } +} + +/** + * Fails the current test and calls Done when a FNakamaWebSocketResponse carries an + * error. Use inside a .Next() callback that expects success. + */ +#define WSS_FAIL_ON_ERROR(Resp, Done) \ + if ((Resp).ErrorCode != ENakamaWebSocketError::None) { \ + AddError(TEXT("Unexpected realtime error in response")); \ + (Done).Execute(); \ + return; \ + } + +// ============================================================================ +// CONNECTION TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaRtConnectionConnectionSpec, "IntegrationTests.NakamaRtConnectionTests.Connection", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaClientConfig ClientConfig; + FNakamaSession Session; + TSharedPtr RtConn; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaRtConnectionConnectionSpec) + +void FNakamaRtConnectionConnectionSpec::Define() +{ + BeforeEach([this]() + { + ClientConfig = FNakamaClientConfig{ RtServerKey, RtHost, RtPort, false }; + RtConn = MakeShared(); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), GenerateId()) + .Next([this, Done](FNakamaSessionResult Result) + { + if (Result.bIsError) + { + AddError(FString::Printf(TEXT("Auth failed: %s"), *Result.Error.Message)); + Done.Execute(); + return; + } + Session = Result.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (RtConn) + { + RtConn->Close(); + RtConn.Reset(); + } + + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + // ----------------------------------------------------------------------- + // Success path + // ----------------------------------------------------------------------- + + Describe("Success", [this]() + { + LatentIt("should connect with a valid session token", [this](const FDoneDelegate& Done) + { + RtConn->Connect(MakeConnParams(Session)).Next([this, Done](FNakamaWebSocketConnectionResult Result) + { + TestFalse("Connection should succeed (bError = false)", Result.ErrorCode != ENakamaWebSocketError::None); + Done.Execute(); + }); + }); + }); + + // ----------------------------------------------------------------------- + // Invalid token + // ----------------------------------------------------------------------- + + Describe("InvalidToken", [this]() + { + LatentIt("should fail with a malformed token", [this](const FDoneDelegate& Done) + { + FNakamaWebSocketConnectionParams Bad = MakeConnParams(Session); + Bad.Token = TEXT("not.a.valid.jwt"); + + RtConn->Connect(Bad).Next([this, Done](FNakamaWebSocketConnectionResult Result) + { + TestTrue("Connection with invalid token should report error", Result.ErrorCode != ENakamaWebSocketError::None); + Done.Execute(); + }); + }); + + LatentIt("should fail with an empty token", [this](const FDoneDelegate& Done) + { + FNakamaWebSocketConnectionParams Bad = MakeConnParams(Session); + Bad.Token = TEXT(""); + + RtConn->Connect(Bad).Next([this, Done](FNakamaWebSocketConnectionResult Result) + { + TestTrue("Connection with empty token should report error", Result.ErrorCode != ENakamaWebSocketError::None); + Done.Execute(); + }); + }); + }); + + // ----------------------------------------------------------------------- + // Wrong endpoint + // ----------------------------------------------------------------------- + + Describe("WrongEndpoint", [this]() + { + LatentIt("should fail when no server is listening on the target port", [this](const FDoneDelegate& Done) + { + // Port 19999 — nothing should be listening here. + FNakamaWebSocketConnectionParams Bad = MakeConnParams(Session, 19999); + + RtConn->Connect(Bad).Next([this, Done](FNakamaWebSocketConnectionResult Result) + { + TestTrue("Connection to unreachable port should report error", Result.ErrorCode != ENakamaWebSocketError::None); + Done.Execute(); + }); + }); + }); + + // ----------------------------------------------------------------------- + // Reconnection + // ----------------------------------------------------------------------- + + Describe("Reconnect", [this]() + { + LatentIt("should reconnect successfully after an explicit Close", [this](const FDoneDelegate& Done) + { + RtConn->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult First) -> TNakamaFuture + { + TestFalse("First connection should succeed", First.ErrorCode != ENakamaWebSocketError::None); + RtConn->Close(); + return RtConn->Connect(MakeConnParams(Session)); + }) + .Next([this, Done](FNakamaWebSocketConnectionResult Second) + { + TestFalse("Connection after explicit Close should succeed", Second.ErrorCode != ENakamaWebSocketError::None); + Done.Execute(); + }); + }); + + LatentIt("should accept a new Connect while already connected", [this](const FDoneDelegate& Done) + { + // A second Connect while already connected — the subsystem should + // auto-close the old socket and open a new one. + RtConn->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult First) -> TNakamaFuture + { + TestFalse("First connection should succeed", First.ErrorCode != ENakamaWebSocketError::None); + return RtConn->Connect(MakeConnParams(Session)); + }) + .Next([this, Done](FNakamaWebSocketConnectionResult Second) + { + TestFalse("Second Connect while already connected should succeed", Second.ErrorCode != ENakamaWebSocketError::None); + Done.Execute(); + }); + }); + + LatentIt("should be usable after reconnect (ping round-trip)", [this](const FDoneDelegate& Done) + { + RtConn->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult First) -> TNakamaFuture + { + TestFalse("First connection", First.ErrorCode != ENakamaWebSocketError::None); + RtConn->Close(); + return RtConn->Connect(MakeConnParams(Session)); + }) + .Next([this](FNakamaWebSocketConnectionResult Second) -> TNakamaFuture + { + TestFalse("Reconnection", Second.ErrorCode != ENakamaWebSocketError::None); + return RtConn->Send(TEXT("ping"), MakeShared()); + }) + .Next([this, Done](FNakamaWebSocketResponse Resp) + { + TestFalse("Ping after reconnect should succeed", Resp.ErrorCode != ENakamaWebSocketError::None); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// PING TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaRtConnectionPingSpec, "IntegrationTests.NakamaRtConnectionTests.Ping", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaClientConfig ClientConfig; + FNakamaSession Session; + TSharedPtr RtConn; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaRtConnectionPingSpec) + +void FNakamaRtConnectionPingSpec::Define() +{ + BeforeEach([this]() + { + ClientConfig = FNakamaClientConfig{ RtServerKey, RtHost, RtPort, false }; + RtConn = MakeShared(); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), GenerateId()) + .Next([this, Done](FNakamaSessionResult R) + { + if (R.bIsError) { AddError(FString::Printf(TEXT("Auth: %s"), *R.Error.Message)); Done.Execute(); return; } + Session = R.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (RtConn) + { + RtConn->Close(); + RtConn.Reset(); + } + + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + Describe("Single", [this]() + { + LatentIt("should receive a pong response to a single ping", [this](const FDoneDelegate& Done) + { + RtConn->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return RtConn->Send(TEXT("ping"), MakeShared()); + }) + .Next([this, Done](FNakamaWebSocketResponse Resp) + { + TestFalse("Ping should receive a non-error pong response", Resp.ErrorCode != ENakamaWebSocketError::None); + Done.Execute(); + }); + }); + }); + + Describe("AutoPing", [this]() + { + LatentIt("should not accumulate pending requests (memory leak regression)", [this](const FDoneDelegate& Done) + { + // Use a fast ping interval so ~10 pings fire within 1 second. + FNakamaWebSocketConnectionParams P = MakeConnParams(Session); + P.PingIntervalSeconds = 0.1f; + + RtConn->Connect(P).Next([this, Done](FNakamaWebSocketConnectionResult CR) + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + if (CR.ErrorCode != ENakamaWebSocketError::None) { Done.Execute(); return; } + + // After 1 s, ~10 auto-pings will have fired. Requests must still be empty. + TWeakPtr WeakConn = RtConn; + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([this, Done, WeakConn](float) -> bool + { + if (TSharedPtr Conn = WeakConn.Pin()) + { + TestEqual(TEXT("Auto-ping must not leak entries into Requests"), Conn->GetPendingRequestCount(), 0); + } + Done.Execute(); + return false; + }), + 1.0f + ); + }); + }); + }); + + Describe("Concurrent", [this]() + { + LatentIt("should handle multiple concurrent pings and resolve all of them", [this](const FDoneDelegate& Done) + { + RtConn->Connect(MakeConnParams(Session)) + .Next([this, Done](FNakamaWebSocketConnectionResult CR) + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + + // Fire several pings simultaneously; track completions with a shared counter. + constexpr int32 NumPings = 5; + TSharedRef> Remaining = MakeShared>(NumPings); + + for (int32 i = 0; i < NumPings; ++i) + { + RtConn->Send(TEXT("ping"), MakeShared()) + .Next([this, Done, Remaining](FNakamaWebSocketResponse Resp) + { + if (Resp.ErrorCode != ENakamaWebSocketError::None) + { + AddError(TEXT("A concurrent ping returned an error")); + } + if (--(*Remaining) == 0) + { + Done.Execute(); + } + }); + } + }); + }); + + LatentIt("should handle pings interleaved with other requests", [this](const FDoneDelegate& Done) + { + RtConn->Connect(MakeConnParams(Session)) + .Next([this, Done](FNakamaWebSocketConnectionResult CR) + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + + // Send a match_create and two pings simultaneously; all three + // must resolve without error. + TSharedRef> Remaining = MakeShared>(3); + + auto Decrement = [this, Done, Remaining](FNakamaWebSocketResponse Resp) + { + if (Resp.ErrorCode != ENakamaWebSocketError::None) AddError(TEXT("Interleaved request returned an error")); + if (--(*Remaining) == 0) Done.Execute(); + }; + + RtConn->Send(TEXT("match_create"), MakeShared()).Next(Decrement); + RtConn->Send(TEXT("ping"), MakeShared()).Next(Decrement); + RtConn->Send(TEXT("ping"), MakeShared()).Next(Decrement); + }); + }); + }); +} + +// ============================================================================ +// CHANNEL TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaRtConnectionChannelSpec, "IntegrationTests.NakamaRtConnectionTests.Channel", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaClientConfig ClientConfig; + FNakamaSession Session; + TSharedPtr RtConn; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateRoomName(){ return TEXT("test-room-") + FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + + TSharedPtr MakeChannelJoinJson(const FString& RoomName) const + { + TSharedPtr J = MakeShared(); + J->SetStringField(TEXT("target"), RoomName); + J->SetNumberField(TEXT("type"), 1); // 1 = room channel + J->SetBoolField (TEXT("persistence"), false); + J->SetBoolField (TEXT("hidden"), false); + return J; + } + + FString ExtractChannelId(const FNakamaWebSocketResponse& Resp) const + { + FString Id; + if (Resp.Data.IsValid()) + { + Resp.Data->TryGetStringField(TEXT("id"), Id); + } + return Id; + } + +END_DEFINE_SPEC(FNakamaRtConnectionChannelSpec) + +void FNakamaRtConnectionChannelSpec::Define() +{ + BeforeEach([this]() + { + ClientConfig = FNakamaClientConfig{ RtServerKey, RtHost, RtPort, false }; + RtConn = MakeShared(); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), GenerateId()) + .Next([this, Done](FNakamaSessionResult R) + { + if (R.bIsError) { AddError(FString::Printf(TEXT("Auth: %s"), *R.Error.Message)); Done.Execute(); return; } + Session = R.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (RtConn) + { + RtConn->Close(); + RtConn.Reset(); + } + + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + Describe("Join", [this]() + { + LatentIt("should join a room channel and receive a channel response", [this](const FDoneDelegate& Done) + { + RtConn->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return RtConn->Send(TEXT("channel_join"), MakeChannelJoinJson(GenerateRoomName())); + }) + .Next([this, Done](FNakamaWebSocketResponse Resp) + { + WSS_FAIL_ON_ERROR(Resp, Done); + FString ChannelId = ExtractChannelId(Resp); + TestFalse("Joined channel should have a non-empty ID", ChannelId.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("SendMessage", [this]() + { + LatentIt("should send a message to a room channel and receive an ack", [this](const FDoneDelegate& Done) + { + RtConn->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return RtConn->Send(TEXT("channel_join"), MakeChannelJoinJson(GenerateRoomName())); + }) + .Next([this](FNakamaWebSocketResponse JoinResp) -> TNakamaFuture + { + if (JoinResp.ErrorCode != ENakamaWebSocketError::None) + { + AddError(TEXT("channel_join failed")); + return MakeCompletedFuture(JoinResp); + } + FString ChannelId = ExtractChannelId(JoinResp); + TestFalse("Channel ID from join", ChannelId.IsEmpty()); + + TSharedPtr SendJson = MakeShared(); + SendJson->SetStringField(TEXT("channel_id"), ChannelId); + SendJson->SetStringField(TEXT("content"), TEXT("{\"msg\":\"hello\"}")); + return RtConn->Send(TEXT("channel_message_send"), SendJson); + }) + .Next([this, Done](FNakamaWebSocketResponse SendResp) + { + WSS_FAIL_ON_ERROR(SendResp, Done); + TestTrue("Send ack response has data", SendResp.Data.IsValid()); + Done.Execute(); + }); + }); + }); + + Describe("Leave", [this]() + { + LatentIt("should leave a channel successfully", [this](const FDoneDelegate& Done) + { + RtConn->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return RtConn->Send(TEXT("channel_join"), MakeChannelJoinJson(GenerateRoomName())); + }) + .Next([this](FNakamaWebSocketResponse JoinResp) -> TNakamaFuture + { + if (JoinResp.ErrorCode != ENakamaWebSocketError::None) + { + AddError(TEXT("channel_join failed")); + return MakeCompletedFuture(JoinResp); + } + FString ChannelId = ExtractChannelId(JoinResp); + TestFalse("Channel ID from join", ChannelId.IsEmpty()); + + TSharedPtr LeaveJson = MakeShared(); + LeaveJson->SetStringField(TEXT("channel_id"), ChannelId); + return RtConn->Send(TEXT("channel_leave"), LeaveJson); + }) + .Next([this, Done](FNakamaWebSocketResponse LeaveResp) + { + WSS_FAIL_ON_ERROR(LeaveResp, Done); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// MATCH TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaRtConnectionMatchSpec, "IntegrationTests.NakamaRtConnectionTests.Match", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaClientConfig ClientConfig; + FNakamaSession Session; + TSharedPtr RtConn; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + + FString ExtractMatchId(const FNakamaWebSocketResponse& Resp) const + { + FString Id; + if (Resp.Data.IsValid()) + { + Resp.Data->TryGetStringField(TEXT("match_id"), Id); + } + return Id; + } + +END_DEFINE_SPEC(FNakamaRtConnectionMatchSpec) + +void FNakamaRtConnectionMatchSpec::Define() +{ + BeforeEach([this]() + { + ClientConfig = FNakamaClientConfig{ RtServerKey, RtHost, RtPort, false }; + RtConn = MakeShared(); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), GenerateId()) + .Next([this, Done](FNakamaSessionResult R) + { + if (R.bIsError) { AddError(FString::Printf(TEXT("Auth: %s"), *R.Error.Message)); Done.Execute(); return; } + Session = R.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (RtConn) + { + RtConn->Close(); + RtConn.Reset(); + } + + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + Describe("Create", [this]() + { + LatentIt("should create a relayed match and receive a match_id", [this](const FDoneDelegate& Done) + { + RtConn->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return RtConn->Send(TEXT("match_create"), MakeShared()); + }) + .Next([this, Done](FNakamaWebSocketResponse Resp) + { + WSS_FAIL_ON_ERROR(Resp, Done); + FString MatchId = ExtractMatchId(Resp); + TestFalse("Created match should have a non-empty match_id", MatchId.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("Leave", [this]() + { + LatentIt("should leave a match without error", [this](const FDoneDelegate& Done) + { + RtConn->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return RtConn->Send(TEXT("match_create"), MakeShared()); + }) + .Next([this](FNakamaWebSocketResponse CreateResp) -> TNakamaFuture + { + if (CreateResp.ErrorCode != ENakamaWebSocketError::None) + { + AddError(TEXT("match_create failed")); + return MakeCompletedFuture(CreateResp); + } + FString MatchId = ExtractMatchId(CreateResp); + TestFalse("Match ID from create", MatchId.IsEmpty()); + + TSharedPtr LeaveJson = MakeShared(); + LeaveJson->SetStringField(TEXT("match_id"), MatchId); + return RtConn->Send(TEXT("match_leave"), LeaveJson); + }) + .Next([this, Done](FNakamaWebSocketResponse LeaveResp) + { + WSS_FAIL_ON_ERROR(LeaveResp, Done); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// MATCHMAKER TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaRtConnectionMatchmakerSpec, "IntegrationTests.NakamaRtConnectionTests.Matchmaker", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaClientConfig ClientConfig; + FNakamaSession Session; + TSharedPtr RtConn; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + + TSharedPtr MakeMatchmakerAddJson() const + { + TSharedPtr J = MakeShared(); + J->SetNumberField(TEXT("min_count"), 2); + J->SetNumberField(TEXT("max_count"), 4); + J->SetStringField(TEXT("query"), TEXT("*")); + return J; + } + + FString ExtractTicket(const FNakamaWebSocketResponse& Resp) const + { + FString Ticket; + if (Resp.Data.IsValid()) + { + Resp.Data->TryGetStringField(TEXT("ticket"), Ticket); + } + return Ticket; + } + +END_DEFINE_SPEC(FNakamaRtConnectionMatchmakerSpec) + +void FNakamaRtConnectionMatchmakerSpec::Define() +{ + BeforeEach([this]() + { + ClientConfig = FNakamaClientConfig{ RtServerKey, RtHost, RtPort, false }; + RtConn = MakeShared(); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), GenerateId()) + .Next([this, Done](FNakamaSessionResult R) + { + if (R.bIsError) { AddError(FString::Printf(TEXT("Auth: %s"), *R.Error.Message)); Done.Execute(); return; } + Session = R.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (RtConn) + { + RtConn->Close(); + RtConn.Reset(); + } + + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + Describe("Add", [this]() + { + LatentIt("should add to matchmaker and receive a ticket", [this](const FDoneDelegate& Done) + { + RtConn->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return RtConn->Send(TEXT("matchmaker_add"), MakeMatchmakerAddJson()); + }) + .Next([this, Done](FNakamaWebSocketResponse Resp) + { + WSS_FAIL_ON_ERROR(Resp, Done); + FString Ticket = ExtractTicket(Resp); + TestFalse("Matchmaker ticket should not be empty", Ticket.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("Remove", [this]() + { + LatentIt("should add to matchmaker and then cancel with the ticket", [this](const FDoneDelegate& Done) + { + RtConn->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return RtConn->Send(TEXT("matchmaker_add"), MakeMatchmakerAddJson()); + }) + .Next([this](FNakamaWebSocketResponse AddResp) -> TNakamaFuture + { + if (AddResp.ErrorCode != ENakamaWebSocketError::None) + { + AddError(TEXT("matchmaker_add failed")); + return MakeCompletedFuture(AddResp); + } + FString Ticket = ExtractTicket(AddResp); + TestFalse("Ticket before remove", Ticket.IsEmpty()); + + TSharedPtr RemoveJson = MakeShared(); + RemoveJson->SetStringField(TEXT("ticket"), Ticket); + return RtConn->Send(TEXT("matchmaker_remove"), RemoveJson); + }) + .Next([this, Done](FNakamaWebSocketResponse RemoveResp) + { + WSS_FAIL_ON_ERROR(RemoveResp, Done); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// STATUS TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaRtConnectionStatusSpec, "IntegrationTests.NakamaRtConnectionTests.Status", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaClientConfig ClientConfig; + FNakamaSession Session; + TSharedPtr RtConn; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaRtConnectionStatusSpec) + +void FNakamaRtConnectionStatusSpec::Define() +{ + BeforeEach([this]() + { + ClientConfig = FNakamaClientConfig{ RtServerKey, RtHost, RtPort, false }; + RtConn = MakeShared(); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), GenerateId()) + .Next([this, Done](FNakamaSessionResult R) + { + if (R.bIsError) { AddError(FString::Printf(TEXT("Auth: %s"), *R.Error.Message)); Done.Execute(); return; } + Session = R.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (RtConn) + { + RtConn->Close(); + RtConn.Reset(); + } + + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + Describe("Update", [this]() + { + LatentIt("should set a user status string", [this](const FDoneDelegate& Done) + { + TSharedPtr Json = MakeShared(); + Json->SetStringField(TEXT("status"), TEXT("Playing a game")); + + RtConn->Connect(MakeConnParams(Session)) + .Next([this, Json](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return RtConn->Send(TEXT("status_update"), Json); + }) + .Next([this, Done](FNakamaWebSocketResponse Resp) + { + WSS_FAIL_ON_ERROR(Resp, Done); + Done.Execute(); + }); + }); + + LatentIt("should clear user status by omitting the status field", [this](const FDoneDelegate& Done) + { + // Sending status_update with no "status" field signals going offline. + RtConn->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return RtConn->Send(TEXT("status_update"), MakeShared()); + }) + .Next([this, Done](FNakamaWebSocketResponse Resp) + { + WSS_FAIL_ON_ERROR(Resp, Done); + Done.Execute(); + }); + }); + }); + + Describe("Follow", [this]() + { + LatentIt("should follow a user ID without error", [this](const FDoneDelegate& Done) + { + // Use a random UUID — Nakama accepts follow requests for + // non-existent users and returns an empty presence list. + TArray> Ids; + Ids.Add(MakeShared(GenerateId())); + TSharedPtr Json = MakeShared(); + Json->SetArrayField(TEXT("user_ids"), Ids); + + RtConn->Connect(MakeConnParams(Session)) + .Next([this, Json](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return RtConn->Send(TEXT("status_follow"), Json); + }) + .Next([this, Done](FNakamaWebSocketResponse Resp) + { + WSS_FAIL_ON_ERROR(Resp, Done); + Done.Execute(); + }); + }); + + LatentIt("should follow and then unfollow a user ID", [this](const FDoneDelegate& Done) + { + FString FakeUserId = GenerateId(); + + TArray> FollowIds; + FollowIds.Add(MakeShared(FakeUserId)); + TSharedPtr FollowJson = MakeShared(); + FollowJson->SetArrayField(TEXT("user_ids"), FollowIds); + + RtConn->Connect(MakeConnParams(Session)) + .Next([this, FollowJson](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return RtConn->Send(TEXT("status_follow"), FollowJson); + }) + .Next([this, FakeUserId](FNakamaWebSocketResponse FollowResp) -> TNakamaFuture + { + if (FollowResp.ErrorCode != ENakamaWebSocketError::None) + { + AddError(TEXT("status_follow failed")); + return MakeCompletedFuture(FollowResp); + } + TArray> UnfollowIds; + UnfollowIds.Add(MakeShared(FakeUserId)); + TSharedPtr UnfollowJson = MakeShared(); + UnfollowJson->SetArrayField(TEXT("user_ids"), UnfollowIds); + return RtConn->Send(TEXT("status_unfollow"), UnfollowJson); + }) + .Next([this, Done](FNakamaWebSocketResponse UnfollowResp) + { + WSS_FAIL_ON_ERROR(UnfollowResp, Done); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// DELEGATE TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaRtConnectionDelegatesSpec, "IntegrationTests.NakamaRtConnectionTests.Delegates", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaClientConfig ClientConfig; + FNakamaSession Session; + TSharedPtr RtConn; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaRtConnectionDelegatesSpec) + +void FNakamaRtConnectionDelegatesSpec::Define() +{ + BeforeEach([this]() + { + ClientConfig = FNakamaClientConfig{ RtServerKey, RtHost, RtPort, false }; + RtConn = MakeShared(); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), GenerateId()) + .Next([this, Done](FNakamaSessionResult R) + { + if (R.bIsError) { AddError(FString::Printf(TEXT("Auth: %s"), *R.Error.Message)); Done.Execute(); return; } + Session = R.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (RtConn) + { + RtConn->Close(); + RtConn.Reset(); + } + + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + Describe("MessageSent", [this]() + { + LatentIt("should fire the MessageSent delegate when a message is dispatched", [this](const FDoneDelegate& Done) + { + RtConn->Connect(MakeConnParams(Session)) + .Next([this, Done](FNakamaWebSocketConnectionResult CR) + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + + // Use a shared handle so we can self-remove; a shared bool prevents + // double-completion if the delegate fires more than once during the test. + TSharedRef Handle = MakeShared(); + TSharedRef bFired = MakeShared(false); + + *Handle = RtConn->MessageSent.AddLambda( + [this, Done, Handle, bFired](const FString& Message) + { + if (*bFired) return; + *bFired = true; + TestFalse("MessageSent payload should not be empty", Message.IsEmpty()); + Done.Execute(); + }); + + // Trigger a send — we don't await the response future here. + RtConn->Send(TEXT("ping"), MakeShared()); + }); + }); + }); + + Describe("ServerResponseReceived", [this]() + { + LatentIt("should fire ServerResponseReceived when the server replies", [this](const FDoneDelegate& Done) + { + RtConn->Connect(MakeConnParams(Session)) + .Next([this, Done](FNakamaWebSocketConnectionResult CR) + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + + TSharedRef Handle = MakeShared(); + TSharedRef bFired = MakeShared(false); + + *Handle = RtConn->ServerResponseReceived.AddLambda( + [this, Done, Handle, bFired](const FString& Message) + { + if (*bFired) return; + *bFired = true; + TestFalse("ServerResponseReceived payload should not be empty", Message.IsEmpty()); + Done.Execute(); + }); + + RtConn->Send(TEXT("match_create"), MakeShared()); + }); + }); + + LatentIt("should fire ServerResponseReceived for every distinct response", [this](const FDoneDelegate& Done) + { + RtConn->Connect(MakeConnParams(Session)) + .Next([this, Done](FNakamaWebSocketConnectionResult CR) + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + + constexpr int32 NumSends = 3; + TSharedRef> FireCount = MakeShared>(0); + TSharedRef Handle = MakeShared(); + + *Handle = RtConn->ServerResponseReceived.AddLambda( + [this, Done, Handle, FireCount](const FString&) + { + if (++(*FireCount) == NumSends) + { + Done.Execute(); + } + }); + + for (int32 i = 0; i < NumSends; ++i) + { + RtConn->Send(TEXT("match_create"), MakeShared()); + } + }); + }); + }); +} + +// ============================================================================ +// DATA RACE REGRESSION TEST +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaRtConnectionDataRaceSpec, + "IntegrationTests.NakamaRtConnectionTests.DataRace", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaClientConfig ClientConfig; + FNakamaSession Session; + TSharedPtr RtConn; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaRtConnectionDataRaceSpec) + +void FNakamaRtConnectionDataRaceSpec::Define() +{ + BeforeEach([this]() + { + ClientConfig = FNakamaClientConfig{ RtServerKey, RtHost, RtPort, false }; + RtConn = MakeShared(); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), GenerateId()) + .Next([this, Done](FNakamaSessionResult R) + { + if (R.bIsError) { AddError(FString::Printf(TEXT("Auth: %s"), *R.Error.Message)); Done.Execute(); return; } + Session = R.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (RtConn) + { + RtConn->Close(); + RtConn.Reset(); + } + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + Describe("bIsConnected", [this]() + { + LatentIt("should not crash when Send races with Connect on bIsConnected", + [this](const FDoneDelegate& Done) + { + // Stress test: Connect fires on the WS thread while the game thread + // hammers Send(). With std::atomic bIsConnected this is safe; + // with a plain bool it would be a C++11 data race (UB). + RtConn->Connect(MakeConnParams(Session)); + + constexpr int32 Iterations = 500; + for (int32 i = 0; i < Iterations; ++i) + { + RtConn->Send(TEXT("match_create"), MakeShared()); + FPlatformProcess::SleepNoStats(0.0f); + } + + RtConn->Close(); + Done.Execute(); + }); + }); +} diff --git a/IntegrationTests/Source/IntegrationTests/Tests/NakamaRtTests.cpp b/IntegrationTests/Source/IntegrationTests/Tests/NakamaRtTests.cpp new file mode 100644 index 000000000..5cac962e2 --- /dev/null +++ b/IntegrationTests/Source/IntegrationTests/Tests/NakamaRtTests.cpp @@ -0,0 +1,1316 @@ +/** + * NakamaRt Integration Test Suite + * + * Tests the typed NakamaRt namespace API layer: ChannelJoin, MatchCreate, + * MatchmakerAdd, StatusUpdate, PartyCreate, and Ping. + * + * Each spec creates an isolated FNakamaRtConnection so the connection is + * fresh per test, matching the isolation strategy used in + * NakamaWebSocketSubsystemTests.cpp. + * + * Requires a running Nakama server (IntegrationTests/server/docker-compose.yml). + * + * Run with: + * -ExecCmds="Automation RunTests IntegrationTests.NakamaRt" + */ + +#include "Misc/AutomationTest.h" +#include "Misc/Guid.h" +#include "Nakama.h" +#include "NakamaRt.h" +#include "NakamaRtConnection.h" + +using namespace Nakama; +using namespace NakamaRt; + +// --------------------------------------------------------------------------- +// Shared constants and helpers (file-local) +// --------------------------------------------------------------------------- + +namespace +{ + static const FString RtServerKey = TEXT("defaultkey"); + static const FString RtHost = TEXT("127.0.0.1"); + static constexpr int32 RtPort = 7350; + + FNakamaWebSocketConnectionParams MakeConnParams(const FNakamaSession& Session) + { + FNakamaWebSocketConnectionParams P; + P.Host = RtHost; + P.Port = RtPort; + P.Token = Session.Token; + P.PingIntervalSeconds = 60.0f; + P.bUseSSL = false; + return P; + } + + /** Ensure the WebSockets module is loaded before creating connections. */ + void EnsureWebSocketsLoaded() + { + if (!FModuleManager::Get().IsModuleLoaded(TEXT("WebSockets"))) + { + FModuleManager::Get().LoadModule(TEXT("WebSockets")); + } + } +} + +/** + * Fails the current test and calls Done when a typed FNakamaRtResult carries + * an error. Use only in terminal .Next() callbacks (it contains a bare return). + */ +#define RT_FAIL_ON_ERROR(Result, Done) \ + if (!(Result).bIsSuccess) { \ + AddError(FString::Printf(TEXT("Unexpected realtime error: %s"), \ + *((Result).Error.IsSet() ? (Result).Error->Message : TEXT("unknown")))); \ + (Done).Execute(); \ + return; \ + } + +// ============================================================================ +// PING TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaRtPingSpec, "IntegrationTests.NakamaRtTests.Ping", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaClientConfig ClientConfig; + FNakamaSession Session; + TSharedPtr Connection; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaRtPingSpec) + +void FNakamaRtPingSpec::Define() +{ + BeforeEach([this]() + { + ClientConfig = FNakamaClientConfig{ RtServerKey, RtHost, RtPort, false }; + EnsureWebSocketsLoaded(); + Connection = MakeShared(); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), GenerateId()) + .Next([this, Done](FNakamaSessionResult R) + { + if (R.bIsError) { AddError(FString::Printf(TEXT("Auth: %s"), *R.Error.Message)); Done.Execute(); return; } + Session = R.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Connection) { Connection->Close(); Connection.Reset(); } + + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + Describe("Single", [this]() + { + LatentIt("should complete a round-trip status update", [this](const FDoneDelegate& Done) + { + Connection->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture> + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::StatusUpdate(Connection, TEXT("")); + }) + .Next([this, Done](FNakamaRtResult Resp) + { + RT_FAIL_ON_ERROR(Resp, Done); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// CHANNEL TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaRtChannelSpec, "IntegrationTests.NakamaRtTests.Channel", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaClientConfig ClientConfig; + FNakamaSession Session; + TSharedPtr Connection; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateRoomName() { return TEXT("rt-test-") + FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaRtChannelSpec) + +void FNakamaRtChannelSpec::Define() +{ + BeforeEach([this]() + { + ClientConfig = FNakamaClientConfig{ RtServerKey, RtHost, RtPort, false }; + EnsureWebSocketsLoaded(); + Connection = MakeShared(); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), GenerateId()) + .Next([this, Done](FNakamaSessionResult R) + { + if (R.bIsError) { AddError(FString::Printf(TEXT("Auth: %s"), *R.Error.Message)); Done.Execute(); return; } + Session = R.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Connection) { Connection->Close(); Connection.Reset(); } + + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + // ----------------------------------------------------------------------- + + Describe("Join", [this]() + { + LatentIt("should join a room channel and receive a channel ID", [this](const FDoneDelegate& Done) + { + Connection->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture> + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::ChannelJoin(Connection, GenerateRoomName(), 1, false, false); + }) + .Next([this, Done](FNakamaRtResult Resp) + { + RT_FAIL_ON_ERROR(Resp, Done); + TestFalse("Joined channel should have a non-empty ID", Resp.Data->Id.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should join with persistence=false and hidden=false as explicit Optional booleans", [this](const FDoneDelegate& Done) + { + Connection->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture> + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::ChannelJoin(Connection, GenerateRoomName(), 1, false, false); + }) + .Next([this, Done](FNakamaRtResult Resp) + { + RT_FAIL_ON_ERROR(Resp, Done); + TestFalse("Channel ID is non-empty", Resp.Data->Id.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("SendMessage", [this]() + { + LatentIt("should send a message and receive an ack with a message_id", [this](const FDoneDelegate& Done) + { + Connection->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture> + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::ChannelJoin(Connection, GenerateRoomName(), 1, true, false); + }) + .Next([this, Done](auto JoinResp) -> TNakamaFuture> + { + if (!JoinResp.bIsSuccess) + { + AddError(TEXT("ChannelJoin failed")); + Done.Execute(); + FNakamaRtError Err = JoinResp.Error.IsSet() ? *JoinResp.Error : FNakamaRtError{}; + return MakeCompletedFuture(FNakamaRtResult::Failure(Err)); + } + TestFalse("Channel ID from join", JoinResp.Data->Id.IsEmpty()); + return NakamaRt::ChannelMessageSend(Connection, JoinResp.Data->Id, TEXT("{\"msg\":\"hello\"}")); + }) + .Next([this, Done](FNakamaRtResult Resp) + { + RT_FAIL_ON_ERROR(Resp, Done); + TestFalse("Ack should contain a message_id", Resp.Data->MessageId.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("UpdateMessage", [this]() + { + LatentIt("should update a sent message without error", [this](const FDoneDelegate& Done) + { + Connection->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture> + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::ChannelJoin(Connection, GenerateRoomName(), 1, true, false); + }) + .Next([this, Done](auto JoinResp) -> TNakamaFuture> + { + if (!JoinResp.bIsSuccess) + { + AddError(TEXT("ChannelJoin failed")); + Done.Execute(); + FNakamaRtError Err = JoinResp.Error.IsSet() ? *JoinResp.Error : FNakamaRtError{}; + return MakeCompletedFuture(FNakamaRtResult::Failure(Err)); + } + return NakamaRt::ChannelMessageSend(Connection, JoinResp.Data->Id, TEXT("{\"msg\":\"original\"}")); + }) + .Next([this, Done](auto SendResp) -> TNakamaFuture> + { + if (!SendResp.bIsSuccess) + { + AddError(TEXT("ChannelMessageSend failed")); + Done.Execute(); + FNakamaRtError Err = SendResp.Error.IsSet() ? *SendResp.Error : FNakamaRtError{}; + return MakeCompletedFuture(FNakamaRtResult::Failure(Err)); + } + TestFalse("Message ID before update", SendResp.Data->MessageId.IsEmpty()); + return NakamaRt::ChannelMessageUpdate(Connection, SendResp.Data->ChannelId, SendResp.Data->MessageId, TEXT("{\"msg\":\"updated\"}")); + }) + .Next([this, Done](FNakamaRtResult Resp) + { + RT_FAIL_ON_ERROR(Resp, Done); + Done.Execute(); + }); + }); + }); + + Describe("RemoveMessage", [this]() + { + LatentIt("should remove a sent message without error", [this](const FDoneDelegate& Done) + { + Connection->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture> + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::ChannelJoin(Connection, GenerateRoomName(), 1, true, false); + }) + .Next([this, Done](auto JoinResp) -> TNakamaFuture> + { + if (!JoinResp.bIsSuccess) + { + AddError(TEXT("ChannelJoin failed")); + Done.Execute(); + FNakamaRtError Err = JoinResp.Error.IsSet() ? *JoinResp.Error : FNakamaRtError{}; + return MakeCompletedFuture(FNakamaRtResult::Failure(Err)); + } + return NakamaRt::ChannelMessageSend(Connection, JoinResp.Data->Id, TEXT("{\"msg\":\"to delete\"}")); + }) + .Next([this, Done](auto SendResp) -> TNakamaFuture> + { + if (!SendResp.bIsSuccess) + { + AddError(TEXT("ChannelMessageSend failed")); + Done.Execute(); + FNakamaRtError Err = SendResp.Error.IsSet() ? *SendResp.Error : FNakamaRtError{}; + return MakeCompletedFuture(FNakamaRtResult::Failure(Err)); + } + return NakamaRt::ChannelMessageRemove(Connection, SendResp.Data->ChannelId, SendResp.Data->MessageId); + }) + .Next([this, Done](FNakamaRtResult Resp) + { + RT_FAIL_ON_ERROR(Resp, Done); + Done.Execute(); + }); + }); + }); + + Describe("Leave", [this]() + { + LatentIt("should leave a channel without error", [this](const FDoneDelegate& Done) + { + Connection->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture> + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::ChannelJoin(Connection, GenerateRoomName(), 1, false, false); + }) + .Next([this, Done](auto JoinResp) -> TNakamaFuture> + { + if (!JoinResp.bIsSuccess) + { + AddError(TEXT("ChannelJoin failed")); + Done.Execute(); + FNakamaRtError Err = JoinResp.Error.IsSet() ? *JoinResp.Error : FNakamaRtError{}; + return MakeCompletedFuture(FNakamaRtResult::Failure(Err)); + } + return NakamaRt::ChannelLeave(Connection, JoinResp.Data->Id); + }) + .Next([this, Done](FNakamaRtResult Resp) + { + RT_FAIL_ON_ERROR(Resp, Done); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// MATCH TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaRtMatchSpec, "IntegrationTests.NakamaRtTests.Match", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaClientConfig ClientConfig; + FNakamaSession Session; + TSharedPtr Connection; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaRtMatchSpec) + +void FNakamaRtMatchSpec::Define() +{ + BeforeEach([this]() + { + ClientConfig = FNakamaClientConfig{ RtServerKey, RtHost, RtPort, false }; + EnsureWebSocketsLoaded(); + Connection = MakeShared(); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), GenerateId()) + .Next([this, Done](FNakamaSessionResult R) + { + if (R.bIsError) { AddError(FString::Printf(TEXT("Auth: %s"), *R.Error.Message)); Done.Execute(); return; } + Session = R.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Connection) { Connection->Close(); Connection.Reset(); } + + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + // ----------------------------------------------------------------------- + + Describe("Create", [this]() + { + LatentIt("should create a relayed match and receive a match_id", [this](const FDoneDelegate& Done) + { + Connection->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture> + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::MatchCreate(Connection, TEXT("")); + }) + .Next([this, Done](FNakamaRtResult Resp) + { + RT_FAIL_ON_ERROR(Resp, Done); + TestFalse("match_id should not be empty", Resp.Data->MatchId.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("Leave", [this]() + { + LatentIt("should leave a match without error", [this](const FDoneDelegate& Done) + { + Connection->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture> + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::MatchCreate(Connection, TEXT("")); + }) + .Next([this, Done](auto CreateResp) -> TNakamaFuture> + { + if (!CreateResp.bIsSuccess) + { + AddError(TEXT("MatchCreate failed")); + Done.Execute(); + FNakamaRtError Err = CreateResp.Error.IsSet() ? *CreateResp.Error : FNakamaRtError{}; + return MakeCompletedFuture(FNakamaRtResult::Failure(Err)); + } + TestFalse("Match ID before leave", CreateResp.Data->MatchId.IsEmpty()); + return NakamaRt::MatchLeave(Connection, CreateResp.Data->MatchId); + }) + .Next([this, Done](FNakamaRtResult Resp) + { + RT_FAIL_ON_ERROR(Resp, Done); + Done.Execute(); + }); + }); + }); + + Describe("SendData", [this]() + { + LatentIt("should send match data without error", [this](const FDoneDelegate& Done) + { + Connection->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture> + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::MatchCreate(Connection, TEXT("")); + }) + .Next([this, Done](FNakamaRtResult CreateResp) + { + RT_FAIL_ON_ERROR(CreateResp, Done); + // MatchDataSend is fire-and-forget — the server sends no response. + NakamaRt::MatchDataSend(Connection, CreateResp.Data->MatchId, 1, TArray{0x48, 0x65, 0x6C, 0x6C, 0x6F}, {}, true); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// MATCHMAKER TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaRtMatchmakerSpec, "IntegrationTests.NakamaRtTests.Matchmaker", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaClientConfig ClientConfig; + FNakamaSession Session; + TSharedPtr Connection; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaRtMatchmakerSpec) + +void FNakamaRtMatchmakerSpec::Define() +{ + BeforeEach([this]() + { + ClientConfig = FNakamaClientConfig{ RtServerKey, RtHost, RtPort, false }; + EnsureWebSocketsLoaded(); + Connection = MakeShared(); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), GenerateId()) + .Next([this, Done](FNakamaSessionResult R) + { + if (R.bIsError) { AddError(FString::Printf(TEXT("Auth: %s"), *R.Error.Message)); Done.Execute(); return; } + Session = R.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Connection) { Connection->Close(); Connection.Reset(); } + + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + // ----------------------------------------------------------------------- + + Describe("Add", [this]() + { + LatentIt("should add to matchmaker and receive a ticket", [this](const FDoneDelegate& Done) + { + Connection->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture> + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::MatchmakerAdd(Connection, 2, 4, TEXT("*"), 2, {}, {}); + }) + .Next([this, Done](FNakamaRtResult Resp) + { + RT_FAIL_ON_ERROR(Resp, Done); + TestFalse("Matchmaker ticket should not be empty", Resp.Data->Ticket.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("Remove", [this]() + { + LatentIt("should cancel matchmaking with the returned ticket", [this](const FDoneDelegate& Done) + { + Connection->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture> + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::MatchmakerAdd(Connection, 2, 4, TEXT("*"), 2, {}, {}); + }) + .Next([this, Done](auto AddResp) -> TNakamaFuture> + { + if (!AddResp.bIsSuccess) + { + AddError(TEXT("MatchmakerAdd failed")); + Done.Execute(); + FNakamaRtError Err = AddResp.Error.IsSet() ? *AddResp.Error : FNakamaRtError{}; + return MakeCompletedFuture(FNakamaRtResult::Failure(Err)); + } + TestFalse("Ticket before remove", AddResp.Data->Ticket.IsEmpty()); + return NakamaRt::MatchmakerRemove(Connection, AddResp.Data->Ticket); + }) + .Next([this, Done](FNakamaRtResult Resp) + { + RT_FAIL_ON_ERROR(Resp, Done); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// MATCH JOIN TESTS (two-client: match_id path and matchmaker-token path) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaRtMatchJoinSpec, "IntegrationTests.NakamaRtTests.MatchJoin", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaClientConfig ClientConfig; + + // Client A — set up by BeforeEach + FNakamaSession Session; + TSharedPtr Connection; + + // Client B — set up inline per test + FNakamaSession Session2; + TSharedPtr Connection2; + + // Shared state threaded through the JoinByMatchId chain via a member + FString PendingMatchId; + + // Counter used by the JoinByToken test — Done fires when both clients joined + int32 MatchJoinedCount = 0; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaRtMatchJoinSpec) + +void FNakamaRtMatchJoinSpec::Define() +{ + BeforeEach([this]() + { + ClientConfig = FNakamaClientConfig{ RtServerKey, RtHost, RtPort, false }; + EnsureWebSocketsLoaded(); + Connection = MakeShared(); + PendingMatchId = TEXT(""); + MatchJoinedCount = 0; + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), GenerateId()) + .Next([this, Done](FNakamaSessionResult R) + { + if (R.bIsError) { AddError(FString::Printf(TEXT("Auth A: %s"), *R.Error.Message)); Done.Execute(); return; } + Session = R.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Connection) { Connection->Close(); Connection.Reset(); } + if (Connection2) { Connection2->Close(); Connection2.Reset(); } + + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session) + .Next([this, Done](FNakamaVoidResult) + { + Session = {}; + if (Session2.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session2) + .Next([this, Done](FNakamaVoidResult) { Session2 = {}; Done.Execute(); }); + }); + }); + + // ----------------------------------------------------------------------- + + Describe("JoinByMatchId", [this]() + { + LatentIt("should join a relayed match created by another client using match_id", [this](const FDoneDelegate& Done) + { + // Chain: Connect A → Create Match → Auth B → Connect B → MatchJoin(match_id) + // PendingMatchId threads the match_id through the chain via spec member state. + Connection->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture> + { + TestFalse("Connect A", CR.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::MatchCreate(Connection, TEXT("")); + }) + .Next([this, Done](auto CreateResult) -> TNakamaFuture + { + if (!CreateResult.bIsSuccess) + { + AddError(TEXT("MatchCreate failed")); + return MakeCompletedFuture(FNakamaSessionResult{}); + } + PendingMatchId = CreateResult.Data->MatchId; + TestFalse("match_id from MatchCreate", PendingMatchId.IsEmpty()); + + FNakamaAccountCustom Account2; + Account2.Id = GenerateId(); + return Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account2.Id); + }) + .Next([this, Done](FNakamaSessionResult R2) -> TNakamaFuture + { + if (R2.bIsError) + { + AddError(FString::Printf(TEXT("Auth B: %s"), *R2.Error.Message)); + Done.Execute(); + return MakeCompletedFuture(FNakamaWebSocketConnectionResult{}); + } + Session2 = R2.Value; + Connection2 = MakeShared(); + return Connection2->Connect(MakeConnParams(Session2)); + }) + .Next([this](FNakamaWebSocketConnectionResult CR2) -> TNakamaFuture> + { + TestFalse("Connect B", CR2.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::MatchJoin(Connection2, PendingMatchId, TEXT(""), {}); + }) + .Next([this, Done](FNakamaRtResult JoinResult) + { + RT_FAIL_ON_ERROR(JoinResult, Done); + TestFalse("Client B joined with non-empty match_id", JoinResult.Data->MatchId.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("JoinByToken", [this]() + { + LatentIt("should join a match via matchmaker token when two clients are matched", [this](const FDoneDelegate& Done) + { + // Chain: Auth B → Connect B → Connect A → set OnMatchmakerMatched → MatchmakerAdd A → MatchmakerAdd B + // The server fires "matchmaker_matched" (no CID, push) to both clients. + // OnMatchmakerMatched handlers extract the token and call MatchJoin; + // Done fires when both clients have joined (MatchJoinedCount reaches 2). + FNakamaAccountCustom Account2; + Account2.Id = GenerateId(); + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account2.Id) + .Next([this, Done](FNakamaSessionResult R2) -> TNakamaFuture + { + if (R2.bIsError) { AddError(FString::Printf(TEXT("Auth B: %s"), *R2.Error.Message)); Done.Execute(); return MakeCompletedFuture(FNakamaWebSocketConnectionResult{}); } + Session2 = R2.Value; + Connection2 = MakeShared(); + return Connection2->Connect(MakeConnParams(Session2)); + }) + .Next([this, Done](FNakamaWebSocketConnectionResult CR2) -> TNakamaFuture + { + TestFalse("Connect B", CR2.ErrorCode != ENakamaWebSocketError::None); + return Connection->Connect(MakeConnParams(Session)); + }) + .Next([this, Done](FNakamaWebSocketConnectionResult CR1) -> TNakamaFuture> + { + TestFalse("Connect A", CR1.ErrorCode != ENakamaWebSocketError::None); + + // Set up typed event callbacks before adding to the matchmaker queue. + Connection->MatchmakerMatched.AddLambda([this, Done](const FNakamaRtMatchmakerMatched& Matched) + { + NakamaRt::MatchJoin(Connection, TEXT(""), Matched.Token, {}) + .Next([this, Done](FNakamaRtResult JoinResult) + { + if (!JoinResult.bIsSuccess) + { + AddError(TEXT("Client A MatchJoin failed")); + Done.Execute(); + return; + } + if (++MatchJoinedCount == 2) { Done.Execute(); } + }); + }); + + Connection2->MatchmakerMatched.AddLambda([this, Done](const FNakamaRtMatchmakerMatched& Matched) + { + NakamaRt::MatchJoin(Connection2, TEXT(""), Matched.Token, {}) + .Next([this, Done](FNakamaRtResult JoinResult) + { + if (!JoinResult.bIsSuccess) + { + AddError(TEXT("Client B MatchJoin failed")); + Done.Execute(); + return; + } + if (++MatchJoinedCount == 2) { Done.Execute(); } + }); + }); + + return NakamaRt::MatchmakerAdd(Connection, 2, 2, TEXT("*"), {}, {}, {}); + }) + .Next([this, Done](auto AddAResp) -> TNakamaFuture> + { + if (!AddAResp.bIsSuccess) + { + AddError(FString::Printf(TEXT("MatchmakerAdd A failed"))); + Done.Execute(); + FNakamaRtError Err = AddAResp.Error.IsSet() ? *AddAResp.Error : FNakamaRtError{}; + return MakeCompletedFuture(FNakamaRtResult::Failure(Err)); + } + return NakamaRt::MatchmakerAdd(Connection2, 2, 2, TEXT("*"), {}, {}, {}); + }) + .Next([this, Done](FNakamaRtResult AddBResp) + { + if (!AddBResp.bIsSuccess) + { + AddError(FString::Printf(TEXT("MatchmakerAdd B failed"))); + Done.Execute(); + } + // Both clients are now queued. The server will push matchmaker_matched to each. + // The OnMatchmakerMatched callbacks extract the token, call MatchJoin, and + // call Done once MatchJoinedCount reaches 2. + }); + }); + }); +} + +// ============================================================================ +// SERVER-PUSH EVENT TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaRtEventSpec, "IntegrationTests.NakamaRtTests.Events", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaClientConfig ClientConfig; + + FNakamaSession Session; + TSharedPtr Connection; + + FNakamaSession Session2; + TSharedPtr Connection2; + + // Threaded through chains when two-step setup needs to pass an ID between lambdas. + FString PendingId; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateRoomName() { return TEXT("rt-evt-") + FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaRtEventSpec) + +void FNakamaRtEventSpec::Define() +{ + BeforeEach([this]() + { + ClientConfig = FNakamaClientConfig{ RtServerKey, RtHost, RtPort, false }; + EnsureWebSocketsLoaded(); + Connection = MakeShared(); + PendingId = TEXT(""); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), GenerateId()) + .Next([this, Done](FNakamaSessionResult R) + { + if (R.bIsError) { AddError(FString::Printf(TEXT("Auth A: %s"), *R.Error.Message)); Done.Execute(); return; } + Session = R.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Connection) { Connection->Close(); Connection.Reset(); } + if (Connection2) { Connection2->Close(); Connection2.Reset(); } + + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session) + .Next([this, Done](FNakamaVoidResult) + { + Session = {}; + if (Session2.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session2) + .Next([this, Done](FNakamaVoidResult) { Session2 = {}; Done.Execute(); }); + }); + }); + + // ----------------------------------------------------------------------- + + Describe("OnMatchPresenceEvent", [this]() + { + LatentIt("should fire when a second client joins the match", [this](const FDoneDelegate& Done) + { + // Client A creates a match and registers OnMatchPresenceEvent. + // Client B authenticates, connects, and joins the same match. + // Done fires when Client A's callback delivers a non-empty joins list. + Connection->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture> + { + TestFalse("Connect A", CR.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::MatchCreate(Connection, TEXT("")); + }) + .Next([this, Done](auto CreateResult) -> TNakamaFuture + { + if (!CreateResult.bIsSuccess) + { + AddError(TEXT("MatchCreate failed")); + return MakeCompletedFuture(FNakamaSessionResult{}); + } + PendingId = CreateResult.Data->MatchId; + + Connection->MatchPresenceEvent.AddLambda([this, Done](const FNakamaRtMatchPresenceEvent& Event) + { + TestEqual("Presence event match_id", Event.MatchId, PendingId); + TestFalse("Joins list should be non-empty", Event.Joins.IsEmpty()); + Done.Execute(); + }); + + return Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), GenerateId()); + }) + .Next([this, Done](FNakamaSessionResult R2) -> TNakamaFuture + { + if (R2.bIsError) { AddError(FString::Printf(TEXT("Auth B: %s"), *R2.Error.Message)); Done.Execute(); return MakeCompletedFuture(FNakamaWebSocketConnectionResult{}); } + Session2 = R2.Value; + Connection2 = MakeShared(); + return Connection2->Connect(MakeConnParams(Session2)); + }) + .Next([this, Done](FNakamaWebSocketConnectionResult CR2) -> TNakamaFuture> + { + TestFalse("Connect B", CR2.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::MatchJoin(Connection2, PendingId, TEXT(""), {}); + }) + .Next([this, Done](FNakamaRtResult JoinResult) + { + if (!JoinResult.bIsSuccess) + { + AddError(TEXT("Client B MatchJoin failed")); + Done.Execute(); + } + // Done fires via MatchPresenceEvent on Client A, not here. + }); + }); + }); + + Describe("OnMatchData", [this]() + { + LatentIt("should fire when another client sends match data", [this](const FDoneDelegate& Done) + { + // Client A creates a match and registers OnMatchData. + // Client B joins the match and sends a data packet. + // Done fires when Client A's callback delivers the expected opcode. + static constexpr int64 TestOpCode = 42; + + Connection->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture> + { + TestFalse("Connect A", CR.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::MatchCreate(Connection, TEXT("")); + }) + .Next([this, Done](auto CreateResult) -> TNakamaFuture + { + if (!CreateResult.bIsSuccess) + { + AddError(TEXT("MatchCreate failed")); + return MakeCompletedFuture(FNakamaSessionResult{}); + } + PendingId = CreateResult.Data->MatchId; + + Connection->MatchData.AddLambda([this, Done](const FNakamaRtMatchData& MatchData) + { + TestEqual("MatchData match_id", MatchData.MatchId, PendingId); + TestEqual("MatchData opcode", MatchData.OpCode, TestOpCode); + Done.Execute(); + }); + + return Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), GenerateId()); + }) + .Next([this, Done](FNakamaSessionResult R2) -> TNakamaFuture + { + if (R2.bIsError) { AddError(FString::Printf(TEXT("Auth B: %s"), *R2.Error.Message)); Done.Execute(); return MakeCompletedFuture(FNakamaWebSocketConnectionResult{}); } + Session2 = R2.Value; + Connection2 = MakeShared(); + return Connection2->Connect(MakeConnParams(Session2)); + }) + .Next([this, Done](FNakamaWebSocketConnectionResult CR2) -> TNakamaFuture> + { + TestFalse("Connect B", CR2.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::MatchJoin(Connection2, PendingId, TEXT(""), {}); + }) + .Next([this, Done](FNakamaRtResult JoinResult) + { + if (!JoinResult.bIsSuccess) + { + AddError(TEXT("Client B MatchJoin failed")); + Done.Execute(); + return; + } + // Client B sends data; Client A will receive it via MatchData. + NakamaRt::MatchDataSend(Connection2, PendingId, TestOpCode, TArray{'h','e','l','l','o'}, {}, true); + }); + }); + + LatentIt("should receive the correct bytes when another client sends match data", [this](const FDoneDelegate& Done) + { + // Client A creates a match and registers OnMatchData expecting specific bytes. + // Client B joins the match and sends a known payload as TArray. + // Done fires when Client A's callback confirms the data bytes match. + static constexpr int64 TestOpCode = 43; + static const TArray TestPayload = {0x01, 0x02, 0x03}; + + Connection->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture> + { + TestFalse("Connect A", CR.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::MatchCreate(Connection, TEXT("")); + }) + .Next([this, Done](auto CreateResult) -> TNakamaFuture + { + if (!CreateResult.bIsSuccess) + { + AddError(TEXT("MatchCreate failed")); + return MakeCompletedFuture(FNakamaSessionResult{}); + } + PendingId = CreateResult.Data->MatchId; + + Connection->MatchData.AddLambda([this, Done](const FNakamaRtMatchData& MatchData) + { + TestEqual("MatchData match_id", MatchData.MatchId, PendingId); + TestEqual("MatchData opcode", MatchData.OpCode, TestOpCode); + TestEqual("MatchData bytes", MatchData.Data, TestPayload); + Done.Execute(); + }); + + return Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), GenerateId()); + }) + .Next([this, Done](FNakamaSessionResult R2) -> TNakamaFuture + { + if (R2.bIsError) { AddError(FString::Printf(TEXT("Auth B: %s"), *R2.Error.Message)); Done.Execute(); return MakeCompletedFuture(FNakamaWebSocketConnectionResult{}); } + Session2 = R2.Value; + Connection2 = MakeShared(); + return Connection2->Connect(MakeConnParams(Session2)); + }) + .Next([this, Done](FNakamaWebSocketConnectionResult CR2) -> TNakamaFuture> + { + TestFalse("Connect B", CR2.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::MatchJoin(Connection2, PendingId, TEXT(""), {}); + }) + .Next([this, Done](FNakamaRtResult JoinResult) + { + if (!JoinResult.bIsSuccess) + { + AddError(TEXT("Client B MatchJoin failed")); + Done.Execute(); + return; + } + NakamaRt::MatchDataSend(Connection2, PendingId, TestOpCode, TestPayload, {}, true); + }); + }); + }); + + Describe("OnChannelPresenceEvent", [this]() + { + LatentIt("should fire when a second client joins the same channel", [this](const FDoneDelegate& Done) + { + // Client A joins a room channel and registers OnChannelPresenceEvent. + // Client B joins the same room. + // Done fires when Client A's callback delivers a non-empty joins list. + FString RoomName = GenerateRoomName(); + + Connection->Connect(MakeConnParams(Session)) + .Next([this, RoomName](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture> + { + TestFalse("Connect A", CR.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::ChannelJoin(Connection, RoomName, 1, false, false); + }) + .Next([this, Done](auto JoinResult) -> TNakamaFuture + { + if (!JoinResult.bIsSuccess) + { + AddError(TEXT("ChannelJoin A failed")); + return MakeCompletedFuture(FNakamaSessionResult{}); + } + PendingId = JoinResult.Data->Id; + + Connection->ChannelPresenceEvent.AddLambda([this, Done](const FNakamaRtChannelPresenceEvent& Event) + { + TestEqual("Presence event channel_id", Event.ChannelId, PendingId); + TestFalse("Joins list should be non-empty", Event.Joins.IsEmpty()); + Done.Execute(); + }); + + return Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), GenerateId()); + }) + .Next([this, Done](FNakamaSessionResult R2) -> TNakamaFuture + { + if (R2.bIsError) { AddError(FString::Printf(TEXT("Auth B: %s"), *R2.Error.Message)); Done.Execute(); return MakeCompletedFuture(FNakamaWebSocketConnectionResult{}); } + Session2 = R2.Value; + Connection2 = MakeShared(); + return Connection2->Connect(MakeConnParams(Session2)); + }) + .Next([this, Done, RoomName](FNakamaWebSocketConnectionResult CR2) -> TNakamaFuture> + { + TestFalse("Connect B", CR2.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::ChannelJoin(Connection2, RoomName, 1, false, false); + }) + .Next([this, Done](FNakamaRtResult JoinResult) + { + if (!JoinResult.bIsSuccess) + { + AddError(TEXT("Client B ChannelJoin failed")); + Done.Execute(); + } + // Done fires via OnChannelPresenceEvent on Client A, not here. + }); + }); + }); +} + +// ============================================================================ +// STATUS TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaRtStatusSpec, "IntegrationTests.NakamaRtTests.Status", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaClientConfig ClientConfig; + FNakamaSession Session; + TSharedPtr Connection; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaRtStatusSpec) + +void FNakamaRtStatusSpec::Define() +{ + BeforeEach([this]() + { + ClientConfig = FNakamaClientConfig{ RtServerKey, RtHost, RtPort, false }; + EnsureWebSocketsLoaded(); + Connection = MakeShared(); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), GenerateId()) + .Next([this, Done](FNakamaSessionResult R) + { + if (R.bIsError) { AddError(FString::Printf(TEXT("Auth: %s"), *R.Error.Message)); Done.Execute(); return; } + Session = R.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Connection) { Connection->Close(); Connection.Reset(); } + + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + // ----------------------------------------------------------------------- + + Describe("Update", [this]() + { + LatentIt("should set a status string without error", [this](const FDoneDelegate& Done) + { + Connection->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture> + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::StatusUpdate(Connection, TEXT("Playing a game")); + }) + .Next([this, Done](FNakamaRtResult Resp) + { + RT_FAIL_ON_ERROR(Resp, Done); + Done.Execute(); + }); + }); + + LatentIt("should clear status by passing an empty string", [this](const FDoneDelegate& Done) + { + Connection->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture> + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::StatusUpdate(Connection, TEXT("")); + }) + .Next([this, Done](FNakamaRtResult Resp) + { + RT_FAIL_ON_ERROR(Resp, Done); + Done.Execute(); + }); + }); + }); + + Describe("Follow", [this]() + { + LatentIt("should follow a user ID without error", [this](const FDoneDelegate& Done) + { + // Nakama accepts follow requests for non-existent users and returns + // an empty presence list, so a random UUID is fine here. + Connection->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture> + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::StatusFollow(Connection, { GenerateId() }, {}); + }) + .Next([this, Done](FNakamaRtResult Resp) + { + RT_FAIL_ON_ERROR(Resp, Done); + Done.Execute(); + }); + }); + + LatentIt("should follow and then unfollow a user ID", [this](const FDoneDelegate& Done) + { + FString FakeUserId = GenerateId(); + Connection->Connect(MakeConnParams(Session)) + .Next([this, FakeUserId](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture> + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::StatusFollow(Connection, { FakeUserId }, {}); + }) + .Next([this, FakeUserId, Done](auto FollowResp) -> TNakamaFuture> + { + if (!FollowResp.bIsSuccess) + { + AddError(TEXT("StatusFollow failed")); + Done.Execute(); + FNakamaRtError Err = FollowResp.Error.IsSet() ? *FollowResp.Error : FNakamaRtError{}; + return MakeCompletedFuture(FNakamaRtResult::Failure(Err)); + } + return NakamaRt::StatusUnfollow(Connection, { FakeUserId }); + }) + .Next([this, Done](FNakamaRtResult Resp) + { + RT_FAIL_ON_ERROR(Resp, Done); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// PARTY TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaRtPartySpec, "IntegrationTests.NakamaRtTests.Party", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaClientConfig ClientConfig; + FNakamaSession Session; + TSharedPtr Connection; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaRtPartySpec) + +void FNakamaRtPartySpec::Define() +{ + BeforeEach([this]() + { + ClientConfig = FNakamaClientConfig{ RtServerKey, RtHost, RtPort, false }; + EnsureWebSocketsLoaded(); + Connection = MakeShared(); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), GenerateId()) + .Next([this, Done](FNakamaSessionResult R) + { + if (R.bIsError) { AddError(FString::Printf(TEXT("Auth: %s"), *R.Error.Message)); Done.Execute(); return; } + Session = R.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Connection) { Connection->Close(); Connection.Reset(); } + + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + // ----------------------------------------------------------------------- + + Describe("Create", [this]() + { + LatentIt("should create a party and receive a party_id", [this](const FDoneDelegate& Done) + { + Connection->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture> + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::PartyCreate(Connection, true, 4, TEXT(""), false); + }) + .Next([this, Done](FNakamaRtResult Resp) + { + RT_FAIL_ON_ERROR(Resp, Done); + TestFalse("party_id should not be empty", Resp.Data->PartyId.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("Close", [this]() + { + LatentIt("should close a party without error", [this](const FDoneDelegate& Done) + { + Connection->Connect(MakeConnParams(Session)) + .Next([this](FNakamaWebSocketConnectionResult CR) -> TNakamaFuture> + { + TestFalse("Connect", CR.ErrorCode != ENakamaWebSocketError::None); + return NakamaRt::PartyCreate(Connection, true, 4, TEXT(""), false); + }) + .Next([this, Done](auto CreateResp) -> TNakamaFuture> + { + if (!CreateResp.bIsSuccess) + { + AddError(TEXT("PartyCreate failed")); + Done.Execute(); + FNakamaRtError Err = CreateResp.Error.IsSet() ? *CreateResp.Error : FNakamaRtError{}; + return MakeCompletedFuture(FNakamaRtResult::Failure(Err)); + } + TestFalse("Party ID before close", CreateResp.Data->PartyId.IsEmpty()); + return NakamaRt::PartyClose(Connection, CreateResp.Data->PartyId); + }) + .Next([this, Done](FNakamaRtResult Resp) + { + RT_FAIL_ON_ERROR(Resp, Done); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// SUBSYSTEM LIFETIME TESTS (no server required) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaRtSubsystemLifetimeSpec, "IntegrationTests.NakamaRtTests.SubsystemLifetime", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + +END_DEFINE_SPEC(FNakamaRtSubsystemLifetimeSpec) + +void FNakamaRtSubsystemLifetimeSpec::Define() +{ + Describe("StaledSubsystem", [this]() + { + LatentIt("should return an error future instead of crashing when subsystem is destroyed", [this](const FDoneDelegate& Done) + { + // 1. Create a local connection and immediately reset it to null, + // simulating a destroyed/stale connection pointer. + TSharedPtr NullConnection = MakeShared(); + NullConnection.Reset(); + + // 2. Call a method while the connection is null/stale. + // Without the validity guard the generated template emits + // Connection->Send(...) + // on a null pointer — guaranteed crash. + // After the fix it must immediately resolve to an error. + NakamaRt::StatusUpdate(NullConnection, TEXT("")) + .Next([this, Done](FNakamaRtResult Resp) + { + TestFalse( + TEXT("Stale-subsystem call must return an error, not success"), + Resp.bIsSuccess); + Done.Execute(); + }); + }); + }); +} diff --git a/IntegrationTests/Source/IntegrationTests/Tests/NakamaTests.cpp b/IntegrationTests/Source/IntegrationTests/Tests/NakamaTests.cpp new file mode 100644 index 000000000..6e59136cd --- /dev/null +++ b/IntegrationTests/Source/IntegrationTests/Tests/NakamaTests.cpp @@ -0,0 +1,5463 @@ +/** + * Nakama Async API Integration Test Suite + * + * Mirror of NakamaApiTests.cpp using the TFuture-based Nakama:: free-function API. + * Each test uses F*Result types instead of separate success/error callback pairs. + */ + +#include +#include "Nakama.h" +#include "Misc/Guid.h" +#include "Serialization/JsonSerializer.h" + +const FNakamaClientConfig ClientConfig = FNakamaClientConfig{TEXT("defaultkey"), TEXT("127.0.0.1"), 7350, false}; + +/** + * Helper macro: early-return on unexpected error inside a callback. + * Use in tests that expect success. + */ +#define ASYNC_FAIL_ON_ERROR(Result, Done) \ + if (Result.bIsError) { \ + AddError(FString::Printf(TEXT("Unexpected error %d: %s"), Result.Error.Code, *Result.Error.Message)); \ + Done.Execute(); \ + return; \ + } + +// ============================================================================ +// AUTHENTICATION TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncAuthSpec, "IntegrationTests.NakamaTests.Auth", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + + FString TestCustomId; + FString TestDeviceId; + FString TestEmail; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateShortId() { return FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaAsyncAuthSpec) + +void FNakamaAsyncAuthSpec::Define() +{ + BeforeEach([this]() + { + TestCustomId = GenerateId(); + TestDeviceId = GenerateId(); + TestEmail = FString::Printf(TEXT("test_%s@example.com"), *GenerateShortId()); + }); + + Describe("CustomAuth", [this]() + { + LatentIt("should authenticate with valid custom ID", [this](const FDoneDelegate& Done) + { + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), TestCustomId).Next([this, Done](FNakamaSessionResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Session has token", !Result.Value.Token.IsEmpty()); + TestTrue("Session has refresh token", !Result.Value.RefreshToken.IsEmpty()); + Nakama::DeleteAccount(ClientConfig, Result.Value).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + }); + + LatentIt("should fail with empty custom ID", [this](const FDoneDelegate& Done) + { + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), TEXT("")).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) + { + AddError(TEXT("Expected error but got success")); + Nakama::DeleteAccount(ClientConfig, Result.Value).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + return; + } + TestTrue("Got error response", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should fail with custom ID too short < 6 chars", [this](const FDoneDelegate& Done) + { + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), TEXT("abc")).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) + { + AddError(TEXT("Expected error but got success")); + Nakama::DeleteAccount(ClientConfig, Result.Value).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + return; + } + TestTrue("Got error response", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should fail with custom ID too long > 128 chars", [this](const FDoneDelegate& Done) + { + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), FString::ChrN(150, 'a')).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) + { + AddError(TEXT("Expected error but got success")); + Nakama::DeleteAccount(ClientConfig, Result.Value).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + return; + } + TestTrue("Got error response", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should authenticate with username", [this](const FDoneDelegate& Done) + { + FString Username = FString::Printf(TEXT("user_%s"), *GenerateShortId()); + Nakama::AuthenticateCustom(ClientConfig, true, Username, TestCustomId).Next([this, Done](FNakamaSessionResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Session has token", !Result.Value.Token.IsEmpty()); + Nakama::DeleteAccount(ClientConfig, Result.Value).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + }); + }); + + Describe("DeviceAuth", [this]() + { + LatentIt("should authenticate with valid device ID", [this](const FDoneDelegate& Done) + { + Nakama::AuthenticateDevice(ClientConfig, true, TEXT(""), TestDeviceId).Next([this, Done](FNakamaSessionResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Session has token", !Result.Value.Token.IsEmpty()); + TestTrue("Session has refresh token", !Result.Value.RefreshToken.IsEmpty()); + Nakama::DeleteAccount(ClientConfig, Result.Value).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + }); + + LatentIt("should fail with empty device ID", [this](const FDoneDelegate& Done) + { + Nakama::AuthenticateDevice(ClientConfig, true, TEXT(""), TEXT("")).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) + { + AddError(TEXT("Expected error but got success")); + Nakama::DeleteAccount(ClientConfig, Result.Value).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + return; + } + TestTrue("Got error response", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should fail with device ID too short < 10 chars", [this](const FDoneDelegate& Done) + { + Nakama::AuthenticateDevice(ClientConfig, true, TEXT(""), TEXT("short")).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) + { + AddError(TEXT("Expected error but got success")); + Nakama::DeleteAccount(ClientConfig, Result.Value).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + return; + } + TestTrue("Got error response", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("EmailAuth", [this]() + { + LatentIt("should authenticate with valid email and password", [this](const FDoneDelegate& Done) + { + Nakama::AuthenticateEmail(ClientConfig, true, TEXT(""), TestEmail, TEXT("password123")).Next([this, Done](FNakamaSessionResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Session has token", !Result.Value.Token.IsEmpty()); + Nakama::DeleteAccount(ClientConfig, Result.Value).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + }); + + LatentIt("should fail with invalid email format", [this](const FDoneDelegate& Done) + { + Nakama::AuthenticateEmail(ClientConfig, true, TEXT(""), TEXT("notanemail"), TEXT("password123")).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) + { + AddError(TEXT("Expected error but got success")); + Nakama::DeleteAccount(ClientConfig, Result.Value).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + return; + } + TestTrue("Got error response", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should fail with password too short < 8 chars", [this](const FDoneDelegate& Done) + { + Nakama::AuthenticateEmail(ClientConfig, true, TEXT(""), TestEmail, TEXT("short")).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) + { + AddError(TEXT("Expected error but got success")); + Nakama::DeleteAccount(ClientConfig, Result.Value).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + return; + } + TestTrue("Got error response", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// ACCOUNT TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncAccountSpec, "IntegrationTests.NakamaTests.Account", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + FString UserId; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateShortId() { return FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaAsyncAccountSpec) + + +void FNakamaAsyncAccountSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id).Next([this, Done](FNakamaSessionResult AuthResult) + { + if (AuthResult.bIsError) { AddError(FString::Printf(TEXT("Auth failed: %s"), *AuthResult.Error.Message)); Done.Execute(); return; } + Session = AuthResult.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + Describe("GetAccount", [this]() + { + LatentIt("should get account for authenticated user", [this](const FDoneDelegate& Done) + { + Nakama::GetAccount(ClientConfig, Session).Next([this, Done](FNakamaAccountResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Account has user ID", !Result.Value.User.Id.IsEmpty()); + UserId = Result.Value.User.Id; + Done.Execute(); + }); + }); + }); + + Describe("UpdateAccount", [this]() + { + LatentIt("should update account display name", [this](const FDoneDelegate& Done) + { + FString NewDisplayName = FString::Printf(TEXT("TestUser_%s"), *GenerateShortId()); + + Nakama::UpdateAccount(ClientConfig, Session, TEXT(""), NewDisplayName, TEXT(""), TEXT("en"), TEXT(""), TEXT("")) + .Next([this](const FNakamaVoid&) + { + return Nakama::GetAccount(ClientConfig, Session); + }) + .Next([this, Done, NewDisplayName](FNakamaAccountResult GetResult) + { + ASYNC_FAIL_ON_ERROR(GetResult, Done); + TestEqual("Display name updated", GetResult.Value.User.DisplayName, NewDisplayName); + Done.Execute(); + }); + }); + }); + + Describe("SessionRefresh", [this]() + { + LatentIt("should refresh session with valid token", [this](const FDoneDelegate& Done) + { + Nakama::SessionRefresh(ClientConfig, Session.RefreshToken, TMap()) + .Next([this, Done](FNakamaSessionResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("New session has token", !Result.Value.Token.IsEmpty()); + TestTrue("New session has refresh token", !Result.Value.RefreshToken.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should fail with invalid refresh token", [this](const FDoneDelegate& Done) + { + Nakama::SessionRefresh(ClientConfig, TEXT("invalid.token.here"), TMap()) + .Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) + { + AddError(TEXT("Expected error but got success")); + Done.Execute(); + return; + } + TestTrue("Got error response", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// FRIENDS TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncFriendsSpec, "IntegrationTests.NakamaTests.Friends", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + FNakamaSession FriendSession; + FString UserId; + FString FriendUserId; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaAsyncFriendsSpec) + + +void FNakamaAsyncFriendsSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id) + .Next([this](const FNakamaSession& AuthResult) + { + Session = AuthResult; + return Nakama::GetAccount(ClientConfig, Session); + }) + .Next([this](const FNakamaAccount& AccResult) + { + UserId = AccResult.User.Id; + FNakamaAccountCustom FriendAccount; + FriendAccount.Id = GenerateId(); + return Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), FriendAccount.Id); + }) + .Next([this](const FNakamaSession& FriendAuthResult) + { + FriendSession = FriendAuthResult; + return Nakama::GetAccount(ClientConfig, FriendSession); + }) + .Next([this, Done](FNakamaAccountResult FriendAccResult) + { + ASYNC_FAIL_ON_ERROR(FriendAccResult, Done); + FriendUserId = FriendAccResult.Value.User.Id; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + auto DeleteMain = [this, Done]() + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }; + + if (FriendSession.Token.IsEmpty()) { DeleteMain(); return; } + Nakama::DeleteAccount(ClientConfig, FriendSession).Next([DeleteMain](FNakamaVoidResult) { DeleteMain(); }); + }); + + + + Describe("ListFriends", [this]() + { + LatentIt("should list friends for authenticated user", [this](const FDoneDelegate& Done) + { + Nakama::ListFriends(ClientConfig, Session, 100, 0, TEXT("")).Next([this, Done](FNakamaFriendListResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Friends list is valid", true); + Done.Execute(); + }); + }); + }); + + Describe("AddFriends", [this]() + { + LatentIt("should add friend by user ID", [this](const FDoneDelegate& Done) + { + TArray Ids; + Ids.Add(FriendUserId); + + Nakama::AddFriends(ClientConfig, Session, Ids, TArray(), TEXT("")).Next([this, Done](FNakamaVoidResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("AddFriends completed successfully", true); + Done.Execute(); + }); + }); + + LatentIt("should not add self as friend", [this](const FDoneDelegate& Done) + { + TArray Ids; + Ids.Add(UserId); + + Nakama::AddFriends(ClientConfig, Session, Ids, TArray(), TEXT("")).Next([this, Done](FNakamaVoidResult AddResult) + { + // Server accepts but should not add self to friend list + if (AddResult.bIsError) + { + // Some servers may return an error, which is also acceptable + Done.Execute(); + return; + } + + // Verify self is not in friend list + Nakama::ListFriends(ClientConfig, Session, 100, 2, TEXT("")).Next([this, Done](FNakamaFriendListResult ListResult) + { + ASYNC_FAIL_ON_ERROR(ListResult, Done); + bool bFoundSelf = false; + for (const auto& Friend : ListResult.Value.Friends) + { + if (Friend.User.Id == UserId) + { + bFoundSelf = true; + break; + } + } + TestTrue("Self not in friend list", !bFoundSelf); + Done.Execute(); + }); + }); + }); + }); + + Describe("BlockFriends", [this]() + { + LatentIt("should block user by ID", [this](const FDoneDelegate& Done) + { + TArray Ids; + Ids.Add(FriendUserId); + + Nakama::BlockFriends(ClientConfig, Session, Ids, TArray()).Next([this, Done](FNakamaVoidResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Block succeeded", true); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// GROUPS TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncGroupsSpec, "IntegrationTests.NakamaTests.Groups", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + FString UserId; + FString CreatedGroupId; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateShortId() { return FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaAsyncGroupsSpec) + + +void FNakamaAsyncGroupsSpec::Define() +{ + BeforeEach([this]() + { + CreatedGroupId.Empty(); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id) + .Next([this](const FNakamaSession& AuthResult) + { + Session = AuthResult; + return Nakama::GetAccount(ClientConfig, Session); + }) + .Next([this, Done](FNakamaAccountResult AccResult) + { + ASYNC_FAIL_ON_ERROR(AccResult, Done); + UserId = AccResult.Value.User.Id; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("CreateGroup", [this]() + { + LatentIt("should create group with valid name", [this](const FDoneDelegate& Done) + { + FString GroupName = FString::Printf(TEXT("TestGroup_%s"), *GenerateShortId()); + + Nakama::CreateGroup(ClientConfig, Session, GroupName, TEXT("Test description"), TEXT(""), TEXT("en"), true, 100) + .Next([this, Done](FNakamaGroupResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Group has ID", !Result.Value.Id.IsEmpty()); + TestTrue("Group has name", !Result.Value.Name.IsEmpty()); + CreatedGroupId = Result.Value.Id; + Done.Execute(); + }); + }); + + LatentIt("should fail with empty group name", [this](const FDoneDelegate& Done) + { + Nakama::CreateGroup(ClientConfig, Session, TEXT(""), TEXT("Description"), TEXT(""), TEXT("en"), true, 100) + .Next([this, Done](FNakamaGroupResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty name", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("ListGroups", [this]() + { + LatentIt("should list groups", [this](const FDoneDelegate& Done) + { + Nakama::ListGroups(ClientConfig, Session, TEXT(""), TEXT(""), 100, TEXT(""), 0, true) + .Next([this, Done](FNakamaGroupListResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Groups list is valid", true); + Done.Execute(); + }); + }); + }); + + Describe("ListUserGroups", [this]() + { + LatentIt("should list groups for user", [this](const FDoneDelegate& Done) + { + FString GroupName = FString::Printf(TEXT("UserGroup_%s"), *GenerateShortId()); + + Nakama::CreateGroup(ClientConfig, Session, GroupName, TEXT("Test"), TEXT(""), TEXT("en"), true, 100) + .Next([this](const FNakamaGroup&) + { + return Nakama::ListUserGroups(ClientConfig, Session, UserId, 100, 0, TEXT("")); + }) + .Next([this, Done](FNakamaUserGroupListResult ListResult) + { + ASYNC_FAIL_ON_ERROR(ListResult, Done); + TestTrue("User has at least one group", ListResult.Value.UserGroups.Num() >= 1); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// STORAGE TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncStorageSpec, "IntegrationTests.NakamaTests.Storage", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + FString UserId; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateShortId() { return FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaAsyncStorageSpec) + + +void FNakamaAsyncStorageSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id) + .Next([this](const FNakamaSession& AuthResult) + { + Session = AuthResult; + return Nakama::GetAccount(ClientConfig, Session); + }) + .Next([this, Done](FNakamaAccountResult AccResult) + { + ASYNC_FAIL_ON_ERROR(AccResult, Done); + UserId = AccResult.Value.User.Id; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("WriteStorageObjects", [this]() + { + LatentIt("should write storage object", [this](const FDoneDelegate& Done) + { + TArray Objects; + FNakamaWriteStorageObject Obj; + Obj.Collection = TEXT("test_collection"); + Obj.Key = FString::Printf(TEXT("test_key_%s"), *GenerateShortId()); + Obj.Value = TEXT("{\"test\": \"value\"}"); + Obj.PermissionRead = 1; + Obj.PermissionWrite = 1; + Objects.Add(Obj); + + Nakama::WriteStorageObjects(ClientConfig, Session, Objects).Next([this, Done](FNakamaStorageObjectAcksResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Got acks", Result.Value.Acks.Num() > 0); + Done.Execute(); + }); + }); + + LatentIt("should fail with invalid JSON value", [this](const FDoneDelegate& Done) + { + TArray Objects; + FNakamaWriteStorageObject Obj; + Obj.Collection = TEXT("test_collection"); + Obj.Key = TEXT("test_key"); + Obj.Value = TEXT("not valid json {{{"); + Obj.PermissionRead = 1; + Obj.PermissionWrite = 1; + Objects.Add(Obj); + + Nakama::WriteStorageObjects(ClientConfig, Session, Objects).Next([this, Done](FNakamaStorageObjectAcksResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for invalid JSON", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("ReadStorageObjects", [this]() + { + LatentIt("should read written storage object", [this](const FDoneDelegate& Done) + { + FString Key = FString::Printf(TEXT("read_key_%s"), *GenerateShortId()); + + TArray WriteObjects; + FNakamaWriteStorageObject WriteObj; + WriteObj.Collection = TEXT("test_collection"); + WriteObj.Key = Key; + WriteObj.Value = TEXT("{\"data\": \"test\"}"); + WriteObj.PermissionRead = 2; + WriteObj.PermissionWrite = 1; + WriteObjects.Add(WriteObj); + + Nakama::WriteStorageObjects(ClientConfig, Session, WriteObjects) + .Next([this, Key](const FNakamaStorageObjectAcks&) + { + TArray ReadIds; + FNakamaReadStorageObjectId ReadId; + ReadId.Collection = TEXT("test_collection"); + ReadId.Key = Key; + ReadId.UserId = UserId; + ReadIds.Add(ReadId); + + return Nakama::ReadStorageObjects(ClientConfig, Session, ReadIds); + }) + .Next([this, Done](FNakamaStorageObjectsResult ReadResult) + { + ASYNC_FAIL_ON_ERROR(ReadResult, Done); + TestTrue("Got objects", ReadResult.Value.Objects.Num() > 0); + Done.Execute(); + }); + }); + }); + + Describe("ListStorageObjects", [this]() + { + LatentIt("should list storage objects", [this](const FDoneDelegate& Done) + { + FString ShortId = GenerateShortId(); + TArray Objects; + for (int32 i = 0; i < 3; i++) + { + FNakamaWriteStorageObject Obj; + Obj.Collection = TEXT("list_collection"); + Obj.Key = FString::Printf(TEXT("list_key_%d_%s"), i, *ShortId); + Obj.Value = FString::Printf(TEXT("{\"index\": %d}"), i); + Obj.PermissionRead = 1; + Obj.PermissionWrite = 1; + Objects.Add(Obj); + } + Nakama::WriteStorageObjects(ClientConfig, Session, Objects) + .Next([this](const FNakamaStorageObjectAcks&) + { + return Nakama::ListStorageObjects(ClientConfig, Session, UserId, TEXT("list_collection"), 100, TEXT("")); + }) + .Next([this, Done](FNakamaStorageObjectListResult ListResult) + { + ASYNC_FAIL_ON_ERROR(ListResult, Done); + TestTrue("Has objects", ListResult.Value.Objects.Num() >= 3); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// LEADERBOARD TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncLeaderboardSpec, "IntegrationTests.NakamaTests.Leaderboard", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaAsyncLeaderboardSpec) + + +void FNakamaAsyncLeaderboardSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id).Next([this, Done](FNakamaSessionResult AuthResult) + { + if (AuthResult.bIsError) { AddError(FString::Printf(TEXT("Auth failed: %s"), *AuthResult.Error.Message)); Done.Execute(); return; } + Session = AuthResult.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("WriteLeaderboardRecord", [this]() + { + LatentIt("should fail with empty leaderboard ID", [this](const FDoneDelegate& Done) + { + // FNakamaWriteLeaderboardRecordRequest_LeaderboardRecordWrite Record; + // Record.Score = 100; + // Record.Subscore = 0; + // Record.Metadata = TEXT("{}"); + // Record.Operator = ENakamaOperator::NO_OVERRIDE; + + Nakama::WriteLeaderboardRecord(ClientConfig, Session, TEXT(""), 100, 0, TEXT("{}"), ENakamaOperator::NO_OVERRIDE) + .Next([this, Done](FNakamaLeaderboardRecordResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty leaderboard ID", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should fail with non-existent leaderboard", [this](const FDoneDelegate& Done) + { + //FNakamaWriteLeaderboardRecordRequest_LeaderboardRecordWrite Record; + //Record.Score = 100; + //Record.Subscore = 0; + //Record.Metadata = TEXT("{}"); + //Record.Operator = ENakamaOperator::NO_OVERRIDE; + + Nakama::WriteLeaderboardRecord(ClientConfig, Session, TEXT("nonexistent_leaderboard_12345"), 100, 0, TEXT("{}"), ENakamaOperator::NO_OVERRIDE) + .Next([this, Done](FNakamaLeaderboardRecordResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got NOT_FOUND error", Result.Error.Code == 5 || Result.Error.Message.Contains(TEXT("not found"))); + Done.Execute(); + }); + }); + }); + + Describe("ListLeaderboardRecords", [this]() + { + LatentIt("should fail with empty leaderboard ID", [this](const FDoneDelegate& Done) + { + Nakama::ListLeaderboardRecords(ClientConfig, Session, TEXT(""), TArray(), 100, TEXT(""), 0) + .Next([this, Done](FNakamaLeaderboardRecordListResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty ID", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should fail with limit too high", [this](const FDoneDelegate& Done) + { + Nakama::ListLeaderboardRecords(ClientConfig, Session, TEXT("test_leaderboard"), TArray(), 2000, TEXT(""), 0) + .Next([this, Done](FNakamaLeaderboardRecordListResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for limit too high", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("DeleteLeaderboardRecord", [this]() + { + LatentIt("should fail with empty leaderboard ID", [this](const FDoneDelegate& Done) + { + Nakama::DeleteLeaderboardRecord(ClientConfig, Session, TEXT("")) + .Next([this, Done](FNakamaVoidResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty leaderboard ID", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// MATCHES TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncMatchesSpec, "IntegrationTests.NakamaTests.Matches", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaAsyncMatchesSpec) + + +void FNakamaAsyncMatchesSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id).Next([this, Done](FNakamaSessionResult AuthResult) + { + if (AuthResult.bIsError) { AddError(FString::Printf(TEXT("Auth failed: %s"), *AuthResult.Error.Message)); Done.Execute(); return; } + Session = AuthResult.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("ListMatches", [this]() + { + LatentIt("should list matches", [this](const FDoneDelegate& Done) + { + Nakama::ListMatches(ClientConfig, Session, 10, false, TEXT(""), 0, 100, TEXT("")) + .Next([this, Done](FNakamaMatchListResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Matches list is valid", true); + Done.Execute(); + }); + }); + + LatentIt("should list authoritative matches only", [this](const FDoneDelegate& Done) + { + Nakama::ListMatches(ClientConfig, Session, 10, true, TEXT(""), 0, 100, TEXT("")) + .Next([this, Done](FNakamaMatchListResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Authoritative matches list is valid", true); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// NOTIFICATIONS TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncNotificationsSpec, "IntegrationTests.NakamaTests.Notifications", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaAsyncNotificationsSpec) + + +void FNakamaAsyncNotificationsSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id).Next([this, Done](FNakamaSessionResult AuthResult) + { + if (AuthResult.bIsError) { AddError(FString::Printf(TEXT("Auth failed: %s"), *AuthResult.Error.Message)); Done.Execute(); return; } + Session = AuthResult.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("ListNotifications", [this]() + { + LatentIt("should list notifications", [this](const FDoneDelegate& Done) + { + Nakama::ListNotifications(ClientConfig, Session, 100, TEXT("")) + .Next([this, Done](FNakamaNotificationListResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Notifications list is valid", true); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// LINK TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncLinkSpec, "IntegrationTests.NakamaTests.Link", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + FString DeviceId; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateShortId() { return FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaAsyncLinkSpec) + + +void FNakamaAsyncLinkSpec::Define() +{ + BeforeEach([this]() + { + DeviceId = GenerateId(); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountDevice Account; + Account.Id = DeviceId; + + Nakama::AuthenticateDevice(ClientConfig, true, TEXT(""), Account.Id).Next([this, Done](FNakamaSessionResult AuthResult) + { + if (AuthResult.bIsError) { AddError(FString::Printf(TEXT("Auth failed: %s"), *AuthResult.Error.Message)); Done.Execute(); return; } + Session = AuthResult.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("LinkCustom", [this]() + { + LatentIt("should link custom ID", [this](const FDoneDelegate& Done) + { + Nakama::LinkCustom(ClientConfig, Session, GenerateId(), TMap()) + .Next([this, Done](FNakamaVoidResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Link succeeded", true); + Done.Execute(); + }); + }); + + LatentIt("should fail with ID too short", [this](const FDoneDelegate& Done) + { + Nakama::LinkCustom(ClientConfig, Session, TEXT("abc"), TMap()) + .Next([this, Done](FNakamaVoidResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for short ID", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("LinkEmail", [this]() + { + LatentIt("should link email", [this](const FDoneDelegate& Done) + { + FString Email = FString::Printf(TEXT("link_%s@example.com"), *GenerateShortId()); + + Nakama::LinkEmail(ClientConfig, Session, Email, TEXT("password123"), TMap()) + .Next([this, Done](FNakamaVoidResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Link succeeded", true); + Done.Execute(); + }); + }); + + LatentIt("should fail with invalid email format", [this](const FDoneDelegate& Done) + { + Nakama::LinkEmail(ClientConfig, Session, TEXT("notanemail"), TEXT("password123"), TMap()) + .Next([this, Done](FNakamaVoidResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for invalid email", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should fail with password too short", [this](const FDoneDelegate& Done) + { + FString Email = FString::Printf(TEXT("short_%s@example.com"), *GenerateShortId()); + + Nakama::LinkEmail(ClientConfig, Session, Email, TEXT("short"), TMap()) + .Next([this, Done](FNakamaVoidResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for short password", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// TOURNAMENT TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncTournamentSpec, "IntegrationTests.NakamaTests.Tournament", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaAsyncTournamentSpec) + + +void FNakamaAsyncTournamentSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id).Next([this, Done](FNakamaSessionResult AuthResult) + { + if (AuthResult.bIsError) { AddError(FString::Printf(TEXT("Auth failed: %s"), *AuthResult.Error.Message)); Done.Execute(); return; } + Session = AuthResult.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("ListTournaments", [this]() + { + LatentIt("should list tournaments", [this](const FDoneDelegate& Done) + { + Nakama::ListTournaments(ClientConfig, Session, 0, 127, 0, 0, 100, TEXT("")) + .Next([this, Done](FNakamaTournamentListResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Tournaments list is valid", true); + Done.Execute(); + }); + }); + + LatentIt("should fail with category end too high", [this](const FDoneDelegate& Done) + { + Nakama::ListTournaments(ClientConfig, Session, 0, 200, 0, 0, 100, TEXT("")) + .Next([this, Done](FNakamaTournamentListResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for invalid category", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should fail with limit too high", [this](const FDoneDelegate& Done) + { + Nakama::ListTournaments(ClientConfig, Session, 0, 127, 0, 0, 2000, TEXT("")) + .Next([this, Done](FNakamaTournamentListResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for limit too high", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("JoinTournament", [this]() + { + LatentIt("should fail with empty tournament ID", [this](const FDoneDelegate& Done) + { + Nakama::JoinTournament(ClientConfig, Session, TEXT("")) + .Next([this, Done](FNakamaVoidResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty tournament ID", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should fail with non-existent tournament", [this](const FDoneDelegate& Done) + { + Nakama::JoinTournament(ClientConfig, Session, TEXT("nonexistent_tournament_12345")) + .Next([this, Done](FNakamaVoidResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got NOT_FOUND error", Result.Error.Code == 5 || Result.Error.Message.Contains(TEXT("not found"))); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// USERS TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncUsersSpec, "IntegrationTests.NakamaTests.Users", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + FString UserId; + FString Username; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateShortId() { return FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaAsyncUsersSpec) + + +void FNakamaAsyncUsersSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + Username = FString::Printf(TEXT("user_%s"), *GenerateShortId()); + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + Nakama::AuthenticateCustom(ClientConfig, true, Username, Account.Id) + .Next([this](const FNakamaSession& AuthResult) + { + Session = AuthResult; + return Nakama::GetAccount(ClientConfig, Session); + }) + .Next([this, Done](FNakamaAccountResult AccResult) + { + ASYNC_FAIL_ON_ERROR(AccResult, Done); + UserId = AccResult.Value.User.Id; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("GetUsers", [this]() + { + LatentIt("should get users by ID", [this](const FDoneDelegate& Done) + { + TArray Ids; + Ids.Add(UserId); + + Nakama::GetUsers(ClientConfig, Session, Ids, TArray(), TArray()) + .Next([this, Done](FNakamaUsersResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Got user", Result.Value.Users.Num() > 0); + if (Result.Value.Users.Num() > 0) + { + TestEqual("User ID matches", Result.Value.Users[0].Id, UserId); + } + Done.Execute(); + }); + }); + + LatentIt("should get users by username", [this](const FDoneDelegate& Done) + { + TArray Usernames; + Usernames.Add(Username); + + Nakama::GetUsers(ClientConfig, Session, TArray(), Usernames, TArray()) + .Next([this, Done](FNakamaUsersResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Got user by username", Result.Value.Users.Num() > 0); + Done.Execute(); + }); + }); + + LatentIt("should return empty for non-existent user", [this](const FDoneDelegate& Done) + { + TArray Ids; + Ids.Add(TEXT("ffffffff-ffff-ffff-ffff-ffffffffffff")); + + Nakama::GetUsers(ClientConfig, Session, Ids, TArray(), TArray()) + .Next([this, Done](FNakamaUsersResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Empty result for non-existent user", Result.Value.Users.Num() == 0); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// SESSION LOGOUT TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncSessionSpec, "IntegrationTests.NakamaTests.Session", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaAsyncSessionSpec) + + +void FNakamaAsyncSessionSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id).Next([this, Done](FNakamaSessionResult AuthResult) + { + if (AuthResult.bIsError) { AddError(FString::Printf(TEXT("Auth failed: %s"), *AuthResult.Error.Message)); Done.Execute(); return; } + Session = AuthResult.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("SessionLogout", [this]() + { + LatentIt("should logout successfully", [this](const FDoneDelegate& Done) + { + Nakama::SessionLogout(ClientConfig, Session, Session.Token, Session.RefreshToken) + .Next([this, Done](FNakamaVoidResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Logout succeeded", true); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// DELETE STORAGE TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncDeleteStorageSpec, "IntegrationTests.NakamaTests.DeleteStorage", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + FString UserId; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateShortId() { return FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaAsyncDeleteStorageSpec) + + +void FNakamaAsyncDeleteStorageSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id) + .Next([this](const FNakamaSession& AuthResult) + { + Session = AuthResult; + return Nakama::GetAccount(ClientConfig, Session); + }) + .Next([this, Done](FNakamaAccountResult AccResult) + { + ASYNC_FAIL_ON_ERROR(AccResult, Done); + UserId = AccResult.Value.User.Id; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("DeleteStorageObjects", [this]() + { + LatentIt("should delete storage object", [this](const FDoneDelegate& Done) + { + FString Key = FString::Printf(TEXT("delete_key_%s"), *GenerateShortId()); + + TArray WriteObjects; + FNakamaWriteStorageObject WriteObj; + WriteObj.Collection = TEXT("delete_collection"); + WriteObj.Key = Key; + WriteObj.Value = TEXT("{\"test\": \"delete\"}"); + WriteObj.PermissionRead = 1; + WriteObj.PermissionWrite = 1; + WriteObjects.Add(WriteObj); + + Nakama::WriteStorageObjects(ClientConfig, Session, WriteObjects) + .Next([this, Key](const FNakamaStorageObjectAcks&) + { + TArray DeleteIds; + FNakamaDeleteStorageObjectId DeleteId; + DeleteId.Collection = TEXT("delete_collection"); + DeleteId.Key = Key; + DeleteIds.Add(DeleteId); + + return Nakama::DeleteStorageObjects(ClientConfig, Session, DeleteIds); + }) + .Next([this, Done](FNakamaVoidResult DeleteResult) + { + ASYNC_FAIL_ON_ERROR(DeleteResult, Done); + TestTrue("Delete succeeded", true); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// GROUP OPERATIONS TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncGroupOpsSpec, "IntegrationTests.NakamaTests.GroupOps", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + FNakamaSession MemberSession; + FString UserId; + FString MemberUserId; + FString GroupId; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateShortId() { return FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaAsyncGroupOpsSpec) + + +void FNakamaAsyncGroupOpsSpec::Define() +{ + BeforeEach([this]() + { + GroupId.Empty(); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id) + .Next([this](const FNakamaSession& AuthResult) + { + Session = AuthResult; + return Nakama::GetAccount(ClientConfig, Session); + }) + .Next([this](const FNakamaAccount& AccResult) + { + UserId = AccResult.User.Id; + FNakamaAccountCustom MemberAccount; + MemberAccount.Id = GenerateId(); + return Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), MemberAccount.Id); + }) + .Next([this](const FNakamaSession& MemberAuthResult) + { + MemberSession = MemberAuthResult; + return Nakama::GetAccount(ClientConfig, MemberSession); + }) + .Next([this, Done](FNakamaAccountResult MemberAccResult) + { + ASYNC_FAIL_ON_ERROR(MemberAccResult, Done); + MemberUserId = MemberAccResult.Value.User.Id; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + Nakama::DeleteAccount(ClientConfig, MemberSession) + .Next([this](FNakamaVoidResult) { return Nakama::DeleteAccount(ClientConfig, Session); }) + .Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("JoinGroup", [this]() + { + LatentIt("should join open group", [this](const FDoneDelegate& Done) + { + FString GroupName = FString::Printf(TEXT("JoinGroup_%s"), *GenerateShortId()); + + Nakama::CreateGroup(ClientConfig, Session, GroupName, TEXT("Test"), TEXT(""), TEXT("en"), true, 100) + .Next([this](const FNakamaGroup& CreateResult) + { + GroupId = CreateResult.Id; + return Nakama::JoinGroup(ClientConfig, MemberSession, GroupId); + }) + .Next([this, Done](FNakamaVoidResult JoinResult) + { + ASYNC_FAIL_ON_ERROR(JoinResult, Done); + TestTrue("Joined open group", true); + Done.Execute(); + }); + }); + + LatentIt("should fail to join non-existent group", [this](const FDoneDelegate& Done) + { + Nakama::JoinGroup(ClientConfig, Session, TEXT("00000000-0000-0000-0000-000000000000")) + .Next([this, Done](FNakamaVoidResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for non-existent group", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("LeaveGroup", [this]() + { + LatentIt("should leave group", [this](const FDoneDelegate& Done) + { + FString GroupName = FString::Printf(TEXT("LeaveGroup_%s"), *GenerateShortId()); + + Nakama::CreateGroup(ClientConfig, Session, GroupName, TEXT("Test"), TEXT(""), TEXT("en"), true, 100) + .Next([this](const FNakamaGroup& CreateResult) + { + GroupId = CreateResult.Id; + return Nakama::JoinGroup(ClientConfig, MemberSession, GroupId); + }) + .Next([this](const FNakamaVoid&) + { + return Nakama::LeaveGroup(ClientConfig, MemberSession, GroupId); + }) + .Next([this, Done](FNakamaVoidResult LeaveResult) + { + ASYNC_FAIL_ON_ERROR(LeaveResult, Done); + TestTrue("Left group", true); + Done.Execute(); + }); + }); + }); + + Describe("UpdateGroup", [this]() + { + LatentIt("should update group name", [this](const FDoneDelegate& Done) + { + FString GroupName = FString::Printf(TEXT("UpdateGroup_%s"), *GenerateShortId()); + FString NewGroupName = FString::Printf(TEXT("Updated_%s"), *GenerateShortId()); + + Nakama::CreateGroup(ClientConfig, Session, GroupName, TEXT("Test"), TEXT(""), TEXT("en"), true, 100) + .Next([this, NewGroupName](const FNakamaGroup& CreateResult) + { + return Nakama::UpdateGroup(ClientConfig, Session, CreateResult.Id, NewGroupName, TEXT("Updated description"), TEXT(""), TEXT("en"), true); + }) + .Next([this, Done](FNakamaVoidResult UpdateResult) + { + ASYNC_FAIL_ON_ERROR(UpdateResult, Done); + TestTrue("Group updated", true); + Done.Execute(); + }); + }); + }); + + Describe("DeleteGroup", [this]() + { + LatentIt("should delete group as superadmin", [this](const FDoneDelegate& Done) + { + FString GroupName = FString::Printf(TEXT("DeleteGroup_%s"), *GenerateShortId()); + + Nakama::CreateGroup(ClientConfig, Session, GroupName, TEXT("Test"), TEXT(""), TEXT("en"), true, 100) + .Next([this](const FNakamaGroup& CreateResult) + { + return Nakama::DeleteGroup(ClientConfig, Session, CreateResult.Id); + }) + .Next([this, Done](FNakamaVoidResult DeleteResult) + { + ASYNC_FAIL_ON_ERROR(DeleteResult, Done); + TestTrue("Group deleted", true); + Done.Execute(); + }); + }); + }); + + Describe("ListGroupUsers", [this]() + { + LatentIt("should list group users", [this](const FDoneDelegate& Done) + { + FString GroupName = FString::Printf(TEXT("ListUsers_%s"), *GenerateShortId()); + + Nakama::CreateGroup(ClientConfig, Session, GroupName, TEXT("Test"), TEXT(""), TEXT("en"), true, 100) + .Next([this](const FNakamaGroup& CreateResult) + { + return Nakama::ListGroupUsers(ClientConfig, Session, CreateResult.Id, 100, 0, TEXT("")); + }) + .Next([this, Done](FNakamaGroupUserListResult ListResult) + { + ASYNC_FAIL_ON_ERROR(ListResult, Done); + TestTrue("Has at least creator", ListResult.Value.GroupUsers.Num() >= 1); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// RPC TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncRpcSpec, "IntegrationTests.NakamaTests.RPC", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaAsyncRpcSpec) + + +void FNakamaAsyncRpcSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id).Next([this, Done](FNakamaSessionResult AuthResult) + { + if (AuthResult.bIsError) { AddError(FString::Printf(TEXT("Auth failed: %s"), *AuthResult.Error.Message)); Done.Execute(); return; } + Session = AuthResult.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("RpcFunc", [this]() + { + LatentIt("should fail with empty RPC ID", [this](const FDoneDelegate& Done) + { + Nakama::RpcFunc(ClientConfig, Session, TEXT(""), TEXT("")) + .Next([this, Done](FNakamaRpcResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty RPC ID", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should fail with non-existent RPC", [this](const FDoneDelegate& Done) + { + Nakama::RpcFunc(ClientConfig, Session, TEXT("nonexistent_rpc_function"), TEXT("")) + .Next([this, Done](FNakamaRpcResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for non-existent RPC", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// AUTH EXTENDED TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncAuthExtSpec, "IntegrationTests.NakamaTests.AuthExt", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateShortId() { return FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaAsyncAuthExtSpec) + + +void FNakamaAsyncAuthExtSpec::Define() +{ + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + Describe("CustomAuthExtended", [this]() + { + LatentIt("should fail with create=false for non-existent user", [this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + Nakama::AuthenticateCustom(ClientConfig, false, TEXT(""), Account.Id).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got NOT_FOUND error", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should authenticate existing user with create=false", [this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id) + .Next([this, Account](const FNakamaSession&) + { + return Nakama::AuthenticateCustom(ClientConfig, false, TEXT(""), Account.Id); + }) + .Next([this, Done](FNakamaSessionResult SecondResult) + { + ASYNC_FAIL_ON_ERROR(SecondResult, Done); + TestTrue("Session has token", !SecondResult.Value.Token.IsEmpty()); + Session = SecondResult.Value; + Done.Execute(); + }); + }); + }); + + Describe("DeviceAuthExtended", [this]() + { + LatentIt("should fail with device ID too long", [this](const FDoneDelegate& Done) + { + FNakamaAccountDevice Account; + Account.Id = FString::ChrN(150, 'a'); + + Nakama::AuthenticateDevice(ClientConfig, true, TEXT(""), Account.Id).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for long device ID", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("FacebookAuth", [this]() + { + LatentIt("should fail with empty token", [this](const FDoneDelegate& Done) + { + FNakamaAccountFacebook Account; + Account.Token = TEXT(""); + + Nakama::AuthenticateFacebook(ClientConfig, true, TEXT(""), false, Account.Token).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty token", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("GoogleAuth", [this]() + { + LatentIt("should fail with empty token", [this](const FDoneDelegate& Done) + { + FNakamaAccountGoogle Account; + Account.Token = TEXT(""); + + Nakama::AuthenticateGoogle(ClientConfig, true, TEXT(""), Account.Token).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty token", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("SteamAuth", [this]() + { + LatentIt("should fail with empty token", [this](const FDoneDelegate& Done) + { + FNakamaAccountSteam Account; + Account.Token = TEXT(""); + + Nakama::AuthenticateSteam(ClientConfig, true, TEXT(""), false, Account.Token).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty token", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("AppleAuth", [this]() + { + LatentIt("should fail with empty token", [this](const FDoneDelegate& Done) + { + FNakamaAccountApple Account; + Account.Token = TEXT(""); + + Nakama::AuthenticateApple(ClientConfig, true, TEXT(""), Account.Token).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty token", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// DELETE FRIENDS TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncDeleteFriendsSpec, "IntegrationTests.NakamaTests.DeleteFriends", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + FNakamaSession FriendSession; + FString UserId; + FString FriendUserId; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaAsyncDeleteFriendsSpec) + + +void FNakamaAsyncDeleteFriendsSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id) + .Next([this](const FNakamaSession& AuthResult) + { + Session = AuthResult; + return Nakama::GetAccount(ClientConfig, Session); + }) + .Next([this](const FNakamaAccount& AccResult) + { + UserId = AccResult.User.Id; + FNakamaAccountCustom FriendAccount; + FriendAccount.Id = GenerateId(); + return Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), FriendAccount.Id); + }) + .Next([this](const FNakamaSession& FriendAuthResult) + { + FriendSession = FriendAuthResult; + return Nakama::GetAccount(ClientConfig, FriendSession); + }) + .Next([this, Done](FNakamaAccountResult FriendAccResult) + { + ASYNC_FAIL_ON_ERROR(FriendAccResult, Done); + FriendUserId = FriendAccResult.Value.User.Id; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + auto DeleteMain = [this, Done]() + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }; + + if (FriendSession.Token.IsEmpty()) { DeleteMain(); return; } + Nakama::DeleteAccount(ClientConfig, FriendSession).Next([DeleteMain](FNakamaVoidResult) { DeleteMain(); }); + }); + + + + Describe("DeleteFriends", [this]() + { + LatentIt("should delete friend by ID", [this](const FDoneDelegate& Done) + { + TArray AddIds; + AddIds.Add(FriendUserId); + + Nakama::AddFriends(ClientConfig, Session, AddIds, TArray(), TEXT("")) + .Next([this](const FNakamaVoid&) + { + TArray DeleteIds; + DeleteIds.Add(FriendUserId); + return Nakama::DeleteFriends(ClientConfig, Session, DeleteIds, TArray()); + }) + .Next([this, Done](FNakamaVoidResult DeleteResult) + { + ASYNC_FAIL_ON_ERROR(DeleteResult, Done); + TestTrue("Friend deleted", true); + Done.Execute(); + }); + }); + + LatentIt("should succeed when deleting non-friend", [this](const FDoneDelegate& Done) + { + TArray DeleteIds; + DeleteIds.Add(FriendUserId); + + Nakama::DeleteFriends(ClientConfig, Session, DeleteIds, TArray()).Next([this, Done](FNakamaVoidResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Delete non-friend succeeded (no-op)", true); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// IMPORT TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncImportSpec, "IntegrationTests.NakamaTests.Import", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaAsyncImportSpec) + + +void FNakamaAsyncImportSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id).Next([this, Done](FNakamaSessionResult AuthResult) + { + if (AuthResult.bIsError) { AddError(FString::Printf(TEXT("Auth failed: %s"), *AuthResult.Error.Message)); Done.Execute(); return; } + Session = AuthResult.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("ImportSteamFriends", [this]() + { + LatentIt("should fail with empty token", [this](const FDoneDelegate& Done) + { + FNakamaAccountSteam Account; + Account.Token = TEXT(""); + + Nakama::ImportSteamFriends(ClientConfig, Session, false, Account.Token) + .Next([this, Done](FNakamaVoidResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty token", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("ImportFacebookFriends", [this]() + { + LatentIt("should fail with empty token", [this](const FDoneDelegate& Done) + { + FNakamaAccountFacebook Account; + Account.Token = TEXT(""); + + Nakama::ImportFacebookFriends(ClientConfig, Session, false, Account.Token) + .Next([this, Done](FNakamaVoidResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty token", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// ADDITIONAL ACCOUNT TESTS (Async) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncAccountExtSpec, "IntegrationTests.NakamaTests.AccountExt", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + FString UserId; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateShortId() { return FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaAsyncAccountExtSpec) + + +void FNakamaAsyncAccountExtSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id) + .Next([this](const FNakamaSession& AuthResult) + { + Session = AuthResult; + return Nakama::GetAccount(ClientConfig, Session); + }) + .Next([this, Done](FNakamaAccountResult AccResult) + { + ASYNC_FAIL_ON_ERROR(AccResult, Done); + UserId = AccResult.Value.User.Id; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("UpdateAccountExtended", [this]() + { + LatentIt("should update all fields", [this](const FDoneDelegate& Done) + { + Nakama::UpdateAccount(ClientConfig, Session, TEXT(""), FString::Printf(TEXT("Display_%s"), *GenerateShortId()), TEXT("https://example.com/avatar.png"), TEXT("es"), TEXT("US"), TEXT("America/New_York")) + .Next([this, Done](FNakamaVoidResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Update all fields succeeded", true); + Done.Execute(); + }); + }); + + LatentIt("should update lang tag", [this](const FDoneDelegate& Done) + { + Nakama::UpdateAccount(ClientConfig, Session, TEXT(""), TEXT(""), TEXT(""), TEXT("fr"), TEXT(""), TEXT("")) + .Next([this](const FNakamaVoid&) + { + return Nakama::GetAccount(ClientConfig, Session); + }) + .Next([this, Done](FNakamaAccountResult AccResult) + { + ASYNC_FAIL_ON_ERROR(AccResult, Done); + TestEqual("Lang tag updated", AccResult.Value.User.LangTag, TEXT("fr")); + Done.Execute(); + }); + }); + + LatentIt("should update location", [this](const FDoneDelegate& Done) + { + Nakama::UpdateAccount(ClientConfig, Session, TEXT(""), TEXT(""), TEXT(""), TEXT(""), TEXT("London"), TEXT("")) + .Next([this](const FNakamaVoid&) + { + return Nakama::GetAccount(ClientConfig, Session); + }) + .Next([this, Done](FNakamaAccountResult AccResult) + { + ASYNC_FAIL_ON_ERROR(AccResult, Done); + TestEqual("Location updated", AccResult.Value.User.Location, TEXT("London")); + Done.Execute(); + }); + }); + + LatentIt("should update timezone", [this](const FDoneDelegate& Done) + { + Nakama::UpdateAccount(ClientConfig, Session, TEXT(""), TEXT(""), TEXT(""), TEXT(""), TEXT(""), TEXT("Europe/London")) + .Next([this](const FNakamaVoid&) + { + return Nakama::GetAccount(ClientConfig, Session); + }) + .Next([this, Done](FNakamaAccountResult AccResult) + { + ASYNC_FAIL_ON_ERROR(AccResult, Done); + TestEqual("Timezone updated", AccResult.Value.User.Timezone, TEXT("Europe/London")); + Done.Execute(); + }); + }); + + LatentIt("should update avatar URL", [this](const FDoneDelegate& Done) + { + FString NewAvatarUrl = TEXT("https://example.com/new-avatar.png"); + Nakama::UpdateAccount(ClientConfig, Session, TEXT(""), TEXT(""), NewAvatarUrl, TEXT(""), TEXT(""), TEXT("")) + .Next([this](const FNakamaVoid&) + { + return Nakama::GetAccount(ClientConfig, Session); + }) + .Next([this, Done, NewAvatarUrl](FNakamaAccountResult AccResult) + { + ASYNC_FAIL_ON_ERROR(AccResult, Done); + TestEqual("Avatar URL updated", AccResult.Value.User.AvatarUrl, NewAvatarUrl); + Done.Execute(); + }); + }); + }); + + Describe("GetAccountDetails", [this]() + { + LatentIt("should return account with devices", [this](const FDoneDelegate& Done) + { + Nakama::GetAccount(ClientConfig, Session).Next([this, Done](FNakamaAccountResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Account has user", !Result.Value.User.Id.IsEmpty()); + TestTrue("Account has create time", Result.Value.User.CreateTime != FDateTime(0)); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// ADDITIONAL FRIENDS TESTS (Async) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncFriendsExtSpec, "IntegrationTests.NakamaTests.FriendsExt", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + FNakamaSession FriendSession; + FString UserId; + FString FriendUserId; + FString FriendUsername; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateShortId() { return FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaAsyncFriendsExtSpec) + + +void FNakamaAsyncFriendsExtSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + FriendUsername = FString::Printf(TEXT("friend_%s"), *GenerateShortId()); + + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id) + .Next([this](const FNakamaSession& AuthResult) + { + Session = AuthResult; + return Nakama::GetAccount(ClientConfig, Session); + }) + .Next([this](const FNakamaAccount& AccResult) + { + UserId = AccResult.User.Id; + FNakamaAccountCustom FriendAccount; + FriendAccount.Id = GenerateId(); + return Nakama::AuthenticateCustom(ClientConfig, true, FriendUsername, FriendAccount.Id); + }) + .Next([this](const FNakamaSession& FriendResult) + { + FriendSession = FriendResult; + return Nakama::GetAccount(ClientConfig, FriendSession); + }) + .Next([this, Done](FNakamaAccountResult FriendAccResult) + { + ASYNC_FAIL_ON_ERROR(FriendAccResult, Done); + FriendUserId = FriendAccResult.Value.User.Id; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + auto DeleteMain = [this, Done]() + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }; + + if (FriendSession.Token.IsEmpty()) { DeleteMain(); return; } + Nakama::DeleteAccount(ClientConfig, FriendSession).Next([DeleteMain](FNakamaVoidResult) { DeleteMain(); }); + }); + + + + Describe("ListFriendsWithFilters", [this]() + { + LatentIt("should list friends with limit", [this](const FDoneDelegate& Done) + { + Nakama::ListFriends(ClientConfig, Session, 5, 0, TEXT("")).Next([this, Done](FNakamaFriendListResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("List friends with limit succeeded", true); + Done.Execute(); + }); + }); + + LatentIt("should list friends with state filter", [this](const FDoneDelegate& Done) + { + Nakama::ListFriends(ClientConfig, Session, 100, 0, TEXT("")).Next([this, Done](FNakamaFriendListResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("List friends by state succeeded", true); + Done.Execute(); + }); + }); + + LatentIt("should list blocked friends", [this](const FDoneDelegate& Done) + { + Nakama::ListFriends(ClientConfig, Session, 100, 3, TEXT("")).Next([this, Done](FNakamaFriendListResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("List blocked friends succeeded", true); + Done.Execute(); + }); + }); + + LatentIt("should return invited friend when filtering by state 1", [this](const FDoneDelegate& Done) + { + Nakama::AddFriends(ClientConfig, Session, {FriendUserId}, {}, TEXT("")) + .Next([this](const FNakamaVoid&) -> TNakamaFuture + { + return Nakama::ListFriends(ClientConfig, Session, {}, FNakamaOptionalInt32(1), TEXT("")); + }) + .Next([this, Done](FNakamaFriendListResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestEqual("state=1 filter returns the invited friend", Result.Value.Friends.Num(), 1); + if (Result.Value.Friends.Num() > 0) + TestEqual("Returned friend is the invited user", Result.Value.Friends[0].User.Id, FriendUserId); + Done.Execute(); + }); + }); + + LatentIt("should return mutual friend when filtering by state 0", [this](const FDoneDelegate& Done) + { + Nakama::AddFriends(ClientConfig, Session, {FriendUserId}, {}, TEXT("")) + .Next([this](const FNakamaVoid&) -> TNakamaFuture + { + return Nakama::AddFriends(ClientConfig, FriendSession, {UserId}, {}, TEXT("")); + }) + .Next([this](const FNakamaVoid&) -> TNakamaFuture + { + return Nakama::ListFriends(ClientConfig, Session, {}, FNakamaOptionalInt32(0), TEXT("")); + }) + .Next([this, Done](FNakamaFriendListResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestEqual("state=0 filter returns the mutual friend", Result.Value.Friends.Num(), 1); + if (Result.Value.Friends.Num() > 0) + TestEqual("Returned friend is the mutual user", Result.Value.Friends[0].User.Id, FriendUserId); + Done.Execute(); + }); + }); + }); + + Describe("AddFriendsByUsername", [this]() + { + LatentIt("should add friend by username", [this](const FDoneDelegate& Done) + { + TArray Usernames; + Usernames.Add(FriendUsername); + Nakama::AddFriends(ClientConfig, Session, TArray(), Usernames, TEXT("")).Next([this, Done](FNakamaVoidResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("AddFriends by username succeeded", true); + Done.Execute(); + }); + }); + + LatentIt("should handle invalid user ID format gracefully", [this](const FDoneDelegate& Done) + { + TArray Ids; + Ids.Add(TEXT("not-a-valid-uuid")); + Nakama::AddFriends(ClientConfig, Session, Ids, TArray(), TEXT("")).Next([this, Done](FNakamaVoidResult Result) + { + TestTrue("Server handled invalid UUID", true); + Done.Execute(); + }); + }); + }); + + Describe("DeleteFriendsByUsername", [this]() + { + LatentIt("should delete friend by username", [this](const FDoneDelegate& Done) + { + TArray AddUsernames; + AddUsernames.Add(FriendUsername); + Nakama::AddFriends(ClientConfig, Session, TArray(), AddUsernames, TEXT("")) + .Next([this](const FNakamaVoid&) + { + TArray DeleteUsernames; + DeleteUsernames.Add(FriendUsername); + return Nakama::DeleteFriends(ClientConfig, Session, TArray(), DeleteUsernames); + }) + .Next([this, Done](FNakamaVoidResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Delete friend by username succeeded", true); + Done.Execute(); + }); + }); + }); + + Describe("BlockFriendsByUsername", [this]() + { + LatentIt("should block user by username", [this](const FDoneDelegate& Done) + { + TArray Usernames; + Usernames.Add(FriendUsername); + Nakama::BlockFriends(ClientConfig, Session, TArray(), Usernames).Next([this, Done](FNakamaVoidResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Block by username succeeded", true); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// ADDITIONAL STORAGE TESTS (Async) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncStorageExtSpec, "IntegrationTests.NakamaTests.StorageExt", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + FString UserId; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateShortId() { return FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaAsyncStorageExtSpec) + + +void FNakamaAsyncStorageExtSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id) + .Next([this](const FNakamaSession& AuthResult) + { + Session = AuthResult; + return Nakama::GetAccount(ClientConfig, Session); + }) + .Next([this, Done](FNakamaAccountResult AccResult) + { + ASYNC_FAIL_ON_ERROR(AccResult, Done); + UserId = AccResult.Value.User.Id; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("WriteStorageMultiple", [this]() + { + LatentIt("should write multiple storage objects", [this](const FDoneDelegate& Done) + { + TArray Objects; + for (int32 i = 0; i < 5; i++) + { + FNakamaWriteStorageObject Object; + Object.Collection = TEXT("multi_collection"); + Object.Key = FString::Printf(TEXT("multi_key_%d_%s"), i, *GenerateShortId()); + Object.Value = FString::Printf(TEXT("{\"index\": %d}"), i); + Object.PermissionRead = 1; + Object.PermissionWrite = 1; + Objects.Add(Object); + } + Nakama::WriteStorageObjects(ClientConfig, Session, Objects).Next([this, Done](FNakamaStorageObjectAcksResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestEqual("Got 5 acks", Result.Value.Acks.Num(), 5); + Done.Execute(); + }); + }); + }); + + Describe("WriteStorageValidation", [this]() + { + LatentIt("should fail with empty collection", [this](const FDoneDelegate& Done) + { + TArray Objects; + FNakamaWriteStorageObject Object; + Object.Collection = TEXT(""); + Object.Key = TEXT("test_key"); + Object.Value = TEXT("{\"test\": true}"); + Objects.Add(Object); + Nakama::WriteStorageObjects(ClientConfig, Session, Objects).Next([this, Done](FNakamaStorageObjectAcksResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty collection", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should fail with empty key", [this](const FDoneDelegate& Done) + { + TArray Objects; + FNakamaWriteStorageObject Object; + Object.Collection = TEXT("test_collection"); + Object.Key = TEXT(""); + Object.Value = TEXT("{\"test\": true}"); + Objects.Add(Object); + Nakama::WriteStorageObjects(ClientConfig, Session, Objects).Next([this, Done](FNakamaStorageObjectAcksResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty key", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should write with public read permission", [this](const FDoneDelegate& Done) + { + TArray Objects; + FNakamaWriteStorageObject Object; + Object.Collection = TEXT("public_collection"); + Object.Key = FString::Printf(TEXT("public_key_%s"), *GenerateShortId()); + Object.Value = TEXT("{\"public\": true}"); + Object.PermissionRead = 2; + Object.PermissionWrite = 1; + Objects.Add(Object); + Nakama::WriteStorageObjects(ClientConfig, Session, Objects).Next([this, Done](FNakamaStorageObjectAcksResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Write with public permission succeeded", Result.Value.Acks.Num() > 0); + Done.Execute(); + }); + }); + + LatentIt("should fail with JSON array as value", [this](const FDoneDelegate& Done) + { + TArray Objects; + FNakamaWriteStorageObject Object; + Object.Collection = TEXT("test_collection"); + Object.Key = TEXT("array_key"); + Object.Value = TEXT("[1, 2, 3]"); + Objects.Add(Object); + Nakama::WriteStorageObjects(ClientConfig, Session, Objects).Next([this, Done](FNakamaStorageObjectAcksResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for JSON array", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("ReadStorageValidation", [this]() + { + LatentIt("should return empty for non-existent key", [this](const FDoneDelegate& Done) + { + TArray ReadIds; + FNakamaReadStorageObjectId ReadId; + ReadId.Collection = TEXT("nonexistent_collection"); + ReadId.Key = TEXT("nonexistent_key"); + ReadId.UserId = UserId; + ReadIds.Add(ReadId); + Nakama::ReadStorageObjects(ClientConfig, Session, ReadIds).Next([this, Done](FNakamaStorageObjectsResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Empty result for non-existent key", Result.Value.Objects.Num() == 0); + Done.Execute(); + }); + }); + + LatentIt("should read multiple objects", [this](const FDoneDelegate& Done) + { + TArray WriteObjects; + TArray Keys; + for (int32 i = 0; i < 3; i++) + { + FString Key = FString::Printf(TEXT("read_multi_%d_%s"), i, *GenerateShortId()); + Keys.Add(Key); + FNakamaWriteStorageObject Object; + Object.Collection = TEXT("read_multi_collection"); + Object.Key = Key; + Object.Value = FString::Printf(TEXT("{\"index\": %d}"), i); + Object.PermissionRead = 1; + Object.PermissionWrite = 1; + WriteObjects.Add(Object); + } + Nakama::WriteStorageObjects(ClientConfig, Session, WriteObjects) + .Next([this, Keys](const FNakamaStorageObjectAcks&) + { + TArray ReadIds; + for (const FString& Key : Keys) + { + FNakamaReadStorageObjectId ReadId; + ReadId.Collection = TEXT("read_multi_collection"); + ReadId.Key = Key; + ReadId.UserId = UserId; + ReadIds.Add(ReadId); + } + return Nakama::ReadStorageObjects(ClientConfig, Session, ReadIds); + }) + .Next([this, Done](FNakamaStorageObjectsResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestEqual("Read 3 objects", Result.Value.Objects.Num(), 3); + Done.Execute(); + }); + }); + }); + + Describe("ListStorageValidation", [this]() + { + LatentIt("should list with small limit", [this](const FDoneDelegate& Done) + { + TArray Objects; + for (int32 i = 0; i < 5; i++) + { + FNakamaWriteStorageObject Object; + Object.Collection = TEXT("list_limit_collection"); + Object.Key = FString::Printf(TEXT("limit_key_%d_%s"), i, *GenerateShortId()); + Object.Value = FString::Printf(TEXT("{\"index\": %d}"), i); + Object.PermissionRead = 1; + Object.PermissionWrite = 1; + Objects.Add(Object); + } + Nakama::WriteStorageObjects(ClientConfig, Session, Objects) + .Next([this](const FNakamaStorageObjectAcks&) + { + return Nakama::ListStorageObjects(ClientConfig, Session, UserId, TEXT("list_limit_collection"), 2, TEXT("")); + }) + .Next([this, Done](FNakamaStorageObjectListResult ListResult) + { + ASYNC_FAIL_ON_ERROR(ListResult, Done); + TestTrue("List respects limit", ListResult.Value.Objects.Num() <= 2); + Done.Execute(); + }); + }); + }); + + Describe("DeleteStorageValidation", [this]() + { + LatentIt("should delete multiple objects", [this](const FDoneDelegate& Done) + { + TArray WriteObjects; + TArray Keys; + for (int32 i = 0; i < 3; i++) + { + FString Key = FString::Printf(TEXT("delete_multi_%d_%s"), i, *GenerateShortId()); + Keys.Add(Key); + FNakamaWriteStorageObject Object; + Object.Collection = TEXT("delete_multi_collection"); + Object.Key = Key; + Object.Value = FString::Printf(TEXT("{\"index\": %d}"), i); + Object.PermissionRead = 1; + Object.PermissionWrite = 1; + WriteObjects.Add(Object); + } + Nakama::WriteStorageObjects(ClientConfig, Session, WriteObjects) + .Next([this, Keys](const FNakamaStorageObjectAcks&) + { + TArray DeleteIds; + for (const FString& Key : Keys) + { + FNakamaDeleteStorageObjectId DeleteId; + DeleteId.Collection = TEXT("delete_multi_collection"); + DeleteId.Key = Key; + DeleteIds.Add(DeleteId); + } + return Nakama::DeleteStorageObjects(ClientConfig, Session, DeleteIds); + }) + .Next([this, Done](FNakamaVoidResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Delete multiple objects succeeded", true); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// ADDITIONAL GROUP TESTS (Async) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncGroupExtSpec, "IntegrationTests.NakamaTests.GroupExt", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + FNakamaSession MemberSession; + FString UserId; + FString MemberUserId; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateShortId() { return FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaAsyncGroupExtSpec) + + +void FNakamaAsyncGroupExtSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id) + .Next([this](const FNakamaSession& AuthResult) + { + Session = AuthResult; + return Nakama::GetAccount(ClientConfig, Session); + }) + .Next([this](const FNakamaAccount& AccResult) + { + UserId = AccResult.User.Id; + FNakamaAccountCustom MemberAccount; + MemberAccount.Id = GenerateId(); + return Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), MemberAccount.Id); + }) + .Next([this](const FNakamaSession& MemberResult) + { + MemberSession = MemberResult; + return Nakama::GetAccount(ClientConfig, MemberSession); + }) + .Next([this, Done](FNakamaAccountResult MemberAccResult) + { + ASYNC_FAIL_ON_ERROR(MemberAccResult, Done); + MemberUserId = MemberAccResult.Value.User.Id; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + Nakama::DeleteAccount(ClientConfig, MemberSession) + .Next([this](FNakamaVoidResult) { return Nakama::DeleteAccount(ClientConfig, Session); }) + .Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("CreateGroupValidation", [this]() + { + LatentIt("should create closed group", [this](const FDoneDelegate& Done) + { + Nakama::CreateGroup(ClientConfig, Session, FString::Printf(TEXT("ClosedGroup_%s"), *GenerateShortId()), TEXT("Closed group for testing"), TEXT(""), TEXT("en"), false, 100) + .Next([this, Done](FNakamaGroupResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Group created", !Result.Value.Id.IsEmpty()); + TestTrue("Open has value", Result.Value.Open.bIsSet); + TestFalse("Group is closed", Result.Value.Open.Value); + Done.Execute(); + }); + }); + + LatentIt("should create group with description and lang tag", [this](const FDoneDelegate& Done) + { + Nakama::CreateGroup(ClientConfig, Session, FString::Printf(TEXT("MetaGroup_%s"), *GenerateShortId()), TEXT("Group with description"), TEXT(""), TEXT("es"), true, 100) + .Next([this, Done](FNakamaGroupResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Group created with description", !Result.Value.Id.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("ListGroupsWithFilters", [this]() + { + LatentIt("should list groups by name prefix", [this](const FDoneDelegate& Done) + { + Nakama::CreateGroup(ClientConfig, Session, FString::Printf(TEXT("SearchGroup_%s"), *GenerateShortId()), TEXT("Test"), TEXT(""), TEXT("en"), true, 100) + .Next([this](const FNakamaGroup&) + { + return Nakama::ListGroups(ClientConfig, Session, TEXT("SearchGroup"), TEXT(""), 100, TEXT(""), 0, false); + }) + .Next([this, Done](FNakamaGroupListResult Result) + { + if (Result.bIsError) { TestTrue("Server handled name filter", true); Done.Execute(); return; } + TestTrue("Found groups by name", Result.Value.Groups.Num() >= 1); + Done.Execute(); + }); + }); + + LatentIt("should list groups with limit", [this](const FDoneDelegate& Done) + { + Nakama::ListGroups(ClientConfig, Session, TEXT(""), TEXT(""), 5, TEXT(""), 0, true).Next([this, Done](FNakamaGroupListResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("List respects limit", Result.Value.Groups.Num() <= 5); + Done.Execute(); + }); + }); + }); + + Describe("GroupMemberOperations", [this]() + { + LatentIt("should add user to group", [this](const FDoneDelegate& Done) + { + Nakama::CreateGroup(ClientConfig, Session, FString::Printf(TEXT("AddUserGroup_%s"), *GenerateShortId()), TEXT("Test"), TEXT(""), TEXT("en"), true, 100) + .Next([this](const FNakamaGroup& GroupResult) + { + TArray UserIds; + UserIds.Add(MemberUserId); + return Nakama::AddGroupUsers(ClientConfig, Session, GroupResult.Id, UserIds); + }) + .Next([this, Done](FNakamaVoidResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Added user to group", true); + Done.Execute(); + }); + }); + + LatentIt("should kick user from group", [this](const FDoneDelegate& Done) + { + auto GId = MakeShared(); + Nakama::CreateGroup(ClientConfig, Session, FString::Printf(TEXT("KickGroup_%s"), *GenerateShortId()), TEXT("Test"), TEXT(""), TEXT("en"), true, 100) + .Next([this, GId](const FNakamaGroup& GroupResult) + { + *GId = GroupResult.Id; + TArray UserIds; + UserIds.Add(MemberUserId); + return Nakama::AddGroupUsers(ClientConfig, Session, *GId, UserIds); + }) + .Next([this, GId](const FNakamaVoid&) + { + TArray KickIds; + KickIds.Add(MemberUserId); + return Nakama::KickGroupUsers(ClientConfig, Session, *GId, KickIds); + }) + .Next([this, Done](FNakamaVoidResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Kicked user from group", true); + Done.Execute(); + }); + }); + + LatentIt("should ban user from group", [this](const FDoneDelegate& Done) + { + Nakama::CreateGroup(ClientConfig, Session, FString::Printf(TEXT("BanGroup_%s"), *GenerateShortId()), TEXT("Test"), TEXT(""), TEXT("en"), true, 100) + .Next([this](const FNakamaGroup& GroupResult) + { + TArray UserIds; + UserIds.Add(MemberUserId); + return Nakama::BanGroupUsers(ClientConfig, Session, GroupResult.Id, UserIds); + }) + .Next([this, Done](FNakamaVoidResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Banned user from group", true); + Done.Execute(); + }); + }); + + LatentIt("should promote user in group", [this](const FDoneDelegate& Done) + { + auto GId = MakeShared(); + Nakama::CreateGroup(ClientConfig, Session, FString::Printf(TEXT("PromoteGroup_%s"), *GenerateShortId()), TEXT("Test"), TEXT(""), TEXT("en"), true, 100) + .Next([this, GId](const FNakamaGroup& GroupResult) + { + *GId = GroupResult.Id; + TArray UserIds; + UserIds.Add(MemberUserId); + return Nakama::AddGroupUsers(ClientConfig, Session, *GId, UserIds); + }) + .Next([this, GId](const FNakamaVoid&) + { + TArray PromoteIds; + PromoteIds.Add(MemberUserId); + return Nakama::PromoteGroupUsers(ClientConfig, Session, *GId, PromoteIds); + }) + .Next([this, Done](FNakamaVoidResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Promoted user in group", true); + Done.Execute(); + }); + }); + + LatentIt("should demote user in group", [this](const FDoneDelegate& Done) + { + auto GId = MakeShared(); + FString TargetUserId = MemberUserId; + Nakama::CreateGroup(ClientConfig, Session, FString::Printf(TEXT("DemoteGroup_%s"), *GenerateShortId()), TEXT("Test"), TEXT(""), TEXT("en"), true, 100) + .Next([this, GId, TargetUserId](const FNakamaGroup& GroupResult) + { + *GId = GroupResult.Id; + TArray UserIds; + UserIds.Add(TargetUserId); + return Nakama::AddGroupUsers(ClientConfig, Session, *GId, UserIds); + }) + .Next([this, GId, TargetUserId](const FNakamaVoid&) + { + TArray PromoteIds; + PromoteIds.Add(TargetUserId); + return Nakama::PromoteGroupUsers(ClientConfig, Session, *GId, PromoteIds); + }) + .Next([this, GId, TargetUserId](const FNakamaVoid&) + { + TArray DemoteIds; + DemoteIds.Add(TargetUserId); + return Nakama::DemoteGroupUsers(ClientConfig, Session, *GId, DemoteIds); + }) + .Next([this, Done](FNakamaVoidResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Demoted user in group", true); + Done.Execute(); + }); + }); + }); + + Describe("ListGroupUsersWithState", [this]() + { + LatentIt("should list group users with state filter", [this](const FDoneDelegate& Done) + { + Nakama::CreateGroup(ClientConfig, Session, FString::Printf(TEXT("ListUsersState_%s"), *GenerateShortId()), TEXT("Test"), TEXT(""), TEXT("en"), true, 100) + .Next([this](const FNakamaGroup& GroupResult) + { + return Nakama::ListGroupUsers(ClientConfig, Session, GroupResult.Id, 100, 0, TEXT("")); + }) + .Next([this, Done](FNakamaGroupUserListResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Found superadmin (creator)", Result.Value.GroupUsers.Num() >= 1); + Done.Execute(); + }); + }); + + LatentIt("should return only superadmin when filtering by state 0", [this](const FDoneDelegate& Done) + { + TSharedPtr GroupId = MakeShared(); + Nakama::CreateGroup(ClientConfig, Session, FString::Printf(TEXT("StateFilter0_%s"), *GenerateShortId()), TEXT("Test"), TEXT(""), TEXT("en"), true, 100) + .Next([this, GroupId](const FNakamaGroup& GroupResult) -> TNakamaFuture + { + *GroupId = GroupResult.Id; + return Nakama::AddGroupUsers(ClientConfig, Session, *GroupId, {MemberUserId}); + }) + .Next([this, GroupId](const FNakamaVoid&) -> TNakamaFuture + { + return Nakama::ListGroupUsers(ClientConfig, Session, *GroupId, 100, FNakamaOptionalInt32(0), TEXT("")); + }) + .Next([this, Done](FNakamaGroupUserListResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestEqual("state=0 filter returns only the superadmin", Result.Value.GroupUsers.Num(), 1); + if (Result.Value.GroupUsers.Num() > 0) + TestEqual("Returned user is the creator", Result.Value.GroupUsers[0].User.Id, UserId); + Done.Execute(); + }); + }); + + LatentIt("should return only member when filtering by state 2", [this](const FDoneDelegate& Done) + { + TSharedPtr GroupId = MakeShared(); + Nakama::CreateGroup(ClientConfig, Session, FString::Printf(TEXT("StateFilter2_%s"), *GenerateShortId()), TEXT("Test"), TEXT(""), TEXT("en"), true, 100) + .Next([this, GroupId](const FNakamaGroup& GroupResult) -> TNakamaFuture + { + *GroupId = GroupResult.Id; + return Nakama::AddGroupUsers(ClientConfig, Session, *GroupId, {MemberUserId}); + }) + .Next([this, GroupId](const FNakamaVoid&) -> TNakamaFuture + { + return Nakama::ListGroupUsers(ClientConfig, Session, *GroupId, 100, FNakamaOptionalInt32(2), TEXT("")); + }) + .Next([this, Done](FNakamaGroupUserListResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestEqual("state=2 filter returns only the added member", Result.Value.GroupUsers.Num(), 1); + if (Result.Value.GroupUsers.Num() > 0) + TestEqual("Returned user is the member", Result.Value.GroupUsers[0].User.Id, MemberUserId); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// ADDITIONAL UNLINK TESTS (Async) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncUnlinkSpec, "IntegrationTests.NakamaTests.Unlink", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + FString DeviceId; + FString CustomId; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateShortId() { return FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaAsyncUnlinkSpec) + + +void FNakamaAsyncUnlinkSpec::Define() +{ + BeforeEach([this]() + { + DeviceId = GenerateId(); + CustomId = GenerateId(); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountDevice Account; + Account.Id = DeviceId; + Nakama::AuthenticateDevice(ClientConfig, true, TEXT(""), Account.Id).Next([this, Done](FNakamaSessionResult AuthResult) + { + if (AuthResult.bIsError) { AddError(FString::Printf(TEXT("Auth failed: %s"), *AuthResult.Error.Message)); Done.Execute(); return; } + Session = AuthResult.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("UnlinkCustom", [this]() + { + LatentIt("should unlink custom ID", [this](const FDoneDelegate& Done) + { + Nakama::LinkCustom(ClientConfig, Session, CustomId, TMap()) + .Next([this](const FNakamaVoid&) + { + return Nakama::UnlinkCustom(ClientConfig, Session, CustomId, TMap()); + }) + .Next([this, Done](FNakamaVoidResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Unlink custom succeeded", true); + Done.Execute(); + }); + }); + }); + + Describe("UnlinkEmail", [this]() + { + LatentIt("should unlink email", [this](const FDoneDelegate& Done) + { + FString Email = FString::Printf(TEXT("unlink_%s@example.com"), *GenerateShortId()); + Nakama::LinkEmail(ClientConfig, Session, Email, TEXT("password123"), TMap()) + .Next([this, Email](const FNakamaVoid&) + { + return Nakama::UnlinkEmail(ClientConfig, Session, Email, TEXT("password123"), TMap()); + }) + .Next([this, Done](FNakamaVoidResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Unlink email succeeded", true); + Done.Execute(); + }); + }); + }); + + Describe("UnlinkDevice", [this]() + { + LatentIt("should unlink device", [this](const FDoneDelegate& Done) + { + FString NewDeviceId = GenerateId(); + Nakama::LinkDevice(ClientConfig, Session, NewDeviceId, TMap()) + .Next([this, NewDeviceId](const FNakamaVoid&) + { + return Nakama::UnlinkDevice(ClientConfig, Session, NewDeviceId, TMap()); + }) + .Next([this, Done](FNakamaVoidResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Unlink device succeeded", true); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// ADDITIONAL MATCHES TESTS (Async) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncMatchesExtSpec, "IntegrationTests.NakamaTests.MatchesExt", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaAsyncMatchesExtSpec) + + +void FNakamaAsyncMatchesExtSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id).Next([this, Done](FNakamaSessionResult AuthResult) + { + if (AuthResult.bIsError) { AddError(FString::Printf(TEXT("Auth failed: %s"), *AuthResult.Error.Message)); Done.Execute(); return; } + Session = AuthResult.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("ListMatchesWithFilters", [this]() + { + LatentIt("should list matches with min size filter", [this](const FDoneDelegate& Done) + { + Nakama::ListMatches(ClientConfig, Session, 100, false, TEXT(""), 2, 100, TEXT("")).Next([this, Done](FNakamaMatchListResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + for (const auto& Match : Result.Value.Matches) { TestTrue("Match has min size", Match.Size >= 2); } + Done.Execute(); + }); + }); + + LatentIt("should list matches with max size filter", [this](const FDoneDelegate& Done) + { + Nakama::ListMatches(ClientConfig, Session, 100, false, TEXT(""), 0, 4, TEXT("")).Next([this, Done](FNakamaMatchListResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + for (const auto& Match : Result.Value.Matches) { TestTrue("Match has max size", Match.Size <= 4); } + Done.Execute(); + }); + }); + + LatentIt("should list matches with label filter", [this](const FDoneDelegate& Done) + { + Nakama::ListMatches(ClientConfig, Session, 10, true, TEXT("test_label"), 0, 100, TEXT("")).Next([this, Done](FNakamaMatchListResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("List with label filter succeeded", true); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// CHANNEL MESSAGES TESTS (Async) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncChannelSpec, "IntegrationTests.NakamaTests.Channel", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateShortId() { return FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaAsyncChannelSpec) + + +void FNakamaAsyncChannelSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id).Next([this, Done](FNakamaSessionResult AuthResult) + { + if (AuthResult.bIsError) { AddError(FString::Printf(TEXT("Auth failed: %s"), *AuthResult.Error.Message)); Done.Execute(); return; } + Session = AuthResult.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("ListChannelMessages", [this]() + { + LatentIt("should fail with empty channel ID", [this](const FDoneDelegate& Done) + { + Nakama::ListChannelMessages(ClientConfig, Session, TEXT(""), 100, true, TEXT("")).Next([this, Done](FNakamaChannelMessageListResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty channel ID", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should list room channel messages", [this](const FDoneDelegate& Done) + { + FString ChannelId = FString::Printf(TEXT("2.testroom_%s"), *GenerateShortId()); + Nakama::ListChannelMessages(ClientConfig, Session, ChannelId, 100, true, TEXT("")).Next([this, Done](FNakamaChannelMessageListResult Result) + { + TestTrue("Got expected response", true); + Done.Execute(); + }); + }); + + LatentIt("should list messages with limit", [this](const FDoneDelegate& Done) + { + FString ChannelId = FString::Printf(TEXT("2.limitroom_%s"), *GenerateShortId()); + Nakama::ListChannelMessages(ClientConfig, Session, ChannelId, 5, true, TEXT("")).Next([this, Done](FNakamaChannelMessageListResult Result) + { + if (!Result.bIsError) { TestTrue("List respects limit", Result.Value.Messages.Num() <= 5); } + else { TestTrue("Got expected error or success", true); } + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// ADDITIONAL NOTIFICATION TESTS (Async) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncNotificationsExtSpec, "IntegrationTests.NakamaTests.NotificationsExt", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaAsyncNotificationsExtSpec) + + +void FNakamaAsyncNotificationsExtSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id).Next([this, Done](FNakamaSessionResult AuthResult) + { + if (AuthResult.bIsError) { AddError(FString::Printf(TEXT("Auth failed: %s"), *AuthResult.Error.Message)); Done.Execute(); return; } + Session = AuthResult.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("ListNotificationsWithFilters", [this]() + { + LatentIt("should list notifications with limit", [this](const FDoneDelegate& Done) + { + Nakama::ListNotifications(ClientConfig, Session, 5, TEXT("")).Next([this, Done](FNakamaNotificationListResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("List respects limit", Result.Value.Notifications.Num() <= 5); + Done.Execute(); + }); + }); + }); + + Describe("DeleteNotifications", [this]() + { + LatentIt("should handle invalid notification ID gracefully", [this](const FDoneDelegate& Done) + { + TArray Ids; + Ids.Add(TEXT("not-a-valid-uuid")); + Nakama::DeleteNotifications(ClientConfig, Session, Ids).Next([this, Done](FNakamaVoidResult Result) + { + TestTrue("Server handled invalid UUID", true); + Done.Execute(); + }); + }); + + LatentIt("should succeed when deleting non-existent notification", [this](const FDoneDelegate& Done) + { + TArray Ids; + Ids.Add(TEXT("ffffffff-ffff-ffff-ffff-ffffffffffff")); + Nakama::DeleteNotifications(ClientConfig, Session, Ids).Next([this, Done](FNakamaVoidResult Result) + { + TestTrue("Delete non-existent notification handled", true); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// ADDITIONAL AUTH VALIDATION TESTS (Async) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncAuthValidationSpec, "IntegrationTests.NakamaTests.Auth.Validation", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateShortId() { return FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaAsyncAuthValidationSpec) + + +void FNakamaAsyncAuthValidationSpec::Define() +{ + + + Describe("CustomAuth.Validation", [this]() + { + LatentIt("should fail with custom ID containing spaces", [this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = TEXT("test id with spaces"); + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for ID with spaces", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should fail with username too long", [this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + Nakama::AuthenticateCustom(ClientConfig, true, FString::ChrN(150, 'x'), Account.Id).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for username too long", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should accept username with special chars", [this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + FString Username = TEXT("u@$^!~+"); + Nakama::AuthenticateCustom(ClientConfig, true, Username, Account.Id).Next([this, Done](FNakamaSessionResult Result) + { + if (Result.bIsError) + { + AddError(FString::Printf(TEXT("Unexpected error %d: %s"), Result.Error.Code, *Result.Error.Message)); + Done.Execute(); + return; + } + TestTrue("Session is valid", !Result.Value.Token.IsEmpty()); + Nakama::DeleteAccount(ClientConfig, Result.Value).Next([Done](FNakamaVoidResult) + { + Done.Execute(); + }); + }); + }); + }); + + Describe("DeviceAuth.Validation", [this]() + { + LatentIt("should fail with device ID containing only spaces", [this](const FDoneDelegate& Done) + { + FNakamaAccountDevice Account; + Account.Id = TEXT(" "); + Nakama::AuthenticateDevice(ClientConfig, true, TEXT(""), Account.Id).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for spaces-only ID", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("EmailAuth.Validation", [this]() + { + LatentIt("should fail with email too short", [this](const FDoneDelegate& Done) + { + FNakamaAccountEmail Account; + Account.Email = TEXT("a@b.c"); + Account.Password = TEXT("password123"); + Nakama::AuthenticateEmail(ClientConfig, true, TEXT(""), Account.Email, Account.Password).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for email too short", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should fail with missing at symbol in email", [this](const FDoneDelegate& Done) + { + FNakamaAccountEmail Account; + Account.Email = TEXT("notanemailaddress.com"); + Account.Password = TEXT("password123"); + Nakama::AuthenticateEmail(ClientConfig, true, TEXT(""), Account.Email, Account.Password).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for missing @ in email", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should fail with empty password", [this](const FDoneDelegate& Done) + { + FNakamaAccountEmail Account; + Account.Email = FString::Printf(TEXT("test_%s@example.com"), *GenerateShortId()); + Account.Password = TEXT(""); + Nakama::AuthenticateEmail(ClientConfig, true, TEXT(""), Account.Email, Account.Password).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty password", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should authenticate existing user with create=false", [this](const FDoneDelegate& Done) + { + FNakamaAccountEmail Account; + Account.Email = FString::Printf(TEXT("existing_%s@example.com"), *GenerateShortId()); + Account.Password = TEXT("password123"); + Nakama::AuthenticateEmail(ClientConfig, true, TEXT(""), Account.Email, Account.Password) + .Next([this, Account](const FNakamaSession&) + { + return Nakama::AuthenticateEmail(ClientConfig, false, TEXT(""), Account.Email, Account.Password); + }) + .Next([this, Done](FNakamaSessionResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Session has token", !Result.Value.Token.IsEmpty()); + Nakama::DeleteAccount(ClientConfig, Result.Value).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + }); + }); +} + +// ============================================================================ +// ACCOUNT DELETE TESTS (Async) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncAccountDeleteSpec, "IntegrationTests.NakamaTests.Account.Delete", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaAsyncAccountDeleteSpec) + + +void FNakamaAsyncAccountDeleteSpec::Define() +{ + + + Describe("DeleteAccount", [this]() + { + LatentIt("should delete account successfully", [this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id) + .Next([this](const FNakamaSession& AuthResult) + { + Session = AuthResult; + return Nakama::DeleteAccount(ClientConfig, Session); + }) + .Next([this, Done](FNakamaVoidResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Account deleted successfully", true); + Done.Execute(); + }); + }); + + LatentIt("should fail to get account after deletion", [this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id) + .Next([this](const FNakamaSession& AuthResult) + { + Session = AuthResult; + return Nakama::DeleteAccount(ClientConfig, Session); + }) + .Next([this](const FNakamaVoid&) + { + return Nakama::GetAccount(ClientConfig, Session); + }) + .Next([this, Done](FNakamaAccountResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got account after deletion")); Done.Execute(); return; } + TestTrue("Got error when accessing deleted account", + Result.Error.Code == NakamaErrorCode::Unauthenticated || + Result.Error.Code == NakamaErrorCode::NotFound || + !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// SOCIAL AUTH VALIDATION TESTS (Async) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncSocialAuthSpec, "IntegrationTests.NakamaTests.Auth.Social", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + + +END_DEFINE_SPEC(FNakamaAsyncSocialAuthSpec) + + +void FNakamaAsyncSocialAuthSpec::Define() +{ + + + Describe("FacebookAuth.Validation", [this]() + { + LatentIt("should fail with empty token", [this](const FDoneDelegate& Done) + { + FNakamaAccountFacebook Account; + Account.Token = TEXT(""); + Nakama::AuthenticateFacebook(ClientConfig, true, TEXT(""), true, Account.Token).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty Facebook token", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("GoogleAuth.Validation", [this]() + { + LatentIt("should fail with empty token", [this](const FDoneDelegate& Done) + { + FNakamaAccountGoogle Account; + Account.Token = TEXT(""); + Nakama::AuthenticateGoogle(ClientConfig, true, TEXT(""), Account.Token).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty Google token", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("SteamAuth.Validation", [this]() + { + LatentIt("should fail with empty token", [this](const FDoneDelegate& Done) + { + FNakamaAccountSteam Account; + Account.Token = TEXT(""); + Nakama::AuthenticateSteam(ClientConfig, true, TEXT(""), true, Account.Token).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty Steam token", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("AppleAuth.Validation", [this]() + { + LatentIt("should fail with empty token", [this](const FDoneDelegate& Done) + { + FNakamaAccountApple Account; + Account.Token = TEXT(""); + Nakama::AuthenticateApple(ClientConfig, true, TEXT(""), Account.Token).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty Apple token", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("GameCenterAuth.Validation", [this]() + { + LatentIt("should fail with missing required fields", [this](const FDoneDelegate& Done) + { + FNakamaAccountGameCenter Account; + Account.PlayerId = TEXT(""); + Account.BundleId = TEXT(""); + Account.TimestampSeconds = 0; + Account.Salt = TEXT(""); + Account.Signature = TEXT(""); + Account.PublicKeyUrl = TEXT(""); + Nakama::AuthenticateGameCenter(ClientConfig, true, TEXT(""), Account.PlayerId, Account.BundleId, Account.TimestampSeconds, Account.Salt, Account.Signature, Account.PublicKeyUrl).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for missing GameCenter fields", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// SESSION EXTENDED TESTS (Async) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncSessionExtSpec, "IntegrationTests.NakamaTests.SessionExt", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaAsyncSessionExtSpec) + + +void FNakamaAsyncSessionExtSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id).Next([this, Done](FNakamaSessionResult AuthResult) + { + if (AuthResult.bIsError) { AddError(FString::Printf(TEXT("Auth failed: %s"), *AuthResult.Error.Message)); Done.Execute(); return; } + Session = AuthResult.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("Logout.Extended", [this]() + { + LatentIt("should logout and invalidate session", [this](const FDoneDelegate& Done) + { + Nakama::SessionLogout(ClientConfig, Session, Session.Token, Session.RefreshToken) + .Next([this](const FNakamaVoid&) + { + return Nakama::GetAccount(ClientConfig, Session); + }) + .Next([this, Done](FNakamaAccountResult Result) + { + TestTrue("Logout succeeded", true); + Done.Execute(); + }); + }); + }); + + Describe("SessionRefresh.Extended", [this]() + { + LatentIt("should fail with empty token", [this](const FDoneDelegate& Done) + { + Nakama::SessionRefresh(ClientConfig, TEXT(""), TMap()).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty refresh token", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should fail with malformed token", [this](const FDoneDelegate& Done) + { + Nakama::SessionRefresh(ClientConfig, TEXT("not-a-valid-jwt-token"), TMap()).Next([this, Done](FNakamaSessionResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for malformed token", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// USERS EXTENDED TESTS (Async) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncUsersExtSpec, "IntegrationTests.NakamaTests.UsersExt", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + FString UserId; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaAsyncUsersExtSpec) + + +void FNakamaAsyncUsersExtSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id) + .Next([this](const FNakamaSession& AuthResult) + { + Session = AuthResult; + return Nakama::GetAccount(ClientConfig, Session); + }) + .Next([this, Done](FNakamaAccountResult AccResult) + { + ASYNC_FAIL_ON_ERROR(AccResult, Done); + UserId = AccResult.Value.User.Id; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("GetUsers.Extended", [this]() + { + LatentIt("should return empty for empty request", [this](const FDoneDelegate& Done) + { + Nakama::GetUsers(ClientConfig, Session, TArray(), TArray(), TArray()).Next([this, Done](FNakamaUsersResult Result) + { + if (!Result.bIsError) { TestTrue("Empty request returns empty result", Result.Value.Users.Num() == 0); } + else { TestTrue("Got expected response", true); } + Done.Execute(); + }); + }); + + LatentIt("should fail with invalid UUID format", [this](const FDoneDelegate& Done) + { + TArray InvalidIds; + InvalidIds.Add(TEXT("not-a-valid-uuid")); + Nakama::GetUsers(ClientConfig, Session, InvalidIds, TArray(), TArray()).Next([this, Done](FNakamaUsersResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for invalid UUID", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should get user by Facebook ID (returns empty if not linked)", [this](const FDoneDelegate& Done) + { + TArray FacebookIds; + FacebookIds.Add(TEXT("123456789")); + Nakama::GetUsers(ClientConfig, Session, TArray(), TArray(), FacebookIds).Next([this, Done](FNakamaUsersResult Result) + { + if (!Result.bIsError) { TestTrue("Facebook ID search returns empty", Result.Value.Users.Num() == 0); } + else { TestTrue("Got expected response", true); } + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// FRIENDS OF FRIENDS TESTS (Async) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncFriendsOfFriendsSpec, "IntegrationTests.NakamaTests.FriendsOfFriends", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + FString UserId; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaAsyncFriendsOfFriendsSpec) + + +void FNakamaAsyncFriendsOfFriendsSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id) + .Next([this](const FNakamaSession& AuthResult) + { + Session = AuthResult; + return Nakama::GetAccount(ClientConfig, Session); + }) + .Next([this, Done](FNakamaAccountResult AccResult) + { + ASYNC_FAIL_ON_ERROR(AccResult, Done); + UserId = AccResult.Value.User.Id; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("ListFriendsOfFriends", [this]() + { + LatentIt("should list friends of friends", [this](const FDoneDelegate& Done) + { + Nakama::ListFriendsOfFriends(ClientConfig, Session, 100, TEXT("")).Next([this, Done](FNakamaFriendsOfFriendsListResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + Done.Execute(); + }); + }); + + LatentIt("should respect limit parameter", [this](const FDoneDelegate& Done) + { + Nakama::ListFriendsOfFriends(ClientConfig, Session, 5, TEXT("")).Next([this, Done](FNakamaFriendsOfFriendsListResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Result respects limit", Result.Value.FriendsOfFriends.Num() <= 5); + Done.Execute(); + }); + }); + }); + + Describe("ListFriends.Validation", [this]() + { + LatentIt("should accept unset limit and state", [this](const FDoneDelegate& Done) + { + Nakama::ListFriends(ClientConfig, Session, {}, {}, TEXT("")).Next([this, Done](FNakamaFriendListResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("ListFriends with no limit accepted", true); + Done.Execute(); + }); + }); + + LatentIt("should fail with invalid state filter", [this](const FDoneDelegate& Done) + { + Nakama::ListFriends(ClientConfig, Session, 100, 99, TEXT("")).Next([this, Done](FNakamaFriendListResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for invalid state", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("AddFriends.Validation", [this]() + { + LatentIt("should accept empty IDs and usernames as no-op", [this](const FDoneDelegate& Done) + { + Nakama::AddFriends(ClientConfig, Session, TArray(), TArray(), TEXT("")).Next([this, Done](FNakamaVoidResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("AddFriends with empty arrays accepted (no-op)", true); + Done.Execute(); + }); + }); + + LatentIt("should fail with invalid UUID", [this](const FDoneDelegate& Done) + { + TArray InvalidIds; + InvalidIds.Add(TEXT("not-a-uuid")); + Nakama::AddFriends(ClientConfig, Session, InvalidIds, TArray(), TEXT("")).Next([this, Done](FNakamaVoidResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for invalid UUID", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("BlockFriends.Validation", [this]() + { + LatentIt("should fail when blocking self", [this](const FDoneDelegate& Done) + { + TArray SelfId; + SelfId.Add(UserId); + Nakama::BlockFriends(ClientConfig, Session, SelfId, TArray()).Next([this, Done](FNakamaVoidResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for blocking self", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// GROUPS PERMISSIONS AND EDGE CASES TESTS (Async) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncGroupPermissionsSpec, "IntegrationTests.NakamaTests.GroupPermissions", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + FNakamaSession Session2; + FString UserId; + FString UserId2; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateShortId() { return FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaAsyncGroupPermissionsSpec) + + +void FNakamaAsyncGroupPermissionsSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account1; + Account1.Id = GenerateId(); + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account1.Id) + .Next([this](const FNakamaSession& Result) + { + Session = Result; + return Nakama::GetAccount(ClientConfig, Session); + }) + .Next([this](const FNakamaAccount& AccResult) + { + UserId = AccResult.User.Id; + FNakamaAccountCustom Account2; + Account2.Id = GenerateId(); + return Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account2.Id); + }) + .Next([this](const FNakamaSession& Result2) + { + Session2 = Result2; + return Nakama::GetAccount(ClientConfig, Session2); + }) + .Next([this, Done](FNakamaAccountResult AccResult2) + { + ASYNC_FAIL_ON_ERROR(AccResult2, Done); + UserId2 = AccResult2.Value.User.Id; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + Nakama::DeleteAccount(ClientConfig, Session) + .Next([this](FNakamaVoidResult) -> TNakamaFuture + { + return Nakama::DeleteAccount(ClientConfig, Session2); + }) + .Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("CreateGroup.Validation", [this]() + { + LatentIt("should accept max count zero", [this](const FDoneDelegate& Done) + { + Nakama::CreateGroup(ClientConfig, Session, FString::Printf(TEXT("testgroup_%s"), *GenerateShortId()), TEXT(""), TEXT(""), TEXT(""), true, 0) + .Next([this, Done](FNakamaGroupResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("CreateGroup with max_count=0 accepted", !Result.Value.Id.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should create closed group", [this](const FDoneDelegate& Done) + { + Nakama::CreateGroup(ClientConfig, Session, FString::Printf(TEXT("closedgroup_%s"), *GenerateShortId()), TEXT(""), TEXT("A closed group"), TEXT(""), false, 100) + .Next([this, Done](FNakamaGroupResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Open is set", Result.Value.Open.bIsSet); + TestFalse("Group is closed", Result.Value.Open.GetValue()); + Done.Execute(); + }); + }); + }); + + Describe("UpdateGroup.Permissions", [this]() + { + LatentIt("should fail with empty group ID", [this](const FDoneDelegate& Done) + { + Nakama::UpdateGroup(ClientConfig, Session, TEXT(""), TEXT("newname"), TEXT(""), TEXT(""), TEXT(""), true).Next([this, Done](FNakamaVoidResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty group ID", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should fail with invalid group ID format", [this](const FDoneDelegate& Done) + { + Nakama::UpdateGroup(ClientConfig, Session, TEXT("not-a-valid-uuid"), TEXT("newname"), TEXT(""), TEXT(""), TEXT(""), true).Next([this, Done](FNakamaVoidResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for invalid group ID", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("DeleteGroup.Permissions", [this]() + { + LatentIt("should fail with invalid group ID format", [this](const FDoneDelegate& Done) + { + Nakama::DeleteGroup(ClientConfig, Session, TEXT("not-a-valid-uuid")).Next([this, Done](FNakamaVoidResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for invalid group ID", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should fail when non-member tries to delete", [this](const FDoneDelegate& Done) + { + Nakama::CreateGroup(ClientConfig, Session, FString::Printf(TEXT("testdelete_%s"), *GenerateShortId()), TEXT(""), TEXT(""), TEXT(""), true, 100) + .Next([this](const FNakamaGroup& GroupResult) + { + return Nakama::DeleteGroup(ClientConfig, Session2, GroupResult.Id); + }) + .Next([this, Done](FNakamaVoidResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but non-member could delete")); Done.Execute(); return; } + TestTrue("Non-member cannot delete group", true); + Done.Execute(); + }); + }); + }); + + Describe("JoinGroup.EdgeCases", [this]() + { + LatentIt("should handle joining non-existent group", [this](const FDoneDelegate& Done) + { + Nakama::JoinGroup(ClientConfig, Session, TEXT("ffffffff-ffff-ffff-ffff-ffffffffffff")).Next([this, Done](FNakamaVoidResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for non-existent group", Result.Error.Code == NakamaErrorCode::NotFound || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should handle joining already joined group", [this](const FDoneDelegate& Done) + { + Nakama::CreateGroup(ClientConfig, Session, FString::Printf(TEXT("testjoin_%s"), *GenerateShortId()), TEXT(""), TEXT(""), TEXT(""), true, 100) + .Next([this](const FNakamaGroup& GroupResult) + { + return Nakama::JoinGroup(ClientConfig, Session, GroupResult.Id); + }) + .Next([this, Done](FNakamaVoidResult Result) + { + TestTrue("Join already-joined group handled", true); + Done.Execute(); + }); + }); + }); + + Describe("LeaveGroup.EdgeCases", [this]() + { + LatentIt("should handle leaving non-member group", [this](const FDoneDelegate& Done) + { + Nakama::CreateGroup(ClientConfig, Session, FString::Printf(TEXT("testleave_%s"), *GenerateShortId()), TEXT(""), TEXT(""), TEXT(""), true, 100) + .Next([this](const FNakamaGroup& GroupResult) + { + return Nakama::LeaveGroup(ClientConfig, Session2, GroupResult.Id); + }) + .Next([this, Done](FNakamaVoidResult Result) + { + TestTrue("Leave non-member group handled", true); + Done.Execute(); + }); + }); + }); + + Describe("ListGroupUsers.Validation", [this]() + { + LatentIt("should fail with invalid group ID format", [this](const FDoneDelegate& Done) + { + Nakama::ListGroupUsers(ClientConfig, Session, TEXT("not-a-valid-uuid"), 100, 0, TEXT("")).Next([this, Done](FNakamaGroupUserListResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for invalid group ID", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("ListUserGroups.Validation", [this]() + { + LatentIt("should fail with invalid user ID format", [this](const FDoneDelegate& Done) + { + Nakama::ListUserGroups(ClientConfig, Session, TEXT("not-a-valid-uuid"), 100, 0, TEXT("")).Next([this, Done](FNakamaUserGroupListResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for invalid user ID", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should list own groups", [this](const FDoneDelegate& Done) + { + Nakama::ListUserGroups(ClientConfig, Session, UserId, 100, 0, TEXT("")).Next([this, Done](FNakamaUserGroupListResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("List own groups succeeded", true); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// STORAGE PERMISSIONS TESTS (Async) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncStoragePermissionsSpec, "IntegrationTests.NakamaTests.StoragePermissions", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + FNakamaSession Session2; + FString UserId; + FString UserId2; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateShortId() { return FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaAsyncStoragePermissionsSpec) + + +void FNakamaAsyncStoragePermissionsSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account1; + Account1.Id = GenerateId(); + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account1.Id) + .Next([this](const FNakamaSession& Result) + { + Session = Result; + return Nakama::GetAccount(ClientConfig, Session); + }) + .Next([this](const FNakamaAccount& AccResult) + { + UserId = AccResult.User.Id; + FNakamaAccountCustom Account2; + Account2.Id = GenerateId(); + return Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account2.Id); + }) + .Next([this](const FNakamaSession& Result2) + { + Session2 = Result2; + return Nakama::GetAccount(ClientConfig, Session2); + }) + .Next([this, Done](FNakamaAccountResult AccResult2) + { + ASYNC_FAIL_ON_ERROR(AccResult2, Done); + UserId2 = AccResult2.Value.User.Id; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + auto DeleteMain = [this, Done]() + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }; + + if (Session2.Token.IsEmpty()) { DeleteMain(); return; } + Nakama::DeleteAccount(ClientConfig, Session2).Next([DeleteMain](FNakamaVoidResult) { DeleteMain(); }); + }); + + + + Describe("WriteStorage.Permissions", [this]() + { + LatentIt("should fail with invalid permission read value", [this](const FDoneDelegate& Done) + { + TArray Objects; + FNakamaWriteStorageObject Obj; + Obj.Collection = TEXT("test_collection"); + Obj.Key = FString::Printf(TEXT("test_key_%s"), *GenerateShortId()); + Obj.Value = TEXT("{\"data\":\"test\"}"); + Obj.PermissionRead = 99; + Obj.PermissionWrite = 1; + Objects.Add(Obj); + Nakama::WriteStorageObjects(ClientConfig, Session, Objects).Next([this, Done](FNakamaStorageObjectAcksResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for invalid permission read", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should fail with invalid permission write value", [this](const FDoneDelegate& Done) + { + TArray Objects; + FNakamaWriteStorageObject Obj; + Obj.Collection = TEXT("test_collection"); + Obj.Key = FString::Printf(TEXT("test_key_%s"), *GenerateShortId()); + Obj.Value = TEXT("{\"data\":\"test\"}"); + Obj.PermissionRead = 1; + Obj.PermissionWrite = 99; + Objects.Add(Obj); + Nakama::WriteStorageObjects(ClientConfig, Session, Objects).Next([this, Done](FNakamaStorageObjectAcksResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for invalid permission write", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("ReadStorage.Permissions", [this]() + { + LatentIt("should read public object from other user", [this](const FDoneDelegate& Done) + { + FString TestKey = FString::Printf(TEXT("public_key_%s"), *GenerateShortId()); + TArray Objects; + FNakamaWriteStorageObject Obj; + Obj.Collection = TEXT("public_collection"); + Obj.Key = TestKey; + Obj.Value = TEXT("{\"data\":\"public\"}"); + Obj.PermissionRead = 2; + Obj.PermissionWrite = 1; + Objects.Add(Obj); + Nakama::WriteStorageObjects(ClientConfig, Session, Objects) + .Next([this, TestKey](const FNakamaStorageObjectAcks&) + { + TArray ReadIds; + FNakamaReadStorageObjectId ReadId; + ReadId.Collection = TEXT("public_collection"); + ReadId.Key = TestKey; + ReadId.UserId = UserId; + ReadIds.Add(ReadId); + return Nakama::ReadStorageObjects(ClientConfig, Session2, ReadIds); + }) + .Next([this, Done](FNakamaStorageObjectsResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("User 2 can read public object", Result.Value.Objects.Num() > 0); + Done.Execute(); + }); + }); + + LatentIt("should not read private object from other user", [this](const FDoneDelegate& Done) + { + FString TestKey = FString::Printf(TEXT("private_key_%s"), *GenerateShortId()); + TArray Objects; + FNakamaWriteStorageObject Obj; + Obj.Collection = TEXT("private_collection"); + Obj.Key = TestKey; + Obj.Value = TEXT("{\"data\":\"private\"}"); + Obj.PermissionRead = 1; + Obj.PermissionWrite = 1; + Objects.Add(Obj); + Nakama::WriteStorageObjects(ClientConfig, Session, Objects) + .Next([this, TestKey](const FNakamaStorageObjectAcks&) + { + TArray ReadIds; + FNakamaReadStorageObjectId ReadId; + ReadId.Collection = TEXT("private_collection"); + ReadId.Key = TestKey; + ReadId.UserId = UserId; + ReadIds.Add(ReadId); + return Nakama::ReadStorageObjects(ClientConfig, Session2, ReadIds); + }) + .Next([this, Done](FNakamaStorageObjectsResult Result) + { + if (!Result.bIsError) { TestTrue("Private object not visible to other user", Result.Value.Objects.Num() == 0); } + else { TestTrue("Server protected private object", true); } + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// CHANNEL EXTENDED TESTS (Async) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncChannelExtSpec, "IntegrationTests.NakamaTests.ChannelExt", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + FString UserId; + FString GroupId; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateShortId() { return FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaAsyncChannelExtSpec) + + +void FNakamaAsyncChannelExtSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id) + .Next([this](const FNakamaSession& AuthResult) + { + Session = AuthResult; + return Nakama::GetAccount(ClientConfig, Session); + }) + .Next([this](const FNakamaAccount& AccResult) + { + UserId = AccResult.User.Id; + return Nakama::CreateGroup(ClientConfig, Session, FString::Printf(TEXT("channelgroup_%s"), *GenerateShortId()), TEXT(""), TEXT(""), TEXT(""), true, 100); + }) + .Next([this, Done](FNakamaGroupResult GroupResult) + { + ASYNC_FAIL_ON_ERROR(GroupResult, Done); + GroupId = GroupResult.Value.Id; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("ListChannelMessages.Extended", [this]() + { + LatentIt("should reject invalid channel ID format", [this](const FDoneDelegate& Done) + { + FString ChannelId = FString::Printf(TEXT("2.%s"), *GroupId); + Nakama::ListChannelMessages(ClientConfig, Session, ChannelId, 100, true, TEXT("")).Next([this, Done](FNakamaChannelMessageListResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error for channel not joined via WebSocket")); Done.Execute(); return; } + TestTrue("Got expected invalid channel error", !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should list messages backward (newest first)", [this](const FDoneDelegate& Done) + { + FString ChannelId = FString::Printf(TEXT("2.backwardroom_%s"), *GenerateShortId()); + Nakama::ListChannelMessages(ClientConfig, Session, ChannelId, 100, false, TEXT("")).Next([this, Done](FNakamaChannelMessageListResult Result) + { + TestTrue("Got expected response", true); + Done.Execute(); + }); + }); + + LatentIt("should fail with invalid channel format", [this](const FDoneDelegate& Done) + { + Nakama::ListChannelMessages(ClientConfig, Session, TEXT("invalid_channel_format"), 100, true, TEXT("")).Next([this, Done](FNakamaChannelMessageListResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for invalid channel format", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should fail with group channel for non-existent group", [this](const FDoneDelegate& Done) + { + Nakama::ListChannelMessages(ClientConfig, Session, TEXT("2.ffffffff-ffff-ffff-ffff-ffffffffffff"), 100, true, TEXT("")).Next([this, Done](FNakamaChannelMessageListResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for non-existent group channel", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// LINKING EXTENDED TESTS (Async) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncLinkExtSpec, "IntegrationTests.NakamaTests.LinkExt", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + FNakamaSession Session2; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateShortId() { return FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaAsyncLinkExtSpec) + + +void FNakamaAsyncLinkExtSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account1; + Account1.Id = GenerateId(); + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account1.Id) + .Next([this](const FNakamaSession& Result) + { + Session = Result; + FNakamaAccountCustom Account2; + Account2.Id = GenerateId(); + return Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account2.Id); + }) + .Next([this, Done](FNakamaSessionResult Result2) + { + ASYNC_FAIL_ON_ERROR(Result2, Done); + Session2 = Result2.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + auto DeleteMain = [this, Done]() + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }; + + if (Session2.Token.IsEmpty()) { DeleteMain(); return; } + Nakama::DeleteAccount(ClientConfig, Session2).Next([DeleteMain](FNakamaVoidResult) { DeleteMain(); }); + }); + + + + Describe("LinkCustom.AlreadyLinked", [this]() + { + LatentIt("should fail when custom ID already linked to another account", [this](const FDoneDelegate& Done) + { + FString CustomId = GenerateId(); + Nakama::LinkCustom(ClientConfig, Session, CustomId, TMap()) + .Next([this, CustomId](const FNakamaVoid&) + { + return Nakama::LinkCustom(ClientConfig, Session2, CustomId, TMap()); + }) + .Next([this, Done](FNakamaVoidResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got ALREADY_EXISTS error", Result.Error.Code == NakamaErrorCode::AlreadyExists || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("LinkDevice.AlreadyLinked", [this]() + { + LatentIt("should fail when device ID already linked to another account", [this](const FDoneDelegate& Done) + { + FString DeviceId = GenerateId(); + Nakama::LinkDevice(ClientConfig, Session, DeviceId, TMap()) + .Next([this, DeviceId](const FNakamaVoid&) + { + return Nakama::LinkDevice(ClientConfig, Session2, DeviceId, TMap()); + }) + .Next([this, Done](FNakamaVoidResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got ALREADY_EXISTS error", Result.Error.Code == NakamaErrorCode::AlreadyExists || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("LinkEmail.AlreadyLinked", [this]() + { + LatentIt("should fail when email already linked to another account", [this](const FDoneDelegate& Done) + { + FString Email = FString::Printf(TEXT("linked_%s@example.com"), *GenerateShortId()); + Nakama::LinkEmail(ClientConfig, Session, Email, TEXT("password123"), TMap()) + .Next([this, Email](const FNakamaVoid&) + { + return Nakama::LinkEmail(ClientConfig, Session2, Email, TEXT("password123"), TMap()); + }) + .Next([this, Done](FNakamaVoidResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got ALREADY_EXISTS error", Result.Error.Code == NakamaErrorCode::AlreadyExists || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("Unlink.NotLinked", [this]() + { + LatentIt("should succeed when unlinking not-linked custom ID", [this](const FDoneDelegate& Done) + { + Nakama::UnlinkCustom(ClientConfig, Session, GenerateId(), TMap()).Next([this, Done](FNakamaVoidResult Result) + { + TestTrue("Got expected response", true); + Done.Execute(); + }); + }); + + LatentIt("should succeed when unlinking not-linked device ID", [this](const FDoneDelegate& Done) + { + Nakama::UnlinkDevice(ClientConfig, Session, GenerateId(), TMap()).Next([this, Done](FNakamaVoidResult Result) + { + TestTrue("Got expected response", true); + Done.Execute(); + }); + }); + + LatentIt("should succeed when unlinking not-linked email", [this](const FDoneDelegate& Done) + { + Nakama::UnlinkEmail(ClientConfig, Session, FString::Printf(TEXT("notlinked_%s@example.com"), *GenerateShortId()), TEXT("password123"), TMap()).Next([this, Done](FNakamaVoidResult Result) + { + TestTrue("Got expected response", true); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// RPC EXTENDED TESTS (Async) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncRpcExtSpec, "IntegrationTests.NakamaTests.RPCExt", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + + static const FString HttpKey; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaAsyncRpcExtSpec) + +const FString FNakamaAsyncRpcExtSpec::HttpKey = TEXT("defaulthttpkey"); + +void FNakamaAsyncRpcExtSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id).Next([this, Done](FNakamaSessionResult AuthResult) + { + if (AuthResult.bIsError) { AddError(FString::Printf(TEXT("Auth failed: %s"), *AuthResult.Error.Message)); Done.Execute(); return; } + Session = AuthResult.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("RpcFunc.WithPayload", [this]() + { + LatentIt("should return NOT_FOUND for non-existent RPC", [this](const FDoneDelegate& Done) + { + TSharedPtr Payload = MakeShared(); + Payload->SetStringField(TEXT("test"), TEXT("data")); + FString PayloadStr; + FJsonSerializer::Serialize(Payload.ToSharedRef(), TJsonWriterFactory<>::Create(&PayloadStr)); + Nakama::RpcFunc(ClientConfig, Session, TEXT("test_echo"), PayloadStr).Next([this, Done](FNakamaRpcResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected NOT_FOUND error but RPC succeeded")); Done.Execute(); return; } + TestTrue("Got NOT_FOUND for non-existent RPC", + Result.Error.Code == NakamaErrorCode::NotFound || + Result.Error.Message.Contains(TEXT("not found"), ESearchCase::IgnoreCase)); + Done.Execute(); + }); + }); + + LatentIt("should return NOT_FOUND for non-existent RPC with empty payload", [this](const FDoneDelegate& Done) + { + Nakama::RpcFunc(ClientConfig, Session, TEXT("test_echo"), TEXT("")).Next([this, Done](FNakamaRpcResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected NOT_FOUND error but RPC succeeded")); Done.Execute(); return; } + TestTrue("Got NOT_FOUND for non-existent RPC", + Result.Error.Code == NakamaErrorCode::NotFound || + Result.Error.Message.Contains(TEXT("not found"), ESearchCase::IgnoreCase)); + Done.Execute(); + }); + }); + }); + + Describe("RpcFunc.WithHttpKey", [this]() + { + LatentIt("should execute RPC with HTTP key (server-to-server)", [this](const FDoneDelegate& Done) + { + TSharedPtr ServerPayload = MakeShared(); + ServerPayload->SetStringField(TEXT("server"), TEXT("call")); + FString ServerPayloadStr; + FJsonSerializer::Serialize(ServerPayload.ToSharedRef(), TJsonWriterFactory<>::Create(&ServerPayloadStr)); + Nakama::RpcFunc(ClientConfig, TEXT("test_echo"), ServerPayloadStr, HttpKey).Next([this, Done](FNakamaRpcResult Result) + { + TestTrue("Got expected response", true); + Done.Execute(); + }); + }); + }); + + Describe("RpcFunc.Validation", [this]() + { + LatentIt("should fail with RPC that returns error", [this](const FDoneDelegate& Done) + { + Nakama::RpcFunc(ClientConfig, Session, TEXT("test_error"), TEXT("")).Next([this, Done](FNakamaRpcResult Result) + { + if (Result.bIsError) { TestTrue("Got error as expected", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); } + else { TestTrue("Got response", true); } + Done.Execute(); + }); + }); + }); + + Describe("RpcFunc.Transform", [this]() + { + LatentIt("should transform a message", [this](const FDoneDelegate& Done) + { + TSharedPtr Payload = MakeShared(); + Payload->SetStringField(TEXT("message"), TEXT("hello")); + FString PayloadStr; + FJsonSerializer::Serialize(Payload.ToSharedRef(), TJsonWriterFactory<>::Create(&PayloadStr)); + Nakama::RpcFunc(ClientConfig, Session, TEXT("transform"), PayloadStr).Next([this, Done](FNakamaRpcResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TSharedPtr Response; + TSharedRef> Reader = TJsonReaderFactory<>::Create(Result.Value.Payload); + if (!FJsonSerializer::Deserialize(Reader, Response) || !Response.IsValid()) + { + AddError(TEXT("Failed to parse transform RPC response JSON")); + Done.Execute(); + return; + } + TestEqual("original matches", Response->GetStringField(TEXT("original")), FString(TEXT("hello"))); + TestEqual("reversed matches", Response->GetStringField(TEXT("reversed")), FString(TEXT("olleh"))); + TestEqual("upper matches", Response->GetStringField(TEXT("upper")), FString(TEXT("HELLO"))); + TestEqual("length matches", static_cast(Response->GetNumberField(TEXT("length"))), 5); + Done.Execute(); + }); + }); + + LatentIt("should return error for missing message field", [this](const FDoneDelegate& Done) + { + TSharedPtr Payload = MakeShared(); + Payload->SetStringField(TEXT("foo"), TEXT("bar")); + FString PayloadStr; + FJsonSerializer::Serialize(Payload.ToSharedRef(), TJsonWriterFactory<>::Create(&PayloadStr)); + Nakama::RpcFunc(ClientConfig, Session, TEXT("transform"), PayloadStr).Next([this, Done](FNakamaRpcResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error for missing message field but RPC succeeded")); Done.Execute(); return; } + TestTrue("Got error for missing message field", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should transform via HTTP key", [this](const FDoneDelegate& Done) + { + TSharedPtr Payload = MakeShared(); + Payload->SetStringField(TEXT("message"), TEXT("world")); + FString PayloadStr; + FJsonSerializer::Serialize(Payload.ToSharedRef(), TJsonWriterFactory<>::Create(&PayloadStr)); + Nakama::RpcFunc(ClientConfig, TEXT("transform"), PayloadStr, HttpKey).Next([this, Done](FNakamaRpcResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TSharedPtr Response; + TSharedRef> Reader = TJsonReaderFactory<>::Create(Result.Value.Payload); + if (!FJsonSerializer::Deserialize(Reader, Response) || !Response.IsValid()) + { + AddError(TEXT("Failed to parse transform RPC response JSON")); + Done.Execute(); + return; + } + TestEqual("original matches", Response->GetStringField(TEXT("original")), FString(TEXT("world"))); + TestEqual("reversed matches", Response->GetStringField(TEXT("reversed")), FString(TEXT("dlrow"))); + TestEqual("upper matches", Response->GetStringField(TEXT("upper")), FString(TEXT("WORLD"))); + TestEqual("length matches", static_cast(Response->GetNumberField(TEXT("length"))), 5); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// MATCHES VALIDATION TESTS (Async) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncMatchesValidationSpec, "IntegrationTests.NakamaTests.MatchesValidation", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaAsyncMatchesValidationSpec) + + +void FNakamaAsyncMatchesValidationSpec::Define() +{ + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id).Next([this, Done](FNakamaSessionResult AuthResult) + { + if (AuthResult.bIsError) { AddError(FString::Printf(TEXT("Auth failed: %s"), *AuthResult.Error.Message)); Done.Execute(); return; } + Session = AuthResult.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + Describe("ListMatches.Validation", [this]() + { + LatentIt("should fail with limit too high", [this](const FDoneDelegate& Done) + { + Nakama::ListMatches(ClientConfig, Session, 2000, false, TEXT(""), 0, 100, TEXT("")).Next([this, Done](FNakamaMatchListResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for limit too high", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should list with combined filters", [this](const FDoneDelegate& Done) + { + Nakama::ListMatches(ClientConfig, Session, 10, true, TEXT(""), 2, 8, TEXT("")).Next([this, Done](FNakamaMatchListResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + for (const auto& Match : Result.Value.Matches) + { + TestTrue("Match is authoritative", Match.Authoritative); + TestTrue("Match size >= min", Match.Size >= 2); + TestTrue("Match size <= max", Match.Size <= 8); + } + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// PURCHASES/SUBSCRIPTIONS TESTS (Async) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncPurchasesSpec, "IntegrationTests.NakamaTests.Purchases", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaAsyncPurchasesSpec) + + +void FNakamaAsyncPurchasesSpec::Define() +{ + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id).Next([this, Done](FNakamaSessionResult AuthResult) + { + if (AuthResult.bIsError) { AddError(FString::Printf(TEXT("Auth failed: %s"), *AuthResult.Error.Message)); Done.Execute(); return; } + Session = AuthResult.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + Describe("ListSubscriptions", [this]() + { + LatentIt("should list subscriptions (empty if none)", [this](const FDoneDelegate& Done) + { + Nakama::ListSubscriptions(ClientConfig, Session, 100, TEXT("")).Next([this, Done](FNakamaSubscriptionListResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("ListSubscriptions succeeded", true); + Done.Execute(); + }); + }); + }); + + Describe("ValidatePurchase.Apple", [this]() + { + LatentIt("should fail with empty receipt", [this](const FDoneDelegate& Done) + { + Nakama::ValidatePurchaseApple(ClientConfig, Session, TEXT(""), false).Next([this, Done](FNakamaValidatePurchaseResponseResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty receipt", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("ValidatePurchase.Google", [this]() + { + LatentIt("should fail with empty purchase", [this](const FDoneDelegate& Done) + { + Nakama::ValidatePurchaseGoogle(ClientConfig, Session, TEXT(""), false).Next([this, Done](FNakamaValidatePurchaseResponseResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty purchase", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("ValidateSubscription.Apple", [this]() + { + LatentIt("should fail with empty receipt", [this](const FDoneDelegate& Done) + { + Nakama::ValidateSubscriptionApple(ClientConfig, Session, TEXT(""), false).Next([this, Done](FNakamaValidateSubscriptionResponseResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty receipt", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("ValidateSubscription.Google", [this]() + { + LatentIt("should fail with empty receipt", [this](const FDoneDelegate& Done) + { + Nakama::ValidateSubscriptionGoogle(ClientConfig, Session, TEXT(""), false).Next([this, Done](FNakamaValidateSubscriptionResponseResult Result) + { + if (!Result.bIsError) { AddError(TEXT("Expected error but got success")); Done.Execute(); return; } + TestTrue("Got error for empty receipt", Result.Error.Code != 0 || !Result.Error.Message.IsEmpty()); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// EVENT TESTS (Async) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncEventSpec, "IntegrationTests.NakamaTests.Event", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaAsyncEventSpec) + + +void FNakamaAsyncEventSpec::Define() +{ + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id).Next([this, Done](FNakamaSessionResult AuthResult) + { + if (AuthResult.bIsError) { AddError(FString::Printf(TEXT("Auth failed: %s"), *AuthResult.Error.Message)); Done.Execute(); return; } + Session = AuthResult.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + Describe("Event", [this]() + { + LatentIt("should submit event successfully", [this](const FDoneDelegate& Done) + { + TMap Properties; + Properties.Add(TEXT("source"), TEXT("test")); + Nakama::Event(ClientConfig, Session, TEXT("test_event"), FDateTime::UtcNow(), false, Properties).Next([this, Done](FNakamaVoidResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Event submitted successfully", true); + Done.Execute(); + }); + }); + + LatentIt("should accept empty event name", [this](const FDoneDelegate& Done) + { + TMap Properties; + Nakama::Event(ClientConfig, Session, TEXT(""), FDateTime::UtcNow(), false, Properties).Next([this, Done](FNakamaVoidResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Event with empty name accepted", true); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// HEALTHCHECK TESTS (Async) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncHealthcheckSpec, "IntegrationTests.NakamaTests.Healthcheck", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + + static const FString HttpKey; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaAsyncHealthcheckSpec) + +const FString FNakamaAsyncHealthcheckSpec::HttpKey = TEXT("defaulthttpkey"); + +void FNakamaAsyncHealthcheckSpec::Define() +{ + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id).Next([this, Done](FNakamaSessionResult AuthResult) + { + if (AuthResult.bIsError) { AddError(FString::Printf(TEXT("Auth failed: %s"), *AuthResult.Error.Message)); Done.Execute(); return; } + Session = AuthResult.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + Describe("Healthcheck", [this]() + { + LatentIt("should return healthy status with session", [this](const FDoneDelegate& Done) + { + Nakama::Healthcheck(ClientConfig, Session).Next([this, Done](FNakamaVoidResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Server is healthy", true); + Done.Execute(); + }); + }); + + LatentIt("should return healthy status with HTTP key", [this](const FDoneDelegate& Done) + { + Nakama::Healthcheck(ClientConfig, HttpKey).Next([this, Done](FNakamaVoidResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Server is healthy with HTTP key", true); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// GROUP USER MANAGEMENT TESTS (Async) +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncGroupUsersSpec, "IntegrationTests.NakamaTests.GroupUsers", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + FNakamaSession Session2; + FNakamaSession Session3; + FString UserId; + FString UserId2; + FString UserId3; + FString GroupId; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + FString GenerateShortId() { return FGuid::NewGuid().ToString(EGuidFormats::Short).Left(8); } + +END_DEFINE_SPEC(FNakamaAsyncGroupUsersSpec) + + +void FNakamaAsyncGroupUsersSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account1; + Account1.Id = GenerateId(); + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account1.Id) + .Next([this](const FNakamaSession& Result) + { + Session = Result; + return Nakama::GetAccount(ClientConfig, Session); + }) + .Next([this](const FNakamaAccount& AccResult) + { + UserId = AccResult.User.Id; + FNakamaAccountCustom Account2; + Account2.Id = GenerateId(); + return Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account2.Id); + }) + .Next([this](const FNakamaSession& Result2) + { + Session2 = Result2; + return Nakama::GetAccount(ClientConfig, Session2); + }) + .Next([this](const FNakamaAccount& AccResult2) + { + UserId2 = AccResult2.User.Id; + FNakamaAccountCustom Account3; + Account3.Id = GenerateId(); + return Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account3.Id); + }) + .Next([this](const FNakamaSession& Result3) + { + Session3 = Result3; + return Nakama::GetAccount(ClientConfig, Session3); + }) + .Next([this, Done](FNakamaAccountResult AccResult3) + { + ASYNC_FAIL_ON_ERROR(AccResult3, Done); + UserId3 = AccResult3.Value.User.Id; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + Nakama::DeleteAccount(ClientConfig, Session3) + .Next([this](FNakamaVoidResult) { return Nakama::DeleteAccount(ClientConfig, Session2); }) + .Next([this](FNakamaVoidResult) { return Nakama::DeleteAccount(ClientConfig, Session); }) + .Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + + + Describe("AddGroupUsers", [this]() + { + LatentIt("should add user to group by owner", [this](const FDoneDelegate& Done) + { + Nakama::CreateGroup(ClientConfig, Session, FString::Printf(TEXT("addusers_%s"), *GenerateShortId()), TEXT(""), TEXT(""), TEXT(""), true, 100) + .Next([this](const FNakamaGroup& Result) + { + GroupId = Result.Id; + TArray UserIds; + UserIds.Add(UserId2); + return Nakama::AddGroupUsers(ClientConfig, Session, GroupId, UserIds); + }) + .Next([this, Done](FNakamaVoidResult AddResult) + { + ASYNC_FAIL_ON_ERROR(AddResult, Done); + TestTrue("User added to group", true); + Done.Execute(); + }); + }); + }); + + Describe("KickGroupUsers", [this]() + { + LatentIt("should kick user from group", [this](const FDoneDelegate& Done) + { + Nakama::CreateGroup(ClientConfig, Session, FString::Printf(TEXT("kickusers_%s"), *GenerateShortId()), TEXT(""), TEXT(""), TEXT(""), true, 100) + .Next([this](const FNakamaGroup& Result) + { + GroupId = Result.Id; + return Nakama::JoinGroup(ClientConfig, Session2, GroupId); + }) + .Next([this](const FNakamaVoid&) + { + TArray UserIds; + UserIds.Add(UserId2); + return Nakama::KickGroupUsers(ClientConfig, Session, GroupId, UserIds); + }) + .Next([this, Done](FNakamaVoidResult KickResult) + { + ASYNC_FAIL_ON_ERROR(KickResult, Done); + TestTrue("User kicked successfully", true); + Done.Execute(); + }); + }); + }); + + Describe("BanGroupUsers", [this]() + { + LatentIt("should ban user from group", [this](const FDoneDelegate& Done) + { + Nakama::CreateGroup(ClientConfig, Session, FString::Printf(TEXT("banusers_%s"), *GenerateShortId()), TEXT(""), TEXT(""), TEXT(""), true, 100) + .Next([this](const FNakamaGroup& Result) + { + GroupId = Result.Id; + return Nakama::JoinGroup(ClientConfig, Session2, GroupId); + }) + .Next([this](const FNakamaVoid&) + { + TArray UserIds; + UserIds.Add(UserId2); + return Nakama::BanGroupUsers(ClientConfig, Session, GroupId, UserIds); + }) + .Next([this, Done](FNakamaVoidResult BanResult) + { + ASYNC_FAIL_ON_ERROR(BanResult, Done); + TestTrue("User banned successfully", true); + Done.Execute(); + }); + }); + + LatentIt("should prevent banned user from rejoining", [this](const FDoneDelegate& Done) + { + Nakama::CreateGroup(ClientConfig, Session, FString::Printf(TEXT("banrejoin_%s"), *GenerateShortId()), TEXT(""), TEXT(""), TEXT(""), true, 100) + .Next([this](const FNakamaGroup& Result) + { + GroupId = Result.Id; + return Nakama::JoinGroup(ClientConfig, Session2, GroupId); + }) + .Next([this](const FNakamaVoid&) + { + TArray UserIds; + UserIds.Add(UserId2); + return Nakama::BanGroupUsers(ClientConfig, Session, GroupId, UserIds); + }) + .Next([this, Done](FNakamaVoidResult BanResult) + { + ASYNC_FAIL_ON_ERROR(BanResult, Done); + Nakama::JoinGroup(ClientConfig, Session2, GroupId).Next([this, Done](FNakamaVoidResult RejoinResult) + { + if (RejoinResult.bIsError) + { + TestTrue("Banned user cannot rejoin", true); + Done.Execute(); + return; + } + Nakama::ListGroupUsers(ClientConfig, Session, GroupId, 100, 0, TEXT("")).Next([this, Done](FNakamaGroupUserListResult ListResult) + { + ASYNC_FAIL_ON_ERROR(ListResult, Done); + bool bFoundUser = false; + for (const auto& GU : ListResult.Value.GroupUsers) + { + if (GU.User.Id == UserId2) { bFoundUser = true; break; } + } + TestFalse("Banned user should not be in group membership", bFoundUser); + Done.Execute(); + }); + }); + }); + }); + }); + + Describe("PromoteGroupUsers", [this]() + { + LatentIt("should promote user to admin", [this](const FDoneDelegate& Done) + { + Nakama::CreateGroup(ClientConfig, Session, FString::Printf(TEXT("promote_%s"), *GenerateShortId()), TEXT(""), TEXT(""), TEXT(""), true, 100) + .Next([this](const FNakamaGroup& Result) + { + GroupId = Result.Id; + return Nakama::JoinGroup(ClientConfig, Session2, GroupId); + }) + .Next([this](const FNakamaVoid&) + { + TArray UserIds; + UserIds.Add(UserId2); + return Nakama::PromoteGroupUsers(ClientConfig, Session, GroupId, UserIds); + }) + .Next([this, Done](FNakamaVoidResult PromoteResult) + { + ASYNC_FAIL_ON_ERROR(PromoteResult, Done); + TestTrue("User promoted successfully", true); + Done.Execute(); + }); + }); + }); + + Describe("DemoteGroupUsers", [this]() + { + LatentIt("should demote admin to member", [this](const FDoneDelegate& Done) + { + Nakama::CreateGroup(ClientConfig, Session, FString::Printf(TEXT("demote_%s"), *GenerateShortId()), TEXT(""), TEXT(""), TEXT(""), true, 100) + .Next([this](const FNakamaGroup& Result) + { + GroupId = Result.Id; + return Nakama::JoinGroup(ClientConfig, Session2, GroupId); + }) + .Next([this](const FNakamaVoid&) + { + TArray UserIds; + UserIds.Add(UserId2); + return Nakama::PromoteGroupUsers(ClientConfig, Session, GroupId, UserIds); + }) + .Next([this](const FNakamaVoid&) + { + TArray UserIds; + UserIds.Add(UserId2); + return Nakama::DemoteGroupUsers(ClientConfig, Session, GroupId, UserIds); + }) + .Next([this, Done](FNakamaVoidResult DemoteResult) + { + ASYNC_FAIL_ON_ERROR(DemoteResult, Done); + TestTrue("User demoted successfully", true); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// CANCELLATION TOKEN TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncCancellationSpec, "IntegrationTests.NakamaTests.Cancellation", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaAsyncCancellationSpec) + + +void FNakamaAsyncCancellationSpec::Define() +{ + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id).Next([this, Done](FNakamaSessionResult AuthResult) + { + if (AuthResult.bIsError) { AddError(FString::Printf(TEXT("Auth failed: %s"), *AuthResult.Error.Message)); Done.Execute(); return; } + Session = AuthResult.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + Describe("CancellationToken", [this]() + { + LatentIt("should return error immediately with pre-cancelled token on GetAccount", [this](const FDoneDelegate& Done) + { + TSharedRef> Token = MakeShared>(true); + Nakama::GetAccount(ClientConfig, Session, {}, Token).Next([this, Done](FNakamaAccountResult Result) + { + TestTrue("Request is error", Result.bIsError); + TestEqual("Error code is -1 (cancelled)", Result.Error.Code, -1); + Done.Execute(); + }); + }); + + LatentIt("should succeed with uncancelled token", [this](const FDoneDelegate& Done) + { + TSharedRef> Token = MakeShared>(false); + Nakama::GetAccount(ClientConfig, Session, {}, Token).Next([this, Done](FNakamaAccountResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + TestTrue("Got valid user ID", !Result.Value.User.Id.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should return error with pre-cancelled token on RpcFunc", [this](const FDoneDelegate& Done) + { + TSharedRef> Token = MakeShared>(true); + TSharedPtr Payload = MakeShared(); + Payload->SetStringField(TEXT("message"), TEXT("hello")); + FString PayloadStr; + FJsonSerializer::Serialize(Payload.ToSharedRef(), TJsonWriterFactory<>::Create(&PayloadStr)); + Nakama::RpcFunc(ClientConfig, Session, TEXT("transform"), PayloadStr, {}, Token).Next([this, Done](FNakamaRpcResult Result) + { + TestTrue("Request is error", Result.bIsError); + TestEqual("Error code is -1 (cancelled)", Result.Error.Code, -1); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// RETRY TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FNakamaAsyncRetrySpec, "IntegrationTests.NakamaTests.Retry", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FNakamaRetryConfig RetryConfig; + FNakamaSession Session; + + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FNakamaAsyncRetrySpec) + + +void FNakamaAsyncRetrySpec::Define() +{ + BeforeEach([this]() + { + RetryConfig.MaxRetries = 2; + RetryConfig.BaseDelayMs = 100; + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + FNakamaAccountCustom Account; + Account.Id = GenerateId(); + + Nakama::AuthenticateCustom(ClientConfig, true, TEXT(""), Account.Id, {}, RetryConfig).Next([this, Done](FNakamaSessionResult AuthResult) + { + if (AuthResult.bIsError) { AddError(FString::Printf(TEXT("Auth failed: %s"), *AuthResult.Error.Message)); Done.Execute(); return; } + Session = AuthResult.Value; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Nakama::DeleteAccount(ClientConfig, Session).Next([Done](FNakamaVoidResult) { Done.Execute(); }); + }); + + Describe("Retry", [this]() + { + LatentIt("should exhaust retries on persistent transient error", [this](const FDoneDelegate& Done) + { + Nakama::RpcFunc(ClientConfig, Session, TEXT("always_fail"), TEXT(""), RetryConfig).Next([this, Done](FNakamaRpcResult Result) + { + TestTrue("Request failed", Result.bIsError); + TestEqual("Error code is 14 (UNAVAILABLE)", Result.Error.Code, 14); + Done.Execute(); + }); + }); + + LatentIt("should succeed after transient failures within retry budget", [this](const FDoneDelegate& Done) + { + TSharedPtr Payload = MakeShared(); + Payload->SetStringField(TEXT("test_id"), GenerateId()); + Payload->SetNumberField(TEXT("fail_count"), 2); + FString PayloadStr; + FJsonSerializer::Serialize(Payload.ToSharedRef(), TJsonWriterFactory<>::Create(&PayloadStr)); + + Nakama::RpcFunc(ClientConfig, Session, TEXT("retry_test"), PayloadStr, RetryConfig).Next([this, Done](FNakamaRpcResult Result) + { + ASYNC_FAIL_ON_ERROR(Result, Done); + + TSharedPtr Response; + TSharedRef> Reader = TJsonReaderFactory<>::Create(Result.Value.Payload); + if (!FJsonSerializer::Deserialize(Reader, Response) || !Response.IsValid()) + { + AddError(TEXT("Failed to parse RPC response JSON")); + Done.Execute(); + return; + } + + TestEqual("Took 3 attempts (1 original + 2 retries)", static_cast(Response->GetNumberField(TEXT("attempts"))), 3); + Done.Execute(); + }); + }); + + LatentIt("should fail immediately on non-transient error without retry", [this](const FDoneDelegate& Done) + { + TSharedPtr Payload = MakeShared(); + Payload->SetStringField(TEXT("foo"), TEXT("bar")); // missing "message" field triggers INVALID_ARGUMENT + FString PayloadStr; + FJsonSerializer::Serialize(Payload.ToSharedRef(), TJsonWriterFactory<>::Create(&PayloadStr)); + + Nakama::RpcFunc(ClientConfig, Session, TEXT("transform"), PayloadStr, RetryConfig).Next([this, Done](FNakamaRpcResult Result) + { + TestTrue("Request failed", Result.bIsError); + TestEqual("Error code is 3 (INVALID_ARGUMENT)", Result.Error.Code, 3); + Done.Execute(); + }); + }); + + LatentIt("should not retry when MaxRetries is zero", [this](const FDoneDelegate& Done) + { + RetryConfig.MaxRetries = 0; + + Nakama::RpcFunc(ClientConfig, Session, TEXT("always_fail"), TEXT(""), RetryConfig).Next([this, Done](FNakamaRpcResult Result) + { + TestTrue("Request failed", Result.bIsError); + TestEqual("Error code is 14 (UNAVAILABLE)", Result.Error.Code, 14); + Done.Execute(); + }); + }); + + LatentIt("should retry on connection failure (unreachable host)", [this](const FDoneDelegate& Done) + { + FNakamaClientConfig BadConfig{TEXT("defaultkey"), TEXT("127.0.0.1"), 19999, false}; + FNakamaRetryConfig ConnRetryConfig; + ConnRetryConfig.MaxRetries = 1; + ConnRetryConfig.BaseDelayMs = 100; + ConnRetryConfig.Timeout = 2.0f; + + double StartTime = FPlatformTime::Seconds(); + Nakama::GetAccount(BadConfig, Session, ConnRetryConfig).Next([this, Done, StartTime](FNakamaAccountResult Result) + { + TestTrue("Request failed", Result.bIsError); + TestEqual("Error code is 0 (connection failed)", Result.Error.Code, 0); + double Elapsed = FPlatformTime::Seconds() - StartTime; + TestTrue("Took long enough to have retried (>0.1s)", Elapsed > 0.1); + Done.Execute(); + }); + }); + + LatentIt("should report expired refresh token as UNAUTHENTICATED (code 16)", [this](const FDoneDelegate& Done) + { + // Force both tokens to appear expired so MaybeRefreshThenCall + // hits the IsRefreshExpired() path. + Session.TokenExpiresAt = 1; // auth token expired (Unix epoch + 1s) + Session.RefreshTokenExpiresAt = 1; // refresh token also expired + + FNakamaRetryConfig RefreshRetryConfig; + RefreshRetryConfig.MaxRetries = 0; + RefreshRetryConfig.bAutoRefreshSession = true; + + Nakama::GetAccount(ClientConfig, Session, RefreshRetryConfig).Next([this, Done](FNakamaAccountResult Result) + { + TestTrue("Request failed", Result.bIsError); + TestEqual("Error code is UNAUTHENTICATED (16)", Result.Error.Code, 16); + Done.Execute(); + }); + }); + + LatentIt("should safely skip OnSessionRefreshed when owner UObject is GC'd", [this](const FDoneDelegate& Done) + { + // Create a temporary UObject that we will destroy before the callback fires. + // This simulates a common pattern: a UObject (e.g. a GameInstance subsystem + // or controller) captures `this` in OnSessionRefreshed, then gets GC'd while + // a retry/refresh is in-flight. + UObject* Victim = NewObject(nullptr, TEXT("/Temp/NakamaTestVictim"), RF_Transient); + Victim->AddToRoot(); // prevent premature GC so we control the lifetime + + // Capture the raw pointer but also set OnSessionRefreshedOwner so the + // retry logic can detect when the owner has been collected. + TSharedRef CallbackFired = MakeShared(false); + FNakamaRetryConfig RefreshRetryConfig; + RefreshRetryConfig.MaxRetries = 0; + RefreshRetryConfig.bAutoRefreshSession = true; + RefreshRetryConfig.OnSessionRefreshedOwner = Victim; + RefreshRetryConfig.OnSessionRefreshed = [Victim, CallbackFired](const FNakamaSession& RefreshedSession) + { + *CallbackFired = true; + // If we get here with a GC'd Victim, this would crash. + // The weak owner guard should prevent this from ever executing. + UE_LOG(LogTemp, Log, TEXT("OnSessionRefreshed called, Victim class: %s"), + *Victim->GetClass()->GetName()); + }; + + // Force the auth token to appear expired so the retry logic triggers + // a session refresh (but keep the refresh token valid). + Session.TokenExpiresAt = 1; + + // Remove the root reference and force GC so the weak pointer becomes stale. + // TWeakObjectPtr only invalidates after the GC clears the weak reference table. + Victim->RemoveFromRoot(); + Victim->MarkAsGarbage(); + CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS); + + // The weak pointer is now stale. The API call will trigger + // MaybeRefreshThenCall -> SessionRefresh, but OnSessionRefreshed + // should be skipped because OnSessionRefreshedOwner is no longer valid. + Nakama::GetAccount(ClientConfig, Session, RefreshRetryConfig).Next([this, Done, CallbackFired](FNakamaAccountResult Result) + { + TestFalse("OnSessionRefreshed should NOT have fired after owner was GC'd", *CallbackFired); + Done.Execute(); + }); + }); + }); +} diff --git a/IntegrationTests/Source/IntegrationTests/Tests/ObjectOwnershipTests.cpp b/IntegrationTests/Source/IntegrationTests/Tests/ObjectOwnershipTests.cpp new file mode 100644 index 000000000..d57e1020c --- /dev/null +++ b/IntegrationTests/Source/IntegrationTests/Tests/ObjectOwnershipTests.cpp @@ -0,0 +1,112 @@ +/** + * Object Ownership Tests + * + * Demonstrates bug: factory functions in NakamaClientBlueprintLibrary.cpp and + * SatoriClientBlueprintLibrary.cpp call NewObject() without an Outer, so + * created async actions are stranded in GetTransientPackage() instead of being + * owned by the WorldContextObject. + * + * All tests in this file are EXPECTED TO FAIL until the bug is fixed. + * Fix: change NewObject() -> NewObject(WorldContextObject) in each factory. + */ + +#include "Misc/AutomationTest.h" +#include "NakamaClientBlueprintLibrary.h" +#include "SatoriClientBlueprintLibrary.h" +#include "UObject/Package.h" +#include "Engine/Engine.h" + +// ============================================================================ +// Nakama — async action outer must be WorldContextObject, not transient package +// ============================================================================ + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FNakamaAsyncActionOuterTest, + "IntegrationTests.ObjectOwnership.NakamaAsyncActionOuter", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter +) + +bool FNakamaAsyncActionOuterTest::RunTest(const FString& Parameters) +{ + // GEngine is a concrete, always-available UObject that is never GetTransientPackage(). + // UObject itself is abstract in UE5.5+ and cannot be directly instantiated. + UObject* WorldContext = GEngine; + + FNakamaClientConfig Client{TEXT("defaultkey"), TEXT("127.0.0.1"), 7350, false}; + FNakamaSession Session; + + // Create the action — do NOT call Activate() so no network I/O occurs. + UNakamaClientHealthcheck* Action = + UNakamaClientHealthcheck::Healthcheck(WorldContext, Client, Session); + + // BUG: NewObject() in the factory passes no Outer, + // so GetOuter() returns GetTransientPackage() instead of WorldContext. + // This test FAILS until the factory is fixed. + TestNotEqual( + TEXT("[BUG] Nakama async action outer is the transient package; expected WorldContextObject"), + Action->GetOuter(), + static_cast(GetTransientPackage()) + ); + + // Also verify we can walk up to the world context via the outer chain. + // GetTypedOuter returns nullptr when the outer chain doesn't pass through WorldContext. + bool bFoundWorldContext = false; + UObject* Outer = Action->GetOuter(); + while (Outer) + { + if (Outer == WorldContext) { bFoundWorldContext = true; break; } + Outer = Outer->GetOuter(); + } + TestTrue( + TEXT("[BUG] WorldContextObject not reachable through async action outer chain"), + bFoundWorldContext + ); + + return true; +} + +// ============================================================================ +// Satori — same bug in SatoriClientBlueprintLibrary factory functions +// ============================================================================ + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FSatoriAsyncActionOuterTest, + "IntegrationTests.ObjectOwnership.SatoriAsyncActionOuter", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter +) + +bool FSatoriAsyncActionOuterTest::RunTest(const FString& Parameters) +{ + UObject* WorldContext = GEngine; + + FSatoriClientConfig Client; + Client.Host = TEXT("127.0.0.1"); + Client.Port = 7480; + Client.ServerKey = TEXT("defaultkey"); + Client.bUseSSL = false; + + // Create the action — do NOT Activate(). + USatoriClientAuthenticate* Action = + USatoriClientAuthenticate::Authenticate(WorldContext, Client, TEXT("test-id"), false, {}, {}); + + // BUG: same pattern — no Outer passed to NewObject. + TestNotEqual( + TEXT("[BUG] Satori async action outer is the transient package; expected WorldContextObject"), + Action->GetOuter(), + static_cast(GetTransientPackage()) + ); + + bool bFoundWorldContext = false; + UObject* Outer = Action->GetOuter(); + while (Outer) + { + if (Outer == WorldContext) { bFoundWorldContext = true; break; } + Outer = Outer->GetOuter(); + } + TestTrue( + TEXT("[BUG] WorldContextObject not reachable through Satori async action outer chain"), + bFoundWorldContext + ); + + return true; +} diff --git a/IntegrationTests/Source/IntegrationTests/Tests/SatoriAsyncTests.cpp b/IntegrationTests/Source/IntegrationTests/Tests/SatoriAsyncTests.cpp index ec615faf6..ad62ad700 100644 --- a/IntegrationTests/Source/IntegrationTests/Tests/SatoriAsyncTests.cpp +++ b/IntegrationTests/Source/IntegrationTests/Tests/SatoriAsyncTests.cpp @@ -1,102 +1,974 @@ /** - * Satori API Integration Test Suite + * Satori Async API Integration Test Suite + * + * Tests for all Satori REST API endpoints using the TFuture-based Satori:: free-function API. + * Mirrors the Nakama async test pattern: each spec authenticates in BeforeEach, then tests + * individual endpoints. */ -#include "CoreMinimal.h" -#include "Misc/AutomationTest.h" -#include "SatoriClient.h" -#include "SatoriEvent.h" +#include "Satori.h" +#include "Misc/Guid.h" #include "SatoriConsoleHelper.h" +/** + * Helper macro: early-return on unexpected error inside a .Next() callback. + */ +#define SATORI_FAIL_ON_ERROR(Result, Done) \ + if (Result.bIsError) { \ + AddError(FString::Printf(TEXT("Unexpected error %d: %s"), Result.Error.Code, *Result.Error.Message)); \ + Done.Execute(); \ + return; \ + } + +// ============================================================================ +// AUTHENTICATION TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FSatoriAsyncAuthSpec, "IntegrationTests.SatoriTests.Auth", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FSatoriClientConfig ClientConfig; + FSatoriRetryConfig RetryConfig; + FString TestIdentityId; + FSatoriSession Session; + + static FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7450; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FSatoriAsyncAuthSpec) + +FString FSatoriAsyncAuthSpec::ServerKey; +const FString FSatoriAsyncAuthSpec::Host = TEXT("127.0.0.1"); + +void FSatoriAsyncAuthSpec::Define() +{ + LatentBeforeEach([this](const FDoneDelegate& Done) + { + GetSatoriApiKey().Next([this, Done](FString Key) + { + ServerKey = Key; + Done.Execute(); + }); + }); + + BeforeEach([this]() + { + ClientConfig = FSatoriClientConfig{ServerKey, Host, Port, false}; + TestIdentityId = GenerateId(); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Satori::DeleteIdentity(ClientConfig, Session).Next([Done](FSatoriVoidResult) { Done.Execute(); }); + }); + + Describe("Authenticate", [this]() + { + LatentIt("should authenticate with valid identity ID", [this](const FDoneDelegate& Done) + { + Satori::Authenticate(ClientConfig, TestIdentityId, false, {}, {}).Next([this, Done](FSatoriSessionResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + TestTrue("Session has token", !Result.Value.Token.IsEmpty()); + TestTrue("Session has refresh token", !Result.Value.RefreshToken.IsEmpty()); + Session = Result.Value; + Done.Execute(); + }); + }); + + LatentIt("should authenticate with no_session flag", [this](const FDoneDelegate& Done) + { + Satori::Authenticate(ClientConfig, TestIdentityId, true, {}, {}).Next([this, Done](FSatoriSessionResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + TestTrue("Token is empty with no_session", Result.Value.Token.IsEmpty()); + Done.Execute(); + }); + }); + + LatentIt("should authenticate with default properties", [this](const FDoneDelegate& Done) + { + TMap Defaults; + Defaults.Add(TEXT("platform"), TEXT("windows")); + Defaults.Add(TEXT("version"), TEXT("1.0")); + + Satori::Authenticate(ClientConfig, TestIdentityId, false, Defaults, {}).Next([this, Done](FSatoriSessionResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + TestTrue("Session has token", !Result.Value.Token.IsEmpty()); + Session = Result.Value; + Done.Execute(); + }); + }); + + LatentIt("should authenticate with custom properties", [this](const FDoneDelegate& Done) + { + TMap Custom; + Custom.Add(TEXT("preferred_mode"), TEXT("ranked")); + + Satori::Authenticate(ClientConfig, TestIdentityId, false, {}, Custom).Next([this, Done](FSatoriSessionResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + TestTrue("Session has token", !Result.Value.Token.IsEmpty()); + Session = Result.Value; + Done.Execute(); + }); + }); + + LatentIt("should fail with empty identity ID", [this](const FDoneDelegate& Done) + { + Satori::Authenticate(ClientConfig, TEXT(""), false, {}, {}).Next([this, Done](FSatoriSessionResult Result) + { + TestTrue("Expected error", Result.bIsError); + Done.Execute(); + }); + }); + + LatentIt("should fail with identity ID too short", [this](const FDoneDelegate& Done) + { + Satori::Authenticate(ClientConfig, TEXT("abc"), false, {}, {}).Next([this, Done](FSatoriSessionResult Result) + { + TestTrue("Expected error", Result.bIsError); + Done.Execute(); + }); + }); + }); + + Describe("AuthenticateRefresh", [this]() + { + LatentIt("should refresh a valid session", [this](const FDoneDelegate& Done) + { + Satori::Authenticate(ClientConfig, TestIdentityId, false, {}, {}).Next([this, Done](FSatoriSessionResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + Session = Result.Value; + const FString RefreshToken = Result.Value.RefreshToken; + + Satori::AuthenticateRefresh(ClientConfig, RefreshToken).Next([this, Done](FSatoriSessionResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + TestTrue("Refreshed session has token", !Result.Value.Token.IsEmpty()); + TestTrue("Refreshed session has refresh token", !Result.Value.RefreshToken.IsEmpty()); + Done.Execute(); + }); + }); + }); + + LatentIt("should fail with invalid refresh token", [this](const FDoneDelegate& Done) + { + Satori::AuthenticateRefresh(ClientConfig, TEXT("invalid-token")).Next([this, Done](FSatoriSessionResult Result) + { + TestTrue("Expected error", Result.bIsError); + Done.Execute(); + }); + }); + }); + + Describe("AuthenticateLogout", [this]() + { + LatentIt("should log out a valid session", [this](const FDoneDelegate& Done) + { + Satori::Authenticate(ClientConfig, TestIdentityId, false, {}, {}).Next([this, Done](FSatoriSessionResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + Session = Result.Value; + const FString Token = Result.Value.Token; + const FString RefreshToken = Result.Value.RefreshToken; + + Satori::AuthenticateLogout(ClientConfig, Token, RefreshToken).Next([this, Done](FSatoriVoidResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + Done.Execute(); + }); + }); + }); + }); +} + +// ============================================================================ +// HEALTHCHECK TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FSatoriAsyncHealthcheckSpec, "IntegrationTests.SatoriTests.Healthcheck", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FSatoriClientConfig ClientConfig; + FSatoriRetryConfig RetryConfig; + FSatoriSession Session; + + static FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7450; + +END_DEFINE_SPEC(FSatoriAsyncHealthcheckSpec) + +FString FSatoriAsyncHealthcheckSpec::ServerKey; +const FString FSatoriAsyncHealthcheckSpec::Host = TEXT("127.0.0.1"); + +void FSatoriAsyncHealthcheckSpec::Define() +{ + LatentBeforeEach([this](const FDoneDelegate& Done) + { + GetSatoriApiKey().Next([this, Done](FString Key) + { + ServerKey = Key; + Done.Execute(); + }); + }); + + BeforeEach([this]() + { + ClientConfig = FSatoriClientConfig{ServerKey, Host, Port, false}; + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Satori::DeleteIdentity(ClientConfig, Session).Next([Done](FSatoriVoidResult) { Done.Execute(); }); + }); + + LatentIt("should return healthy", [this](const FDoneDelegate& Done) + { + Satori::Healthcheck(ClientConfig, FSatoriSession{}).Next([this, Done](FSatoriVoidResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + Done.Execute(); + }); + }); + + LatentIt("readycheck should return ready", [this](const FDoneDelegate& Done) + { + Satori::Readycheck(ClientConfig, FSatoriSession{}).Next([this, Done](FSatoriVoidResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + Done.Execute(); + }); + }); +} + // ============================================================================ -// SERVER EVENT TESTS +// IDENTITY TESTS // ============================================================================ -BEGIN_DEFINE_SPEC(FSatoriServerEventSpec, "Satori.ServerEvents", - EAutomationTestFlags::EditorContext | EAutomationTestFlags::ProductFilter) +BEGIN_DEFINE_SPEC(FSatoriAsyncIdentitySpec, "IntegrationTests.SatoriTests.Identity", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) - USatoriClient* Client; + FSatoriClientConfig ClientConfig; + FSatoriRetryConfig RetryConfig; + FSatoriSession Session; static FString ServerKey; static const FString Host; static constexpr int32 Port = 7450; -END_DEFINE_SPEC(FSatoriServerEventSpec) + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } -FString FSatoriServerEventSpec::ServerKey; -const FString FSatoriServerEventSpec::Host = TEXT("127.0.0.1"); +END_DEFINE_SPEC(FSatoriAsyncIdentitySpec) -void FSatoriServerEventSpec::Define() +FString FSatoriAsyncIdentitySpec::ServerKey; +const FString FSatoriAsyncIdentitySpec::Host = TEXT("127.0.0.1"); + +void FSatoriAsyncIdentitySpec::Define() { LatentBeforeEach([this](const FDoneDelegate& Done) { GetSatoriApiKey().Next([this, Done](FString Key) { - if (Key.IsEmpty()) + ServerKey = Key; + Done.Execute(); + }); + }); + + BeforeEach([this]() + { + ClientConfig = FSatoriClientConfig{ServerKey, Host, Port, false}; + Session = FSatoriSession{}; + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Satori::DeleteIdentity(ClientConfig, Session).Next([Done](FSatoriVoidResult) { Done.Execute(); }); + }); + + Describe("Identify", [this]() + { + LatentIt("should enrich session with a new identity", [this](const FDoneDelegate& Done) + { + const FString InitialId = GenerateId(); + const FString NewId = GenerateId(); + + Satori::Authenticate(ClientConfig, InitialId, false, {}, {}).Next([this, NewId](const FSatoriSession& Sess) + { + Session = Sess; + return Satori::Identify(ClientConfig, Session, NewId, {}, {}); + }).Next([this, Done](FSatoriSessionResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + TestTrue("New session has token", !Result.Value.Token.IsEmpty()); + Done.Execute(); + }); + }); + }); + + Describe("DeleteIdentity", [this]() + { + LatentIt("should delete an identity", [this](const FDoneDelegate& Done) + { + const FString Id = GenerateId(); + + Satori::Authenticate(ClientConfig, Id, false, {}, {}).Next([this](const FSatoriSession& Sess) { - AddError(TEXT("Failed to retrieve Satori API key from console")); + Session = Sess; + return Satori::DeleteIdentity(ClientConfig, Session); + }).Next([this, Done](FSatoriVoidResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); Done.Execute(); - return Key; - } + }); + }); + }); +} + +// ============================================================================ +// PROPERTIES TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FSatoriAsyncPropertiesSpec, "IntegrationTests.SatoriTests.Properties", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FSatoriClientConfig ClientConfig; + FSatoriRetryConfig RetryConfig; + FSatoriSession Session; + + static FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7450; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FSatoriAsyncPropertiesSpec) + +FString FSatoriAsyncPropertiesSpec::ServerKey; +const FString FSatoriAsyncPropertiesSpec::Host = TEXT("127.0.0.1"); + +void FSatoriAsyncPropertiesSpec::Define() +{ + LatentBeforeEach([this](const FDoneDelegate& Done) + { + GetSatoriApiKey().Next([this, Done](FString Key) + { ServerKey = Key; - Client = USatoriClient::CreateDefaultClient(Key, Host, Port, false, true); Done.Execute(); - return Key; }); }); - LatentIt("should post a basic server event", [this](const FDoneDelegate& Done) + BeforeEach([this]() + { + ClientConfig = FSatoriClientConfig{ServerKey, Host, Port, false}; + Session = FSatoriSession{}; + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Satori::DeleteIdentity(ClientConfig, Session).Next([Done](FSatoriVoidResult) { Done.Execute(); }); + }); + + Describe("ListProperties", [this]() { - FSatoriEvent Event; - Event.Name = TEXT("gameStarted"); - Event.Value = TEXT("test-game"); - Event.Timestamp = FDateTime::UtcNow(); + LatentIt("should list properties for authenticated identity", [this](const FDoneDelegate& Done) + { + const FString Id = GenerateId(); + TMap Defaults; + Defaults.Add(TEXT("platform"), TEXT("windows")); - Client->PostServerEvent({Event}, - [Done]() { Done.Execute(); }, - [this, Done](const FSatoriError& Error) + Satori::Authenticate(ClientConfig, Id, false, Defaults, {}).Next([this](const FSatoriSession& Sess) + { + Session = Sess; + return Satori::ListProperties(ClientConfig, Session); + }).Next([this, Done](FSatoriPropertiesResult Result) { - AddError(FString::Printf(TEXT("PostServerEvent failed: %s"), *Error.Message)); + SATORI_FAIL_ON_ERROR(Result, Done); + const auto& Props = Result.Value; + TestTrue("Default properties contain platform", Props.Default.Contains(TEXT("platform"))); Done.Execute(); }); + }); }); - LatentIt("should post a server event with metadata and value", [this](const FDoneDelegate& Done) + Describe("UpdateProperties", [this]() { - FSatoriEvent Event; - Event.Name = TEXT("purchaseCompleted"); - Event.Value = TEXT("100"); - Event.Metadata.Add(TEXT("platform"), TEXT("windows")); - Event.Metadata.Add(TEXT("version"), TEXT("1.0")); - Event.Timestamp = FDateTime::UtcNow(); + LatentIt("should update custom properties", [this](const FDoneDelegate& Done) + { + const FString Id = GenerateId(); + + Satori::Authenticate(ClientConfig, Id, false, {}, {}).Next([this](const FSatoriSession& Sess) + { + Session = Sess; - Client->PostServerEvent({Event}, - [Done]() { Done.Execute(); }, - [this, Done](const FSatoriError& Error) + TMap Custom; + Custom.Add(TEXT("level"), TEXT("5")); + Custom.Add(TEXT("rank"), TEXT("gold")); + return Satori::UpdateProperties(ClientConfig, Session, false, {}, Custom); + }).Next([this](const FSatoriVoid&) { - AddError(FString::Printf(TEXT("PostServerEvent with metadata failed: %s"), *Error.Message)); + return Satori::ListProperties(ClientConfig, Session); + }).Next([this, Done](FSatoriPropertiesResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + const auto& Props = Result.Value; + TestEqual("Custom level", Props.Custom.FindRef(TEXT("level")), TEXT("5")); + TestEqual("Custom rank", Props.Custom.FindRef(TEXT("rank")), TEXT("gold")); Done.Execute(); }); + }); + + LatentIt("should update default properties", [this](const FDoneDelegate& Done) + { + const FString Id = GenerateId(); + + Satori::Authenticate(ClientConfig, Id, false, {}, {}).Next([this](const FSatoriSession& Sess) + { + Session = Sess; + + TMap Defaults; + Defaults.Add(TEXT("region"), TEXT("eu-west")); + return Satori::UpdateProperties(ClientConfig, Session, false, Defaults, {}); + }).Next([this](const FSatoriVoid&) + { + return Satori::ListProperties(ClientConfig, Session); + }).Next([this, Done](FSatoriPropertiesResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + TestEqual("Default region", Result.Value.Default.FindRef(TEXT("region")), TEXT("eu-west")); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// EVENT TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FSatoriAsyncEventSpec, "IntegrationTests.SatoriTests.Event", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FSatoriClientConfig ClientConfig; + FSatoriRetryConfig RetryConfig; + FSatoriSession Session; + + static FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7450; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FSatoriAsyncEventSpec) + +FString FSatoriAsyncEventSpec::ServerKey; +const FString FSatoriAsyncEventSpec::Host = TEXT("127.0.0.1"); + +void FSatoriAsyncEventSpec::Define() +{ + LatentBeforeEach([this](const FDoneDelegate& Done) + { + GetSatoriApiKey().Next([this, Done](FString Key) + { + ServerKey = Key; + Done.Execute(); + }); }); - LatentIt("should post multiple server events in one call", [this](const FDoneDelegate& Done) + BeforeEach([this]() { - FSatoriEvent Event1; - Event1.Name = TEXT("gameStarted"); - Event1.Value = TEXT("test-game"); - Event1.Timestamp = FDateTime::UtcNow(); + ClientConfig = FSatoriClientConfig{ServerKey, Host, Port, false}; + Session = FSatoriSession{}; + }); - FSatoriEvent Event2; - Event2.Name = TEXT("screenViewed"); - Event2.Value = TEXT("main_menu"); - Event2.Timestamp = FDateTime::UtcNow(); + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Satori::DeleteIdentity(ClientConfig, Session).Next([Done](FSatoriVoidResult) { Done.Execute(); }); + }); - Client->PostServerEvent({Event1, Event2}, - [Done]() { Done.Execute(); }, - [this, Done](const FSatoriError& Error) + Describe("Event", [this]() + { + LatentIt("should publish a single event", [this](const FDoneDelegate& Done) + { + Satori::Authenticate(ClientConfig, GenerateId(), false, {}, {}).Next([this](const FSatoriSession& Sess) + { + Session = Sess; + + FSatoriEvent Evt; + Evt.Name = TEXT("game_start"); + Evt.Value = TEXT("tutorial"); + + return Satori::Event(ClientConfig, Session, {Evt}); + }).Next([this, Done](FSatoriVoidResult Result) { - AddError(FString::Printf(TEXT("PostServerEvent with multiple events failed: %s"), *Error.Message)); + SATORI_FAIL_ON_ERROR(Result, Done); Done.Execute(); }); + }); + + LatentIt("should publish multiple events", [this](const FDoneDelegate& Done) + { + Satori::Authenticate(ClientConfig, GenerateId(), false, {}, {}).Next([this](const FSatoriSession& Sess) + { + Session = Sess; + + FSatoriEvent Evt1; + Evt1.Name = TEXT("game_start"); + Evt1.Value = TEXT("pvp"); + + FSatoriEvent Evt2; + Evt2.Name = TEXT("level_complete"); + Evt2.Value = TEXT("3"); + Evt2.Metadata.Add(TEXT("score"), TEXT("1500")); + + return Satori::Event(ClientConfig, Session, {Evt1, Evt2}); + }).Next([this, Done](FSatoriVoidResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + Done.Execute(); + }); + }); + + LatentIt("should publish event with metadata", [this](const FDoneDelegate& Done) + { + Satori::Authenticate(ClientConfig, GenerateId(), false, {}, {}).Next([this](const FSatoriSession& Sess) + { + Session = Sess; + + FSatoriEvent Evt; + Evt.Name = TEXT("purchase"); + Evt.Value = TEXT("gem_pack_100"); + Evt.Metadata.Add(TEXT("currency"), TEXT("USD")); + Evt.Metadata.Add(TEXT("amount"), TEXT("9.99")); + + return Satori::Event(ClientConfig, Session, {Evt}); + }).Next([this, Done](FSatoriVoidResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// FLAGS TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FSatoriAsyncFlagsSpec, "IntegrationTests.SatoriTests.Flags", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FSatoriClientConfig ClientConfig; + FSatoriRetryConfig RetryConfig; + FSatoriSession Session; + + static FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7450; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FSatoriAsyncFlagsSpec) + +FString FSatoriAsyncFlagsSpec::ServerKey; +const FString FSatoriAsyncFlagsSpec::Host = TEXT("127.0.0.1"); + +void FSatoriAsyncFlagsSpec::Define() +{ + LatentBeforeEach([this](const FDoneDelegate& Done) + { + GetSatoriApiKey().Next([this, Done](FString Key) + { + ServerKey = Key; + Done.Execute(); + }); + }); + + BeforeEach([this]() + { + ClientConfig = FSatoriClientConfig{ServerKey, Host, Port, false}; + Session = FSatoriSession{}; + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Satori::DeleteIdentity(ClientConfig, Session).Next([Done](FSatoriVoidResult) { Done.Execute(); }); + }); + + Describe("GetFlags", [this]() + { + LatentIt("should list all flags", [this](const FDoneDelegate& Done) + { + Satori::Authenticate(ClientConfig, GenerateId(), false, {}, {}).Next([this](const FSatoriSession& Sess) + { + Session = Sess; + return Satori::GetFlags(ClientConfig, Session, {}, {}); + }).Next([this, Done](FSatoriFlagListResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + // Fresh server may have no flags configured, but the call should succeed + Done.Execute(); + }); + }); + + LatentIt("should filter flags by name", [this](const FDoneDelegate& Done) + { + Satori::Authenticate(ClientConfig, GenerateId(), false, {}, {}).Next([this](const FSatoriSession& Sess) + { + Session = Sess; + return Satori::GetFlags(ClientConfig, Session, {TEXT("nonexistent_flag")}, {}); + }).Next([this, Done](FSatoriFlagListResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + TestEqual("No flags returned for nonexistent name", Result.Value.Flags.Num(), 0); + Done.Execute(); + }); + }); + }); + + Describe("GetFlagOverrides", [this]() + { + LatentIt("should list all flag overrides", [this](const FDoneDelegate& Done) + { + Satori::Authenticate(ClientConfig, GenerateId(), false, {}, {}).Next([this](const FSatoriSession& Sess) + { + Session = Sess; + return Satori::GetFlagOverrides(ClientConfig, Session, {}, {}); + }).Next([this, Done](FSatoriFlagOverrideListResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// EXPERIMENTS TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FSatoriAsyncExperimentsSpec, "IntegrationTests.SatoriTests.Experiments", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FSatoriClientConfig ClientConfig; + FSatoriRetryConfig RetryConfig; + FSatoriSession Session; + + static FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7450; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FSatoriAsyncExperimentsSpec) + +FString FSatoriAsyncExperimentsSpec::ServerKey; +const FString FSatoriAsyncExperimentsSpec::Host = TEXT("127.0.0.1"); + +void FSatoriAsyncExperimentsSpec::Define() +{ + LatentBeforeEach([this](const FDoneDelegate& Done) + { + GetSatoriApiKey().Next([this, Done](FString Key) + { + ServerKey = Key; + Done.Execute(); + }); + }); + + BeforeEach([this]() + { + ClientConfig = FSatoriClientConfig{ServerKey, Host, Port, false}; + Session = FSatoriSession{}; + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Satori::DeleteIdentity(ClientConfig, Session).Next([Done](FSatoriVoidResult) { Done.Execute(); }); + }); + + Describe("GetExperiments", [this]() + { + LatentIt("should list all experiments", [this](const FDoneDelegate& Done) + { + Satori::Authenticate(ClientConfig, GenerateId(), false, {}, {}).Next([this](const FSatoriSession& Sess) + { + Session = Sess; + return Satori::GetExperiments(ClientConfig, Session, {}, {}); + }).Next([this, Done](FSatoriExperimentListResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + Done.Execute(); + }); + }); + + LatentIt("should filter experiments by name", [this](const FDoneDelegate& Done) + { + Satori::Authenticate(ClientConfig, GenerateId(), false, {}, {}).Next([this](const FSatoriSession& Sess) + { + Session = Sess; + return Satori::GetExperiments(ClientConfig, Session, {TEXT("nonexistent_experiment")}, {}); + }).Next([this, Done](FSatoriExperimentListResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + TestEqual("No experiments for nonexistent name", Result.Value.Experiments.Num(), 0); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// LIVE EVENTS TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FSatoriAsyncLiveEventsSpec, "IntegrationTests.SatoriTests.LiveEvents", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FSatoriClientConfig ClientConfig; + FSatoriRetryConfig RetryConfig; + FSatoriSession Session; + + static FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7450; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FSatoriAsyncLiveEventsSpec) + +FString FSatoriAsyncLiveEventsSpec::ServerKey; +const FString FSatoriAsyncLiveEventsSpec::Host = TEXT("127.0.0.1"); + +void FSatoriAsyncLiveEventsSpec::Define() +{ + LatentBeforeEach([this](const FDoneDelegate& Done) + { + GetSatoriApiKey().Next([this, Done](FString Key) + { + ServerKey = Key; + Done.Execute(); + }); + }); + + BeforeEach([this]() + { + ClientConfig = FSatoriClientConfig{ServerKey, Host, Port, false}; + Session = FSatoriSession{}; + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Satori::DeleteIdentity(ClientConfig, Session).Next([Done](FSatoriVoidResult) { Done.Execute(); }); + }); + + Describe("GetLiveEvents", [this]() + { + LatentIt("should list all live events", [this](const FDoneDelegate& Done) + { + Satori::Authenticate(ClientConfig, GenerateId(), false, {}, {}).Next([this](const FSatoriSession& Sess) + { + Session = Sess; + return Satori::GetLiveEvents(ClientConfig, Session, {}, {}, 0, 0, 0, 0); + }).Next([this, Done](FSatoriLiveEventListResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + Done.Execute(); + }); + }); + + LatentIt("should filter live events by name", [this](const FDoneDelegate& Done) + { + Satori::Authenticate(ClientConfig, GenerateId(), false, {}, {}).Next([this](const FSatoriSession& Sess) + { + Session = Sess; + return Satori::GetLiveEvents(ClientConfig, Session, {TEXT("nonexistent_event")}, {}, 0, 0, 0, 0); + }).Next([this, Done](FSatoriLiveEventListResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + TestEqual("No live events for nonexistent name", Result.Value.LiveEvents.Num(), 0); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// MESSAGES TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FSatoriAsyncMessagesSpec, "IntegrationTests.SatoriTests.Messages", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FSatoriClientConfig ClientConfig; + FSatoriRetryConfig RetryConfig; + FSatoriSession Session; + + static FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7450; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FSatoriAsyncMessagesSpec) + +FString FSatoriAsyncMessagesSpec::ServerKey; +const FString FSatoriAsyncMessagesSpec::Host = TEXT("127.0.0.1"); + +void FSatoriAsyncMessagesSpec::Define() +{ + LatentBeforeEach([this](const FDoneDelegate& Done) + { + GetSatoriApiKey().Next([this, Done](FString Key) + { + ServerKey = Key; + Done.Execute(); + }); + }); + + BeforeEach([this]() + { + ClientConfig = FSatoriClientConfig{ServerKey, Host, Port, false}; + Session = FSatoriSession{}; + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Satori::DeleteIdentity(ClientConfig, Session).Next([Done](FSatoriVoidResult) { Done.Execute(); }); + }); + + Describe("GetMessageList", [this]() + { + LatentIt("should list messages for identity", [this](const FDoneDelegate& Done) + { + Satori::Authenticate(ClientConfig, GenerateId(), false, {}, {}).Next([this](const FSatoriSession& Sess) + { + Session = Sess; + return Satori::GetMessageList(ClientConfig, Session, 10, true, TEXT(""), {}); + }).Next([this, Done](FSatoriGetMessageListResponseResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + // Fresh identity has no messages + TestEqual("No messages for fresh identity", Result.Value.Messages.Num(), 0); + Done.Execute(); + }); + }); + + LatentIt("should list messages in reverse order", [this](const FDoneDelegate& Done) + { + Satori::Authenticate(ClientConfig, GenerateId(), false, {}, {}).Next([this](const FSatoriSession& Sess) + { + Session = Sess; + return Satori::GetMessageList(ClientConfig, Session, 10, false, TEXT(""), {}); + }).Next([this, Done](FSatoriGetMessageListResponseResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// SESSION LIFECYCLE TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FSatoriAsyncSessionSpec, "IntegrationTests.SatoriTests.Session", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FSatoriClientConfig ClientConfig; + FSatoriRetryConfig RetryConfig; + FSatoriSession Session; + + static FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7450; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FSatoriAsyncSessionSpec) + +FString FSatoriAsyncSessionSpec::ServerKey; +const FString FSatoriAsyncSessionSpec::Host = TEXT("127.0.0.1"); + +void FSatoriAsyncSessionSpec::Define() +{ + LatentBeforeEach([this](const FDoneDelegate& Done) + { + GetSatoriApiKey().Next([this, Done](FString Key) + { + ServerKey = Key; + Done.Execute(); + }); + }); + + BeforeEach([this]() + { + ClientConfig = FSatoriClientConfig{ServerKey, Host, Port, false}; + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) { Done.Execute(); return; } + Satori::DeleteIdentity(ClientConfig, Session).Next([Done](FSatoriVoidResult) { Done.Execute(); }); + }); + + Describe("Full lifecycle", [this]() + { + LatentIt("should authenticate, use API, refresh, and logout", [this](const FDoneDelegate& Done) + { + const FString Id = GenerateId(); + + Satori::Authenticate(ClientConfig, Id, false, {}, {}).Next([this](const FSatoriSession& Sess) + { + Session = Sess; + return Satori::GetFlags(ClientConfig, Session, {}, {}); + }).Next([this](const FSatoriFlagList&) + { + return Satori::AuthenticateRefresh(ClientConfig, Session.RefreshToken); + }).Next([this, Done](FSatoriSessionResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + const FString Token = Result.Value.Token; + const FString RefreshToken = Result.Value.RefreshToken; + + Satori::AuthenticateLogout(ClientConfig, Token, RefreshToken).Next([this, Done](FSatoriVoidResult Result) + { + SATORI_FAIL_ON_ERROR(Result, Done); + Done.Execute(); + }); + }); + }); + }); + + Describe("Expired session", [this]() + { + LatentIt("should fail API call with invalid bearer token", [this](const FDoneDelegate& Done) + { + FSatoriSession FakeSession; + FakeSession.Token = TEXT("invalid.bearer.token"); + FakeSession.RefreshToken = TEXT("invalid.refresh.token"); + + Satori::GetFlags(ClientConfig, FakeSession, {}, {}).Next([this, Done](FSatoriFlagListResult Result) + { + TestTrue("Expected error with invalid session", Result.bIsError); + Done.Execute(); + }); + }); }); } diff --git a/IntegrationTests/Source/IntegrationTests/Tests/SatoriBlueprintTests.cpp b/IntegrationTests/Source/IntegrationTests/Tests/SatoriBlueprintTests.cpp new file mode 100644 index 000000000..c73d94b65 --- /dev/null +++ b/IntegrationTests/Source/IntegrationTests/Tests/SatoriBlueprintTests.cpp @@ -0,0 +1,977 @@ +/** + * Satori Blueprint Async Action Test Suite + * + * Tests for SatoriBlueprints module UBlueprintAsyncActionBase subclasses. + * Each test fires a BP async action, then verifies the server-side effect + * through the C++ client (which supports lambda callbacks natively). + * + * Mirrors the Nakama BP test pattern from NakamaBlueprintTests.cpp. + */ + +#include "Misc/AutomationTest.h" +#include "SatoriApi.h" +#include "SatoriClientBlueprintLibrary.h" +#include "Misc/Guid.h" +#include "Containers/Ticker.h" +#include "SatoriConsoleHelper.h" + +// Helper: keep the BP action alive for the duration of the async call (tests pass +// nullptr as WorldContextObject so RegisterWithGameInstance is a no-op), then run +// verification once the action completes. +// The base class constructor sets RF_StrongRefOnFrame, and SetReadyToDestroy() +// clears it. We AddToRoot() to prevent GC across frames, then poll until +// RF_StrongRefOnFrame is cleared (signalling the action called SetReadyToDestroy). +static void VerifyWhenComplete(UBlueprintAsyncActionBase* Action, TFunction VerifyFunc) +{ + Action->AddToRoot(); + + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([Action, VerifyFunc = MoveTemp(VerifyFunc)](float) -> bool + { + if (Action->HasAnyFlags(RF_StrongRefOnFrame)) + { + return true; // keep ticking — action hasn't called SetReadyToDestroy yet + } + Action->RemoveFromRoot(); + VerifyFunc(); + return false; + }), + 0.0f + ); +} + +// ============================================================================ +// HEALTHCHECK TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FSatoriBPHealthcheckSpec, "IntegrationTests.SatoriBlueprintTests.Healthcheck", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FSatoriSession Session; + FSatoriClientConfig ClientConfig; + + static FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7450; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FSatoriBPHealthcheckSpec) + +FString FSatoriBPHealthcheckSpec::ServerKey; +const FString FSatoriBPHealthcheckSpec::Host = TEXT("127.0.0.1"); + +void FSatoriBPHealthcheckSpec::Define() +{ + LatentBeforeEach([this](const FDoneDelegate& Done) + { + GetSatoriApiKey().Next([this, Done](FString Key) + { + ServerKey = Key; + ClientConfig = FSatoriClientConfig{ServerKey, Host, Port, false}; + Done.Execute(); + }); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + SatoriApi::Authenticate(ClientConfig, GenerateId(), false, {}, {}, + [this, Done](const FSatoriSession& Result) + { + Session = Result; + Done.Execute(); + }, + [this, Done](const FSatoriError& Error) + { + AddError(FString::Printf(TEXT("Setup auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + SatoriApi::DeleteIdentity(ClientConfig, Session, + [Done]() { Done.Execute(); }, + [Done](const FSatoriError&) { Done.Execute(); } + ); + }); + + Describe("Healthcheck", [this]() + { + LatentIt("should pass healthcheck", [this](const FDoneDelegate& Done) + { + auto* Action = USatoriClientHealthcheck::Healthcheck(nullptr, ClientConfig, Session); + Action->Activate(); + + VerifyWhenComplete(Action, [this, Done]() + { + SatoriApi::Healthcheck(ClientConfig, Session, + [Done]() { Done.Execute(); }, + [this, Done](const FSatoriError& Error) + { + AddError(FString::Printf(TEXT("Healthcheck failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); + + Describe("Readycheck", [this]() + { + LatentIt("should pass readycheck", [this](const FDoneDelegate& Done) + { + auto* Action = USatoriClientReadycheck::Readycheck(nullptr, ClientConfig, Session); + Action->Activate(); + + VerifyWhenComplete(Action, [this, Done]() + { + SatoriApi::Readycheck(ClientConfig, Session, + [Done]() { Done.Execute(); }, + [this, Done](const FSatoriError& Error) + { + AddError(FString::Printf(TEXT("Readycheck failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); +} + +// ============================================================================ +// AUTHENTICATION TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FSatoriBPAuthSpec, "IntegrationTests.SatoriBlueprintTests.Auth", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FSatoriClientConfig ClientConfig; + FSatoriSession Session; + + static FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7450; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FSatoriBPAuthSpec) + +FString FSatoriBPAuthSpec::ServerKey; +const FString FSatoriBPAuthSpec::Host = TEXT("127.0.0.1"); + +void FSatoriBPAuthSpec::Define() +{ + LatentBeforeEach([this](const FDoneDelegate& Done) + { + Session = FSatoriSession(); + GetSatoriApiKey().Next([this, Done](FString Key) + { + ServerKey = Key; + ClientConfig = FSatoriClientConfig{ServerKey, Host, Port, false}; + Done.Execute(); + }); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + if (Session.Token.IsEmpty()) + { + Done.Execute(); + return; + } + SatoriApi::DeleteIdentity(ClientConfig, Session, + [Done]() { Done.Execute(); }, + [Done](const FSatoriError&) { Done.Execute(); } + ); + }); + + Describe("Authenticate", [this]() + { + LatentIt("should authenticate with valid identity ID", [this](const FDoneDelegate& Done) + { + const FString Id = GenerateId(); + + auto* Action = USatoriClientAuthenticate::Authenticate( + nullptr, ClientConfig, Id, false, {}, {}); + Action->Activate(); + + // Verify: re-auth with the same ID should succeed + VerifyWhenComplete(Action, [this, Done, Id]() + { + SatoriApi::Authenticate(ClientConfig, Id, false, {}, {}, + [this, Done](const FSatoriSession& Result) + { + Session = Result; + TestTrue("Session has token", !Result.Token.IsEmpty()); + Done.Execute(); + }, + [this, Done](const FSatoriError& Error) + { + AddError(FString::Printf(TEXT("Re-auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); + + Describe("AuthenticateRefresh", [this]() + { + LatentIt("should refresh a session via BP action", [this](const FDoneDelegate& Done) + { + // Setup: authenticate via C++ to get a refresh token + SatoriApi::Authenticate(ClientConfig, GenerateId(), false, {}, {}, + [this, Done](const FSatoriSession& Result) + { + Session = Result; + auto* Action = USatoriClientAuthenticateRefresh::AuthenticateRefresh( + nullptr, ClientConfig, Result.RefreshToken); + Action->Activate(); + + // Verify: server still responds to healthcheck + VerifyWhenComplete(Action, [this, Done]() + { + SatoriApi::Healthcheck(ClientConfig, FSatoriSession{}, + [Done]() { Done.Execute(); }, + [this, Done](const FSatoriError& Error) + { + AddError(FString::Printf(TEXT("Healthcheck failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }, + [this, Done](const FSatoriError& Error) + { + AddError(FString::Printf(TEXT("Setup auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + + Describe("AuthenticateLogout", [this]() + { + LatentIt("should logout a session via BP action", [this](const FDoneDelegate& Done) + { + // Setup: authenticate via C++ to get tokens + SatoriApi::Authenticate(ClientConfig, GenerateId(), false, {}, {}, + [this, Done](const FSatoriSession& Result) + { + Session = Result; + auto* Action = USatoriClientAuthenticateLogout::AuthenticateLogout( + nullptr, ClientConfig, Result.Token, Result.RefreshToken); + Action->Activate(); + + // Verify: action completed; server still responds + VerifyWhenComplete(Action, [this, Done]() + { + SatoriApi::Healthcheck(ClientConfig, FSatoriSession{}, + [Done]() { Done.Execute(); }, + [this, Done](const FSatoriError& Error) + { + AddError(FString::Printf(TEXT("Healthcheck failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }, + [this, Done](const FSatoriError& Error) + { + AddError(FString::Printf(TEXT("Setup auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); +} + +// ============================================================================ +// IDENTITY TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FSatoriBPIdentitySpec, "IntegrationTests.SatoriBlueprintTests.Identity", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FSatoriClientConfig ClientConfig; + FSatoriSession Session; + + static FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7450; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FSatoriBPIdentitySpec) + +FString FSatoriBPIdentitySpec::ServerKey; +const FString FSatoriBPIdentitySpec::Host = TEXT("127.0.0.1"); + +void FSatoriBPIdentitySpec::Define() +{ + LatentBeforeEach([this](const FDoneDelegate& Done) + { + GetSatoriApiKey().Next([this, Done](FString Key) + { + ServerKey = Key; + ClientConfig = FSatoriClientConfig{ServerKey, Host, Port, false}; + Done.Execute(); + }); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + SatoriApi::Authenticate(ClientConfig, GenerateId(), false, {}, {}, + [this, Done](const FSatoriSession& Result) + { + Session = Result; + Done.Execute(); + }, + [this, Done](const FSatoriError& Error) + { + AddError(FString::Printf(TEXT("Setup auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + SatoriApi::DeleteIdentity(ClientConfig, Session, + [Done]() { Done.Execute(); }, + [Done](const FSatoriError&) { Done.Execute(); } + ); + }); + + Describe("Identify", [this]() + { + LatentIt("should identify with a new identity", [this](const FDoneDelegate& Done) + { + const FString NewId = GenerateId(); + + auto* Action = USatoriClientIdentify::Identify( + nullptr, ClientConfig, Session, NewId, {}, {}); + Action->Activate(); + + // Verify: action completed (Identify returns a new session, old one is invalid) + VerifyWhenComplete(Action, [this, Done]() + { + Done.Execute(); + }); + }); + }); + + Describe("DeleteIdentity", [this]() + { + LatentIt("should delete an identity", [this](const FDoneDelegate& Done) + { + auto* Action = USatoriClientDeleteIdentity::DeleteIdentity( + nullptr, ClientConfig, Session); + Action->Activate(); + + // Verify: action completed without crash + VerifyWhenComplete(Action, [this, Done]() + { + Done.Execute(); + }); + }); + }); +} + +// ============================================================================ +// PROPERTIES TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FSatoriBPPropertiesSpec, "IntegrationTests.SatoriBlueprintTests.Properties", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FSatoriClientConfig ClientConfig; + FSatoriSession Session; + + static FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7450; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FSatoriBPPropertiesSpec) + +FString FSatoriBPPropertiesSpec::ServerKey; +const FString FSatoriBPPropertiesSpec::Host = TEXT("127.0.0.1"); + +void FSatoriBPPropertiesSpec::Define() +{ + LatentBeforeEach([this](const FDoneDelegate& Done) + { + GetSatoriApiKey().Next([this, Done](FString Key) + { + ServerKey = Key; + ClientConfig = FSatoriClientConfig{ServerKey, Host, Port, false}; + Done.Execute(); + }); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + SatoriApi::Authenticate(ClientConfig, GenerateId(), false, {}, {}, + [this, Done](const FSatoriSession& Result) + { + Session = Result; + Done.Execute(); + }, + [this, Done](const FSatoriError& Error) + { + AddError(FString::Printf(TEXT("Setup auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + SatoriApi::DeleteIdentity(ClientConfig, Session, + [Done]() { Done.Execute(); }, + [Done](const FSatoriError&) { Done.Execute(); } + ); + }); + + Describe("ListProperties", [this]() + { + LatentIt("should list properties for authenticated identity", [this](const FDoneDelegate& Done) + { + auto* Action = USatoriClientListProperties::ListProperties( + nullptr, ClientConfig, Session); + Action->Activate(); + + // Verify via C++ ListProperties + VerifyWhenComplete(Action, [this, Done]() + { + SatoriApi::ListProperties(ClientConfig, Session, + [this, Done](const FSatoriProperties&) + { + Done.Execute(); + }, + [this, Done](const FSatoriError& Error) + { + AddError(FString::Printf(TEXT("ListProperties failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); + + Describe("UpdateProperties", [this]() + { + LatentIt("should update custom properties via BP action", [this](const FDoneDelegate& Done) + { + TMap Custom; + Custom.Add(TEXT("level"), TEXT("5")); + + auto* Action = USatoriClientUpdateProperties::UpdateProperties( + nullptr, ClientConfig, Session, false, {}, Custom); + Action->Activate(); + + // Verify: property was persisted + VerifyWhenComplete(Action, [this, Done]() + { + SatoriApi::ListProperties(ClientConfig, Session, + [this, Done](const FSatoriProperties& Props) + { + TestEqual("Custom level", Props.Custom.FindRef(TEXT("level")), TEXT("5")); + Done.Execute(); + }, + [this, Done](const FSatoriError& Error) + { + AddError(FString::Printf(TEXT("ListProperties failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); +} + +// ============================================================================ +// EVENT TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FSatoriBPEventSpec, "IntegrationTests.SatoriBlueprintTests.Event", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FSatoriClientConfig ClientConfig; + FSatoriSession Session; + + static FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7450; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FSatoriBPEventSpec) + +FString FSatoriBPEventSpec::ServerKey; +const FString FSatoriBPEventSpec::Host = TEXT("127.0.0.1"); + +void FSatoriBPEventSpec::Define() +{ + LatentBeforeEach([this](const FDoneDelegate& Done) + { + GetSatoriApiKey().Next([this, Done](FString Key) + { + ServerKey = Key; + ClientConfig = FSatoriClientConfig{ServerKey, Host, Port, false}; + Done.Execute(); + }); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + SatoriApi::Authenticate(ClientConfig, GenerateId(), false, {}, {}, + [this, Done](const FSatoriSession& Result) + { + Session = Result; + Done.Execute(); + }, + [this, Done](const FSatoriError& Error) + { + AddError(FString::Printf(TEXT("Setup auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + SatoriApi::DeleteIdentity(ClientConfig, Session, + [Done]() { Done.Execute(); }, + [Done](const FSatoriError&) { Done.Execute(); } + ); + }); + + Describe("Event", [this]() + { + LatentIt("should publish an event via BP action", [this](const FDoneDelegate& Done) + { + FSatoriEvent Evt; + Evt.Name = TEXT("game_start"); + Evt.Value = TEXT("tutorial"); + + auto* Action = USatoriClientEvent::Event( + nullptr, ClientConfig, Session, {Evt}); + Action->Activate(); + + // Verify: action completed; server still responds + VerifyWhenComplete(Action, [this, Done]() + { + SatoriApi::Healthcheck(ClientConfig, Session, + [Done]() { Done.Execute(); }, + [this, Done](const FSatoriError& Error) + { + AddError(FString::Printf(TEXT("Healthcheck failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); +} + +// ============================================================================ +// FLAGS TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FSatoriBPFlagsSpec, "IntegrationTests.SatoriBlueprintTests.Flags", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FSatoriClientConfig ClientConfig; + FSatoriSession Session; + + static FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7450; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FSatoriBPFlagsSpec) + +FString FSatoriBPFlagsSpec::ServerKey; +const FString FSatoriBPFlagsSpec::Host = TEXT("127.0.0.1"); + +void FSatoriBPFlagsSpec::Define() +{ + LatentBeforeEach([this](const FDoneDelegate& Done) + { + GetSatoriApiKey().Next([this, Done](FString Key) + { + ServerKey = Key; + ClientConfig = FSatoriClientConfig{ServerKey, Host, Port, false}; + Done.Execute(); + }); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + SatoriApi::Authenticate(ClientConfig, GenerateId(), false, {}, {}, + [this, Done](const FSatoriSession& Result) + { + Session = Result; + Done.Execute(); + }, + [this, Done](const FSatoriError& Error) + { + AddError(FString::Printf(TEXT("Setup auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + SatoriApi::DeleteIdentity(ClientConfig, Session, + [Done]() { Done.Execute(); }, + [Done](const FSatoriError&) { Done.Execute(); } + ); + }); + + Describe("GetFlags", [this]() + { + LatentIt("should list all flags via BP action", [this](const FDoneDelegate& Done) + { + auto* Action = USatoriClientGetFlags::GetFlags( + nullptr, ClientConfig, Session, {}, {}); + Action->Activate(); + + // Verify via C++ GetFlags + VerifyWhenComplete(Action, [this, Done]() + { + SatoriApi::GetFlags(ClientConfig, Session, {}, {}, + [this, Done](const FSatoriFlagList&) + { + Done.Execute(); + }, + [this, Done](const FSatoriError& Error) + { + AddError(FString::Printf(TEXT("GetFlags failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); + + Describe("GetFlagOverrides", [this]() + { + LatentIt("should list all flag overrides via BP action", [this](const FDoneDelegate& Done) + { + auto* Action = USatoriClientGetFlagOverrides::GetFlagOverrides( + nullptr, ClientConfig, Session, {}, {}); + Action->Activate(); + + // Verify via C++ GetFlagOverrides + VerifyWhenComplete(Action, [this, Done]() + { + SatoriApi::GetFlagOverrides(ClientConfig, Session, {}, {}, + [this, Done](const FSatoriFlagOverrideList&) + { + Done.Execute(); + }, + [this, Done](const FSatoriError& Error) + { + AddError(FString::Printf(TEXT("GetFlagOverrides failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); +} + +// ============================================================================ +// EXPERIMENTS TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FSatoriBPExperimentsSpec, "IntegrationTests.SatoriBlueprintTests.Experiments", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FSatoriClientConfig ClientConfig; + FSatoriSession Session; + + static FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7450; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FSatoriBPExperimentsSpec) + +FString FSatoriBPExperimentsSpec::ServerKey; +const FString FSatoriBPExperimentsSpec::Host = TEXT("127.0.0.1"); + +void FSatoriBPExperimentsSpec::Define() +{ + LatentBeforeEach([this](const FDoneDelegate& Done) + { + GetSatoriApiKey().Next([this, Done](FString Key) + { + ServerKey = Key; + ClientConfig = FSatoriClientConfig{ServerKey, Host, Port, false}; + Done.Execute(); + }); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + SatoriApi::Authenticate(ClientConfig, GenerateId(), false, {}, {}, + [this, Done](const FSatoriSession& Result) + { + Session = Result; + Done.Execute(); + }, + [this, Done](const FSatoriError& Error) + { + AddError(FString::Printf(TEXT("Setup auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + SatoriApi::DeleteIdentity(ClientConfig, Session, + [Done]() { Done.Execute(); }, + [Done](const FSatoriError&) { Done.Execute(); } + ); + }); + + Describe("GetExperiments", [this]() + { + LatentIt("should list all experiments via BP action", [this](const FDoneDelegate& Done) + { + auto* Action = USatoriClientGetExperiments::GetExperiments( + nullptr, ClientConfig, Session, {}, {}); + Action->Activate(); + + // Verify via C++ GetExperiments + VerifyWhenComplete(Action, [this, Done]() + { + SatoriApi::GetExperiments(ClientConfig, Session, {}, {}, + [this, Done](const FSatoriExperimentList&) + { + Done.Execute(); + }, + [this, Done](const FSatoriError& Error) + { + AddError(FString::Printf(TEXT("GetExperiments failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); +} + +// ============================================================================ +// LIVE EVENTS TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FSatoriBPLiveEventsSpec, "IntegrationTests.SatoriBlueprintTests.LiveEvents", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FSatoriClientConfig ClientConfig; + FSatoriSession Session; + + static FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7450; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FSatoriBPLiveEventsSpec) + +FString FSatoriBPLiveEventsSpec::ServerKey; +const FString FSatoriBPLiveEventsSpec::Host = TEXT("127.0.0.1"); + +void FSatoriBPLiveEventsSpec::Define() +{ + LatentBeforeEach([this](const FDoneDelegate& Done) + { + GetSatoriApiKey().Next([this, Done](FString Key) + { + ServerKey = Key; + ClientConfig = FSatoriClientConfig{ServerKey, Host, Port, false}; + Done.Execute(); + }); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + SatoriApi::Authenticate(ClientConfig, GenerateId(), false, {}, {}, + [this, Done](const FSatoriSession& Result) + { + Session = Result; + Done.Execute(); + }, + [this, Done](const FSatoriError& Error) + { + AddError(FString::Printf(TEXT("Setup auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + SatoriApi::DeleteIdentity(ClientConfig, Session, + [Done]() { Done.Execute(); }, + [Done](const FSatoriError&) { Done.Execute(); } + ); + }); + + Describe("GetLiveEvents", [this]() + { + LatentIt("should list all live events via BP action", [this](const FDoneDelegate& Done) + { + auto* Action = USatoriClientGetLiveEvents::GetLiveEvents( + nullptr, ClientConfig, Session, {}, {}, 0, 0, 0, 0); + Action->Activate(); + + // Verify via C++ GetLiveEvents + VerifyWhenComplete(Action, [this, Done]() + { + SatoriApi::GetLiveEvents(ClientConfig, Session, {}, {}, 0, 0, 0, 0, + [this, Done](const FSatoriLiveEventList&) + { + Done.Execute(); + }, + [this, Done](const FSatoriError& Error) + { + AddError(FString::Printf(TEXT("GetLiveEvents failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); +} + +// ============================================================================ +// MESSAGES TESTS +// ============================================================================ + +BEGIN_DEFINE_SPEC(FSatoriBPMessagesSpec, "IntegrationTests.SatoriBlueprintTests.Messages", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) + + FSatoriClientConfig ClientConfig; + FSatoriSession Session; + + static FString ServerKey; + static const FString Host; + static constexpr int32 Port = 7450; + + FString GenerateId() { return FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); } + +END_DEFINE_SPEC(FSatoriBPMessagesSpec) + +FString FSatoriBPMessagesSpec::ServerKey; +const FString FSatoriBPMessagesSpec::Host = TEXT("127.0.0.1"); + +void FSatoriBPMessagesSpec::Define() +{ + LatentBeforeEach([this](const FDoneDelegate& Done) + { + GetSatoriApiKey().Next([this, Done](FString Key) + { + ServerKey = Key; + ClientConfig = FSatoriClientConfig{ServerKey, Host, Port, false}; + Done.Execute(); + }); + }); + + LatentBeforeEach([this](const FDoneDelegate& Done) + { + SatoriApi::Authenticate(ClientConfig, GenerateId(), false, {}, {}, + [this, Done](const FSatoriSession& Result) + { + Session = Result; + Done.Execute(); + }, + [this, Done](const FSatoriError& Error) + { + AddError(FString::Printf(TEXT("Setup auth failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + + LatentAfterEach([this](const FDoneDelegate& Done) + { + SatoriApi::DeleteIdentity(ClientConfig, Session, + [Done]() { Done.Execute(); }, + [Done](const FSatoriError&) { Done.Execute(); } + ); + }); + + Describe("GetMessageList", [this]() + { + LatentIt("should list messages for identity via BP action", [this](const FDoneDelegate& Done) + { + auto* Action = USatoriClientGetMessageList::GetMessageList( + nullptr, ClientConfig, Session, 10, true, TEXT(""), {}); + Action->Activate(); + + // Verify via C++ GetMessageList + VerifyWhenComplete(Action, [this, Done]() + { + SatoriApi::GetMessageList(ClientConfig, Session, 10, true, TEXT(""), {}, + [this, Done](const FSatoriGetMessageListResponse& Result) + { + TestEqual("No messages for fresh identity", Result.Messages.Num(), 0); + Done.Execute(); + }, + [this, Done](const FSatoriError& Error) + { + AddError(FString::Printf(TEXT("GetMessageList failed: %s"), *Error.Message)); + Done.Execute(); + } + ); + }); + }); + }); + + Describe("UpdateMessage", [this]() + { + LatentIt("should update a message via BP action", [this](const FDoneDelegate& Done) + { + // UpdateMessage with a nonexistent ID should still not crash the action + auto* Action = USatoriClientUpdateMessage::UpdateMessage( + nullptr, ClientConfig, Session, TEXT("nonexistent-id"), 0, 0); + Action->Activate(); + + // Verify: action completed (may error, but should not crash) + VerifyWhenComplete(Action, [this, Done]() + { + Done.Execute(); + }); + }); + }); + + Describe("DeleteMessage", [this]() + { + LatentIt("should delete a message via BP action", [this](const FDoneDelegate& Done) + { + // DeleteMessage with a nonexistent ID should still not crash the action + auto* Action = USatoriClientDeleteMessage::DeleteMessage( + nullptr, ClientConfig, Session, TEXT("nonexistent-id")); + Action->Activate(); + + // Verify: action completed (may error, but should not crash) + VerifyWhenComplete(Action, [this, Done]() + { + Done.Execute(); + }); + }); + }); +} diff --git a/IntegrationTests/Source/IntegrationTests/Tests/SatoriConsoleHelper.cpp b/IntegrationTests/Source/IntegrationTests/Tests/SatoriConsoleHelper.cpp new file mode 100644 index 000000000..6654746b0 --- /dev/null +++ b/IntegrationTests/Source/IntegrationTests/Tests/SatoriConsoleHelper.cpp @@ -0,0 +1,127 @@ +#include "SatoriConsoleHelper.h" + +#include "Misc/Guid.h" +#include "HttpModule.h" +#include "Interfaces/IHttpRequest.h" +#include "Interfaces/IHttpResponse.h" +#include "Dom/JsonObject.h" +#include "Serialization/JsonReader.h" +#include "Serialization/JsonSerializer.h" + +static FString GCachedApiKey; + +// Makes a Console REST request. OnComplete is always called; Json is null on failure. +static void MakeConsoleRequest( + const FString& Url, + const FString& Method, + const FString& BearerToken, + const FString& Body, + TFunction)> OnComplete) +{ + auto Request = FHttpModule::Get().CreateRequest(); + Request->SetURL(Url); + Request->SetVerb(Method); + Request->SetHeader(TEXT("Content-Type"), TEXT("application/json")); + if (!BearerToken.IsEmpty()) + { + Request->SetHeader(TEXT("Authorization"), FString::Printf(TEXT("Bearer %s"), *BearerToken)); + } + if (!Body.IsEmpty()) + { + Request->SetContentAsString(Body); + } + Request->OnProcessRequestComplete().BindLambda( + [OnComplete](FHttpRequestPtr, FHttpResponsePtr Res, bool bSuccess) + { + TSharedPtr Json; + if (bSuccess && Res.IsValid()) + { + TSharedRef> Reader = TJsonReaderFactory<>::Create(Res->GetContentAsString()); + FJsonSerializer::Deserialize(Reader, Json); + } + OnComplete(Json); + }); + Request->ProcessRequest(); +} + +// Seeds event and property configurations sequentially (ignores errors, they may already exist). +// Resolves the promise with KeyValue when all steps complete. +static void SeedConfigAndFinish( + const FString& Token, + const FString& KeyValue, + TSharedPtr> Promise, + TArray> Steps, + int32 Index) +{ + if (Index >= Steps.Num()) + { + GCachedApiKey = KeyValue; + Promise->SetValue(KeyValue); + return; + } + + const FString Url = FString::Printf(TEXT("http://127.0.0.1:7451%s"), *Steps[Index].Key); + MakeConsoleRequest(Url, TEXT("POST"), Token, Steps[Index].Value, + [Token, KeyValue, Promise, Steps, Index](TSharedPtr) + { + SeedConfigAndFinish(Token, KeyValue, Promise, Steps, Index + 1); + }); +} + +static void GetKeyThenSeed(const FString& Token, const FString& KeyValue, TSharedPtr> Promise) +{ + const FString EventBody = TEXT("{\"value_schema_id\":\"00000000-0000-0000-0000-000000000002\",\"metadata_schema_id\":\"00000000-0000-0000-0000-000000000001\""); + const FString PropBody = TEXT("{\"schema_id\":\"00000000-0000-0000-0000-000000000002\""); + + TArray> Steps = { + {TEXT("/v1/console/event"), FString::Printf(TEXT("{\"name\":\"game_start\",%s}"), *EventBody.Mid(1))}, + {TEXT("/v1/console/event"), FString::Printf(TEXT("{\"name\":\"level_complete\",%s}"), *EventBody.Mid(1))}, + {TEXT("/v1/console/event"), FString::Printf(TEXT("{\"name\":\"purchase\",%s}"), *EventBody.Mid(1))}, + {TEXT("/v1/console/property"), FString::Printf(TEXT("{\"name\":\"level\",%s}"), *PropBody.Mid(1))}, + {TEXT("/v1/console/property"), FString::Printf(TEXT("{\"name\":\"rank\",%s}"), *PropBody.Mid(1))}, + {TEXT("/v1/console/property"), FString::Printf(TEXT("{\"name\":\"preferred_mode\",%s}"), *PropBody.Mid(1))}, + }; + SeedConfigAndFinish(Token, KeyValue, Promise, MoveTemp(Steps), 0); +} + +TFuture GetSatoriApiKey() +{ + TSharedPtr> Promise = MakeShared>(); + TFuture Future = Promise->GetFuture(); + + if (!GCachedApiKey.IsEmpty()) + { + Promise->SetValue(GCachedApiKey); + return Future; + } + + // Step 1: Authenticate with the Console API admin user. + MakeConsoleRequest( + TEXT("http://127.0.0.1:7451/v1/console/authenticate"), + TEXT("POST"), TEXT(""), + TEXT("{\"email\":\"admin@satoriserver.com\",\"password\":\"satoripassword\"}"), + [Promise](TSharedPtr Json) + { + FString Token; + if (!Json.IsValid() || !Json->TryGetStringField(TEXT("token"), Token) || Token.IsEmpty()) + { + Promise->SetValue(TEXT("")); + return; + } + + // Step 2: Create a unique key for this test session. + // Each shard runs as a separate process with its own GCachedApiKey, so using a + // shared key (list + rotate) causes race conditions. A unique name per shard avoids conflicts. + const FString KeyName = FGuid::NewGuid().ToString(EGuidFormats::Digits).ToLower(); + MakeConsoleRequest(TEXT("http://127.0.0.1:7451/v1/console/apikey"), TEXT("POST"), Token, + FString::Printf(TEXT("{\"name\":\"%s\"}"), *KeyName), + [Promise, Token](TSharedPtr Json) + { + FString KeyValue; + if (Json.IsValid()) Json->TryGetStringField(TEXT("value"), KeyValue); + GetKeyThenSeed(Token, KeyValue, Promise); + }); + }); + + return Future; +} diff --git a/IntegrationTests/Source/IntegrationTests/Tests/SatoriConsoleHelper.h b/IntegrationTests/Source/IntegrationTests/Tests/SatoriConsoleHelper.h index 39731787c..799c07abd 100644 --- a/IntegrationTests/Source/IntegrationTests/Tests/SatoriConsoleHelper.h +++ b/IntegrationTests/Source/IntegrationTests/Tests/SatoriConsoleHelper.h @@ -2,89 +2,11 @@ #include "CoreMinimal.h" #include "Async/Future.h" -#include "Misc/Guid.h" -#include "HttpModule.h" -#include "Interfaces/IHttpRequest.h" -#include "Interfaces/IHttpResponse.h" -#include "Dom/JsonObject.h" -#include "Serialization/JsonReader.h" -#include "Serialization/JsonSerializer.h" -static FString GCachedApiKey; - -// Makes a Console REST request. OnComplete is always called; Json is null on failure. -static void MakeConsoleRequest( - const FString& Url, - const FString& Method, - const FString& BearerToken, - const FString& Body, - TFunction)> OnComplete) -{ - auto Request = FHttpModule::Get().CreateRequest(); - Request->SetURL(Url); - Request->SetVerb(Method); - Request->SetHeader(TEXT("Content-Type"), TEXT("application/json")); - if (!BearerToken.IsEmpty()) - { - Request->SetHeader(TEXT("Authorization"), FString::Printf(TEXT("Bearer %s"), *BearerToken)); - } - if (!Body.IsEmpty()) - { - Request->SetContentAsString(Body); - } - Request->OnProcessRequestComplete().BindLambda( - [OnComplete](FHttpRequestPtr, FHttpResponsePtr Res, bool bSuccess) - { - TSharedPtr Json; - if (bSuccess && Res.IsValid()) - { - TSharedRef> Reader = TJsonReaderFactory<>::Create(Res->GetContentAsString()); - FJsonSerializer::Deserialize(Reader, Json); - } - OnComplete(Json); - }); - Request->ProcessRequest(); -} - -static TFuture GetSatoriApiKey() -{ - TSharedPtr> Promise = MakeShared>(); - TFuture Future = Promise->GetFuture(); - - if (!GCachedApiKey.IsEmpty()) - { - Promise->SetValue(GCachedApiKey); - return Future; - } - - // Step 1: Authenticate with the Console API admin user. - MakeConsoleRequest( - TEXT("http://127.0.0.1:7451/v1/console/authenticate"), - TEXT("POST"), TEXT(""), - TEXT("{\"email\":\"admin@satoriserver.com\",\"password\":\"satoripassword\"}"), - [Promise](TSharedPtr Json) - { - FString Token; - if (!Json.IsValid() || !Json->TryGetStringField(TEXT("token"), Token) || Token.IsEmpty()) - { - Promise->SetValue(TEXT("")); - return; - } - - // Step 2: Create a unique key for this test session. - // Each shard runs as a separate process with its own GCachedApiKey, so using a - // shared key (list + rotate) causes race conditions. A unique name per shard avoids conflicts. - const FString KeyName = FGuid::NewGuid().ToString(EGuidFormats::Digits).ToLower(); - MakeConsoleRequest(TEXT("http://127.0.0.1:7451/v1/console/apikey"), TEXT("POST"), Token, - FString::Printf(TEXT("{\"name\":\"%s\"}"), *KeyName), - [Promise](TSharedPtr Json) - { - FString KeyValue; - if (Json.IsValid()) Json->TryGetStringField(TEXT("value"), KeyValue); - GCachedApiKey = KeyValue; - Promise->SetValue(KeyValue); - }); - }); - - return Future; -} +/** + * Fetches the Satori API key from the Console REST API. + * + * On first call: authenticates as admin, lists existing keys (creating one if none exist), + * and caches the result. Subsequent calls return the cached value immediately. + */ +TFuture GetSatoriApiKey(); diff --git a/IntegrationTests/server/go.mod b/IntegrationTests/server/go.mod new file mode 100644 index 000000000..6fd896f82 --- /dev/null +++ b/IntegrationTests/server/go.mod @@ -0,0 +1,7 @@ +module nakama-unreal/nakama-server + +go 1.25.5 + +require github.com/heroiclabs/nakama-common v1.44.2 + +require google.golang.org/protobuf v1.36.11 // indirect diff --git a/Nakama/Config/FilterPlugin.ini b/Nakama/Config/FilterPlugin.ini deleted file mode 100644 index ccebca2f3..000000000 --- a/Nakama/Config/FilterPlugin.ini +++ /dev/null @@ -1,8 +0,0 @@ -[FilterPlugin] -; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and -; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively. -; -; Examples: -; /README.txt -; /Extras/... -; /Binaries/ThirdParty/*.dll diff --git a/Nakama/Nakama.uplugin b/Nakama/Nakama.uplugin index d7b17832f..04b2251a6 100644 --- a/Nakama/Nakama.uplugin +++ b/Nakama/Nakama.uplugin @@ -1,22 +1,22 @@ { - "FileVersion": 3, - "Version": 2403, - "VersionName": "2.11.1", - "FriendlyName": "Nakama client", - "Description": "A UE4 and UE5 client for the Nakama server.", - "Category": "HeroicLabs.Nakama", - "CreatedBy": "Heroic Labs", - "CreatedByURL": "http://heroiclabs.com/", - "DocsURL": "https://heroiclabs.com/docs/", - "MarketplaceURL": "", - "SupportURL": "", - "EnabledByDefault": false, - "CanContainContent": true, - "IsBetaVersion": false, - "Installed": false, + "FileVersion": 3, + "Version": 3000, + "VersionName": "3.0.0", + "FriendlyName": "Nakama client", + "Description": "A UE4 and UE5 client for the Nakama server.", + "Category": "HeroicLabs.Nakama", + "CreatedBy": "Heroic Labs", + "CreatedByURL": "http://heroiclabs.com/", + "DocsURL": "https://heroiclabs.com/docs/", + "MarketplaceURL": "", + "SupportURL": "", + "EnabledByDefault": false, + "CanContainContent": true, + "IsBetaVersion": false, + "Installed": false, "Modules": [ { - "Name": "NakamaUnreal", + "Name": "Nakama", "Type": "Runtime", "LoadingPhase": "Default", "WhitelistPlatforms": [ @@ -26,30 +26,6 @@ "Mac", "Android" ] - }, - { - "Name": "NakamaBlueprints", - "Type": "Runtime", - "LoadingPhase": "Default", - "WhitelistPlatforms": [ - "Win64", - "Linux", - "IOS", - "Mac", - "Android" - ] - }, - { - "Name": "NakamaTests", - "Type": "DeveloperTool", - "LoadingPhase": "Default", - "WhitelistPlatforms": [ - "Win64", - "Linux", - "IOS", - "Mac", - "Android" - ] } ] } diff --git a/Nakama/Resources/nakama.version b/Nakama/Resources/nakama.version new file mode 100644 index 000000000..3052f8ae6 --- /dev/null +++ b/Nakama/Resources/nakama.version @@ -0,0 +1 @@ +4f025eb98e7731528c2e54504a0f6697b0fb68fd diff --git a/Nakama/Source/Nakama/Nakama.Build.cs b/Nakama/Source/Nakama/Nakama.Build.cs new file mode 100644 index 000000000..c260f4be0 --- /dev/null +++ b/Nakama/Source/Nakama/Nakama.Build.cs @@ -0,0 +1,79 @@ +/* +* Copyright 2025 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using UnrealBuildTool; +using System.IO; + +public class Nakama : ModuleRules +{ + public Nakama(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicIncludePaths.AddRange( + new string[] { + // ... add public include paths required here ... + } + ); + + + PrivateIncludePaths.AddRange( + new string[] { + // ... add other private include paths required here ... + } + ); + + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", "HTTP", "JsonUtilities", "NakamaApi" + // ... add other public dependencies that you statically link with here ... + } + ); + + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "CoreUObject", + "Engine", + "Slate", + "SlateCore", + "Engine", + "JsonUtilities", + "Json", + "HTTP" + // ... add private dependencies that you statically link with here ... + } + ); + + + DynamicallyLoadedModuleNames.AddRange( + new string[] + { + // ... add any modules that your module loads dynamically here ... + } + ); + + PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Public")); + PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Private")); + + PrivateIncludePaths.Add(Path.Combine(ModuleDirectory, "Public")); + + RuntimeDependencies.Add(Path.Combine(PluginDirectory, "Resources", "nakama.version"), StagedFileType.NonUFS); + } +} diff --git a/Nakama/Source/Nakama/Private/Nakama.gen.cpp b/Nakama/Source/Nakama/Private/Nakama.gen.cpp new file mode 100644 index 000000000..d4fac2d6e --- /dev/null +++ b/Nakama/Source/Nakama/Private/Nakama.gen.cpp @@ -0,0 +1,2883 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + + + +#include "Nakama.gen.h" +#include "JsonObjectConverter.h" + + + + +FNakamaAccount FNakamaAccount::FromJson(const TSharedPtr& Json) +{ + FNakamaAccount Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaAccount from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaAccount::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaAccountRefresh FNakamaAccountRefresh::FromJson(const TSharedPtr& Json) +{ + FNakamaAccountRefresh Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaAccountRefresh from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaAccountRefresh::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaAccountApple FNakamaAccountApple::FromJson(const TSharedPtr& Json) +{ + FNakamaAccountApple Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaAccountApple from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaAccountApple::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaAccountCustom FNakamaAccountCustom::FromJson(const TSharedPtr& Json) +{ + FNakamaAccountCustom Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaAccountCustom from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaAccountCustom::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaAccountDevice FNakamaAccountDevice::FromJson(const TSharedPtr& Json) +{ + FNakamaAccountDevice Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaAccountDevice from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaAccountDevice::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaAccountEmail FNakamaAccountEmail::FromJson(const TSharedPtr& Json) +{ + FNakamaAccountEmail Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaAccountEmail from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaAccountEmail::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaAccountFacebook FNakamaAccountFacebook::FromJson(const TSharedPtr& Json) +{ + FNakamaAccountFacebook Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaAccountFacebook from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaAccountFacebook::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaAccountFacebookInstantGame FNakamaAccountFacebookInstantGame::FromJson(const TSharedPtr& Json) +{ + FNakamaAccountFacebookInstantGame Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaAccountFacebookInstantGame from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaAccountFacebookInstantGame::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaAccountGameCenter FNakamaAccountGameCenter::FromJson(const TSharedPtr& Json) +{ + FNakamaAccountGameCenter Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaAccountGameCenter from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaAccountGameCenter::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaAccountGoogle FNakamaAccountGoogle::FromJson(const TSharedPtr& Json) +{ + FNakamaAccountGoogle Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaAccountGoogle from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaAccountGoogle::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaAccountSteam FNakamaAccountSteam::FromJson(const TSharedPtr& Json) +{ + FNakamaAccountSteam Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaAccountSteam from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaAccountSteam::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaAddFriendsRequest FNakamaAddFriendsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaAddFriendsRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaAddFriendsRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaAddFriendsRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaAddGroupUsersRequest FNakamaAddGroupUsersRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaAddGroupUsersRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaAddGroupUsersRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaAddGroupUsersRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaSessionRefreshRequest FNakamaSessionRefreshRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaSessionRefreshRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaSessionRefreshRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaSessionRefreshRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaSessionLogoutRequest FNakamaSessionLogoutRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaSessionLogoutRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaSessionLogoutRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaSessionLogoutRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaAuthenticateAppleRequest FNakamaAuthenticateAppleRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaAuthenticateAppleRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaAuthenticateAppleRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaAuthenticateAppleRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaAuthenticateCustomRequest FNakamaAuthenticateCustomRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaAuthenticateCustomRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaAuthenticateCustomRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaAuthenticateCustomRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaAuthenticateDeviceRequest FNakamaAuthenticateDeviceRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaAuthenticateDeviceRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaAuthenticateDeviceRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaAuthenticateDeviceRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaAuthenticateEmailRequest FNakamaAuthenticateEmailRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaAuthenticateEmailRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaAuthenticateEmailRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaAuthenticateEmailRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaAuthenticateFacebookRequest FNakamaAuthenticateFacebookRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaAuthenticateFacebookRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaAuthenticateFacebookRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaAuthenticateFacebookRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaAuthenticateFacebookInstantGameRequest FNakamaAuthenticateFacebookInstantGameRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaAuthenticateFacebookInstantGameRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaAuthenticateFacebookInstantGameRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaAuthenticateFacebookInstantGameRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaAuthenticateGameCenterRequest FNakamaAuthenticateGameCenterRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaAuthenticateGameCenterRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaAuthenticateGameCenterRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaAuthenticateGameCenterRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaAuthenticateGoogleRequest FNakamaAuthenticateGoogleRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaAuthenticateGoogleRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaAuthenticateGoogleRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaAuthenticateGoogleRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaAuthenticateSteamRequest FNakamaAuthenticateSteamRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaAuthenticateSteamRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaAuthenticateSteamRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaAuthenticateSteamRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaBanGroupUsersRequest FNakamaBanGroupUsersRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaBanGroupUsersRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaBanGroupUsersRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaBanGroupUsersRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaBlockFriendsRequest FNakamaBlockFriendsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaBlockFriendsRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaBlockFriendsRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaBlockFriendsRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaChannelMessage FNakamaChannelMessage::FromJson(const TSharedPtr& Json) +{ + FNakamaChannelMessage Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaChannelMessage from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaChannelMessage::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaChannelMessageList FNakamaChannelMessageList::FromJson(const TSharedPtr& Json) +{ + FNakamaChannelMessageList Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaChannelMessageList from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaChannelMessageList::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaCreateGroupRequest FNakamaCreateGroupRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaCreateGroupRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaCreateGroupRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaCreateGroupRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaDeleteFriendsRequest FNakamaDeleteFriendsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaDeleteFriendsRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaDeleteFriendsRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaDeleteFriendsRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaDeleteGroupRequest FNakamaDeleteGroupRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaDeleteGroupRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaDeleteGroupRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaDeleteGroupRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaDeleteLeaderboardRecordRequest FNakamaDeleteLeaderboardRecordRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaDeleteLeaderboardRecordRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaDeleteLeaderboardRecordRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaDeleteLeaderboardRecordRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaDeleteNotificationsRequest FNakamaDeleteNotificationsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaDeleteNotificationsRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaDeleteNotificationsRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaDeleteNotificationsRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaDeleteTournamentRecordRequest FNakamaDeleteTournamentRecordRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaDeleteTournamentRecordRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaDeleteTournamentRecordRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaDeleteTournamentRecordRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaDeleteStorageObjectId FNakamaDeleteStorageObjectId::FromJson(const TSharedPtr& Json) +{ + FNakamaDeleteStorageObjectId Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaDeleteStorageObjectId from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaDeleteStorageObjectId::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaDeleteStorageObjectsRequest FNakamaDeleteStorageObjectsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaDeleteStorageObjectsRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaDeleteStorageObjectsRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaDeleteStorageObjectsRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaEvent FNakamaEvent::FromJson(const TSharedPtr& Json) +{ + FNakamaEvent Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaEvent from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaEvent::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaFriend FNakamaFriend::FromJson(const TSharedPtr& Json) +{ + FNakamaFriend Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaFriend from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaFriend::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaFriendList FNakamaFriendList::FromJson(const TSharedPtr& Json) +{ + FNakamaFriendList Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaFriendList from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaFriendList::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaFriendsOfFriendsList FNakamaFriendsOfFriendsList::FromJson(const TSharedPtr& Json) +{ + FNakamaFriendsOfFriendsList Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaFriendsOfFriendsList from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaFriendsOfFriendsList::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaFriendOfFriend FNakamaFriendOfFriend::FromJson(const TSharedPtr& Json) +{ + FNakamaFriendOfFriend Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaFriendOfFriend from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaFriendOfFriend::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaGetUsersRequest FNakamaGetUsersRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaGetUsersRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaGetUsersRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaGetUsersRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaGetSubscriptionRequest FNakamaGetSubscriptionRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaGetSubscriptionRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaGetSubscriptionRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaGetSubscriptionRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaGroup FNakamaGroup::FromJson(const TSharedPtr& Json) +{ + FNakamaGroup Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaGroup from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaGroup::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaGroupList FNakamaGroupList::FromJson(const TSharedPtr& Json) +{ + FNakamaGroupList Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaGroupList from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaGroupList::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaGroupUserList FNakamaGroupUserList::FromJson(const TSharedPtr& Json) +{ + FNakamaGroupUserList Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaGroupUserList from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaGroupUserList::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaGroupUser FNakamaGroupUser::FromJson(const TSharedPtr& Json) +{ + FNakamaGroupUser Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaGroupUser from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaGroupUser::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaImportFacebookFriendsRequest FNakamaImportFacebookFriendsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaImportFacebookFriendsRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaImportFacebookFriendsRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaImportFacebookFriendsRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaImportSteamFriendsRequest FNakamaImportSteamFriendsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaImportSteamFriendsRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaImportSteamFriendsRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaImportSteamFriendsRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaJoinGroupRequest FNakamaJoinGroupRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaJoinGroupRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaJoinGroupRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaJoinGroupRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaJoinTournamentRequest FNakamaJoinTournamentRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaJoinTournamentRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaJoinTournamentRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaJoinTournamentRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaKickGroupUsersRequest FNakamaKickGroupUsersRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaKickGroupUsersRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaKickGroupUsersRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaKickGroupUsersRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaLeaderboard FNakamaLeaderboard::FromJson(const TSharedPtr& Json) +{ + FNakamaLeaderboard Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaLeaderboard from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaLeaderboard::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaLeaderboardList FNakamaLeaderboardList::FromJson(const TSharedPtr& Json) +{ + FNakamaLeaderboardList Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaLeaderboardList from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaLeaderboardList::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaLeaderboardRecord FNakamaLeaderboardRecord::FromJson(const TSharedPtr& Json) +{ + FNakamaLeaderboardRecord Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaLeaderboardRecord from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaLeaderboardRecord::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaLeaderboardRecordList FNakamaLeaderboardRecordList::FromJson(const TSharedPtr& Json) +{ + FNakamaLeaderboardRecordList Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaLeaderboardRecordList from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaLeaderboardRecordList::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaLeaveGroupRequest FNakamaLeaveGroupRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaLeaveGroupRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaLeaveGroupRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaLeaveGroupRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaLinkFacebookRequest FNakamaLinkFacebookRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaLinkFacebookRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaLinkFacebookRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaLinkFacebookRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaLinkSteamRequest FNakamaLinkSteamRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaLinkSteamRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaLinkSteamRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaLinkSteamRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaListChannelMessagesRequest FNakamaListChannelMessagesRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListChannelMessagesRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaListChannelMessagesRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaListChannelMessagesRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaListFriendsRequest FNakamaListFriendsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListFriendsRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaListFriendsRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaListFriendsRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaListFriendsOfFriendsRequest FNakamaListFriendsOfFriendsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListFriendsOfFriendsRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaListFriendsOfFriendsRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaListFriendsOfFriendsRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaListGroupsRequest FNakamaListGroupsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListGroupsRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaListGroupsRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaListGroupsRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaListGroupUsersRequest FNakamaListGroupUsersRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListGroupUsersRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaListGroupUsersRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaListGroupUsersRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaListLeaderboardRecordsAroundOwnerRequest FNakamaListLeaderboardRecordsAroundOwnerRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListLeaderboardRecordsAroundOwnerRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaListLeaderboardRecordsAroundOwnerRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaListLeaderboardRecordsAroundOwnerRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaListLeaderboardRecordsRequest FNakamaListLeaderboardRecordsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListLeaderboardRecordsRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaListLeaderboardRecordsRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaListLeaderboardRecordsRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaListMatchesRequest FNakamaListMatchesRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListMatchesRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaListMatchesRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaListMatchesRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaListNotificationsRequest FNakamaListNotificationsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListNotificationsRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaListNotificationsRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaListNotificationsRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaListStorageObjectsRequest FNakamaListStorageObjectsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListStorageObjectsRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaListStorageObjectsRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaListStorageObjectsRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaListSubscriptionsRequest FNakamaListSubscriptionsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListSubscriptionsRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaListSubscriptionsRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaListSubscriptionsRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaListTournamentRecordsAroundOwnerRequest FNakamaListTournamentRecordsAroundOwnerRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListTournamentRecordsAroundOwnerRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaListTournamentRecordsAroundOwnerRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaListTournamentRecordsAroundOwnerRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaListTournamentRecordsRequest FNakamaListTournamentRecordsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListTournamentRecordsRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaListTournamentRecordsRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaListTournamentRecordsRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaListTournamentsRequest FNakamaListTournamentsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListTournamentsRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaListTournamentsRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaListTournamentsRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaListUserGroupsRequest FNakamaListUserGroupsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListUserGroupsRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaListUserGroupsRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaListUserGroupsRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaMatch FNakamaMatch::FromJson(const TSharedPtr& Json) +{ + FNakamaMatch Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaMatch from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaMatch::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaMatchList FNakamaMatchList::FromJson(const TSharedPtr& Json) +{ + FNakamaMatchList Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaMatchList from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaMatchList::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaMatchmakerCompletionStats FNakamaMatchmakerCompletionStats::FromJson(const TSharedPtr& Json) +{ + FNakamaMatchmakerCompletionStats Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaMatchmakerCompletionStats from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaMatchmakerCompletionStats::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaMatchmakerStats FNakamaMatchmakerStats::FromJson(const TSharedPtr& Json) +{ + FNakamaMatchmakerStats Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaMatchmakerStats from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaMatchmakerStats::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaNotification FNakamaNotification::FromJson(const TSharedPtr& Json) +{ + FNakamaNotification Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaNotification from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaNotification::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaNotificationList FNakamaNotificationList::FromJson(const TSharedPtr& Json) +{ + FNakamaNotificationList Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaNotificationList from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaNotificationList::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaPromoteGroupUsersRequest FNakamaPromoteGroupUsersRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaPromoteGroupUsersRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaPromoteGroupUsersRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaPromoteGroupUsersRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaDemoteGroupUsersRequest FNakamaDemoteGroupUsersRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaDemoteGroupUsersRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaDemoteGroupUsersRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaDemoteGroupUsersRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaReadStorageObjectId FNakamaReadStorageObjectId::FromJson(const TSharedPtr& Json) +{ + FNakamaReadStorageObjectId Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaReadStorageObjectId from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaReadStorageObjectId::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaReadStorageObjectsRequest FNakamaReadStorageObjectsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaReadStorageObjectsRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaReadStorageObjectsRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaReadStorageObjectsRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaRpc FNakamaRpc::FromJson(const TSharedPtr& Json) +{ + FNakamaRpc Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaRpc from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaRpc::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaSession FNakamaSession::FromJson(const TSharedPtr& Json) +{ + FNakamaSession Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaSession from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaSession::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaStorageObject FNakamaStorageObject::FromJson(const TSharedPtr& Json) +{ + FNakamaStorageObject Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaStorageObject from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaStorageObject::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaStorageObjectAck FNakamaStorageObjectAck::FromJson(const TSharedPtr& Json) +{ + FNakamaStorageObjectAck Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaStorageObjectAck from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaStorageObjectAck::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaStorageObjectAcks FNakamaStorageObjectAcks::FromJson(const TSharedPtr& Json) +{ + FNakamaStorageObjectAcks Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaStorageObjectAcks from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaStorageObjectAcks::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaStorageObjects FNakamaStorageObjects::FromJson(const TSharedPtr& Json) +{ + FNakamaStorageObjects Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaStorageObjects from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaStorageObjects::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaStorageObjectList FNakamaStorageObjectList::FromJson(const TSharedPtr& Json) +{ + FNakamaStorageObjectList Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaStorageObjectList from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaStorageObjectList::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaTournament FNakamaTournament::FromJson(const TSharedPtr& Json) +{ + FNakamaTournament Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaTournament from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaTournament::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaTournamentList FNakamaTournamentList::FromJson(const TSharedPtr& Json) +{ + FNakamaTournamentList Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaTournamentList from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaTournamentList::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaTournamentRecordList FNakamaTournamentRecordList::FromJson(const TSharedPtr& Json) +{ + FNakamaTournamentRecordList Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaTournamentRecordList from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaTournamentRecordList::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaUpdateAccountRequest FNakamaUpdateAccountRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaUpdateAccountRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaUpdateAccountRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaUpdateAccountRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaUpdateGroupRequest FNakamaUpdateGroupRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaUpdateGroupRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaUpdateGroupRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaUpdateGroupRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaUser FNakamaUser::FromJson(const TSharedPtr& Json) +{ + FNakamaUser Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaUser from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaUser::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaUserGroupList FNakamaUserGroupList::FromJson(const TSharedPtr& Json) +{ + FNakamaUserGroupList Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaUserGroupList from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaUserGroupList::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaUserGroup FNakamaUserGroup::FromJson(const TSharedPtr& Json) +{ + FNakamaUserGroup Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaUserGroup from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaUserGroup::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaUsers FNakamaUsers::FromJson(const TSharedPtr& Json) +{ + FNakamaUsers Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaUsers from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaUsers::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaValidatePurchaseAppleRequest FNakamaValidatePurchaseAppleRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaValidatePurchaseAppleRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaValidatePurchaseAppleRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaValidatePurchaseAppleRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaValidateSubscriptionAppleRequest FNakamaValidateSubscriptionAppleRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaValidateSubscriptionAppleRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaValidateSubscriptionAppleRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaValidateSubscriptionAppleRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaValidatePurchaseGoogleRequest FNakamaValidatePurchaseGoogleRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaValidatePurchaseGoogleRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaValidatePurchaseGoogleRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaValidatePurchaseGoogleRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaValidateSubscriptionGoogleRequest FNakamaValidateSubscriptionGoogleRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaValidateSubscriptionGoogleRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaValidateSubscriptionGoogleRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaValidateSubscriptionGoogleRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaValidatePurchaseHuaweiRequest FNakamaValidatePurchaseHuaweiRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaValidatePurchaseHuaweiRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaValidatePurchaseHuaweiRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaValidatePurchaseHuaweiRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaValidatePurchaseFacebookInstantRequest FNakamaValidatePurchaseFacebookInstantRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaValidatePurchaseFacebookInstantRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaValidatePurchaseFacebookInstantRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaValidatePurchaseFacebookInstantRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaValidatedPurchase FNakamaValidatedPurchase::FromJson(const TSharedPtr& Json) +{ + FNakamaValidatedPurchase Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaValidatedPurchase from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaValidatedPurchase::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaValidatePurchaseResponse FNakamaValidatePurchaseResponse::FromJson(const TSharedPtr& Json) +{ + FNakamaValidatePurchaseResponse Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaValidatePurchaseResponse from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaValidatePurchaseResponse::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaValidateSubscriptionResponse FNakamaValidateSubscriptionResponse::FromJson(const TSharedPtr& Json) +{ + FNakamaValidateSubscriptionResponse Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaValidateSubscriptionResponse from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaValidateSubscriptionResponse::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaValidatedSubscription FNakamaValidatedSubscription::FromJson(const TSharedPtr& Json) +{ + FNakamaValidatedSubscription Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaValidatedSubscription from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaValidatedSubscription::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaPurchaseList FNakamaPurchaseList::FromJson(const TSharedPtr& Json) +{ + FNakamaPurchaseList Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaPurchaseList from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaPurchaseList::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaSubscriptionList FNakamaSubscriptionList::FromJson(const TSharedPtr& Json) +{ + FNakamaSubscriptionList Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaSubscriptionList from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaSubscriptionList::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaWriteLeaderboardRecordRequest FNakamaWriteLeaderboardRecordRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaWriteLeaderboardRecordRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaWriteLeaderboardRecordRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaWriteLeaderboardRecordRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaLeaderboardRecordWrite FNakamaLeaderboardRecordWrite::FromJson(const TSharedPtr& Json) +{ + FNakamaLeaderboardRecordWrite Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaLeaderboardRecordWrite from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaLeaderboardRecordWrite::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaWriteStorageObject FNakamaWriteStorageObject::FromJson(const TSharedPtr& Json) +{ + FNakamaWriteStorageObject Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaWriteStorageObject from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaWriteStorageObject::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaWriteStorageObjectsRequest FNakamaWriteStorageObjectsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaWriteStorageObjectsRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaWriteStorageObjectsRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaWriteStorageObjectsRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaWriteTournamentRecordRequest FNakamaWriteTournamentRecordRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaWriteTournamentRecordRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaWriteTournamentRecordRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaWriteTournamentRecordRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaTournamentRecordWrite FNakamaTournamentRecordWrite::FromJson(const TSharedPtr& Json) +{ + FNakamaTournamentRecordWrite Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaTournamentRecordWrite from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaTournamentRecordWrite::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaListPartiesRequest FNakamaListPartiesRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListPartiesRequest Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaListPartiesRequest from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaListPartiesRequest::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaParty FNakamaParty::FromJson(const TSharedPtr& Json) +{ + FNakamaParty Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaParty from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaParty::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaPartyList FNakamaPartyList::FromJson(const TSharedPtr& Json) +{ + FNakamaPartyList Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaPartyList from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaPartyList::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + + + +FNakamaApiRequestModel NakamaInternal::BuildAddFriendsRequest ( + const FNakamaAddFriendsRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildAddGroupUsersRequest ( + const FNakamaAddGroupUsersRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildSessionRefreshRequest ( + const FNakamaSessionRefreshRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildSessionLogoutRequest ( + const FNakamaSessionLogoutRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildAuthenticateAppleRequest ( + const FNakamaAuthenticateAppleRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildAuthenticateCustomRequest ( + const FNakamaAuthenticateCustomRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildAuthenticateDeviceRequest ( + const FNakamaAuthenticateDeviceRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildAuthenticateEmailRequest ( + const FNakamaAuthenticateEmailRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildAuthenticateFacebookRequest ( + const FNakamaAuthenticateFacebookRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildAuthenticateFacebookInstantGameRequest ( + const FNakamaAuthenticateFacebookInstantGameRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildAuthenticateGameCenterRequest ( + const FNakamaAuthenticateGameCenterRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildAuthenticateGoogleRequest ( + const FNakamaAuthenticateGoogleRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildAuthenticateSteamRequest ( + const FNakamaAuthenticateSteamRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildBanGroupUsersRequest ( + const FNakamaBanGroupUsersRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildBlockFriendsRequest ( + const FNakamaBlockFriendsRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildCreateGroupRequest ( + const FNakamaCreateGroupRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildDeleteAccountRequest ( +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildDeleteFriendsRequest ( + const FNakamaDeleteFriendsRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildDeleteGroupRequest ( + const FNakamaDeleteGroupRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildDeleteLeaderboardRecordRequest ( + const FNakamaDeleteLeaderboardRecordRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildDeleteNotificationsRequest ( + const FNakamaDeleteNotificationsRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildDeleteTournamentRecordRequest ( + const FNakamaDeleteTournamentRecordRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildDeleteStorageObjectsRequest ( + const FNakamaDeleteStorageObjectsRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildEventRequest ( + const FNakamaEvent& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildGetAccountRequest ( +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildGetUsersRequest ( + const FNakamaGetUsersRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildGetSubscriptionRequest ( + const FNakamaGetSubscriptionRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildGetMatchmakerStatsRequest ( +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildHealthcheckRequest ( +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildImportFacebookFriendsRequest ( + const FNakamaImportFacebookFriendsRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildImportSteamFriendsRequest ( + const FNakamaImportSteamFriendsRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildJoinGroupRequest ( + const FNakamaJoinGroupRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildJoinTournamentRequest ( + const FNakamaJoinTournamentRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildKickGroupUsersRequest ( + const FNakamaKickGroupUsersRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildLeaveGroupRequest ( + const FNakamaLeaveGroupRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildLinkAppleRequest ( + const FNakamaAccountApple& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildLinkCustomRequest ( + const FNakamaAccountCustom& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildLinkDeviceRequest ( + const FNakamaAccountDevice& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildLinkEmailRequest ( + const FNakamaAccountEmail& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildLinkFacebookRequest ( + const FNakamaLinkFacebookRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildLinkFacebookInstantGameRequest ( + const FNakamaAccountFacebookInstantGame& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildLinkGameCenterRequest ( + const FNakamaAccountGameCenter& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildLinkGoogleRequest ( + const FNakamaAccountGoogle& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildLinkSteamRequest ( + const FNakamaLinkSteamRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildListChannelMessagesRequest ( + const FNakamaListChannelMessagesRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildListFriendsRequest ( + const FNakamaListFriendsRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildListFriendsOfFriendsRequest ( + const FNakamaListFriendsOfFriendsRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildListGroupsRequest ( + const FNakamaListGroupsRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildListGroupUsersRequest ( + const FNakamaListGroupUsersRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildListLeaderboardRecordsRequest ( + const FNakamaListLeaderboardRecordsRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildListLeaderboardRecordsAroundOwnerRequest ( + const FNakamaListLeaderboardRecordsAroundOwnerRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildListMatchesRequest ( + const FNakamaListMatchesRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildListPartiesRequest ( + const FNakamaListPartiesRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildListNotificationsRequest ( + const FNakamaListNotificationsRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildListStorageObjectsRequest ( + const FNakamaListStorageObjectsRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildListSubscriptionsRequest ( + const FNakamaListSubscriptionsRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildListTournamentsRequest ( + const FNakamaListTournamentsRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildListTournamentRecordsRequest ( + const FNakamaListTournamentRecordsRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildListTournamentRecordsAroundOwnerRequest ( + const FNakamaListTournamentRecordsAroundOwnerRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildListUserGroupsRequest ( + const FNakamaListUserGroupsRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildPromoteGroupUsersRequest ( + const FNakamaPromoteGroupUsersRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildDemoteGroupUsersRequest ( + const FNakamaDemoteGroupUsersRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildReadStorageObjectsRequest ( + const FNakamaReadStorageObjectsRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildRpcFuncRequest ( + const FNakamaRpc& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildUnlinkAppleRequest ( + const FNakamaAccountApple& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildUnlinkCustomRequest ( + const FNakamaAccountCustom& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildUnlinkDeviceRequest ( + const FNakamaAccountDevice& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildUnlinkEmailRequest ( + const FNakamaAccountEmail& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildUnlinkFacebookRequest ( + const FNakamaAccountFacebook& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildUnlinkFacebookInstantGameRequest ( + const FNakamaAccountFacebookInstantGame& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildUnlinkGameCenterRequest ( + const FNakamaAccountGameCenter& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildUnlinkGoogleRequest ( + const FNakamaAccountGoogle& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildUnlinkSteamRequest ( + const FNakamaAccountSteam& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildUpdateAccountRequest ( + const FNakamaUpdateAccountRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildUpdateGroupRequest ( + const FNakamaUpdateGroupRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildValidatePurchaseAppleRequest ( + const FNakamaValidatePurchaseAppleRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildValidateSubscriptionAppleRequest ( + const FNakamaValidateSubscriptionAppleRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildValidatePurchaseGoogleRequest ( + const FNakamaValidatePurchaseGoogleRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildValidateSubscriptionGoogleRequest ( + const FNakamaValidateSubscriptionGoogleRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildValidatePurchaseHuaweiRequest ( + const FNakamaValidatePurchaseHuaweiRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildValidatePurchaseFacebookInstantRequest ( + const FNakamaValidatePurchaseFacebookInstantRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildWriteLeaderboardRecordRequest ( + const FNakamaWriteLeaderboardRecordRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildWriteStorageObjectsRequest ( + const FNakamaWriteStorageObjectsRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} + +FNakamaApiRequestModel NakamaInternal::BuildWriteTournamentRecordRequest ( + const FNakamaWriteTournamentRecordRequest& Params; +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} diff --git a/Nakama/Source/Nakama/Private/NakamaClient.cpp b/Nakama/Source/Nakama/Private/NakamaClient.cpp new file mode 100644 index 000000000..87e1e429b --- /dev/null +++ b/Nakama/Source/Nakama/Private/NakamaClient.cpp @@ -0,0 +1,12539 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + +#include "Nakama.h" +#include "Containers/Ticker.h" + +bool Nakama::IsTransientError(const FNakamaError& Error) +{ + switch (Error.Code) + { + case 0: // Connection failed (TCP refused, DNS timeout, etc.) + case 4: // DEADLINE_EXCEEDED + case 13: // INTERNAL + case 14: // UNAVAILABLE + return true; + default: + return false; + } +} + +float Nakama::CalculateBackoff(int32 Attempt, const FNakamaRetryConfig& Config) +{ + const float ExponentialDelay = FMath::Pow(2.0f, static_cast(Attempt - 1)) * Config.BaseDelayMs; + const float Jitter = FMath::FRand() * ExponentialDelay; + return Jitter / 1000.0f; +} + +namespace +{ + +/** Optionally refresh the session before calling the RPC. */ +void MaybeRefreshThenCall( + const TSharedRef& SessionState, + const FNakamaClientConfig& ClientConfig, + const FNakamaRetryConfig& RetryConfig, + const TSharedRef>& CancellationToken, + const TSharedRef>& OnError, + TFunction OnReady) +{ + if (!RetryConfig.bAutoRefreshSession + || SessionState->RefreshToken.IsEmpty() + || !SessionState->IsExpired(RetryConfig.AutoRefreshBufferSeconds)) + { + OnReady(); + return; + } + + if (SessionState->IsRefreshExpired()) + { + (*OnError)(FNakamaError(TEXT("Refresh token has expired"), 16)); + return; + } + + auto OnSessionRefreshed = RetryConfig.OnSessionRefreshed; + auto OnSessionRefreshedOwner = RetryConfig.OnSessionRefreshedOwner; + NakamaApi::SessionRefresh( + ClientConfig, + SessionState->RefreshToken, + {}, + [SessionState, OnSessionRefreshed, OnSessionRefreshedOwner, OnReady = MoveTemp(OnReady)](const FNakamaSession& RefreshedSession) mutable + { + SessionState->Update(RefreshedSession.Token, RefreshedSession.RefreshToken); + if (OnSessionRefreshed && (OnSessionRefreshedOwner.IsExplicitlyNull() || OnSessionRefreshedOwner.IsValid())) + { + OnSessionRefreshed(*SessionState); + } + OnReady(); + }, + [OnError](const FNakamaError& Error) + { + (*OnError)(FNakamaError(FString::Printf(TEXT("Session refresh failed: %s"), *Error.Message), Error.Code)); + }, + RetryConfig.Timeout, + CancellationToken); +} + +} // anonymous namespace +TNakamaFuture Nakama::AddFriends( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& Ids, + const TArray& Usernames, + const FString& Metadata, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Ids + , Usernames + , Metadata + ]() + { + + NakamaApi::AddFriends( + ClientConfig, + HttpKey, + Ids, + Usernames, + Metadata, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::AddFriends( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const TArray& Ids, + const TArray& Usernames, + const FString& Metadata, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Ids + , Usernames + , Metadata + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Ids + , Usernames + , Metadata + ]() + { + + NakamaApi::AddFriends( + ClientConfig, + *SessionState, + Ids, + Usernames, + Metadata, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::AddGroupUsers( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& GroupId, + const TArray& UserIds, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , GroupId + , UserIds + ]() + { + + NakamaApi::AddGroupUsers( + ClientConfig, + HttpKey, + GroupId, + UserIds, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::AddGroupUsers( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& GroupId, + const TArray& UserIds, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , GroupId + , UserIds + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , GroupId + , UserIds + ]() + { + + NakamaApi::AddGroupUsers( + ClientConfig, + *SessionState, + GroupId, + UserIds, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::SessionRefresh( + const FNakamaClientConfig& ClientConfig, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaSessionResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Token + , Vars + ]() + { + + NakamaApi::SessionRefresh( + ClientConfig, + Token, + Vars, + [FutureState, DoRequest, OnError](const FNakamaSession& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaSessionResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::SessionLogout( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Token, + const FString& RefreshToken, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Token + , RefreshToken + ]() + { + + NakamaApi::SessionLogout( + ClientConfig, + HttpKey, + Token, + RefreshToken, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::SessionLogout( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Token, + const FString& RefreshToken, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Token + , RefreshToken + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Token + , RefreshToken + ]() + { + + NakamaApi::SessionLogout( + ClientConfig, + *SessionState, + Token, + RefreshToken, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::AuthenticateApple( + const FNakamaClientConfig& ClientConfig, + FNakamaOptionalBool Create, + const FString& Username, + const FString& AccountToken, + const TMap& AccountVars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaSessionResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , AccountToken + , AccountVars + , Create + , Username + ]() + { + FNakamaAccountApple Account; + Account.Token = AccountToken; + Account.Vars = AccountVars; + + NakamaApi::AuthenticateApple( + ClientConfig, + Account, + Create, + Username, + [FutureState, DoRequest, OnError](const FNakamaSession& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaSessionResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::AuthenticateCustom( + const FNakamaClientConfig& ClientConfig, + FNakamaOptionalBool Create, + const FString& Username, + const FString& AccountId, + const TMap& AccountVars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaSessionResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , AccountId + , AccountVars + , Create + , Username + ]() + { + FNakamaAccountCustom Account; + Account.Id = AccountId; + Account.Vars = AccountVars; + + NakamaApi::AuthenticateCustom( + ClientConfig, + Account, + Create, + Username, + [FutureState, DoRequest, OnError](const FNakamaSession& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaSessionResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::AuthenticateDevice( + const FNakamaClientConfig& ClientConfig, + FNakamaOptionalBool Create, + const FString& Username, + const FString& AccountId, + const TMap& AccountVars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaSessionResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , AccountId + , AccountVars + , Create + , Username + ]() + { + FNakamaAccountDevice Account; + Account.Id = AccountId; + Account.Vars = AccountVars; + + NakamaApi::AuthenticateDevice( + ClientConfig, + Account, + Create, + Username, + [FutureState, DoRequest, OnError](const FNakamaSession& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaSessionResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::AuthenticateEmail( + const FNakamaClientConfig& ClientConfig, + FNakamaOptionalBool Create, + const FString& Username, + const FString& AccountEmail, + const FString& AccountPassword, + const TMap& AccountVars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaSessionResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , AccountEmail + , AccountPassword + , AccountVars + , Create + , Username + ]() + { + FNakamaAccountEmail Account; + Account.Email = AccountEmail; + Account.Password = AccountPassword; + Account.Vars = AccountVars; + + NakamaApi::AuthenticateEmail( + ClientConfig, + Account, + Create, + Username, + [FutureState, DoRequest, OnError](const FNakamaSession& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaSessionResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::AuthenticateFacebook( + const FNakamaClientConfig& ClientConfig, + FNakamaOptionalBool Create, + const FString& Username, + FNakamaOptionalBool Sync, + const FString& AccountToken, + const TMap& AccountVars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaSessionResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , AccountToken + , AccountVars + , Create + , Username + , Sync + ]() + { + FNakamaAccountFacebook Account; + Account.Token = AccountToken; + Account.Vars = AccountVars; + + NakamaApi::AuthenticateFacebook( + ClientConfig, + Account, + Create, + Username, + Sync, + [FutureState, DoRequest, OnError](const FNakamaSession& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaSessionResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::AuthenticateFacebookInstantGame( + const FNakamaClientConfig& ClientConfig, + FNakamaOptionalBool Create, + const FString& Username, + const FString& AccountSignedPlayerInfo, + const TMap& AccountVars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaSessionResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , AccountSignedPlayerInfo + , AccountVars + , Create + , Username + ]() + { + FNakamaAccountFacebookInstantGame Account; + Account.SignedPlayerInfo = AccountSignedPlayerInfo; + Account.Vars = AccountVars; + + NakamaApi::AuthenticateFacebookInstantGame( + ClientConfig, + Account, + Create, + Username, + [FutureState, DoRequest, OnError](const FNakamaSession& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaSessionResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::AuthenticateGameCenter( + const FNakamaClientConfig& ClientConfig, + FNakamaOptionalBool Create, + const FString& Username, + const FString& AccountPlayerId, + const FString& AccountBundleId, + int64 AccountTimestampSeconds, + const FString& AccountSalt, + const FString& AccountSignature, + const FString& AccountPublicKeyUrl, + const TMap& AccountVars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaSessionResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , AccountPlayerId + , AccountBundleId + , AccountTimestampSeconds + , AccountSalt + , AccountSignature + , AccountPublicKeyUrl + , AccountVars + , Create + , Username + ]() + { + FNakamaAccountGameCenter Account; + Account.PlayerId = AccountPlayerId; + Account.BundleId = AccountBundleId; + Account.TimestampSeconds = AccountTimestampSeconds; + Account.Salt = AccountSalt; + Account.Signature = AccountSignature; + Account.PublicKeyUrl = AccountPublicKeyUrl; + Account.Vars = AccountVars; + + NakamaApi::AuthenticateGameCenter( + ClientConfig, + Account, + Create, + Username, + [FutureState, DoRequest, OnError](const FNakamaSession& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaSessionResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::AuthenticateGoogle( + const FNakamaClientConfig& ClientConfig, + FNakamaOptionalBool Create, + const FString& Username, + const FString& AccountToken, + const TMap& AccountVars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaSessionResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , AccountToken + , AccountVars + , Create + , Username + ]() + { + FNakamaAccountGoogle Account; + Account.Token = AccountToken; + Account.Vars = AccountVars; + + NakamaApi::AuthenticateGoogle( + ClientConfig, + Account, + Create, + Username, + [FutureState, DoRequest, OnError](const FNakamaSession& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaSessionResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::AuthenticateSteam( + const FNakamaClientConfig& ClientConfig, + FNakamaOptionalBool Create, + const FString& Username, + FNakamaOptionalBool Sync, + const FString& AccountToken, + const TMap& AccountVars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaSessionResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , AccountToken + , AccountVars + , Create + , Username + , Sync + ]() + { + FNakamaAccountSteam Account; + Account.Token = AccountToken; + Account.Vars = AccountVars; + + NakamaApi::AuthenticateSteam( + ClientConfig, + Account, + Create, + Username, + Sync, + [FutureState, DoRequest, OnError](const FNakamaSession& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaSessionResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::BanGroupUsers( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& GroupId, + const TArray& UserIds, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , GroupId + , UserIds + ]() + { + + NakamaApi::BanGroupUsers( + ClientConfig, + HttpKey, + GroupId, + UserIds, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::BanGroupUsers( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& GroupId, + const TArray& UserIds, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , GroupId + , UserIds + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , GroupId + , UserIds + ]() + { + + NakamaApi::BanGroupUsers( + ClientConfig, + *SessionState, + GroupId, + UserIds, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::BlockFriends( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& Ids, + const TArray& Usernames, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Ids + , Usernames + ]() + { + + NakamaApi::BlockFriends( + ClientConfig, + HttpKey, + Ids, + Usernames, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::BlockFriends( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const TArray& Ids, + const TArray& Usernames, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Ids + , Usernames + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Ids + , Usernames + ]() + { + + NakamaApi::BlockFriends( + ClientConfig, + *SessionState, + Ids, + Usernames, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::CreateGroup( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Name, + const FString& Description, + const FString& LangTag, + const FString& AvatarUrl, + bool Open, + int32 MaxCount, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaGroupResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Name + , Description + , LangTag + , AvatarUrl + , Open + , MaxCount + ]() + { + + NakamaApi::CreateGroup( + ClientConfig, + HttpKey, + Name, + Description, + LangTag, + AvatarUrl, + Open, + MaxCount, + [FutureState, DoRequest, OnError](const FNakamaGroup& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaGroupResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::CreateGroup( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Name, + const FString& Description, + const FString& LangTag, + const FString& AvatarUrl, + bool Open, + int32 MaxCount, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaGroupResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Name + , Description + , LangTag + , AvatarUrl + , Open + , MaxCount + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Name + , Description + , LangTag + , AvatarUrl + , Open + , MaxCount + ]() + { + + NakamaApi::CreateGroup( + ClientConfig, + *SessionState, + Name, + Description, + LangTag, + AvatarUrl, + Open, + MaxCount, + [FutureState, DoRequest, OnError](const FNakamaGroup& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaGroupResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::DeleteAccount( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + ]() + { + + NakamaApi::DeleteAccount( + ClientConfig, + HttpKey, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::DeleteAccount( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + ]() + { + + NakamaApi::DeleteAccount( + ClientConfig, + *SessionState, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::DeleteFriends( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& Ids, + const TArray& Usernames, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Ids + , Usernames + ]() + { + + NakamaApi::DeleteFriends( + ClientConfig, + HttpKey, + Ids, + Usernames, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::DeleteFriends( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const TArray& Ids, + const TArray& Usernames, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Ids + , Usernames + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Ids + , Usernames + ]() + { + + NakamaApi::DeleteFriends( + ClientConfig, + *SessionState, + Ids, + Usernames, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::DeleteGroup( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& GroupId, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , GroupId + ]() + { + + NakamaApi::DeleteGroup( + ClientConfig, + HttpKey, + GroupId, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::DeleteGroup( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& GroupId, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , GroupId + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , GroupId + ]() + { + + NakamaApi::DeleteGroup( + ClientConfig, + *SessionState, + GroupId, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::DeleteLeaderboardRecord( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& LeaderboardId, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , LeaderboardId + ]() + { + + NakamaApi::DeleteLeaderboardRecord( + ClientConfig, + HttpKey, + LeaderboardId, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::DeleteLeaderboardRecord( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& LeaderboardId, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , LeaderboardId + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , LeaderboardId + ]() + { + + NakamaApi::DeleteLeaderboardRecord( + ClientConfig, + *SessionState, + LeaderboardId, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::DeleteNotifications( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& Ids, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Ids + ]() + { + + NakamaApi::DeleteNotifications( + ClientConfig, + HttpKey, + Ids, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::DeleteNotifications( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const TArray& Ids, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Ids + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Ids + ]() + { + + NakamaApi::DeleteNotifications( + ClientConfig, + *SessionState, + Ids, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::DeleteTournamentRecord( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& TournamentId, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , TournamentId + ]() + { + + NakamaApi::DeleteTournamentRecord( + ClientConfig, + HttpKey, + TournamentId, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::DeleteTournamentRecord( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& TournamentId, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , TournamentId + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , TournamentId + ]() + { + + NakamaApi::DeleteTournamentRecord( + ClientConfig, + *SessionState, + TournamentId, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::DeleteStorageObjects( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& ObjectIds, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , ObjectIds + ]() + { + + NakamaApi::DeleteStorageObjects( + ClientConfig, + HttpKey, + ObjectIds, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::DeleteStorageObjects( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const TArray& ObjectIds, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , ObjectIds + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , ObjectIds + ]() + { + + NakamaApi::DeleteStorageObjects( + ClientConfig, + *SessionState, + ObjectIds, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::Event( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Name, + const FDateTime& Timestamp, + bool External, + const TMap& Properties, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Name + , Timestamp + , External + , Properties + ]() + { + + NakamaApi::Event( + ClientConfig, + HttpKey, + Name, + Timestamp, + External, + Properties, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::Event( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Name, + const FDateTime& Timestamp, + bool External, + const TMap& Properties, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Name + , Timestamp + , External + , Properties + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Name + , Timestamp + , External + , Properties + ]() + { + + NakamaApi::Event( + ClientConfig, + *SessionState, + Name, + Timestamp, + External, + Properties, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::GetAccount( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaAccountResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + ]() + { + + NakamaApi::GetAccount( + ClientConfig, + HttpKey, + [FutureState, DoRequest, OnError](const FNakamaAccount& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaAccountResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::GetAccount( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaAccountResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + ]() + { + + NakamaApi::GetAccount( + ClientConfig, + *SessionState, + [FutureState, DoRequest, OnError](const FNakamaAccount& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaAccountResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::GetUsers( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& Ids, + const TArray& Usernames, + const TArray& FacebookIds, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaUsersResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Ids + , Usernames + , FacebookIds + ]() + { + + NakamaApi::GetUsers( + ClientConfig, + HttpKey, + Ids, + Usernames, + FacebookIds, + [FutureState, DoRequest, OnError](const FNakamaUsers& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaUsersResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::GetUsers( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const TArray& Ids, + const TArray& Usernames, + const TArray& FacebookIds, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaUsersResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Ids + , Usernames + , FacebookIds + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Ids + , Usernames + , FacebookIds + ]() + { + + NakamaApi::GetUsers( + ClientConfig, + *SessionState, + Ids, + Usernames, + FacebookIds, + [FutureState, DoRequest, OnError](const FNakamaUsers& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaUsersResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::GetSubscription( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& ProductId, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidatedSubscriptionResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , ProductId + ]() + { + + NakamaApi::GetSubscription( + ClientConfig, + HttpKey, + ProductId, + [FutureState, DoRequest, OnError](const FNakamaValidatedSubscription& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidatedSubscriptionResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::GetSubscription( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& ProductId, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidatedSubscriptionResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , ProductId + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , ProductId + ]() + { + + NakamaApi::GetSubscription( + ClientConfig, + *SessionState, + ProductId, + [FutureState, DoRequest, OnError](const FNakamaValidatedSubscription& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidatedSubscriptionResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::GetMatchmakerStats( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaMatchmakerStatsResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + ]() + { + + NakamaApi::GetMatchmakerStats( + ClientConfig, + HttpKey, + [FutureState, DoRequest, OnError](const FNakamaMatchmakerStats& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaMatchmakerStatsResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::GetMatchmakerStats( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaMatchmakerStatsResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + ]() + { + + NakamaApi::GetMatchmakerStats( + ClientConfig, + *SessionState, + [FutureState, DoRequest, OnError](const FNakamaMatchmakerStats& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaMatchmakerStatsResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::Healthcheck( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + ]() + { + + NakamaApi::Healthcheck( + ClientConfig, + HttpKey, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::Healthcheck( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + ]() + { + + NakamaApi::Healthcheck( + ClientConfig, + *SessionState, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ImportFacebookFriends( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + FNakamaOptionalBool Reset, + const FString& AccountToken, + const TMap& AccountVars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , AccountToken + , AccountVars + , Reset + ]() + { + FNakamaAccountFacebook Account; + Account.Token = AccountToken; + Account.Vars = AccountVars; + + NakamaApi::ImportFacebookFriends( + ClientConfig, + HttpKey, + Account, + Reset, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ImportFacebookFriends( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + FNakamaOptionalBool Reset, + const FString& AccountToken, + const TMap& AccountVars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , AccountToken + , AccountVars + , Reset + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , AccountToken + , AccountVars + , Reset + ]() + { + FNakamaAccountFacebook Account; + Account.Token = AccountToken; + Account.Vars = AccountVars; + + NakamaApi::ImportFacebookFriends( + ClientConfig, + *SessionState, + Account, + Reset, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ImportSteamFriends( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + FNakamaOptionalBool Reset, + const FString& AccountToken, + const TMap& AccountVars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , AccountToken + , AccountVars + , Reset + ]() + { + FNakamaAccountSteam Account; + Account.Token = AccountToken; + Account.Vars = AccountVars; + + NakamaApi::ImportSteamFriends( + ClientConfig, + HttpKey, + Account, + Reset, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ImportSteamFriends( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + FNakamaOptionalBool Reset, + const FString& AccountToken, + const TMap& AccountVars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , AccountToken + , AccountVars + , Reset + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , AccountToken + , AccountVars + , Reset + ]() + { + FNakamaAccountSteam Account; + Account.Token = AccountToken; + Account.Vars = AccountVars; + + NakamaApi::ImportSteamFriends( + ClientConfig, + *SessionState, + Account, + Reset, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::JoinGroup( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& GroupId, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , GroupId + ]() + { + + NakamaApi::JoinGroup( + ClientConfig, + HttpKey, + GroupId, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::JoinGroup( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& GroupId, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , GroupId + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , GroupId + ]() + { + + NakamaApi::JoinGroup( + ClientConfig, + *SessionState, + GroupId, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::JoinTournament( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& TournamentId, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , TournamentId + ]() + { + + NakamaApi::JoinTournament( + ClientConfig, + HttpKey, + TournamentId, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::JoinTournament( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& TournamentId, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , TournamentId + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , TournamentId + ]() + { + + NakamaApi::JoinTournament( + ClientConfig, + *SessionState, + TournamentId, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::KickGroupUsers( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& GroupId, + const TArray& UserIds, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , GroupId + , UserIds + ]() + { + + NakamaApi::KickGroupUsers( + ClientConfig, + HttpKey, + GroupId, + UserIds, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::KickGroupUsers( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& GroupId, + const TArray& UserIds, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , GroupId + , UserIds + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , GroupId + , UserIds + ]() + { + + NakamaApi::KickGroupUsers( + ClientConfig, + *SessionState, + GroupId, + UserIds, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::LeaveGroup( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& GroupId, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , GroupId + ]() + { + + NakamaApi::LeaveGroup( + ClientConfig, + HttpKey, + GroupId, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::LeaveGroup( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& GroupId, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , GroupId + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , GroupId + ]() + { + + NakamaApi::LeaveGroup( + ClientConfig, + *SessionState, + GroupId, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::LinkApple( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Token + , Vars + ]() + { + + NakamaApi::LinkApple( + ClientConfig, + HttpKey, + Token, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::LinkApple( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Token + , Vars + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Token + , Vars + ]() + { + + NakamaApi::LinkApple( + ClientConfig, + *SessionState, + Token, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::LinkCustom( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Id, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Id + , Vars + ]() + { + + NakamaApi::LinkCustom( + ClientConfig, + HttpKey, + Id, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::LinkCustom( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Id, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Id + , Vars + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Id + , Vars + ]() + { + + NakamaApi::LinkCustom( + ClientConfig, + *SessionState, + Id, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::LinkDevice( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Id, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Id + , Vars + ]() + { + + NakamaApi::LinkDevice( + ClientConfig, + HttpKey, + Id, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::LinkDevice( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Id, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Id + , Vars + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Id + , Vars + ]() + { + + NakamaApi::LinkDevice( + ClientConfig, + *SessionState, + Id, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::LinkEmail( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Email, + const FString& Password, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Email + , Password + , Vars + ]() + { + + NakamaApi::LinkEmail( + ClientConfig, + HttpKey, + Email, + Password, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::LinkEmail( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Email, + const FString& Password, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Email + , Password + , Vars + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Email + , Password + , Vars + ]() + { + + NakamaApi::LinkEmail( + ClientConfig, + *SessionState, + Email, + Password, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::LinkFacebook( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + FNakamaOptionalBool Sync, + const FString& AccountToken, + const TMap& AccountVars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , AccountToken + , AccountVars + , Sync + ]() + { + FNakamaAccountFacebook Account; + Account.Token = AccountToken; + Account.Vars = AccountVars; + + NakamaApi::LinkFacebook( + ClientConfig, + HttpKey, + Account, + Sync, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::LinkFacebook( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + FNakamaOptionalBool Sync, + const FString& AccountToken, + const TMap& AccountVars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , AccountToken + , AccountVars + , Sync + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , AccountToken + , AccountVars + , Sync + ]() + { + FNakamaAccountFacebook Account; + Account.Token = AccountToken; + Account.Vars = AccountVars; + + NakamaApi::LinkFacebook( + ClientConfig, + *SessionState, + Account, + Sync, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::LinkFacebookInstantGame( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& SignedPlayerInfo, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , SignedPlayerInfo + , Vars + ]() + { + + NakamaApi::LinkFacebookInstantGame( + ClientConfig, + HttpKey, + SignedPlayerInfo, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::LinkFacebookInstantGame( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& SignedPlayerInfo, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , SignedPlayerInfo + , Vars + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , SignedPlayerInfo + , Vars + ]() + { + + NakamaApi::LinkFacebookInstantGame( + ClientConfig, + *SessionState, + SignedPlayerInfo, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::LinkGameCenter( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& PlayerId, + const FString& BundleId, + int64 TimestampSeconds, + const FString& Salt, + const FString& Signature, + const FString& PublicKeyUrl, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , PlayerId + , BundleId + , TimestampSeconds + , Salt + , Signature + , PublicKeyUrl + , Vars + ]() + { + + NakamaApi::LinkGameCenter( + ClientConfig, + HttpKey, + PlayerId, + BundleId, + TimestampSeconds, + Salt, + Signature, + PublicKeyUrl, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::LinkGameCenter( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& PlayerId, + const FString& BundleId, + int64 TimestampSeconds, + const FString& Salt, + const FString& Signature, + const FString& PublicKeyUrl, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , PlayerId + , BundleId + , TimestampSeconds + , Salt + , Signature + , PublicKeyUrl + , Vars + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , PlayerId + , BundleId + , TimestampSeconds + , Salt + , Signature + , PublicKeyUrl + , Vars + ]() + { + + NakamaApi::LinkGameCenter( + ClientConfig, + *SessionState, + PlayerId, + BundleId, + TimestampSeconds, + Salt, + Signature, + PublicKeyUrl, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::LinkGoogle( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Token + , Vars + ]() + { + + NakamaApi::LinkGoogle( + ClientConfig, + HttpKey, + Token, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::LinkGoogle( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Token + , Vars + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Token + , Vars + ]() + { + + NakamaApi::LinkGoogle( + ClientConfig, + *SessionState, + Token, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::LinkSteam( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + FNakamaOptionalBool Sync, + const FString& AccountToken, + const TMap& AccountVars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , AccountToken + , AccountVars + , Sync + ]() + { + FNakamaAccountSteam Account; + Account.Token = AccountToken; + Account.Vars = AccountVars; + + NakamaApi::LinkSteam( + ClientConfig, + HttpKey, + Account, + Sync, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::LinkSteam( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + FNakamaOptionalBool Sync, + const FString& AccountToken, + const TMap& AccountVars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , AccountToken + , AccountVars + , Sync + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , AccountToken + , AccountVars + , Sync + ]() + { + FNakamaAccountSteam Account; + Account.Token = AccountToken; + Account.Vars = AccountVars; + + NakamaApi::LinkSteam( + ClientConfig, + *SessionState, + Account, + Sync, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListChannelMessages( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& ChannelId, + FNakamaOptionalInt32 Limit, + FNakamaOptionalBool Forward, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaChannelMessageListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , ChannelId + , Limit + , Forward + , Cursor + ]() + { + + NakamaApi::ListChannelMessages( + ClientConfig, + HttpKey, + ChannelId, + Limit, + Forward, + Cursor, + [FutureState, DoRequest, OnError](const FNakamaChannelMessageList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaChannelMessageListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListChannelMessages( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& ChannelId, + FNakamaOptionalInt32 Limit, + FNakamaOptionalBool Forward, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaChannelMessageListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , ChannelId + , Limit + , Forward + , Cursor + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , ChannelId + , Limit + , Forward + , Cursor + ]() + { + + NakamaApi::ListChannelMessages( + ClientConfig, + *SessionState, + ChannelId, + Limit, + Forward, + Cursor, + [FutureState, DoRequest, OnError](const FNakamaChannelMessageList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaChannelMessageListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListFriends( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + FNakamaOptionalInt32 Limit, + FNakamaOptionalInt32 State, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaFriendListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Limit + , State + , Cursor + ]() + { + + NakamaApi::ListFriends( + ClientConfig, + HttpKey, + Limit, + State, + Cursor, + [FutureState, DoRequest, OnError](const FNakamaFriendList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaFriendListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListFriends( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + FNakamaOptionalInt32 Limit, + FNakamaOptionalInt32 State, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaFriendListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Limit + , State + , Cursor + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Limit + , State + , Cursor + ]() + { + + NakamaApi::ListFriends( + ClientConfig, + *SessionState, + Limit, + State, + Cursor, + [FutureState, DoRequest, OnError](const FNakamaFriendList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaFriendListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListFriendsOfFriends( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaFriendsOfFriendsListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Limit + , Cursor + ]() + { + + NakamaApi::ListFriendsOfFriends( + ClientConfig, + HttpKey, + Limit, + Cursor, + [FutureState, DoRequest, OnError](const FNakamaFriendsOfFriendsList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaFriendsOfFriendsListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListFriendsOfFriends( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaFriendsOfFriendsListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Limit + , Cursor + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Limit + , Cursor + ]() + { + + NakamaApi::ListFriendsOfFriends( + ClientConfig, + *SessionState, + Limit, + Cursor, + [FutureState, DoRequest, OnError](const FNakamaFriendsOfFriendsList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaFriendsOfFriendsListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListGroups( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Name, + const FString& Cursor, + FNakamaOptionalInt32 Limit, + const FString& LangTag, + FNakamaOptionalInt32 Members, + FNakamaOptionalBool Open, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaGroupListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Name + , Cursor + , Limit + , LangTag + , Members + , Open + ]() + { + + NakamaApi::ListGroups( + ClientConfig, + HttpKey, + Name, + Cursor, + Limit, + LangTag, + Members, + Open, + [FutureState, DoRequest, OnError](const FNakamaGroupList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaGroupListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListGroups( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Name, + const FString& Cursor, + FNakamaOptionalInt32 Limit, + const FString& LangTag, + FNakamaOptionalInt32 Members, + FNakamaOptionalBool Open, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaGroupListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Name + , Cursor + , Limit + , LangTag + , Members + , Open + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Name + , Cursor + , Limit + , LangTag + , Members + , Open + ]() + { + + NakamaApi::ListGroups( + ClientConfig, + *SessionState, + Name, + Cursor, + Limit, + LangTag, + Members, + Open, + [FutureState, DoRequest, OnError](const FNakamaGroupList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaGroupListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListGroupUsers( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& GroupId, + FNakamaOptionalInt32 Limit, + FNakamaOptionalInt32 State, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaGroupUserListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , GroupId + , Limit + , State + , Cursor + ]() + { + + NakamaApi::ListGroupUsers( + ClientConfig, + HttpKey, + GroupId, + Limit, + State, + Cursor, + [FutureState, DoRequest, OnError](const FNakamaGroupUserList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaGroupUserListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListGroupUsers( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& GroupId, + FNakamaOptionalInt32 Limit, + FNakamaOptionalInt32 State, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaGroupUserListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , GroupId + , Limit + , State + , Cursor + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , GroupId + , Limit + , State + , Cursor + ]() + { + + NakamaApi::ListGroupUsers( + ClientConfig, + *SessionState, + GroupId, + Limit, + State, + Cursor, + [FutureState, DoRequest, OnError](const FNakamaGroupUserList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaGroupUserListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListLeaderboardRecords( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& LeaderboardId, + const TArray& OwnerIds, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + FNakamaOptionalInt64 Expiry, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaLeaderboardRecordListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , LeaderboardId + , OwnerIds + , Limit + , Cursor + , Expiry + ]() + { + + NakamaApi::ListLeaderboardRecords( + ClientConfig, + HttpKey, + LeaderboardId, + OwnerIds, + Limit, + Cursor, + Expiry, + [FutureState, DoRequest, OnError](const FNakamaLeaderboardRecordList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaLeaderboardRecordListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListLeaderboardRecords( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& LeaderboardId, + const TArray& OwnerIds, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + FNakamaOptionalInt64 Expiry, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaLeaderboardRecordListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , LeaderboardId + , OwnerIds + , Limit + , Cursor + , Expiry + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , LeaderboardId + , OwnerIds + , Limit + , Cursor + , Expiry + ]() + { + + NakamaApi::ListLeaderboardRecords( + ClientConfig, + *SessionState, + LeaderboardId, + OwnerIds, + Limit, + Cursor, + Expiry, + [FutureState, DoRequest, OnError](const FNakamaLeaderboardRecordList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaLeaderboardRecordListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListLeaderboardRecordsAroundOwner( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& LeaderboardId, + FNakamaOptionalInt32 Limit, + const FString& OwnerId, + FNakamaOptionalInt64 Expiry, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaLeaderboardRecordListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , LeaderboardId + , Limit + , OwnerId + , Expiry + , Cursor + ]() + { + + NakamaApi::ListLeaderboardRecordsAroundOwner( + ClientConfig, + HttpKey, + LeaderboardId, + Limit, + OwnerId, + Expiry, + Cursor, + [FutureState, DoRequest, OnError](const FNakamaLeaderboardRecordList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaLeaderboardRecordListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListLeaderboardRecordsAroundOwner( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& LeaderboardId, + FNakamaOptionalInt32 Limit, + const FString& OwnerId, + FNakamaOptionalInt64 Expiry, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaLeaderboardRecordListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , LeaderboardId + , Limit + , OwnerId + , Expiry + , Cursor + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , LeaderboardId + , Limit + , OwnerId + , Expiry + , Cursor + ]() + { + + NakamaApi::ListLeaderboardRecordsAroundOwner( + ClientConfig, + *SessionState, + LeaderboardId, + Limit, + OwnerId, + Expiry, + Cursor, + [FutureState, DoRequest, OnError](const FNakamaLeaderboardRecordList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaLeaderboardRecordListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListMatches( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + FNakamaOptionalInt32 Limit, + FNakamaOptionalBool Authoritative, + const FString& Label, + FNakamaOptionalInt32 MinSize, + FNakamaOptionalInt32 MaxSize, + const FString& Query, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaMatchListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Limit + , Authoritative + , Label + , MinSize + , MaxSize + , Query + ]() + { + + NakamaApi::ListMatches( + ClientConfig, + HttpKey, + Limit, + Authoritative, + Label, + MinSize, + MaxSize, + Query, + [FutureState, DoRequest, OnError](const FNakamaMatchList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaMatchListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListMatches( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + FNakamaOptionalInt32 Limit, + FNakamaOptionalBool Authoritative, + const FString& Label, + FNakamaOptionalInt32 MinSize, + FNakamaOptionalInt32 MaxSize, + const FString& Query, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaMatchListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Limit + , Authoritative + , Label + , MinSize + , MaxSize + , Query + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Limit + , Authoritative + , Label + , MinSize + , MaxSize + , Query + ]() + { + + NakamaApi::ListMatches( + ClientConfig, + *SessionState, + Limit, + Authoritative, + Label, + MinSize, + MaxSize, + Query, + [FutureState, DoRequest, OnError](const FNakamaMatchList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaMatchListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListParties( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + FNakamaOptionalInt32 Limit, + FNakamaOptionalBool Open, + const FString& Query, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaPartyListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Limit + , Open + , Query + , Cursor + ]() + { + + NakamaApi::ListParties( + ClientConfig, + HttpKey, + Limit, + Open, + Query, + Cursor, + [FutureState, DoRequest, OnError](const FNakamaPartyList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaPartyListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListParties( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + FNakamaOptionalInt32 Limit, + FNakamaOptionalBool Open, + const FString& Query, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaPartyListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Limit + , Open + , Query + , Cursor + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Limit + , Open + , Query + , Cursor + ]() + { + + NakamaApi::ListParties( + ClientConfig, + *SessionState, + Limit, + Open, + Query, + Cursor, + [FutureState, DoRequest, OnError](const FNakamaPartyList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaPartyListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListNotifications( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + FNakamaOptionalInt32 Limit, + const FString& CacheableCursor, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaNotificationListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Limit + , CacheableCursor + ]() + { + + NakamaApi::ListNotifications( + ClientConfig, + HttpKey, + Limit, + CacheableCursor, + [FutureState, DoRequest, OnError](const FNakamaNotificationList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaNotificationListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListNotifications( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + FNakamaOptionalInt32 Limit, + const FString& CacheableCursor, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaNotificationListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Limit + , CacheableCursor + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Limit + , CacheableCursor + ]() + { + + NakamaApi::ListNotifications( + ClientConfig, + *SessionState, + Limit, + CacheableCursor, + [FutureState, DoRequest, OnError](const FNakamaNotificationList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaNotificationListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListStorageObjects( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& UserId, + const FString& Collection, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaStorageObjectListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , UserId + , Collection + , Limit + , Cursor + ]() + { + + NakamaApi::ListStorageObjects( + ClientConfig, + HttpKey, + UserId, + Collection, + Limit, + Cursor, + [FutureState, DoRequest, OnError](const FNakamaStorageObjectList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaStorageObjectListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListStorageObjects( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& UserId, + const FString& Collection, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaStorageObjectListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , UserId + , Collection + , Limit + , Cursor + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , UserId + , Collection + , Limit + , Cursor + ]() + { + + NakamaApi::ListStorageObjects( + ClientConfig, + *SessionState, + UserId, + Collection, + Limit, + Cursor, + [FutureState, DoRequest, OnError](const FNakamaStorageObjectList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaStorageObjectListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListSubscriptions( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaSubscriptionListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Limit + , Cursor + ]() + { + + NakamaApi::ListSubscriptions( + ClientConfig, + HttpKey, + Limit, + Cursor, + [FutureState, DoRequest, OnError](const FNakamaSubscriptionList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaSubscriptionListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListSubscriptions( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaSubscriptionListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Limit + , Cursor + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Limit + , Cursor + ]() + { + + NakamaApi::ListSubscriptions( + ClientConfig, + *SessionState, + Limit, + Cursor, + [FutureState, DoRequest, OnError](const FNakamaSubscriptionList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaSubscriptionListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListTournaments( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + FNakamaOptionalInt32 CategoryStart, + FNakamaOptionalInt32 CategoryEnd, + FNakamaOptionalInt32 StartTime, + FNakamaOptionalInt32 EndTime, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaTournamentListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , CategoryStart + , CategoryEnd + , StartTime + , EndTime + , Limit + , Cursor + ]() + { + + NakamaApi::ListTournaments( + ClientConfig, + HttpKey, + CategoryStart, + CategoryEnd, + StartTime, + EndTime, + Limit, + Cursor, + [FutureState, DoRequest, OnError](const FNakamaTournamentList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaTournamentListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListTournaments( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + FNakamaOptionalInt32 CategoryStart, + FNakamaOptionalInt32 CategoryEnd, + FNakamaOptionalInt32 StartTime, + FNakamaOptionalInt32 EndTime, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaTournamentListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , CategoryStart + , CategoryEnd + , StartTime + , EndTime + , Limit + , Cursor + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , CategoryStart + , CategoryEnd + , StartTime + , EndTime + , Limit + , Cursor + ]() + { + + NakamaApi::ListTournaments( + ClientConfig, + *SessionState, + CategoryStart, + CategoryEnd, + StartTime, + EndTime, + Limit, + Cursor, + [FutureState, DoRequest, OnError](const FNakamaTournamentList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaTournamentListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListTournamentRecords( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& TournamentId, + const TArray& OwnerIds, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + FNakamaOptionalInt64 Expiry, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaTournamentRecordListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , TournamentId + , OwnerIds + , Limit + , Cursor + , Expiry + ]() + { + + NakamaApi::ListTournamentRecords( + ClientConfig, + HttpKey, + TournamentId, + OwnerIds, + Limit, + Cursor, + Expiry, + [FutureState, DoRequest, OnError](const FNakamaTournamentRecordList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaTournamentRecordListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListTournamentRecords( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& TournamentId, + const TArray& OwnerIds, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + FNakamaOptionalInt64 Expiry, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaTournamentRecordListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , TournamentId + , OwnerIds + , Limit + , Cursor + , Expiry + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , TournamentId + , OwnerIds + , Limit + , Cursor + , Expiry + ]() + { + + NakamaApi::ListTournamentRecords( + ClientConfig, + *SessionState, + TournamentId, + OwnerIds, + Limit, + Cursor, + Expiry, + [FutureState, DoRequest, OnError](const FNakamaTournamentRecordList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaTournamentRecordListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListTournamentRecordsAroundOwner( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& TournamentId, + FNakamaOptionalInt32 Limit, + const FString& OwnerId, + FNakamaOptionalInt64 Expiry, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaTournamentRecordListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , TournamentId + , Limit + , OwnerId + , Expiry + , Cursor + ]() + { + + NakamaApi::ListTournamentRecordsAroundOwner( + ClientConfig, + HttpKey, + TournamentId, + Limit, + OwnerId, + Expiry, + Cursor, + [FutureState, DoRequest, OnError](const FNakamaTournamentRecordList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaTournamentRecordListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListTournamentRecordsAroundOwner( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& TournamentId, + FNakamaOptionalInt32 Limit, + const FString& OwnerId, + FNakamaOptionalInt64 Expiry, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaTournamentRecordListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , TournamentId + , Limit + , OwnerId + , Expiry + , Cursor + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , TournamentId + , Limit + , OwnerId + , Expiry + , Cursor + ]() + { + + NakamaApi::ListTournamentRecordsAroundOwner( + ClientConfig, + *SessionState, + TournamentId, + Limit, + OwnerId, + Expiry, + Cursor, + [FutureState, DoRequest, OnError](const FNakamaTournamentRecordList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaTournamentRecordListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListUserGroups( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& UserId, + FNakamaOptionalInt32 Limit, + FNakamaOptionalInt32 State, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaUserGroupListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , UserId + , Limit + , State + , Cursor + ]() + { + + NakamaApi::ListUserGroups( + ClientConfig, + HttpKey, + UserId, + Limit, + State, + Cursor, + [FutureState, DoRequest, OnError](const FNakamaUserGroupList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaUserGroupListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ListUserGroups( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& UserId, + FNakamaOptionalInt32 Limit, + FNakamaOptionalInt32 State, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaUserGroupListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , UserId + , Limit + , State + , Cursor + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , UserId + , Limit + , State + , Cursor + ]() + { + + NakamaApi::ListUserGroups( + ClientConfig, + *SessionState, + UserId, + Limit, + State, + Cursor, + [FutureState, DoRequest, OnError](const FNakamaUserGroupList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaUserGroupListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::PromoteGroupUsers( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& GroupId, + const TArray& UserIds, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , GroupId + , UserIds + ]() + { + + NakamaApi::PromoteGroupUsers( + ClientConfig, + HttpKey, + GroupId, + UserIds, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::PromoteGroupUsers( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& GroupId, + const TArray& UserIds, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , GroupId + , UserIds + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , GroupId + , UserIds + ]() + { + + NakamaApi::PromoteGroupUsers( + ClientConfig, + *SessionState, + GroupId, + UserIds, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::DemoteGroupUsers( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& GroupId, + const TArray& UserIds, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , GroupId + , UserIds + ]() + { + + NakamaApi::DemoteGroupUsers( + ClientConfig, + HttpKey, + GroupId, + UserIds, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::DemoteGroupUsers( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& GroupId, + const TArray& UserIds, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , GroupId + , UserIds + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , GroupId + , UserIds + ]() + { + + NakamaApi::DemoteGroupUsers( + ClientConfig, + *SessionState, + GroupId, + UserIds, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ReadStorageObjects( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& ObjectIds, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaStorageObjectsResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , ObjectIds + ]() + { + + NakamaApi::ReadStorageObjects( + ClientConfig, + HttpKey, + ObjectIds, + [FutureState, DoRequest, OnError](const FNakamaStorageObjects& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaStorageObjectsResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ReadStorageObjects( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const TArray& ObjectIds, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaStorageObjectsResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , ObjectIds + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , ObjectIds + ]() + { + + NakamaApi::ReadStorageObjects( + ClientConfig, + *SessionState, + ObjectIds, + [FutureState, DoRequest, OnError](const FNakamaStorageObjects& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaStorageObjectsResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::RpcFunc( + const FNakamaClientConfig& ClientConfig, + const FString& Id, + const FString& Payload, + const FString& HttpKey, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaRpcResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Id + , Payload + , HttpKey + ]() + { + + NakamaApi::RpcFunc( + ClientConfig, + Id, + Payload, + HttpKey, + [FutureState, DoRequest, OnError](const FNakamaRpc& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaRpcResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::RpcFunc( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Id, + const FString& Payload, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaRpcResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Id + , Payload + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Id + , Payload + ]() + { + + NakamaApi::RpcFunc( + ClientConfig, + *SessionState, + Id, + Payload, + [FutureState, DoRequest, OnError](const FNakamaRpc& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaRpcResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::UnlinkApple( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Token + , Vars + ]() + { + + NakamaApi::UnlinkApple( + ClientConfig, + HttpKey, + Token, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::UnlinkApple( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Token + , Vars + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Token + , Vars + ]() + { + + NakamaApi::UnlinkApple( + ClientConfig, + *SessionState, + Token, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::UnlinkCustom( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Id, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Id + , Vars + ]() + { + + NakamaApi::UnlinkCustom( + ClientConfig, + HttpKey, + Id, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::UnlinkCustom( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Id, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Id + , Vars + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Id + , Vars + ]() + { + + NakamaApi::UnlinkCustom( + ClientConfig, + *SessionState, + Id, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::UnlinkDevice( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Id, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Id + , Vars + ]() + { + + NakamaApi::UnlinkDevice( + ClientConfig, + HttpKey, + Id, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::UnlinkDevice( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Id, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Id + , Vars + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Id + , Vars + ]() + { + + NakamaApi::UnlinkDevice( + ClientConfig, + *SessionState, + Id, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::UnlinkEmail( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Email, + const FString& Password, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Email + , Password + , Vars + ]() + { + + NakamaApi::UnlinkEmail( + ClientConfig, + HttpKey, + Email, + Password, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::UnlinkEmail( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Email, + const FString& Password, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Email + , Password + , Vars + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Email + , Password + , Vars + ]() + { + + NakamaApi::UnlinkEmail( + ClientConfig, + *SessionState, + Email, + Password, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::UnlinkFacebook( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Token + , Vars + ]() + { + + NakamaApi::UnlinkFacebook( + ClientConfig, + HttpKey, + Token, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::UnlinkFacebook( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Token + , Vars + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Token + , Vars + ]() + { + + NakamaApi::UnlinkFacebook( + ClientConfig, + *SessionState, + Token, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::UnlinkFacebookInstantGame( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& SignedPlayerInfo, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , SignedPlayerInfo + , Vars + ]() + { + + NakamaApi::UnlinkFacebookInstantGame( + ClientConfig, + HttpKey, + SignedPlayerInfo, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::UnlinkFacebookInstantGame( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& SignedPlayerInfo, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , SignedPlayerInfo + , Vars + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , SignedPlayerInfo + , Vars + ]() + { + + NakamaApi::UnlinkFacebookInstantGame( + ClientConfig, + *SessionState, + SignedPlayerInfo, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::UnlinkGameCenter( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& PlayerId, + const FString& BundleId, + int64 TimestampSeconds, + const FString& Salt, + const FString& Signature, + const FString& PublicKeyUrl, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , PlayerId + , BundleId + , TimestampSeconds + , Salt + , Signature + , PublicKeyUrl + , Vars + ]() + { + + NakamaApi::UnlinkGameCenter( + ClientConfig, + HttpKey, + PlayerId, + BundleId, + TimestampSeconds, + Salt, + Signature, + PublicKeyUrl, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::UnlinkGameCenter( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& PlayerId, + const FString& BundleId, + int64 TimestampSeconds, + const FString& Salt, + const FString& Signature, + const FString& PublicKeyUrl, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , PlayerId + , BundleId + , TimestampSeconds + , Salt + , Signature + , PublicKeyUrl + , Vars + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , PlayerId + , BundleId + , TimestampSeconds + , Salt + , Signature + , PublicKeyUrl + , Vars + ]() + { + + NakamaApi::UnlinkGameCenter( + ClientConfig, + *SessionState, + PlayerId, + BundleId, + TimestampSeconds, + Salt, + Signature, + PublicKeyUrl, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::UnlinkGoogle( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Token + , Vars + ]() + { + + NakamaApi::UnlinkGoogle( + ClientConfig, + HttpKey, + Token, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::UnlinkGoogle( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Token + , Vars + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Token + , Vars + ]() + { + + NakamaApi::UnlinkGoogle( + ClientConfig, + *SessionState, + Token, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::UnlinkSteam( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Token + , Vars + ]() + { + + NakamaApi::UnlinkSteam( + ClientConfig, + HttpKey, + Token, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::UnlinkSteam( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Token + , Vars + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Token + , Vars + ]() + { + + NakamaApi::UnlinkSteam( + ClientConfig, + *SessionState, + Token, + Vars, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::UpdateAccount( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Username, + const FString& DisplayName, + const FString& AvatarUrl, + const FString& LangTag, + const FString& Location, + const FString& Timezone, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Username + , DisplayName + , AvatarUrl + , LangTag + , Location + , Timezone + ]() + { + + NakamaApi::UpdateAccount( + ClientConfig, + HttpKey, + Username, + DisplayName, + AvatarUrl, + LangTag, + Location, + Timezone, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::UpdateAccount( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Username, + const FString& DisplayName, + const FString& AvatarUrl, + const FString& LangTag, + const FString& Location, + const FString& Timezone, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Username + , DisplayName + , AvatarUrl + , LangTag + , Location + , Timezone + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Username + , DisplayName + , AvatarUrl + , LangTag + , Location + , Timezone + ]() + { + + NakamaApi::UpdateAccount( + ClientConfig, + *SessionState, + Username, + DisplayName, + AvatarUrl, + LangTag, + Location, + Timezone, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::UpdateGroup( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& GroupId, + const FString& Name, + const FString& Description, + const FString& LangTag, + const FString& AvatarUrl, + FNakamaOptionalBool Open, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , GroupId + , Name + , Description + , LangTag + , AvatarUrl + , Open + ]() + { + + NakamaApi::UpdateGroup( + ClientConfig, + HttpKey, + GroupId, + Name, + Description, + LangTag, + AvatarUrl, + Open, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::UpdateGroup( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& GroupId, + const FString& Name, + const FString& Description, + const FString& LangTag, + const FString& AvatarUrl, + FNakamaOptionalBool Open, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , GroupId + , Name + , Description + , LangTag + , AvatarUrl + , Open + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , GroupId + , Name + , Description + , LangTag + , AvatarUrl + , Open + ]() + { + + NakamaApi::UpdateGroup( + ClientConfig, + *SessionState, + GroupId, + Name, + Description, + LangTag, + AvatarUrl, + Open, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaVoidResult{ FNakamaVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ValidatePurchaseApple( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Receipt, + FNakamaOptionalBool Persist, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidatePurchaseResponseResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Receipt + , Persist + ]() + { + + NakamaApi::ValidatePurchaseApple( + ClientConfig, + HttpKey, + Receipt, + Persist, + [FutureState, DoRequest, OnError](const FNakamaValidatePurchaseResponse& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidatePurchaseResponseResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ValidatePurchaseApple( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Receipt, + FNakamaOptionalBool Persist, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidatePurchaseResponseResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Receipt + , Persist + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Receipt + , Persist + ]() + { + + NakamaApi::ValidatePurchaseApple( + ClientConfig, + *SessionState, + Receipt, + Persist, + [FutureState, DoRequest, OnError](const FNakamaValidatePurchaseResponse& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidatePurchaseResponseResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ValidateSubscriptionApple( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Receipt, + FNakamaOptionalBool Persist, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidateSubscriptionResponseResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Receipt + , Persist + ]() + { + + NakamaApi::ValidateSubscriptionApple( + ClientConfig, + HttpKey, + Receipt, + Persist, + [FutureState, DoRequest, OnError](const FNakamaValidateSubscriptionResponse& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidateSubscriptionResponseResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ValidateSubscriptionApple( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Receipt, + FNakamaOptionalBool Persist, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidateSubscriptionResponseResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Receipt + , Persist + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Receipt + , Persist + ]() + { + + NakamaApi::ValidateSubscriptionApple( + ClientConfig, + *SessionState, + Receipt, + Persist, + [FutureState, DoRequest, OnError](const FNakamaValidateSubscriptionResponse& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidateSubscriptionResponseResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ValidatePurchaseGoogle( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Purchase, + FNakamaOptionalBool Persist, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidatePurchaseResponseResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Purchase + , Persist + ]() + { + + NakamaApi::ValidatePurchaseGoogle( + ClientConfig, + HttpKey, + Purchase, + Persist, + [FutureState, DoRequest, OnError](const FNakamaValidatePurchaseResponse& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidatePurchaseResponseResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ValidatePurchaseGoogle( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Purchase, + FNakamaOptionalBool Persist, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidatePurchaseResponseResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Purchase + , Persist + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Purchase + , Persist + ]() + { + + NakamaApi::ValidatePurchaseGoogle( + ClientConfig, + *SessionState, + Purchase, + Persist, + [FutureState, DoRequest, OnError](const FNakamaValidatePurchaseResponse& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidatePurchaseResponseResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ValidateSubscriptionGoogle( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Receipt, + FNakamaOptionalBool Persist, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidateSubscriptionResponseResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Receipt + , Persist + ]() + { + + NakamaApi::ValidateSubscriptionGoogle( + ClientConfig, + HttpKey, + Receipt, + Persist, + [FutureState, DoRequest, OnError](const FNakamaValidateSubscriptionResponse& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidateSubscriptionResponseResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ValidateSubscriptionGoogle( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Receipt, + FNakamaOptionalBool Persist, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidateSubscriptionResponseResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Receipt + , Persist + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Receipt + , Persist + ]() + { + + NakamaApi::ValidateSubscriptionGoogle( + ClientConfig, + *SessionState, + Receipt, + Persist, + [FutureState, DoRequest, OnError](const FNakamaValidateSubscriptionResponse& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidateSubscriptionResponseResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ValidatePurchaseHuawei( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Purchase, + const FString& Signature, + FNakamaOptionalBool Persist, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidatePurchaseResponseResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Purchase + , Signature + , Persist + ]() + { + + NakamaApi::ValidatePurchaseHuawei( + ClientConfig, + HttpKey, + Purchase, + Signature, + Persist, + [FutureState, DoRequest, OnError](const FNakamaValidatePurchaseResponse& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidatePurchaseResponseResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ValidatePurchaseHuawei( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Purchase, + const FString& Signature, + FNakamaOptionalBool Persist, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidatePurchaseResponseResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Purchase + , Signature + , Persist + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Purchase + , Signature + , Persist + ]() + { + + NakamaApi::ValidatePurchaseHuawei( + ClientConfig, + *SessionState, + Purchase, + Signature, + Persist, + [FutureState, DoRequest, OnError](const FNakamaValidatePurchaseResponse& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidatePurchaseResponseResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ValidatePurchaseFacebookInstant( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& SignedRequest, + FNakamaOptionalBool Persist, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidatePurchaseResponseResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , SignedRequest + , Persist + ]() + { + + NakamaApi::ValidatePurchaseFacebookInstant( + ClientConfig, + HttpKey, + SignedRequest, + Persist, + [FutureState, DoRequest, OnError](const FNakamaValidatePurchaseResponse& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidatePurchaseResponseResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::ValidatePurchaseFacebookInstant( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& SignedRequest, + FNakamaOptionalBool Persist, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidatePurchaseResponseResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , SignedRequest + , Persist + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , SignedRequest + , Persist + ]() + { + + NakamaApi::ValidatePurchaseFacebookInstant( + ClientConfig, + *SessionState, + SignedRequest, + Persist, + [FutureState, DoRequest, OnError](const FNakamaValidatePurchaseResponse& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaValidatePurchaseResponseResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::WriteLeaderboardRecord( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& LeaderboardId, + int64 RecordScore, + int64 RecordSubscore, + const FString& RecordMetadata, + ENakamaOperator RecordOperator, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaLeaderboardRecordResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , LeaderboardId + , RecordScore + , RecordSubscore + , RecordMetadata + , RecordOperator + ]() + { + FNakamaWriteLeaderboardRecordRequestLeaderboardRecordWrite Record; + Record.Score = RecordScore; + Record.Subscore = RecordSubscore; + Record.Metadata = RecordMetadata; + Record.Operator = RecordOperator; + + NakamaApi::WriteLeaderboardRecord( + ClientConfig, + HttpKey, + LeaderboardId, + Record, + [FutureState, DoRequest, OnError](const FNakamaLeaderboardRecord& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaLeaderboardRecordResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::WriteLeaderboardRecord( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& LeaderboardId, + int64 RecordScore, + int64 RecordSubscore, + const FString& RecordMetadata, + ENakamaOperator RecordOperator, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaLeaderboardRecordResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , LeaderboardId + , RecordScore + , RecordSubscore + , RecordMetadata + , RecordOperator + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , LeaderboardId + , RecordScore + , RecordSubscore + , RecordMetadata + , RecordOperator + ]() + { + FNakamaWriteLeaderboardRecordRequestLeaderboardRecordWrite Record; + Record.Score = RecordScore; + Record.Subscore = RecordSubscore; + Record.Metadata = RecordMetadata; + Record.Operator = RecordOperator; + + NakamaApi::WriteLeaderboardRecord( + ClientConfig, + *SessionState, + LeaderboardId, + Record, + [FutureState, DoRequest, OnError](const FNakamaLeaderboardRecord& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaLeaderboardRecordResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::WriteStorageObjects( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& Objects, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaStorageObjectAcksResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Objects + ]() + { + + NakamaApi::WriteStorageObjects( + ClientConfig, + HttpKey, + Objects, + [FutureState, DoRequest, OnError](const FNakamaStorageObjectAcks& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaStorageObjectAcksResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::WriteStorageObjects( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const TArray& Objects, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaStorageObjectAcksResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Objects + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Objects + ]() + { + + NakamaApi::WriteStorageObjects( + ClientConfig, + *SessionState, + Objects, + [FutureState, DoRequest, OnError](const FNakamaStorageObjectAcks& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaStorageObjectAcksResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::WriteTournamentRecord( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& TournamentId, + int64 RecordScore, + int64 RecordSubscore, + const FString& RecordMetadata, + ENakamaOperator RecordOperator, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaLeaderboardRecordResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , TournamentId + , RecordScore + , RecordSubscore + , RecordMetadata + , RecordOperator + ]() + { + FNakamaWriteTournamentRecordRequestTournamentRecordWrite Record; + Record.Score = RecordScore; + Record.Subscore = RecordSubscore; + Record.Metadata = RecordMetadata; + Record.Operator = RecordOperator; + + NakamaApi::WriteTournamentRecord( + ClientConfig, + HttpKey, + TournamentId, + Record, + [FutureState, DoRequest, OnError](const FNakamaLeaderboardRecord& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaLeaderboardRecordResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} +TNakamaFuture Nakama::WriteTournamentRecord( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& TournamentId, + int64 RecordScore, + int64 RecordSubscore, + const FString& RecordMetadata, + ENakamaOperator RecordOperator, + const FNakamaRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FNakamaError& Error) + { + if (Nakama::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Nakama::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaLeaderboardRecordResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , TournamentId + , RecordScore + , RecordSubscore + , RecordMetadata + , RecordOperator + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , TournamentId + , RecordScore + , RecordSubscore + , RecordMetadata + , RecordOperator + ]() + { + FNakamaWriteTournamentRecordRequestTournamentRecordWrite Record; + Record.Score = RecordScore; + Record.Subscore = RecordSubscore; + Record.Metadata = RecordMetadata; + Record.Operator = RecordOperator; + + NakamaApi::WriteTournamentRecord( + ClientConfig, + *SessionState, + TournamentId, + Record, + [FutureState, DoRequest, OnError](const FNakamaLeaderboardRecord& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FNakamaLeaderboardRecordResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TNakamaFuture(FutureState); +} diff --git a/Nakama/Source/Nakama/Private/NakamaClientConfig.cpp b/Nakama/Source/Nakama/Private/NakamaClientConfig.cpp new file mode 100644 index 000000000..04deb06c6 --- /dev/null +++ b/Nakama/Source/Nakama/Private/NakamaClientConfig.cpp @@ -0,0 +1,23 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NakamaClientConfig.h" + +FString FNakamaClientConfig::GetBaseUrl() const noexcept +{ + const FString Scheme = bUseSSL ? TEXT("https") : TEXT("http"); + return FString::Printf(TEXT("%s://%s:%d"), *Scheme, *Host, Port); +} diff --git a/Nakama/Source/NakamaBlueprints/Public/NakamaBlueprints.h b/Nakama/Source/Nakama/Private/NakamaModule.cpp similarity index 56% rename from Nakama/Source/NakamaBlueprints/Public/NakamaBlueprints.h rename to Nakama/Source/Nakama/Private/NakamaModule.cpp index db69caea8..c061d2a70 100644 --- a/Nakama/Source/NakamaBlueprints/Public/NakamaBlueprints.h +++ b/Nakama/Source/Nakama/Private/NakamaModule.cpp @@ -1,5 +1,5 @@ /* -* Copyright 2025 The Nakama Authors + * Copyright 2026 The Nakama Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,22 +14,22 @@ * limitations under the License. */ -#pragma once - -//#include "NakamaUnreal.h" -//#include "nonstd/optional.hpp" -#include "CoreMinimal.h" -#include "Modules/ModuleManager.h" - -DECLARE_LOG_CATEGORY_EXTERN(LogNakamaBlueprints, Log, All); +#include "NakamaModule.h" +#include "NakamaApi.h" +void FNakamaModule::StartupModule() +{ + UE_LOG(LogNakama, Log, TEXT("Nakama module starting")); + if (!FModuleManager::Get().IsModuleLoaded("WebSockets")) + { + FModuleManager::Get().LoadModule("WebSockets"); + } +} -class FNakamaBlueprintsModule : public IModuleInterface +void FNakamaModule::ShutdownModule() { -public: + UE_LOG(LogNakama, Log, TEXT("Nakama module shutting down")); +} - /** IModuleInterface implementation */ - virtual void StartupModule() override; - virtual void ShutdownModule() override; +IMPLEMENT_MODULE(FNakamaModule, Nakama) -}; diff --git a/Nakama/Source/Nakama/Private/NakamaRt.cpp b/Nakama/Source/Nakama/Private/NakamaRt.cpp new file mode 100644 index 000000000..499c5e375 --- /dev/null +++ b/Nakama/Source/Nakama/Private/NakamaRt.cpp @@ -0,0 +1,1831 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NakamaRt.h" + +namespace NakamaRt +{ + +TNakamaFuture> ChannelJoin( + const TSharedPtr& Connection + , const FString& Target + , int32 Type + , FNakamaRtOptionalBool Persistence + , FNakamaRtOptionalBool Hidden +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtChannelJoin RequestMsg; + RequestMsg.Target = Target; + RequestMsg.Type = Type; + RequestMsg.Persistence = Persistence; + RequestMsg.Hidden = Hidden; + + return Connection->Send(TEXT("channel_join"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + FNakamaRtChannel ResultData = FNakamaRtChannel::FromJson(Response.Data); + FNakamaRtResult Result + = FNakamaRtResult::Success(ResultData); + + return MakeCompletedFuture(Result); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> ChannelLeave( + const TSharedPtr& Connection + , const FString& ChannelId +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtChannelLeave RequestMsg; + RequestMsg.ChannelId = ChannelId; + + return Connection->Send(TEXT("channel_leave"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + return MakeCompletedFuture(FNakamaRtResult::Success({})); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> ChannelMessageSend( + const TSharedPtr& Connection + , const FString& ChannelId + , const FString& Content +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtChannelMessageSend RequestMsg; + RequestMsg.ChannelId = ChannelId; + RequestMsg.Content = Content; + + return Connection->Send(TEXT("channel_message_send"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + FNakamaRtChannelMessageAck ResultData = FNakamaRtChannelMessageAck::FromJson(Response.Data); + FNakamaRtResult Result + = FNakamaRtResult::Success(ResultData); + + return MakeCompletedFuture(Result); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> ChannelMessageUpdate( + const TSharedPtr& Connection + , const FString& ChannelId + , const FString& MessageId + , const FString& Content +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtChannelMessageUpdate RequestMsg; + RequestMsg.ChannelId = ChannelId; + RequestMsg.MessageId = MessageId; + RequestMsg.Content = Content; + + return Connection->Send(TEXT("channel_message_update"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + FNakamaRtChannelMessageAck ResultData = FNakamaRtChannelMessageAck::FromJson(Response.Data); + FNakamaRtResult Result + = FNakamaRtResult::Success(ResultData); + + return MakeCompletedFuture(Result); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> ChannelMessageRemove( + const TSharedPtr& Connection + , const FString& ChannelId + , const FString& MessageId +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtChannelMessageRemove RequestMsg; + RequestMsg.ChannelId = ChannelId; + RequestMsg.MessageId = MessageId; + + return Connection->Send(TEXT("channel_message_remove"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + FNakamaRtChannelMessageAck ResultData = FNakamaRtChannelMessageAck::FromJson(Response.Data); + FNakamaRtResult Result + = FNakamaRtResult::Success(ResultData); + + return MakeCompletedFuture(Result); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> MatchCreate( + const TSharedPtr& Connection + , const FString& Name +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtMatchCreate RequestMsg; + RequestMsg.Name = Name; + + return Connection->Send(TEXT("match_create"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + FNakamaRtMatch ResultData = FNakamaRtMatch::FromJson(Response.Data); + FNakamaRtResult Result + = FNakamaRtResult::Success(ResultData); + + return MakeCompletedFuture(Result); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> MatchDataSend( + const TSharedPtr& Connection + , const FString& MatchId + , int64 OpCode + , const TArray& Data + , const TArray& Presences + , bool Reliable +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtMatchDataSend RequestMsg; + RequestMsg.MatchId = MatchId; + RequestMsg.OpCode = OpCode; + RequestMsg.Data = Data; + RequestMsg.Presences = Presences; + RequestMsg.Reliable = Reliable; + + return Connection->Send(TEXT("match_data_send"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + return MakeCompletedFuture(FNakamaRtResult::Success({})); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> MatchJoin( + const TSharedPtr& Connection + , const FString& MatchId + , const FString& Token + , const TMap& Metadata +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtMatchJoin RequestMsg; + RequestMsg.MatchId = MatchId; + RequestMsg.Token = Token; + RequestMsg.Metadata = Metadata; + + return Connection->Send(TEXT("match_join"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + FNakamaRtMatch ResultData = FNakamaRtMatch::FromJson(Response.Data); + FNakamaRtResult Result + = FNakamaRtResult::Success(ResultData); + + return MakeCompletedFuture(Result); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> MatchLeave( + const TSharedPtr& Connection + , const FString& MatchId +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtMatchLeave RequestMsg; + RequestMsg.MatchId = MatchId; + + return Connection->Send(TEXT("match_leave"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + return MakeCompletedFuture(FNakamaRtResult::Success({})); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> MatchmakerAdd( + const TSharedPtr& Connection + , int32 MinCount + , int32 MaxCount + , const FString& Query + , FNakamaRtOptionalInt32 CountMultiple + , const TMap& StringProperties + , const TMap& NumericProperties +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtMatchmakerAdd RequestMsg; + RequestMsg.MinCount = MinCount; + RequestMsg.MaxCount = MaxCount; + RequestMsg.Query = Query; + RequestMsg.CountMultiple = CountMultiple; + RequestMsg.StringProperties = StringProperties; + RequestMsg.NumericProperties = NumericProperties; + + return Connection->Send(TEXT("matchmaker_add"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + FNakamaRtMatchmakerTicket ResultData = FNakamaRtMatchmakerTicket::FromJson(Response.Data); + FNakamaRtResult Result + = FNakamaRtResult::Success(ResultData); + + return MakeCompletedFuture(Result); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> MatchmakerRemove( + const TSharedPtr& Connection + , const FString& Ticket +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtMatchmakerRemove RequestMsg; + RequestMsg.Ticket = Ticket; + + return Connection->Send(TEXT("matchmaker_remove"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + return MakeCompletedFuture(FNakamaRtResult::Success({})); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> Rpc( + const TSharedPtr& Connection + , const FString& Id + , const FString& Payload + , const FString& HttpKey +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtRpc RequestMsg; + RequestMsg.Id = Id; + RequestMsg.Payload = Payload; + RequestMsg.HttpKey = HttpKey; + + return Connection->Send(TEXT("rpc"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + FNakamaRtRpc ResultData = FNakamaRtRpc::FromJson(Response.Data); + FNakamaRtResult Result + = FNakamaRtResult::Success(ResultData); + + return MakeCompletedFuture(Result); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> StatusFollow( + const TSharedPtr& Connection + , const TArray& UserIds + , const TArray& Usernames +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtStatusFollow RequestMsg; + RequestMsg.UserIds = UserIds; + RequestMsg.Usernames = Usernames; + + return Connection->Send(TEXT("status_follow"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + FNakamaRtStatus ResultData = FNakamaRtStatus::FromJson(Response.Data); + FNakamaRtResult Result + = FNakamaRtResult::Success(ResultData); + + return MakeCompletedFuture(Result); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> StatusUnfollow( + const TSharedPtr& Connection + , const TArray& UserIds +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtStatusUnfollow RequestMsg; + RequestMsg.UserIds = UserIds; + + return Connection->Send(TEXT("status_unfollow"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + return MakeCompletedFuture(FNakamaRtResult::Success({})); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> StatusUpdate( + const TSharedPtr& Connection + , const FString& Status +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtStatusUpdate RequestMsg; + RequestMsg.Status = Status; + + return Connection->Send(TEXT("status_update"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + return MakeCompletedFuture(FNakamaRtResult::Success({})); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> Ping( + const TSharedPtr& Connection +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtPing RequestMsg; + + return Connection->Send(TEXT("ping"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + FNakamaRtPong ResultData = FNakamaRtPong::FromJson(Response.Data); + FNakamaRtResult Result + = FNakamaRtResult::Success(ResultData); + + return MakeCompletedFuture(Result); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> PartyCreate( + const TSharedPtr& Connection + , bool Open + , int32 MaxSize + , const FString& Label + , bool Hidden +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtPartyCreate RequestMsg; + RequestMsg.Open = Open; + RequestMsg.MaxSize = MaxSize; + RequestMsg.Label = Label; + RequestMsg.Hidden = Hidden; + + return Connection->Send(TEXT("party_create"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + FNakamaRtParty ResultData = FNakamaRtParty::FromJson(Response.Data); + FNakamaRtResult Result + = FNakamaRtResult::Success(ResultData); + + return MakeCompletedFuture(Result); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> PartyJoin( + const TSharedPtr& Connection + , const FString& PartyId +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtPartyJoin RequestMsg; + RequestMsg.PartyId = PartyId; + + return Connection->Send(TEXT("party_join"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + return MakeCompletedFuture(FNakamaRtResult::Success({})); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> PartyLeave( + const TSharedPtr& Connection + , const FString& PartyId +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtPartyLeave RequestMsg; + RequestMsg.PartyId = PartyId; + + return Connection->Send(TEXT("party_leave"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + return MakeCompletedFuture(FNakamaRtResult::Success({})); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> PartyPromote( + const TSharedPtr& Connection + , const FString& PartyId + , const FNakamaRtUserPresence& Presence +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtPartyPromote RequestMsg; + RequestMsg.PartyId = PartyId; + RequestMsg.Presence = Presence; + + return Connection->Send(TEXT("party_promote"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + return MakeCompletedFuture(FNakamaRtResult::Success({})); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> PartyAccept( + const TSharedPtr& Connection + , const FString& PartyId + , const FNakamaRtUserPresence& Presence +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtPartyAccept RequestMsg; + RequestMsg.PartyId = PartyId; + RequestMsg.Presence = Presence; + + return Connection->Send(TEXT("party_accept"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + return MakeCompletedFuture(FNakamaRtResult::Success({})); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> PartyRemove( + const TSharedPtr& Connection + , const FString& PartyId + , const FNakamaRtUserPresence& Presence +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtPartyRemove RequestMsg; + RequestMsg.PartyId = PartyId; + RequestMsg.Presence = Presence; + + return Connection->Send(TEXT("party_remove"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + return MakeCompletedFuture(FNakamaRtResult::Success({})); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> PartyClose( + const TSharedPtr& Connection + , const FString& PartyId +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtPartyClose RequestMsg; + RequestMsg.PartyId = PartyId; + + return Connection->Send(TEXT("party_close"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + return MakeCompletedFuture(FNakamaRtResult::Success({})); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> PartyJoinRequestList( + const TSharedPtr& Connection + , const FString& PartyId +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtPartyJoinRequestList RequestMsg; + RequestMsg.PartyId = PartyId; + + return Connection->Send(TEXT("party_join_request_list"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + FNakamaRtPartyJoinRequest ResultData = FNakamaRtPartyJoinRequest::FromJson(Response.Data); + FNakamaRtResult Result + = FNakamaRtResult::Success(ResultData); + + return MakeCompletedFuture(Result); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> PartyMatchmakerAdd( + const TSharedPtr& Connection + , const FString& PartyId + , int32 MinCount + , int32 MaxCount + , const FString& Query + , FNakamaRtOptionalInt32 CountMultiple + , const TMap& StringProperties + , const TMap& NumericProperties +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtPartyMatchmakerAdd RequestMsg; + RequestMsg.PartyId = PartyId; + RequestMsg.MinCount = MinCount; + RequestMsg.MaxCount = MaxCount; + RequestMsg.Query = Query; + RequestMsg.CountMultiple = CountMultiple; + RequestMsg.StringProperties = StringProperties; + RequestMsg.NumericProperties = NumericProperties; + + return Connection->Send(TEXT("party_matchmaker_add"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + FNakamaRtPartyMatchmakerTicket ResultData = FNakamaRtPartyMatchmakerTicket::FromJson(Response.Data); + FNakamaRtResult Result + = FNakamaRtResult::Success(ResultData); + + return MakeCompletedFuture(Result); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> PartyMatchmakerRemove( + const TSharedPtr& Connection + , const FString& PartyId + , const FString& Ticket +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtPartyMatchmakerRemove RequestMsg; + RequestMsg.PartyId = PartyId; + RequestMsg.Ticket = Ticket; + + return Connection->Send(TEXT("party_matchmaker_remove"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + return MakeCompletedFuture(FNakamaRtResult::Success({})); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> PartyDataSend( + const TSharedPtr& Connection + , const FString& PartyId + , int64 OpCode + , const TArray& Data +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtPartyDataSend RequestMsg; + RequestMsg.PartyId = PartyId; + RequestMsg.OpCode = OpCode; + RequestMsg.Data = Data; + + return Connection->Send(TEXT("party_data_send"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + return MakeCompletedFuture(FNakamaRtResult::Success({})); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +TNakamaFuture> PartyUpdate( + const TSharedPtr& Connection + , const FString& PartyId + , const FString& Label + , bool Open + , bool Hidden +) +{ + if (!Connection.IsValid()) + { + FNakamaRtError Error; + Error.Code = -1; + Error.Message = TEXT("Realtime Connection is not initialized or is invalid."); + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + } + + FNakamaRtPartyUpdate RequestMsg; + RequestMsg.PartyId = PartyId; + RequestMsg.Label = Label; + RequestMsg.Open = Open; + RequestMsg.Hidden = Hidden; + + return Connection->Send(TEXT("party_update"), RequestMsg.ToJson()) + .Next([](FNakamaWebSocketResponse Response) + { + if (Response.ErrorCode == ENakamaWebSocketError::None) + { + return MakeCompletedFuture(FNakamaRtResult::Success({})); + } + + FNakamaRtError Error; + Error.Code = -1; + switch (Response.ErrorCode) + { + case ENakamaWebSocketError::ConnectionAlreadyInProgress: + Error.Message = TEXT("Connection is in progress."); + break; + case ENakamaWebSocketError::ConnectionFailed: + Error.Message = TEXT("WebSocket failed to connect."); + break; + case ENakamaWebSocketError::ConnectionAborted: + Error.Message = TEXT("WebSocket connection aborted."); + break; + case ENakamaWebSocketError::NotConnected: + Error.Message = TEXT("WebSocket is not connected."); + break; + case ENakamaWebSocketError::ConnectionClosed: + Error.Message = TEXT("WebSocket connection was closed."); + break; + case ENakamaWebSocketError::ServerError: + Error = FNakamaRtError::FromJson(Response.Data); + break; + default: + Error.Message = TEXT("Unknown error."); + break; + } + + FNakamaRtResult ErrorResult + = FNakamaRtResult::Failure(Error); + return MakeCompletedFuture(ErrorResult); + }); +} + +} + diff --git a/Nakama/Source/Nakama/Private/NakamaRtConnection.cpp b/Nakama/Source/Nakama/Private/NakamaRtConnection.cpp new file mode 100644 index 000000000..b72fdcd59 --- /dev/null +++ b/Nakama/Source/Nakama/Private/NakamaRtConnection.cpp @@ -0,0 +1,606 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NakamaRtConnection.h" +#include "WebSocketsModule.h" +#include "NakamaApi.h" +#include "GenericPlatform/GenericPlatformHttp.h" + +FNakamaRtConnection::FNakamaRtConnection() +{ + +} +FNakamaRtConnection::~FNakamaRtConnection() +{ + Close(); +} + +TNakamaFuture FNakamaRtConnection::Connect(const FNakamaWebSocketConnectionParams& Params) +{ + if (ConnectionState == ENakamaRtConnectionState::Connected) + { + return MakeCompletedFuture(FNakamaWebSocketConnectionResult { .bSuccess = true }); + } + if (ConnectionState == ENakamaRtConnectionState::ConnectionInProgress) + { + return MakeCompletedFuture(FNakamaWebSocketConnectionResult { .ErrorCode = ENakamaWebSocketError::ConnectionAlreadyInProgress }); + } + + ConnectionParams = Params; + + // + // Construct the URL + FString Url; + if (Params.bUseSSL) + { + Url = TEXT("wss://"); + } + else + { + Url = TEXT("ws://"); + } + + const FString TokenParam = TEXT("token=") + FGenericPlatformHttp::UrlEncode(Params.Token); + Url += Params.Host + TEXT(":") + FString::FromInt(Params.Port) + TEXT("/ws"); + Url += TEXT("?"); + Url += TokenParam; + + // Create the websocket + TSharedPtr ThisWebSocket = FWebSocketsModule::Get().CreateWebSocket(Url); + this->WebSocket = ThisWebSocket; + + TWeakPtr WeakSelf = AsShared(); + + // + // Connectivity + WebSocket->OnConnected().AddLambda([WeakSelf, ThisWebSocket]() + { + if (TSharedPtr StrongThis = WeakSelf.Pin()) + { + if (StrongThis->WebSocket == ThisWebSocket) StrongThis->OnConnected(); + } + }); + WebSocket->OnConnectionError().AddLambda([WeakSelf, ThisWebSocket](const FString& Error) + { + if (TSharedPtr StrongThis = WeakSelf.Pin()) + { + if (StrongThis->WebSocket == ThisWebSocket) StrongThis->OnConnectionError(Error); + } + }); + + // + // Messages + WebSocket->OnMessage().AddLambda([WeakSelf, ThisWebSocket](const FString& Message) + { + if (TSharedPtr StrongThis = WeakSelf.Pin()) + { + if (StrongThis->WebSocket == ThisWebSocket) StrongThis->OnMessage(Message); + } + }); + WebSocket->OnMessageSent().AddLambda([WeakSelf, ThisWebSocket](const FString& Message) + { + if (TSharedPtr StrongThis = WeakSelf.Pin()) + { + if (StrongThis->WebSocket == ThisWebSocket) StrongThis->OnMessageSent(Message); + } + }); + + // + // Disconnections + WebSocket->OnClosed().AddLambda([WeakSelf, ThisWebSocket](int32 StatusCode, const FString& Reason, bool bWasClean) + { + if (TSharedPtr StrongThis = WeakSelf.Pin()) + { + if (StrongThis->WebSocket == ThisWebSocket) StrongThis->OnClosed(StatusCode, Reason, bWasClean); + } + }); + + ConnectionState = ENakamaRtConnectionState::ConnectionInProgress; + ConnectionPromise = MakeShared::FState>(); + + // + // Connect + WebSocket->Connect(); + + return TNakamaFuture(ConnectionPromise); +} + +void FNakamaRtConnection::Close() +{ + if (ConnectionState == ENakamaRtConnectionState::Disconnected) + { + return; + } + if (ConnectionState == ENakamaRtConnectionState::ConnectionInProgress) + { + if (WebSocket.IsValid()) + { + WebSocket->Close(); + WebSocket.Reset(); + } + if (ConnectionPromise.IsValid()) + { + auto LocalState = MoveTemp(ConnectionPromise); + LocalState->Resolve(FNakamaWebSocketConnectionResult{ .ErrorCode = ENakamaWebSocketError::ConnectionAborted }); + } + ConnectionState = ENakamaRtConnectionState::Disconnected; + return; + } + + StopPingLoop(); + + // Eagerly resolve all pending futures so their tasks don't become zombies + // that fire UE_LOG during engine teardown. + // (The async OnClosed path is skipped by the stale-socket guard when we + // call WebSocket.Reset() before the close handshake completes.) + { + FScopeLock Lock(&RequestsLock); + for (const auto& Request : Requests) + { + Request.Value->Resolve(FNakamaWebSocketResponse{ .ErrorCode = ENakamaWebSocketError::ConnectionClosed }); + } + Requests.Empty(); + } + + if (WebSocket.IsValid()) + { + WebSocket->Close(); + WebSocket.Reset(); + } + + ConnectionState = ENakamaRtConnectionState::Disconnected; + + if (Closed.IsBound()) + { + Closed.Broadcast(1000, TEXT(""), true); + } +} + +TNakamaFuture FNakamaRtConnection::Send(const FString& RequestName, const TSharedPtr& Data) +{ + // + // Create an Envelope, embed a new guid as Cid. + const FString Cid = FGuid::NewGuid().ToString(); + TSharedPtr Envelope = MakeShareable(new FJsonObject()); + + Envelope->SetStringField(TEXT("cid"), Cid); + Envelope->SetObjectField(RequestName, Data); + + FString EnvelopeJson; + const TSharedRef> Writer = TJsonWriterFactory<>::Create(&EnvelopeJson); + FJsonSerializer::Serialize(Envelope.ToSharedRef(), Writer); + + TSharedPtr LocalWebSocket; + TSharedPtr::FState> ResponseState; + { + FScopeLock Lock(&RequestsLock); + + if (ConnectionState != ENakamaRtConnectionState::Connected || !WebSocket.IsValid()) + { + UE_LOG(LogNakama, Warning, TEXT("WebSocket is not connected or invalid.")); + return MakeCompletedFuture({ .ErrorCode = ENakamaWebSocketError::NotConnected }); + } + + // + // Create state, associate it with the Cid. + ResponseState = MakeShared::FState>(); + LocalWebSocket = WebSocket; + Requests.Add(Cid, ResponseState.ToSharedRef()); + } + + // + // Send the envelope outside the lock so WebSocket callbacks can't re-enter RequestsLock. + // Will activate the future on message received. + LocalWebSocket->Send(EnvelopeJson); + + return TNakamaFuture(ResponseState); +} + +int32 FNakamaRtConnection::GetPendingRequestCount() +{ + FScopeLock Lock(&RequestsLock); + return Requests.Num(); +} + +void FNakamaRtConnection::StartPingLoop() +{ + TWeakPtr WeakSelf = AsShared(); + + PingTimerHandle = FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([WeakSelf](float DeltaTime) + { + if (TSharedPtr StrongThis = WeakSelf.Pin()) + { + return StrongThis->SendPing(); + } + return false; + }), + ConnectionParams.PingIntervalSeconds + ); +} + +bool FNakamaRtConnection::SendPing() +{ + TSharedPtr LocalWebSocket; + { + FScopeLock Lock(&RequestsLock); + if (ConnectionState != ENakamaRtConnectionState::Connected || !WebSocket.IsValid()) + { + return false; + } + LocalWebSocket = WebSocket; + } + + TSharedPtr Envelope = MakeShareable(new FJsonObject()); + Envelope->SetObjectField(TEXT("ping"), MakeShareable(new FJsonObject())); + + FString EnvelopeJson; + const TSharedRef> Writer = TJsonWriterFactory<>::Create(&EnvelopeJson); + FJsonSerializer::Serialize(Envelope.ToSharedRef(), Writer); + + LocalWebSocket->Send(EnvelopeJson); + return true; +} + +void FNakamaRtConnection::StopPingLoop() +{ + if (PingTimerHandle.IsValid()) + { + FTSTicker::GetCoreTicker().RemoveTicker(PingTimerHandle); + PingTimerHandle.Reset(); + } +} + +void FNakamaRtConnection::OnConnected() +{ + if (ConnectionState != ENakamaRtConnectionState::ConnectionInProgress) + { + return; + } + ConnectionState = ENakamaRtConnectionState::Connected; + + UE_LOG(LogNakama, Display, TEXT("WebSocket Connected.")); + + TWeakPtr WeakSelf = AsShared(); + ServerEventReceived.AddLambda([WeakSelf](const TSharedPtr& Envelope) + { + AsyncTask(ENamedThreads::GameThread, [WeakSelf, Envelope]() + { + if (TSharedPtr StrongThis = WeakSelf.Pin()) + { + StrongThis->HandleServerEvent(Envelope); + } + }); + }); + + StartPingLoop(); + + TSharedPtr::FState> LocalState = MoveTemp(ConnectionPromise); + if (LocalState.IsValid()) + { + LocalState->Resolve(FNakamaWebSocketConnectionResult{ .bSuccess = true }); + } +} + +void FNakamaRtConnection::OnConnectionError(const FString& Error) +{ + UE_LOG(LogNakama, Warning, TEXT("WebSocket Connection Error: %s"), *Error); + + ConnectionState = ENakamaRtConnectionState::Disconnected; + + TSharedPtr::FState> LocalState = MoveTemp(ConnectionPromise); + if (LocalState.IsValid()) + { + LocalState->Resolve(FNakamaWebSocketConnectionResult{ .ErrorCode = ENakamaWebSocketError::ConnectionFailed }); + } +} + +void FNakamaRtConnection::OnMessage(const FString& Message) +{ + // + // Try parse the message as JSON. + TSharedPtr JsonObject; + TSharedRef> JsonReader = TJsonReaderFactory<>::Create(Message); + if (!FJsonSerializer::Deserialize(JsonReader, JsonObject)) + { + if (MessageError.IsBound()) + { + MessageError.Broadcast(EWebSocketMessageError::WS_ERROR_MESSAGE_MALFORMED, TEXT("Failed to deserialize JSON.")); + } + return; + } + + // + // Log message (unless it's a pong). + const bool bIsPong = JsonObject->HasField(TEXT("pong")); + if (!bIsPong) + { + UE_LOG(LogNakama, Verbose, TEXT("WebSocket Message Received: %s"), *Message); + } + + // + // Try get the Cid from the message before anything else. + // Error responses from the server also carry a Cid. + FString Cid; + JsonObject->TryGetStringField(TEXT("cid"), Cid); + + // + // Handle possible error. + if (JsonObject->HasField(TEXT("error"))) + { + // If there is a pending request for this Cid, resolve it with an error + // so callers are not left waiting forever. + if (!Cid.IsEmpty()) + { + FScopeLock Lock(&RequestsLock); + if (auto* RequestPtr = Requests.Find(Cid)) + { + TSharedRef::FState> Request = *RequestPtr; + const TSharedPtr* ErrorObj; + TSharedPtr ErrorData; + if (JsonObject->TryGetObjectField(TEXT("error"), ErrorObj)) + { + ErrorData = *ErrorObj; + } + Request->Resolve(FNakamaWebSocketResponse{ .Data = ErrorData, .ErrorCode = ENakamaWebSocketError::ServerError }); + Requests.Remove(Cid); + } + } + + if (MessageError.IsBound()) + { + const TSharedPtr* ErrorObj; + FString ErrorMsg; + if (JsonObject->TryGetObjectField(TEXT("error"), ErrorObj)) + { + (*ErrorObj)->TryGetStringField(TEXT("message"), ErrorMsg); + } + MessageError.Broadcast(EWebSocketMessageError::WS_ERROR_MESSAGE_HASERROR, ErrorMsg); + } + return; + } + + // + // If we have a Cid, this is a response to a request. + if (!Cid.IsEmpty()) + { + FScopeLock Lock(&RequestsLock); + + // We should have a pending request for this CID. + if (auto* RequestPtr = Requests.Find(Cid)) + { + // The envelope has exactly one field besides "cid" (the oneof payload). + // Extract it so callers receive the payload directly, not the envelope wrapper. + TSharedPtr Payload; + for (const auto& Field : JsonObject->Values) + { + if (Field.Key != TEXT("cid")) + { + const TSharedPtr* ObjPtr; + if (Field.Value->TryGetObject(ObjPtr)) + { + Payload = *ObjPtr; + } + break; + } + } + + TSharedRef::FState> Request = *RequestPtr; + Request->Resolve(FNakamaWebSocketResponse{ .Data = Payload }); + + Requests.Remove(Cid); + } + else + { + UE_LOG(LogNakama, Warning, TEXT("No matching request for CID %s"), *Cid); + if (MessageError.IsBound()) + { + MessageError.Broadcast(EWebSocketMessageError::WS_ERROR_RESPONSE_NOCID, TEXT("No matching request for CID")); + } + } + + if (ServerResponseReceived.IsBound() && !bIsPong) + { + ServerResponseReceived.Broadcast(Message); + } + } + // Otherwise, this is a server event we need to handle. + else + { + if (ServerEventReceived.IsBound()) + { + ServerEventReceived.Broadcast(JsonObject); + } + } +} + +void FNakamaRtConnection::OnMessageSent(const FString& Message) +{ + UE_LOG(LogNakama, Verbose, TEXT("Message Sent: %s"), *Message); + + if (MessageSent.IsBound()) + { + MessageSent.Broadcast(Message); + } +} + +void FNakamaRtConnection::OnClosed(int32 StatusCode, const FString& Reason, bool bWasClean) +{ + if (ConnectionState == ENakamaRtConnectionState::Disconnected) + { + return; + } + ConnectionState = ENakamaRtConnectionState::Disconnected; + StopPingLoop(); + + if (bWasClean) + { + UE_LOG( + LogNakama, + Display, + TEXT("WebSocket closed cleanly with status code: %d"), + StatusCode); + } + else + { + UE_LOG( + LogNakama, + Warning, + TEXT("Web Socket closed non-cleanly with status code: %d. Reason: %s."), + StatusCode, + *Reason); + } + + { + FScopeLock Lock(&RequestsLock); + + for (const auto& Request : Requests) + { + Request.Value->Resolve(FNakamaWebSocketResponse{ .ErrorCode = ENakamaWebSocketError::ConnectionClosed }); + } + Requests.Empty(); + } + + if (Closed.IsBound()) + { + Closed.Broadcast(StatusCode, Reason, bWasClean); + } +}; + +void FNakamaRtConnection::HandleServerEvent(const TSharedPtr& Envelope) +{ + if (!Envelope.IsValid()) + { + return; + } + if (Envelope->HasField(TEXT("channel_message"))) + { + if (ChannelMessage.IsBound()) + { + const FNakamaRtChannelMessage Data = FNakamaRtChannelMessage::FromJson(Envelope->GetObjectField(TEXT("channel_message"))); + ChannelMessage.Broadcast(Data); + } + return; + } + if (Envelope->HasField(TEXT("channel_presence_event"))) + { + if (ChannelPresenceEvent.IsBound()) + { + const FNakamaRtChannelPresenceEvent Data = FNakamaRtChannelPresenceEvent::FromJson(Envelope->GetObjectField(TEXT("channel_presence_event"))); + ChannelPresenceEvent.Broadcast(Data); + } + return; + } + if (Envelope->HasField(TEXT("match_data"))) + { + if (MatchData.IsBound()) + { + const FNakamaRtMatchData Data = FNakamaRtMatchData::FromJson(Envelope->GetObjectField(TEXT("match_data"))); + MatchData.Broadcast(Data); + } + return; + } + if (Envelope->HasField(TEXT("match_presence_event"))) + { + if (MatchPresenceEvent.IsBound()) + { + const FNakamaRtMatchPresenceEvent Data = FNakamaRtMatchPresenceEvent::FromJson(Envelope->GetObjectField(TEXT("match_presence_event"))); + MatchPresenceEvent.Broadcast(Data); + } + return; + } + if (Envelope->HasField(TEXT("matchmaker_matched"))) + { + if (MatchmakerMatched.IsBound()) + { + const FNakamaRtMatchmakerMatched Data = FNakamaRtMatchmakerMatched::FromJson(Envelope->GetObjectField(TEXT("matchmaker_matched"))); + MatchmakerMatched.Broadcast(Data); + } + return; + } + if (Envelope->HasField(TEXT("notifications"))) + { + if (Notifications.IsBound()) + { + const FNakamaRtNotifications Data = FNakamaRtNotifications::FromJson(Envelope->GetObjectField(TEXT("notifications"))); + Notifications.Broadcast(Data); + } + return; + } + if (Envelope->HasField(TEXT("party_leader"))) + { + if (PartyLeader.IsBound()) + { + const FNakamaRtPartyLeader Data = FNakamaRtPartyLeader::FromJson(Envelope->GetObjectField(TEXT("party_leader"))); + PartyLeader.Broadcast(Data); + } + return; + } + if (Envelope->HasField(TEXT("party_join_request"))) + { + if (PartyJoinRequest.IsBound()) + { + const FNakamaRtPartyJoinRequest Data = FNakamaRtPartyJoinRequest::FromJson(Envelope->GetObjectField(TEXT("party_join_request"))); + PartyJoinRequest.Broadcast(Data); + } + return; + } + if (Envelope->HasField(TEXT("party_data"))) + { + if (PartyData.IsBound()) + { + const FNakamaRtPartyData Data = FNakamaRtPartyData::FromJson(Envelope->GetObjectField(TEXT("party_data"))); + PartyData.Broadcast(Data); + } + return; + } + if (Envelope->HasField(TEXT("party_presence_event"))) + { + if (PartyPresenceEvent.IsBound()) + { + const FNakamaRtPartyPresenceEvent Data = FNakamaRtPartyPresenceEvent::FromJson(Envelope->GetObjectField(TEXT("party_presence_event"))); + PartyPresenceEvent.Broadcast(Data); + } + return; + } + if (Envelope->HasField(TEXT("status_presence_event"))) + { + if (StatusPresenceEvent.IsBound()) + { + const FNakamaRtStatusPresenceEvent Data = FNakamaRtStatusPresenceEvent::FromJson(Envelope->GetObjectField(TEXT("status_presence_event"))); + StatusPresenceEvent.Broadcast(Data); + } + return; + } + if (Envelope->HasField(TEXT("stream_data"))) + { + if (StreamData.IsBound()) + { + const FNakamaRtStreamData Data = FNakamaRtStreamData::FromJson(Envelope->GetObjectField(TEXT("stream_data"))); + StreamData.Broadcast(Data); + } + return; + } + if (Envelope->HasField(TEXT("stream_presence_event"))) + { + if (StreamPresenceEvent.IsBound()) + { + const FNakamaRtStreamPresenceEvent Data = FNakamaRtStreamPresenceEvent::FromJson(Envelope->GetObjectField(TEXT("stream_presence_event"))); + StreamPresenceEvent.Broadcast(Data); + } + return; + } +} diff --git a/Nakama/Source/Nakama/Private/NakamaSession.cpp b/Nakama/Source/Nakama/Private/NakamaSession.cpp new file mode 100644 index 000000000..f3c8a3eb0 --- /dev/null +++ b/Nakama/Source/Nakama/Private/NakamaSession.cpp @@ -0,0 +1,166 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NakamaSession.h" + +bool FNakamaSession::ParseJwtPayload(const FString& Jwt, TSharedPtr& Out) noexcept +{ + TArray Parts; + Jwt.ParseIntoArray(Parts, TEXT(".")); + if (Parts.Num() < 2) + { + return false; + } + + // Base64url -> standard Base64 + FString Payload = Parts[1]; + Payload.ReplaceInline(TEXT("-"), TEXT("+")); + Payload.ReplaceInline(TEXT("_"), TEXT("/")); + + // Pad to multiple of 4 + while (Payload.Len() % 4 != 0) + { + Payload += TEXT("="); + } + + TArray DecodedBytes; + if (!FBase64::Decode(Payload, DecodedBytes)) + { + return false; + } + + const FUTF8ToTCHAR Converter(reinterpret_cast(DecodedBytes.GetData()), DecodedBytes.Num()); + FString JsonString(Converter.Length(), Converter.Get()); + + TSharedRef> Reader = TJsonReaderFactory<>::Create(JsonString); + return FJsonSerializer::Deserialize(Reader, Out) && Out.IsValid(); +} + +void FNakamaSession::ParseTokens() noexcept +{ + UserId.Empty(); + Username.Empty(); + TokenExpiresAt = 0; + TokenIssuedAt = 0; + RefreshTokenExpiresAt = 0; + Vars.Empty(); + + // Parse auth token + TSharedPtr TokenPayload; + if (!Token.IsEmpty() && ParseJwtPayload(Token, TokenPayload)) + { + if (TokenPayload->HasField(TEXT("uid"))) + { + UserId = TokenPayload->GetStringField(TEXT("uid")); + } + if (TokenPayload->HasField(TEXT("usn"))) + { + Username = TokenPayload->GetStringField(TEXT("usn")); + } + if (TokenPayload->HasField(TEXT("exp"))) + { + TokenExpiresAt = static_cast(TokenPayload->GetNumberField(TEXT("exp"))); + } + if (TokenPayload->HasField(TEXT("iat"))) + { + TokenIssuedAt = static_cast(TokenPayload->GetNumberField(TEXT("iat"))); + } + if (TokenPayload->HasField(TEXT("vrs"))) + { + const TSharedPtr* VrsObj; + if (TokenPayload->TryGetObjectField(TEXT("vrs"), VrsObj)) + { + for (const auto& Pair : (*VrsObj)->Values) + { + Vars.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + } + + // Parse refresh token + TSharedPtr RefreshPayload; + if (!RefreshToken.IsEmpty() && ParseJwtPayload(RefreshToken, RefreshPayload)) + { + if (RefreshPayload->HasField(TEXT("exp"))) + { + RefreshTokenExpiresAt = static_cast(RefreshPayload->GetNumberField(TEXT("exp"))); + } + } +} + +bool FNakamaSession::IsExpired(int64 BufferSeconds) const noexcept +{ + if (TokenExpiresAt == 0) + { + return true; + } + return (TokenExpiresAt - BufferSeconds) <= FDateTime::UtcNow().ToUnixTimestamp(); +} + +bool FNakamaSession::IsRefreshExpired(int64 BufferSeconds) const noexcept +{ + if (RefreshTokenExpiresAt == 0) + { + return true; + } + return (RefreshTokenExpiresAt - BufferSeconds) <= FDateTime::UtcNow().ToUnixTimestamp(); +} + +void FNakamaSession::Update(const FString& NewToken, const FString& NewRefreshToken) noexcept +{ + Token = NewToken; + RefreshToken = NewRefreshToken; + ParseTokens(); +} + +FNakamaSession FNakamaSession::FromJson(const TSharedPtr& Json) noexcept +{ + FNakamaSession Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("created"))) + { + Result.Created = Json->GetBoolField(TEXT("created")); + } + if (Json->HasField(TEXT("token"))) + { + Result.Token = Json->GetStringField(TEXT("token")); + } + if (Json->HasField(TEXT("refresh_token"))) + { + Result.RefreshToken = Json->GetStringField(TEXT("refresh_token")); + } + Result.ParseTokens(); + return Result; +} + +TSharedPtr FNakamaSession::ToJson() const noexcept +{ + TSharedPtr Json = MakeShared(); + Json->SetBoolField(TEXT("created"), Created); + if (!Token.IsEmpty()) + { + Json->SetStringField(TEXT("token"), Token); + } + if (!RefreshToken.IsEmpty()) + { + Json->SetStringField(TEXT("refresh_token"), RefreshToken); + } + return Json; +} diff --git a/Nakama/Source/Nakama/Public/AsyncFuture.h b/Nakama/Source/Nakama/Public/AsyncFuture.h new file mode 100644 index 000000000..2e7976f96 --- /dev/null +++ b/Nakama/Source/Nakama/Public/AsyncFuture.h @@ -0,0 +1,198 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "CoreMinimal.h" +#include "Tasks/Task.h" +#include "Async/TaskGraphInterfaces.h" +#include + +// Forward declaration +template struct TAsyncFuture; + +/** Type trait: is T a TAsyncFuture? */ +template struct TIsTAsyncFuture : std::false_type {}; +template struct TIsTAsyncFuture> : std::true_type {}; + +/** + * Chainable, game-thread-safe future for async operations. + * + * Parameterized by a concrete result type (e.g. FNakamaSessionResult or + * FSatoriSessionResult). Every user-visible callback is dispatched to the + * game thread via AsyncTask(ENamedThreads::GameThread, ...) so that callers + * can safely touch UObject*, fire delegates, or update UI without additional + * marshalling. + * + * Three .Next() overloads are provided: + * + * 1. Chaining with auto-propagation: + * Next(callback(const ValueType&) -> TAsyncFuture) + * Available only when ResultT defines ::ValueType. + * On error the error is forwarded to the outer future and the callback is + * skipped entirely (error propagation runs on the background thread because + * no user code is involved). + * + * 2. Chaining without auto-propagation: + * Next(callback(ResultT) -> TAsyncFuture) + * Available for all ResultT, including types without ::ValueType (e.g. + * WebSocket envelope types). The callback receives the full result and is + * responsible for inspecting bIsError and deciding what to return. + * + * 3. Terminal: + * Next(callback(ResultT) -> void) + * Called unconditionally when the future resolves. The caller inspects + * the result for errors inside the callback. + */ +template +struct TAsyncFuture +{ + using WrappedResultType = ResultT; + + struct FState + { + ResultT Result{}; + UE::Tasks::FTaskEvent Event{ UE_SOURCE_LOCATION }; + void Resolve(ResultT&& InResult) + { + Result = MoveTemp(InResult); + Event.Trigger(); + } + }; + + TSharedPtr State; + + TAsyncFuture() = default; + explicit TAsyncFuture(TSharedPtr InState) noexcept + : State(MoveTemp(InState)) {} + TAsyncFuture(TAsyncFuture&& Other) noexcept = default; + TAsyncFuture& operator=(TAsyncFuture&& Other) noexcept = default; + TAsyncFuture(const TAsyncFuture&) = delete; + TAsyncFuture& operator=(const TAsyncFuture&) = delete; + + /** + * Overload 1 — Chaining with auto-propagation. + * callback(const ValueType&) -> TAsyncFuture + * Only enabled when ResultT has ::ValueType. + * On error, propagates to OtherResult without calling the callback. + */ + template, const VT&>, + std::enable_if_t::value, int> = 0> + Ret Next(Func&& Callback) && noexcept + { + using InnerResultT = typename Ret::WrappedResultType; + auto OuterState = MakeShared::FState>(); + auto CapturedState = State; + UE::Tasks::Launch(UE_SOURCE_LOCATION, + [Cb = Forward(Callback), CapturedState, OuterState]() mutable + { + if (CapturedState->Result.bIsError) + { + // Error propagation: no user code involved, safe on any thread. + OuterState->Resolve(InnerResultT{{}, CapturedState->Result.Error, true}); + return; + } + // User callback may touch UObject* or fire delegates — dispatch to game thread. + AsyncTask(ENamedThreads::GameThread, + [Cb = MoveTemp(Cb), CapturedState, OuterState]() mutable + { + Ret Inner = Cb(CapturedState->Result.Value); + auto InnerState = Inner.State; + UE::Tasks::Launch(UE_SOURCE_LOCATION, + [InnerState, OuterState]() + { + OuterState->Resolve(MoveTemp(InnerState->Result)); + }, + InnerState->Event); + }); + }, + CapturedState->Event); + State.Reset(); + return TAsyncFuture(OuterState); + } + + /** + * Overload 2 — Chaining, user handles errors. + * callback(ResultT) -> TAsyncFuture + * Available for all ResultT, including WebSocket types without ::ValueType. + * The callback receives the full result and is responsible for error handling. + */ + template, ResultT>, + std::enable_if_t::value, int> = 0> + Ret Next(Func&& Callback) && noexcept + { + using InnerResultT = typename Ret::WrappedResultType; + auto OuterState = MakeShared::FState>(); + auto CapturedState = State; + UE::Tasks::Launch(UE_SOURCE_LOCATION, + [Cb = Forward(Callback), CapturedState, OuterState]() mutable + { + // User callback may touch UObject* or fire delegates — dispatch to game thread. + AsyncTask(ENamedThreads::GameThread, + [Cb = MoveTemp(Cb), CapturedState, OuterState]() mutable + { + Ret Inner = Cb(MoveTemp(CapturedState->Result)); + auto InnerState = Inner.State; + UE::Tasks::Launch(UE_SOURCE_LOCATION, + [InnerState, OuterState]() + { + OuterState->Resolve(MoveTemp(InnerState->Result)); + }, + InnerState->Event); + }); + }, + CapturedState->Event); + State.Reset(); + return TAsyncFuture(OuterState); + } + + /** Overload 3 — Terminal. callback(ResultT) -> void */ + template, ResultT>, + std::enable_if_t::value, int> = 0> + void Next(Func&& Callback) && noexcept + { + auto CapturedState = State; + UE::Tasks::Launch(UE_SOURCE_LOCATION, + [Cb = Forward(Callback), CapturedState]() mutable + { + // User callback may touch UObject* or fire delegates — dispatch to game thread. + AsyncTask(ENamedThreads::GameThread, + [Cb = MoveTemp(Cb), CapturedState]() mutable + { + Cb(MoveTemp(CapturedState->Result)); + }); + }, + CapturedState->Event); + State.Reset(); + } +}; + +/** + * Create a pre-resolved TAsyncFuture. + * Useful for returning an immediate result (e.g. a local error) from a + * chaining callback without going through the network stack. + */ +template +TAsyncFuture MakeCompletedAsyncFuture(ResultT Value) +{ + auto State = MakeShared::FState>(); + State->Resolve(MoveTemp(Value)); + return TAsyncFuture(State); +} diff --git a/Nakama/Source/Nakama/Public/Nakama.gen.h b/Nakama/Source/Nakama/Public/Nakama.gen.h new file mode 100644 index 000000000..a1a0e8ab3 --- /dev/null +++ b/Nakama/Source/Nakama/Public/Nakama.gen.h @@ -0,0 +1,3136 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + + + +#pragma once + +#include "CoreMinimal.h" +#include "Nakama.gen.generated.h" + + + +UENUM(BlueprintType) +enum class ENakamaFriend_State : uint8 +{ + + FRIEND = 0, + INVITE_SENT = 1, + INVITE_RECEIVED = 2, + BLOCKED = 3, +}; +UENUM(BlueprintType) +enum class ENakamaGroupUser_State : uint8 +{ + + SUPERADMIN = 0, + ADMIN = 1, + MEMBER = 2, + JOIN_REQUEST = 3, +}; +UENUM(BlueprintType) +enum class ENakamaUserGroup_State : uint8 +{ + + SUPERADMIN = 0, + ADMIN = 1, + MEMBER = 2, + JOIN_REQUEST = 3, +}; +UENUM(BlueprintType) +enum class ENakamaStoreProvider : uint8 +{ + + APPLE_APP_STORE = 0, + GOOGLE_PLAY_STORE = 1, + HUAWEI_APP_GALLERY = 2, + FACEBOOK_INSTANT_STORE = 3, +}; +UENUM(BlueprintType) +enum class ENakamaStoreEnvironment : uint8 +{ + + UNKNOWN = 0, + SANDBOX = 1, + PRODUCTION = 2, +}; +UENUM(BlueprintType) +enum class ENakamaOperator : uint8 +{ + + NO_OVERRIDE = 0, + BEST = 1, + SET = 2, + INCREMENT = 3, + DECREMENT = 4, +}; + + + + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaAccount +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "user")) + User_TODO User; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "wallet")) + string_TODO Wallet; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "email")) + string_TODO Email; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "devices")) + TArray Devices; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "custom_id")) + string_TODO CustomId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "verify_time")) + Timestamp_TODO VerifyTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "disable_time")) + Timestamp_TODO DisableTime; + + static FNakamaAccount FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaAccountRefresh +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "token")) + string_TODO Token; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "vars")) + TMap Vars; + + static FNakamaAccountRefresh FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaAccountApple +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "token")) + string_TODO Token; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "vars")) + TMap Vars; + + static FNakamaAccountApple FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaAccountCustom +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "id")) + string_TODO Id; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "vars")) + TMap Vars; + + static FNakamaAccountCustom FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaAccountDevice +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "id")) + string_TODO Id; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "vars")) + TMap Vars; + + static FNakamaAccountDevice FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaAccountEmail +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "email")) + string_TODO Email; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "password")) + string_TODO Password; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "vars")) + TMap Vars; + + static FNakamaAccountEmail FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaAccountFacebook +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "token")) + string_TODO Token; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "vars")) + TMap Vars; + + static FNakamaAccountFacebook FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaAccountFacebookInstantGame +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "signed_player_info")) + string_TODO SignedPlayerInfo; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "vars")) + TMap Vars; + + static FNakamaAccountFacebookInstantGame FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaAccountGameCenter +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "player_id")) + string_TODO PlayerId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "bundle_id")) + string_TODO BundleId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "timestamp_seconds")) + int64_TODO TimestampSeconds; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "salt")) + string_TODO Salt; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "signature")) + string_TODO Signature; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "public_key_url")) + string_TODO PublicKeyUrl; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "vars")) + TMap Vars; + + static FNakamaAccountGameCenter FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaAccountGoogle +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "token")) + string_TODO Token; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "vars")) + TMap Vars; + + static FNakamaAccountGoogle FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaAccountSteam +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "token")) + string_TODO Token; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "vars")) + TMap Vars; + + static FNakamaAccountSteam FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaAddFriendsRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "ids")) + TArray Ids; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "usernames")) + TArray Usernames; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "metadata")) + string_TODO Metadata; + + static FNakamaAddFriendsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaAddGroupUsersRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "group_id")) + string_TODO GroupId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "user_ids")) + TArray UserIds; + + static FNakamaAddGroupUsersRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaSessionRefreshRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "token")) + string_TODO Token; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "vars")) + TMap Vars; + + static FNakamaSessionRefreshRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaSessionLogoutRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "token")) + string_TODO Token; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "refresh_token")) + string_TODO RefreshToken; + + static FNakamaSessionLogoutRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaAuthenticateAppleRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "account")) + AccountApple_TODO Account; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "create")) + BoolValue_TODO Create; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "username")) + string_TODO Username; + + static FNakamaAuthenticateAppleRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaAuthenticateCustomRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "account")) + AccountCustom_TODO Account; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "create")) + BoolValue_TODO Create; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "username")) + string_TODO Username; + + static FNakamaAuthenticateCustomRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaAuthenticateDeviceRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "account")) + AccountDevice_TODO Account; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "create")) + BoolValue_TODO Create; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "username")) + string_TODO Username; + + static FNakamaAuthenticateDeviceRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaAuthenticateEmailRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "account")) + AccountEmail_TODO Account; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "create")) + BoolValue_TODO Create; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "username")) + string_TODO Username; + + static FNakamaAuthenticateEmailRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaAuthenticateFacebookRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "account")) + AccountFacebook_TODO Account; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "create")) + BoolValue_TODO Create; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "username")) + string_TODO Username; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "sync")) + BoolValue_TODO Sync; + + static FNakamaAuthenticateFacebookRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaAuthenticateFacebookInstantGameRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "account")) + AccountFacebookInstantGame_TODO Account; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "create")) + BoolValue_TODO Create; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "username")) + string_TODO Username; + + static FNakamaAuthenticateFacebookInstantGameRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaAuthenticateGameCenterRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "account")) + AccountGameCenter_TODO Account; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "create")) + BoolValue_TODO Create; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "username")) + string_TODO Username; + + static FNakamaAuthenticateGameCenterRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaAuthenticateGoogleRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "account")) + AccountGoogle_TODO Account; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "create")) + BoolValue_TODO Create; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "username")) + string_TODO Username; + + static FNakamaAuthenticateGoogleRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaAuthenticateSteamRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "account")) + AccountSteam_TODO Account; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "create")) + BoolValue_TODO Create; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "username")) + string_TODO Username; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "sync")) + BoolValue_TODO Sync; + + static FNakamaAuthenticateSteamRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaBanGroupUsersRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "group_id")) + string_TODO GroupId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "user_ids")) + TArray UserIds; + + static FNakamaBanGroupUsersRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaBlockFriendsRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "ids")) + TArray Ids; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "usernames")) + TArray Usernames; + + static FNakamaBlockFriendsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaChannelMessage +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "channel_id")) + string_TODO ChannelId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "message_id")) + string_TODO MessageId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "code")) + Int32Value_TODO Code; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "sender_id")) + string_TODO SenderId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "username")) + string_TODO Username; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "content")) + string_TODO Content; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "create_time")) + Timestamp_TODO CreateTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "update_time")) + Timestamp_TODO UpdateTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "persistent")) + BoolValue_TODO Persistent; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "room_name")) + string_TODO RoomName; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "group_id")) + string_TODO GroupId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "user_id_one")) + string_TODO UserIdOne; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "user_id_two")) + string_TODO UserIdTwo; + + static FNakamaChannelMessage FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaChannelMessageList +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "messages")) + TArray Messages; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "next_cursor")) + string_TODO NextCursor; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "prev_cursor")) + string_TODO PrevCursor; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cacheable_cursor")) + string_TODO CacheableCursor; + + static FNakamaChannelMessageList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaCreateGroupRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "name")) + string_TODO Name; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "description")) + string_TODO Description; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "lang_tag")) + string_TODO LangTag; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "avatar_url")) + string_TODO AvatarUrl; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "open")) + bool_TODO Open; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "max_count")) + int32_TODO MaxCount; + + static FNakamaCreateGroupRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaDeleteFriendsRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "ids")) + TArray Ids; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "usernames")) + TArray Usernames; + + static FNakamaDeleteFriendsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaDeleteGroupRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "group_id")) + string_TODO GroupId; + + static FNakamaDeleteGroupRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaDeleteLeaderboardRecordRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "leaderboard_id")) + string_TODO LeaderboardId; + + static FNakamaDeleteLeaderboardRecordRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaDeleteNotificationsRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "ids")) + TArray Ids; + + static FNakamaDeleteNotificationsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaDeleteTournamentRecordRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "tournament_id")) + string_TODO TournamentId; + + static FNakamaDeleteTournamentRecordRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaDeleteStorageObjectId +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "collection")) + string_TODO Collection; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "key")) + string_TODO Key; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "version")) + string_TODO Version; + + static FNakamaDeleteStorageObjectId FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaDeleteStorageObjectsRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "object_ids")) + TArray ObjectIds; + + static FNakamaDeleteStorageObjectsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaEvent +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "name")) + string_TODO Name; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "timestamp")) + Timestamp_TODO Timestamp; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "external")) + bool_TODO External; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "properties")) + TMap Properties; + + static FNakamaEvent FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaFriend +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "user")) + User_TODO User; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "state")) + Int32Value_TODO State; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "update_time")) + Timestamp_TODO UpdateTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "metadata")) + string_TODO Metadata; + + static FNakamaFriend FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaFriendList +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "friends")) + TArray Friends; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cursor")) + string_TODO Cursor; + + static FNakamaFriendList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaFriendsOfFriendsList +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "friends_of_friends")) + TArray FriendsOfFriends; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cursor")) + string_TODO Cursor; + + static FNakamaFriendsOfFriendsList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaFriendOfFriend +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "referrer")) + string_TODO Referrer; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "user")) + User_TODO User; + + static FNakamaFriendOfFriend FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaGetUsersRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "ids")) + TArray Ids; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "usernames")) + TArray Usernames; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "facebook_ids")) + TArray FacebookIds; + + static FNakamaGetUsersRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaGetSubscriptionRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "product_id")) + string_TODO ProductId; + + static FNakamaGetSubscriptionRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaGroup +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "id")) + string_TODO Id; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "creator_id")) + string_TODO CreatorId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "name")) + string_TODO Name; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "description")) + string_TODO Description; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "lang_tag")) + string_TODO LangTag; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "metadata")) + string_TODO Metadata; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "avatar_url")) + string_TODO AvatarUrl; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "open")) + BoolValue_TODO Open; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "edge_count")) + int32_TODO EdgeCount; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "max_count")) + int32_TODO MaxCount; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "create_time")) + Timestamp_TODO CreateTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "update_time")) + Timestamp_TODO UpdateTime; + + static FNakamaGroup FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaGroupList +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "groups")) + TArray Groups; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cursor")) + string_TODO Cursor; + + static FNakamaGroupList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaGroupUserList +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "group_users")) + TArray GroupUsers; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cursor")) + string_TODO Cursor; + + static FNakamaGroupUserList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaGroupUser +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "user")) + User_TODO User; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "state")) + Int32Value_TODO State; + + static FNakamaGroupUser FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaImportFacebookFriendsRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "account")) + AccountFacebook_TODO Account; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "reset")) + BoolValue_TODO Reset; + + static FNakamaImportFacebookFriendsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaImportSteamFriendsRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "account")) + AccountSteam_TODO Account; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "reset")) + BoolValue_TODO Reset; + + static FNakamaImportSteamFriendsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaJoinGroupRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "group_id")) + string_TODO GroupId; + + static FNakamaJoinGroupRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaJoinTournamentRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "tournament_id")) + string_TODO TournamentId; + + static FNakamaJoinTournamentRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaKickGroupUsersRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "group_id")) + string_TODO GroupId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "user_ids")) + TArray UserIds; + + static FNakamaKickGroupUsersRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaLeaderboard +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "id")) + string_TODO Id; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "sort_order")) + uint32_TODO SortOrder; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "operator")) + Operator_TODO Operator; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "prev_reset")) + uint32_TODO PrevReset; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "next_reset")) + uint32_TODO NextReset; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "metadata")) + string_TODO Metadata; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "create_time")) + Timestamp_TODO CreateTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "authoritative")) + bool_TODO Authoritative; + + static FNakamaLeaderboard FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaLeaderboardList +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "leaderboards")) + TArray Leaderboards; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cursor")) + string_TODO Cursor; + + static FNakamaLeaderboardList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaLeaderboardRecord +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "leaderboard_id")) + string_TODO LeaderboardId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "owner_id")) + string_TODO OwnerId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "username")) + StringValue_TODO Username; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "score")) + int64_TODO Score; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "subscore")) + int64_TODO Subscore; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "num_score")) + int32_TODO NumScore; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "metadata")) + string_TODO Metadata; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "create_time")) + Timestamp_TODO CreateTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "update_time")) + Timestamp_TODO UpdateTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "expiry_time")) + Timestamp_TODO ExpiryTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "rank")) + int64_TODO Rank; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "max_num_score")) + uint32_TODO MaxNumScore; + + static FNakamaLeaderboardRecord FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaLeaderboardRecordList +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "records")) + TArray Records; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "owner_records")) + TArray OwnerRecords; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "next_cursor")) + string_TODO NextCursor; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "prev_cursor")) + string_TODO PrevCursor; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "rank_count")) + int64_TODO RankCount; + + static FNakamaLeaderboardRecordList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaLeaveGroupRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "group_id")) + string_TODO GroupId; + + static FNakamaLeaveGroupRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaLinkFacebookRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "account")) + AccountFacebook_TODO Account; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "sync")) + BoolValue_TODO Sync; + + static FNakamaLinkFacebookRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaLinkSteamRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "account")) + AccountSteam_TODO Account; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "sync")) + BoolValue_TODO Sync; + + static FNakamaLinkSteamRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaListChannelMessagesRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "channel_id")) + string_TODO ChannelId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "limit")) + Int32Value_TODO Limit; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "forward")) + BoolValue_TODO Forward; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cursor")) + string_TODO Cursor; + + static FNakamaListChannelMessagesRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaListFriendsRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "limit")) + Int32Value_TODO Limit; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "state")) + Int32Value_TODO State; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cursor")) + string_TODO Cursor; + + static FNakamaListFriendsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaListFriendsOfFriendsRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "limit")) + Int32Value_TODO Limit; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cursor")) + string_TODO Cursor; + + static FNakamaListFriendsOfFriendsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaListGroupsRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "name")) + string_TODO Name; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cursor")) + string_TODO Cursor; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "limit")) + Int32Value_TODO Limit; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "lang_tag")) + string_TODO LangTag; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "members")) + Int32Value_TODO Members; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "open")) + BoolValue_TODO Open; + + static FNakamaListGroupsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaListGroupUsersRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "group_id")) + string_TODO GroupId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "limit")) + Int32Value_TODO Limit; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "state")) + Int32Value_TODO State; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cursor")) + string_TODO Cursor; + + static FNakamaListGroupUsersRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaListLeaderboardRecordsAroundOwnerRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "leaderboard_id")) + string_TODO LeaderboardId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "limit")) + UInt32Value_TODO Limit; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "owner_id")) + string_TODO OwnerId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "expiry")) + Int64Value_TODO Expiry; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cursor")) + string_TODO Cursor; + + static FNakamaListLeaderboardRecordsAroundOwnerRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaListLeaderboardRecordsRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "leaderboard_id")) + string_TODO LeaderboardId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "owner_ids")) + TArray OwnerIds; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "limit")) + Int32Value_TODO Limit; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cursor")) + string_TODO Cursor; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "expiry")) + Int64Value_TODO Expiry; + + static FNakamaListLeaderboardRecordsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaListMatchesRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "limit")) + Int32Value_TODO Limit; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "authoritative")) + BoolValue_TODO Authoritative; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "label")) + StringValue_TODO Label; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "min_size")) + Int32Value_TODO MinSize; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "max_size")) + Int32Value_TODO MaxSize; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "query")) + StringValue_TODO Query; + + static FNakamaListMatchesRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaListNotificationsRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "limit")) + Int32Value_TODO Limit; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cacheable_cursor")) + string_TODO CacheableCursor; + + static FNakamaListNotificationsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaListStorageObjectsRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "user_id")) + string_TODO UserId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "collection")) + string_TODO Collection; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "limit")) + Int32Value_TODO Limit; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cursor")) + string_TODO Cursor; + + static FNakamaListStorageObjectsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaListSubscriptionsRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "limit")) + Int32Value_TODO Limit; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cursor")) + string_TODO Cursor; + + static FNakamaListSubscriptionsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaListTournamentRecordsAroundOwnerRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "tournament_id")) + string_TODO TournamentId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "limit")) + UInt32Value_TODO Limit; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "owner_id")) + string_TODO OwnerId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "expiry")) + Int64Value_TODO Expiry; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cursor")) + string_TODO Cursor; + + static FNakamaListTournamentRecordsAroundOwnerRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaListTournamentRecordsRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "tournament_id")) + string_TODO TournamentId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "owner_ids")) + TArray OwnerIds; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "limit")) + Int32Value_TODO Limit; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cursor")) + string_TODO Cursor; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "expiry")) + Int64Value_TODO Expiry; + + static FNakamaListTournamentRecordsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaListTournamentsRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "category_start")) + UInt32Value_TODO CategoryStart; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "category_end")) + UInt32Value_TODO CategoryEnd; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "start_time")) + UInt32Value_TODO StartTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "end_time")) + UInt32Value_TODO EndTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "limit")) + Int32Value_TODO Limit; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cursor")) + string_TODO Cursor; + + static FNakamaListTournamentsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaListUserGroupsRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "user_id")) + string_TODO UserId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "limit")) + Int32Value_TODO Limit; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "state")) + Int32Value_TODO State; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cursor")) + string_TODO Cursor; + + static FNakamaListUserGroupsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaMatch +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "match_id")) + string_TODO MatchId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "authoritative")) + bool_TODO Authoritative; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "label")) + StringValue_TODO Label; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "size")) + int32_TODO Size; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "tick_rate")) + int32_TODO TickRate; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "handler_name")) + string_TODO HandlerName; + + static FNakamaMatch FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaMatchList +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "matches")) + TArray Matches; + + static FNakamaMatchList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaMatchmakerCompletionStats +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "create_time")) + Timestamp_TODO CreateTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "complete_time")) + Timestamp_TODO CompleteTime; + + static FNakamaMatchmakerCompletionStats FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaMatchmakerStats +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "ticket_count")) + int32_TODO TicketCount; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "oldest_ticket_create_time")) + Timestamp_TODO OldestTicketCreateTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "completions")) + TArray Completions; + + static FNakamaMatchmakerStats FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaNotification +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "id")) + string_TODO Id; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "subject")) + string_TODO Subject; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "content")) + string_TODO Content; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "code")) + int32_TODO Code; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "sender_id")) + string_TODO SenderId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "create_time")) + Timestamp_TODO CreateTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "persistent")) + bool_TODO Persistent; + + static FNakamaNotification FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaNotificationList +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "notifications")) + TArray Notifications; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cacheable_cursor")) + string_TODO CacheableCursor; + + static FNakamaNotificationList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaPromoteGroupUsersRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "group_id")) + string_TODO GroupId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "user_ids")) + TArray UserIds; + + static FNakamaPromoteGroupUsersRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaDemoteGroupUsersRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "group_id")) + string_TODO GroupId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "user_ids")) + TArray UserIds; + + static FNakamaDemoteGroupUsersRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaReadStorageObjectId +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "collection")) + string_TODO Collection; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "key")) + string_TODO Key; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "user_id")) + string_TODO UserId; + + static FNakamaReadStorageObjectId FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaReadStorageObjectsRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "object_ids")) + TArray ObjectIds; + + static FNakamaReadStorageObjectsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaRpc +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "id")) + string_TODO Id; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "payload")) + string_TODO Payload; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "http_key")) + string_TODO HttpKey; + + static FNakamaRpc FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaSession +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "created")) + bool_TODO Created; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "token")) + string_TODO Token; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "refresh_token")) + string_TODO RefreshToken; + + static FNakamaSession FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaStorageObject +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "collection")) + string_TODO Collection; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "key")) + string_TODO Key; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "user_id")) + string_TODO UserId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "value")) + string_TODO Value; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "version")) + string_TODO Version; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "permission_read")) + int32_TODO PermissionRead; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "permission_write")) + int32_TODO PermissionWrite; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "create_time")) + Timestamp_TODO CreateTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "update_time")) + Timestamp_TODO UpdateTime; + + static FNakamaStorageObject FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaStorageObjectAck +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "collection")) + string_TODO Collection; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "key")) + string_TODO Key; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "version")) + string_TODO Version; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "user_id")) + string_TODO UserId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "create_time")) + Timestamp_TODO CreateTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "update_time")) + Timestamp_TODO UpdateTime; + + static FNakamaStorageObjectAck FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaStorageObjectAcks +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "acks")) + TArray Acks; + + static FNakamaStorageObjectAcks FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaStorageObjects +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "objects")) + TArray Objects; + + static FNakamaStorageObjects FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaStorageObjectList +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "objects")) + TArray Objects; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cursor")) + string_TODO Cursor; + + static FNakamaStorageObjectList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaTournament +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "id")) + string_TODO Id; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "title")) + string_TODO Title; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "description")) + string_TODO Description; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "category")) + uint32_TODO Category; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "sort_order")) + uint32_TODO SortOrder; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "size")) + uint32_TODO Size; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "max_size")) + uint32_TODO MaxSize; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "max_num_score")) + uint32_TODO MaxNumScore; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "can_enter")) + bool_TODO CanEnter; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "end_active")) + uint32_TODO EndActive; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "next_reset")) + uint32_TODO NextReset; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "metadata")) + string_TODO Metadata; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "create_time")) + Timestamp_TODO CreateTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "start_time")) + Timestamp_TODO StartTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "end_time")) + Timestamp_TODO EndTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "duration")) + uint32_TODO Duration; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "start_active")) + uint32_TODO StartActive; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "prev_reset")) + uint32_TODO PrevReset; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "operator")) + Operator_TODO Operator; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "authoritative")) + bool_TODO Authoritative; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "join_required")) + bool_TODO JoinRequired; + + static FNakamaTournament FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaTournamentList +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "tournaments")) + TArray Tournaments; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cursor")) + string_TODO Cursor; + + static FNakamaTournamentList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaTournamentRecordList +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "records")) + TArray Records; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "owner_records")) + TArray OwnerRecords; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "next_cursor")) + string_TODO NextCursor; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "prev_cursor")) + string_TODO PrevCursor; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "rank_count")) + int64_TODO RankCount; + + static FNakamaTournamentRecordList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaUpdateAccountRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "username")) + StringValue_TODO Username; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "display_name")) + StringValue_TODO DisplayName; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "avatar_url")) + StringValue_TODO AvatarUrl; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "lang_tag")) + StringValue_TODO LangTag; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "location")) + StringValue_TODO Location; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "timezone")) + StringValue_TODO Timezone; + + static FNakamaUpdateAccountRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaUpdateGroupRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "group_id")) + string_TODO GroupId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "name")) + StringValue_TODO Name; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "description")) + StringValue_TODO Description; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "lang_tag")) + StringValue_TODO LangTag; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "avatar_url")) + StringValue_TODO AvatarUrl; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "open")) + BoolValue_TODO Open; + + static FNakamaUpdateGroupRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaUser +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "id")) + string_TODO Id; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "username")) + string_TODO Username; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "display_name")) + string_TODO DisplayName; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "avatar_url")) + string_TODO AvatarUrl; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "lang_tag")) + string_TODO LangTag; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "location")) + string_TODO Location; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "timezone")) + string_TODO Timezone; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "metadata")) + string_TODO Metadata; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "facebook_id")) + string_TODO FacebookId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "google_id")) + string_TODO GoogleId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "gamecenter_id")) + string_TODO GamecenterId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "steam_id")) + string_TODO SteamId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "online")) + bool_TODO Online; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "edge_count")) + int32_TODO EdgeCount; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "create_time")) + Timestamp_TODO CreateTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "update_time")) + Timestamp_TODO UpdateTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "facebook_instant_game_id")) + string_TODO FacebookInstantGameId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "apple_id")) + string_TODO AppleId; + + static FNakamaUser FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaUserGroupList +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "user_groups")) + TArray UserGroups; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cursor")) + string_TODO Cursor; + + static FNakamaUserGroupList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaUserGroup +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "group")) + Group_TODO Group; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "state")) + Int32Value_TODO State; + + static FNakamaUserGroup FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaUsers +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "users")) + TArray Users; + + static FNakamaUsers FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaValidatePurchaseAppleRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "receipt")) + string_TODO Receipt; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "persist")) + BoolValue_TODO Persist; + + static FNakamaValidatePurchaseAppleRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaValidateSubscriptionAppleRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "receipt")) + string_TODO Receipt; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "persist")) + BoolValue_TODO Persist; + + static FNakamaValidateSubscriptionAppleRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaValidatePurchaseGoogleRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "purchase")) + string_TODO Purchase; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "persist")) + BoolValue_TODO Persist; + + static FNakamaValidatePurchaseGoogleRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaValidateSubscriptionGoogleRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "receipt")) + string_TODO Receipt; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "persist")) + BoolValue_TODO Persist; + + static FNakamaValidateSubscriptionGoogleRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaValidatePurchaseHuaweiRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "purchase")) + string_TODO Purchase; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "signature")) + string_TODO Signature; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "persist")) + BoolValue_TODO Persist; + + static FNakamaValidatePurchaseHuaweiRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaValidatePurchaseFacebookInstantRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "signed_request")) + string_TODO SignedRequest; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "persist")) + BoolValue_TODO Persist; + + static FNakamaValidatePurchaseFacebookInstantRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaValidatedPurchase +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "user_id")) + string_TODO UserId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "product_id")) + string_TODO ProductId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "transaction_id")) + string_TODO TransactionId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "store")) + StoreProvider_TODO Store; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "purchase_time")) + Timestamp_TODO PurchaseTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "create_time")) + Timestamp_TODO CreateTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "update_time")) + Timestamp_TODO UpdateTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "refund_time")) + Timestamp_TODO RefundTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "provider_response")) + string_TODO ProviderResponse; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "environment")) + StoreEnvironment_TODO Environment; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "seen_before")) + bool_TODO SeenBefore; + + static FNakamaValidatedPurchase FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaValidatePurchaseResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "validated_purchases")) + TArray ValidatedPurchases; + + static FNakamaValidatePurchaseResponse FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaValidateSubscriptionResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "validated_subscription")) + ValidatedSubscription_TODO ValidatedSubscription; + + static FNakamaValidateSubscriptionResponse FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaValidatedSubscription +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "user_id")) + string_TODO UserId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "product_id")) + string_TODO ProductId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "original_transaction_id")) + string_TODO OriginalTransactionId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "store")) + StoreProvider_TODO Store; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "purchase_time")) + Timestamp_TODO PurchaseTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "create_time")) + Timestamp_TODO CreateTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "update_time")) + Timestamp_TODO UpdateTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "environment")) + StoreEnvironment_TODO Environment; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "expiry_time")) + Timestamp_TODO ExpiryTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "refund_time")) + Timestamp_TODO RefundTime; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "provider_response")) + string_TODO ProviderResponse; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "provider_notification")) + string_TODO ProviderNotification; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "active")) + bool_TODO Active; + + static FNakamaValidatedSubscription FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaPurchaseList +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "validated_purchases")) + TArray ValidatedPurchases; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cursor")) + string_TODO Cursor; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "prev_cursor")) + string_TODO PrevCursor; + + static FNakamaPurchaseList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaSubscriptionList +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "validated_subscriptions")) + TArray ValidatedSubscriptions; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cursor")) + string_TODO Cursor; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "prev_cursor")) + string_TODO PrevCursor; + + static FNakamaSubscriptionList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaWriteLeaderboardRecordRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "leaderboard_id")) + string_TODO LeaderboardId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "record")) + LeaderboardRecordWrite_TODO Record; + + static FNakamaWriteLeaderboardRecordRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaLeaderboardRecordWrite +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "score")) + int64_TODO Score; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "subscore")) + int64_TODO Subscore; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "metadata")) + string_TODO Metadata; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "operator")) + Operator_TODO Operator; + + static FNakamaLeaderboardRecordWrite FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaWriteStorageObject +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "collection")) + string_TODO Collection; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "key")) + string_TODO Key; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "value")) + string_TODO Value; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "version")) + string_TODO Version; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "permission_read")) + Int32Value_TODO PermissionRead; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "permission_write")) + Int32Value_TODO PermissionWrite; + + static FNakamaWriteStorageObject FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaWriteStorageObjectsRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "objects")) + TArray Objects; + + static FNakamaWriteStorageObjectsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaWriteTournamentRecordRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "tournament_id")) + string_TODO TournamentId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "record")) + TournamentRecordWrite_TODO Record; + + static FNakamaWriteTournamentRecordRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaTournamentRecordWrite +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "score")) + int64_TODO Score; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "subscore")) + int64_TODO Subscore; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "metadata")) + string_TODO Metadata; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "operator")) + Operator_TODO Operator; + + static FNakamaTournamentRecordWrite FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaListPartiesRequest +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "limit")) + Int32Value_TODO Limit; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "open")) + BoolValue_TODO Open; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "query")) + StringValue_TODO Query; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cursor")) + StringValue_TODO Cursor; + + static FNakamaListPartiesRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaParty +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "party_id")) + string_TODO PartyId; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "open")) + bool_TODO Open; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "hidden")) + bool_TODO Hidden; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "max_size")) + int32_TODO MaxSize; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "label")) + string_TODO Label; + + static FNakamaParty FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaPartyList +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "parties")) + TArray Parties; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "cursor")) + string_TODO Cursor; + + static FNakamaPartyList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + + + +/** Tag type used as the value type for RPCs that return no data. */ +struct NAKAMA_API FNakamaVoid {}; +struct NAKAMA_API FNakamaVoidResult +{ + using ValueType = FNakamaVoid; + FNakamaVoid Value{}; + FNakamaError Error; + bool bIsError = true; +}; + + +struct NAKAMA_API FNakamaSessionResult +{ + using ValueType = FNakamaSession; + FNakamaSession Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaSessionResult +{ + using ValueType = FNakamaSession; + FNakamaSession Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaSessionResult +{ + using ValueType = FNakamaSession; + FNakamaSession Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaSessionResult +{ + using ValueType = FNakamaSession; + FNakamaSession Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaSessionResult +{ + using ValueType = FNakamaSession; + FNakamaSession Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaSessionResult +{ + using ValueType = FNakamaSession; + FNakamaSession Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaSessionResult +{ + using ValueType = FNakamaSession; + FNakamaSession Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaSessionResult +{ + using ValueType = FNakamaSession; + FNakamaSession Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaSessionResult +{ + using ValueType = FNakamaSession; + FNakamaSession Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaSessionResult +{ + using ValueType = FNakamaSession; + FNakamaSession Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaGroupResult +{ + using ValueType = FNakamaGroup; + FNakamaGroup Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaAccountResult +{ + using ValueType = FNakamaAccount; + FNakamaAccount Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaUsersResult +{ + using ValueType = FNakamaUsers; + FNakamaUsers Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaValidatedSubscriptionResult +{ + using ValueType = FNakamaValidatedSubscription; + FNakamaValidatedSubscription Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaMatchmakerStatsResult +{ + using ValueType = FNakamaMatchmakerStats; + FNakamaMatchmakerStats Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaChannelMessageListResult +{ + using ValueType = FNakamaChannelMessageList; + FNakamaChannelMessageList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaFriendListResult +{ + using ValueType = FNakamaFriendList; + FNakamaFriendList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaFriendsOfFriendsListResult +{ + using ValueType = FNakamaFriendsOfFriendsList; + FNakamaFriendsOfFriendsList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaGroupListResult +{ + using ValueType = FNakamaGroupList; + FNakamaGroupList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaGroupUserListResult +{ + using ValueType = FNakamaGroupUserList; + FNakamaGroupUserList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaLeaderboardRecordListResult +{ + using ValueType = FNakamaLeaderboardRecordList; + FNakamaLeaderboardRecordList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaLeaderboardRecordListResult +{ + using ValueType = FNakamaLeaderboardRecordList; + FNakamaLeaderboardRecordList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaMatchListResult +{ + using ValueType = FNakamaMatchList; + FNakamaMatchList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaPartyListResult +{ + using ValueType = FNakamaPartyList; + FNakamaPartyList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaNotificationListResult +{ + using ValueType = FNakamaNotificationList; + FNakamaNotificationList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaStorageObjectListResult +{ + using ValueType = FNakamaStorageObjectList; + FNakamaStorageObjectList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaSubscriptionListResult +{ + using ValueType = FNakamaSubscriptionList; + FNakamaSubscriptionList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaTournamentListResult +{ + using ValueType = FNakamaTournamentList; + FNakamaTournamentList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaTournamentRecordListResult +{ + using ValueType = FNakamaTournamentRecordList; + FNakamaTournamentRecordList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaTournamentRecordListResult +{ + using ValueType = FNakamaTournamentRecordList; + FNakamaTournamentRecordList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaUserGroupListResult +{ + using ValueType = FNakamaUserGroupList; + FNakamaUserGroupList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaStorageObjectsResult +{ + using ValueType = FNakamaStorageObjects; + FNakamaStorageObjects Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaRpcResult +{ + using ValueType = FNakamaRpc; + FNakamaRpc Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaValidatePurchaseResponseResult +{ + using ValueType = FNakamaValidatePurchaseResponse; + FNakamaValidatePurchaseResponse Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaValidateSubscriptionResponseResult +{ + using ValueType = FNakamaValidateSubscriptionResponse; + FNakamaValidateSubscriptionResponse Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaValidatePurchaseResponseResult +{ + using ValueType = FNakamaValidatePurchaseResponse; + FNakamaValidatePurchaseResponse Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaValidateSubscriptionResponseResult +{ + using ValueType = FNakamaValidateSubscriptionResponse; + FNakamaValidateSubscriptionResponse Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaValidatePurchaseResponseResult +{ + using ValueType = FNakamaValidatePurchaseResponse; + FNakamaValidatePurchaseResponse Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaValidatePurchaseResponseResult +{ + using ValueType = FNakamaValidatePurchaseResponse; + FNakamaValidatePurchaseResponse Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaLeaderboardRecordResult +{ + using ValueType = FNakamaLeaderboardRecord; + FNakamaLeaderboardRecord Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaStorageObjectAcksResult +{ + using ValueType = FNakamaStorageObjectAcks; + FNakamaStorageObjectAcks Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaLeaderboardRecordResult +{ + using ValueType = FNakamaLeaderboardRecord; + FNakamaLeaderboardRecord Value {}; + FNakamaError Error; + bool bIsError = true; +}; + + +namespace NakamaInternal +{ + +FNakamaApiRequestModel NAKAMA_API BuildAddFriendsRequest ( + const FNakamaAddFriendsRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildAddGroupUsersRequest ( + const FNakamaAddGroupUsersRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildSessionRefreshRequest ( + const FNakamaSessionRefreshRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildSessionLogoutRequest ( + const FNakamaSessionLogoutRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildAuthenticateAppleRequest ( + const FNakamaAuthenticateAppleRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildAuthenticateCustomRequest ( + const FNakamaAuthenticateCustomRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildAuthenticateDeviceRequest ( + const FNakamaAuthenticateDeviceRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildAuthenticateEmailRequest ( + const FNakamaAuthenticateEmailRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildAuthenticateFacebookRequest ( + const FNakamaAuthenticateFacebookRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildAuthenticateFacebookInstantGameRequest ( + const FNakamaAuthenticateFacebookInstantGameRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildAuthenticateGameCenterRequest ( + const FNakamaAuthenticateGameCenterRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildAuthenticateGoogleRequest ( + const FNakamaAuthenticateGoogleRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildAuthenticateSteamRequest ( + const FNakamaAuthenticateSteamRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildBanGroupUsersRequest ( + const FNakamaBanGroupUsersRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildBlockFriendsRequest ( + const FNakamaBlockFriendsRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildCreateGroupRequest ( + const FNakamaCreateGroupRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildDeleteAccountRequest ( +); + +FNakamaApiRequestModel NAKAMA_API BuildDeleteFriendsRequest ( + const FNakamaDeleteFriendsRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildDeleteGroupRequest ( + const FNakamaDeleteGroupRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildDeleteLeaderboardRecordRequest ( + const FNakamaDeleteLeaderboardRecordRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildDeleteNotificationsRequest ( + const FNakamaDeleteNotificationsRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildDeleteTournamentRecordRequest ( + const FNakamaDeleteTournamentRecordRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildDeleteStorageObjectsRequest ( + const FNakamaDeleteStorageObjectsRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildEventRequest ( + const FNakamaEvent& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildGetAccountRequest ( +); + +FNakamaApiRequestModel NAKAMA_API BuildGetUsersRequest ( + const FNakamaGetUsersRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildGetSubscriptionRequest ( + const FNakamaGetSubscriptionRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildGetMatchmakerStatsRequest ( +); + +FNakamaApiRequestModel NAKAMA_API BuildHealthcheckRequest ( +); + +FNakamaApiRequestModel NAKAMA_API BuildImportFacebookFriendsRequest ( + const FNakamaImportFacebookFriendsRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildImportSteamFriendsRequest ( + const FNakamaImportSteamFriendsRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildJoinGroupRequest ( + const FNakamaJoinGroupRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildJoinTournamentRequest ( + const FNakamaJoinTournamentRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildKickGroupUsersRequest ( + const FNakamaKickGroupUsersRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildLeaveGroupRequest ( + const FNakamaLeaveGroupRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildLinkAppleRequest ( + const FNakamaAccountApple& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildLinkCustomRequest ( + const FNakamaAccountCustom& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildLinkDeviceRequest ( + const FNakamaAccountDevice& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildLinkEmailRequest ( + const FNakamaAccountEmail& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildLinkFacebookRequest ( + const FNakamaLinkFacebookRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildLinkFacebookInstantGameRequest ( + const FNakamaAccountFacebookInstantGame& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildLinkGameCenterRequest ( + const FNakamaAccountGameCenter& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildLinkGoogleRequest ( + const FNakamaAccountGoogle& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildLinkSteamRequest ( + const FNakamaLinkSteamRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildListChannelMessagesRequest ( + const FNakamaListChannelMessagesRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildListFriendsRequest ( + const FNakamaListFriendsRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildListFriendsOfFriendsRequest ( + const FNakamaListFriendsOfFriendsRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildListGroupsRequest ( + const FNakamaListGroupsRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildListGroupUsersRequest ( + const FNakamaListGroupUsersRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildListLeaderboardRecordsRequest ( + const FNakamaListLeaderboardRecordsRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildListLeaderboardRecordsAroundOwnerRequest ( + const FNakamaListLeaderboardRecordsAroundOwnerRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildListMatchesRequest ( + const FNakamaListMatchesRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildListPartiesRequest ( + const FNakamaListPartiesRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildListNotificationsRequest ( + const FNakamaListNotificationsRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildListStorageObjectsRequest ( + const FNakamaListStorageObjectsRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildListSubscriptionsRequest ( + const FNakamaListSubscriptionsRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildListTournamentsRequest ( + const FNakamaListTournamentsRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildListTournamentRecordsRequest ( + const FNakamaListTournamentRecordsRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildListTournamentRecordsAroundOwnerRequest ( + const FNakamaListTournamentRecordsAroundOwnerRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildListUserGroupsRequest ( + const FNakamaListUserGroupsRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildPromoteGroupUsersRequest ( + const FNakamaPromoteGroupUsersRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildDemoteGroupUsersRequest ( + const FNakamaDemoteGroupUsersRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildReadStorageObjectsRequest ( + const FNakamaReadStorageObjectsRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildRpcFuncRequest ( + const FNakamaRpc& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildUnlinkAppleRequest ( + const FNakamaAccountApple& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildUnlinkCustomRequest ( + const FNakamaAccountCustom& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildUnlinkDeviceRequest ( + const FNakamaAccountDevice& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildUnlinkEmailRequest ( + const FNakamaAccountEmail& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildUnlinkFacebookRequest ( + const FNakamaAccountFacebook& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildUnlinkFacebookInstantGameRequest ( + const FNakamaAccountFacebookInstantGame& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildUnlinkGameCenterRequest ( + const FNakamaAccountGameCenter& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildUnlinkGoogleRequest ( + const FNakamaAccountGoogle& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildUnlinkSteamRequest ( + const FNakamaAccountSteam& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildUpdateAccountRequest ( + const FNakamaUpdateAccountRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildUpdateGroupRequest ( + const FNakamaUpdateGroupRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildValidatePurchaseAppleRequest ( + const FNakamaValidatePurchaseAppleRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildValidateSubscriptionAppleRequest ( + const FNakamaValidateSubscriptionAppleRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildValidatePurchaseGoogleRequest ( + const FNakamaValidatePurchaseGoogleRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildValidateSubscriptionGoogleRequest ( + const FNakamaValidateSubscriptionGoogleRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildValidatePurchaseHuaweiRequest ( + const FNakamaValidatePurchaseHuaweiRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildValidatePurchaseFacebookInstantRequest ( + const FNakamaValidatePurchaseFacebookInstantRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildWriteLeaderboardRecordRequest ( + const FNakamaWriteLeaderboardRecordRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildWriteStorageObjectsRequest ( + const FNakamaWriteStorageObjectsRequest& Params; +); + +FNakamaApiRequestModel NAKAMA_API BuildWriteTournamentRecordRequest ( + const FNakamaWriteTournamentRecordRequest& Params; +); +} + diff --git a/Nakama/Source/Nakama/Public/NakamaApiRequestModel.h b/Nakama/Source/Nakama/Public/NakamaApiRequestModel.h new file mode 100644 index 000000000..f1b9231d1 --- /dev/null +++ b/Nakama/Source/Nakama/Public/NakamaApiRequestModel.h @@ -0,0 +1,26 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CoreMinimal.h" + +struct NAKAMA_API FNakamaApiRequestModel +{ + FString Url; + + // TODO: Request Method + + // TODO: Request Body +}; diff --git a/Nakama/Source/Nakama/Public/NakamaClient.h b/Nakama/Source/Nakama/Public/NakamaClient.h new file mode 100644 index 000000000..edf05430b --- /dev/null +++ b/Nakama/Source/Nakama/Public/NakamaClient.h @@ -0,0 +1,3515 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + +#pragma once + +#include "CoreMinimal.h" +#include "NakamaApi.h" +#include "NakamaFuture.h" + +/** Tag type used as the value type for RPCs that return no data. */ +struct NAKAMA_API FNakamaVoid {}; +struct NAKAMA_API FNakamaVoidResult +{ + using ValueType = FNakamaVoid; + FNakamaVoid Value{}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaSessionResult +{ + using ValueType = FNakamaSession; + FNakamaSession Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaGroupResult +{ + using ValueType = FNakamaGroup; + FNakamaGroup Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaAccountResult +{ + using ValueType = FNakamaAccount; + FNakamaAccount Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaUsersResult +{ + using ValueType = FNakamaUsers; + FNakamaUsers Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaValidatedSubscriptionResult +{ + using ValueType = FNakamaValidatedSubscription; + FNakamaValidatedSubscription Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaMatchmakerStatsResult +{ + using ValueType = FNakamaMatchmakerStats; + FNakamaMatchmakerStats Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaChannelMessageListResult +{ + using ValueType = FNakamaChannelMessageList; + FNakamaChannelMessageList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaFriendListResult +{ + using ValueType = FNakamaFriendList; + FNakamaFriendList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaFriendsOfFriendsListResult +{ + using ValueType = FNakamaFriendsOfFriendsList; + FNakamaFriendsOfFriendsList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaGroupListResult +{ + using ValueType = FNakamaGroupList; + FNakamaGroupList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaGroupUserListResult +{ + using ValueType = FNakamaGroupUserList; + FNakamaGroupUserList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaLeaderboardRecordListResult +{ + using ValueType = FNakamaLeaderboardRecordList; + FNakamaLeaderboardRecordList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaMatchListResult +{ + using ValueType = FNakamaMatchList; + FNakamaMatchList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaPartyListResult +{ + using ValueType = FNakamaPartyList; + FNakamaPartyList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaNotificationListResult +{ + using ValueType = FNakamaNotificationList; + FNakamaNotificationList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaStorageObjectListResult +{ + using ValueType = FNakamaStorageObjectList; + FNakamaStorageObjectList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaSubscriptionListResult +{ + using ValueType = FNakamaSubscriptionList; + FNakamaSubscriptionList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaTournamentListResult +{ + using ValueType = FNakamaTournamentList; + FNakamaTournamentList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaTournamentRecordListResult +{ + using ValueType = FNakamaTournamentRecordList; + FNakamaTournamentRecordList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaUserGroupListResult +{ + using ValueType = FNakamaUserGroupList; + FNakamaUserGroupList Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaStorageObjectsResult +{ + using ValueType = FNakamaStorageObjects; + FNakamaStorageObjects Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaRpcResult +{ + using ValueType = FNakamaRpc; + FNakamaRpc Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaValidatePurchaseResponseResult +{ + using ValueType = FNakamaValidatePurchaseResponse; + FNakamaValidatePurchaseResponse Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaValidateSubscriptionResponseResult +{ + using ValueType = FNakamaValidateSubscriptionResponse; + FNakamaValidateSubscriptionResponse Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaLeaderboardRecordResult +{ + using ValueType = FNakamaLeaderboardRecord; + FNakamaLeaderboardRecord Value {}; + FNakamaError Error; + bool bIsError = true; +}; +struct NAKAMA_API FNakamaStorageObjectAcksResult +{ + using ValueType = FNakamaStorageObjectAcks; + FNakamaStorageObjectAcks Value {}; + FNakamaError Error; + bool bIsError = true; +}; + +/** Retry configuration for transient error handling + session auto-refresh. */ +struct NAKAMA_API FNakamaRetryConfig +{ + /** Maximum number of retry attempts (0 = no retries). */ + int32 MaxRetries = 4; + + /** Base delay in milliseconds before first retry. Actual delay uses exponential backoff with full jitter. */ + int32 BaseDelayMs = 500; + + /** Whether to automatically refresh expired sessions before making API calls. */ + bool bAutoRefreshSession = true; + + /** How many seconds before expiry a session is considered "about to expire" and gets refreshed. */ + int64 AutoRefreshBufferSeconds = 300; + + /** HTTP request timeout in seconds. */ + float Timeout = 10.0f; + + /** + * Called after a session is automatically refreshed, so callers can persist the updated session. + * + * If the callback captures a UObject pointer, set OnSessionRefreshedOwner to that object. + * The callback will be skipped if the owner has been garbage-collected, preventing + * dangling-pointer crashes during in-flight retries. + */ + TFunction OnSessionRefreshed; + + /** Optional weak owner for OnSessionRefreshed. When set, the callback is skipped if the owner is stale. */ + TWeakObjectPtr OnSessionRefreshedOwner; +}; + +/** + * High-level Nakama API: free functions with retry logic + session auto-refresh. + * + * Transient errors (UNAVAILABLE, INTERNAL, DEADLINE_EXCEEDED) are + * automatically retried with exponential backoff and full jitter. + */ +namespace Nakama +{ + /** Returns true if the error code is considered transient (eligible for retry). */ + NAKAMA_API bool IsTransientError(const FNakamaError& Error); + + /** Compute backoff delay in seconds for a given attempt using exponential backoff with full jitter. */ + NAKAMA_API float CalculateBackoff(int32 Attempt, const FNakamaRetryConfig& Config); + + /** + * Add friends by ID or username to a user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Ids The account id of a user. + * @param Usernames The account username of a user. + * @param Metadata Optional metadata to add to friends. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture AddFriends( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& Ids, + const TArray& Usernames, + const FString& Metadata, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Add friends by ID or username to a user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Ids The account id of a user. + * @param Usernames The account username of a user. + * @param Metadata Optional metadata to add to friends. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture AddFriends( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const TArray& Ids, + const TArray& Usernames, + const FString& Metadata, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Add users to a group. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param GroupId The group to add users to. + * @param UserIds The users to add. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture AddGroupUsers( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& GroupId, + const TArray& UserIds, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Add users to a group. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param GroupId The group to add users to. + * @param UserIds The users to add. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture AddGroupUsers( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& GroupId, + const TArray& UserIds, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Refresh a user's session using a refresh token retrieved from a previous authentication request. + * + * @param Config The client configuration. + * @param Token Refresh token. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture SessionRefresh( + const FNakamaClientConfig& ClientConfig, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Log out a session, invalidate a refresh token, or log out all sessions/refresh tokens for a user. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Token Session token to log out. + * @param RefreshToken Refresh token to invalidate. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture SessionLogout( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Token, + const FString& RefreshToken, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Log out a session, invalidate a refresh token, or log out all sessions/refresh tokens for a user. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Token Session token to log out. + * @param RefreshToken Refresh token to invalidate. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture SessionLogout( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Token, + const FString& RefreshToken, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Authenticate a user with an Apple ID against the server. + * + * @param Config The client configuration. + * @param Create Register the account if the user does not already exist. + * @param Username Set the username on the account at register. Must be unique. + * @param AccountToken The ID token received from Apple to validate. + * @param AccountVars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture AuthenticateApple( + const FNakamaClientConfig& ClientConfig, + FNakamaOptionalBool Create, + const FString& Username, + const FString& AccountToken, + const TMap& AccountVars = {}, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Authenticate a user with a custom id against the server. + * + * @param Config The client configuration. + * @param Create Register the account if the user does not already exist. + * @param Username Set the username on the account at register. Must be unique. + * @param AccountId A custom identifier. + * @param AccountVars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture AuthenticateCustom( + const FNakamaClientConfig& ClientConfig, + FNakamaOptionalBool Create, + const FString& Username, + const FString& AccountId, + const TMap& AccountVars = {}, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Authenticate a user with a device id against the server. + * + * @param Config The client configuration. + * @param Create Register the account if the user does not already exist. + * @param Username Set the username on the account at register. Must be unique. + * @param AccountId A device identifier. Should be obtained by a platform-specific device API. + * @param AccountVars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture AuthenticateDevice( + const FNakamaClientConfig& ClientConfig, + FNakamaOptionalBool Create, + const FString& Username, + const FString& AccountId, + const TMap& AccountVars = {}, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Authenticate a user with an email+password against the server. + * + * @param Config The client configuration. + * @param Create Register the account if the user does not already exist. + * @param Username Set the username on the account at register. Must be unique. + * @param AccountEmail A valid RFC-5322 email address. + * @param AccountPassword A password for the user account. + * @param AccountVars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture AuthenticateEmail( + const FNakamaClientConfig& ClientConfig, + FNakamaOptionalBool Create, + const FString& Username, + const FString& AccountEmail, + const FString& AccountPassword, + const TMap& AccountVars = {}, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Authenticate a user with a Facebook OAuth token against the server. + * + * @param Config The client configuration. + * @param Create Register the account if the user does not already exist. + * @param Username Set the username on the account at register. Must be unique. + * @param Sync Import Facebook friends for the user. + * @param AccountToken The OAuth token received from Facebook to access their profile API. + * @param AccountVars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture AuthenticateFacebook( + const FNakamaClientConfig& ClientConfig, + FNakamaOptionalBool Create, + const FString& Username, + FNakamaOptionalBool Sync, + const FString& AccountToken, + const TMap& AccountVars = {}, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Authenticate a user with a Facebook Instant Game token against the server. + * + * @param Config The client configuration. + * @param Create Register the account if the user does not already exist. + * @param Username Set the username on the account at register. Must be unique. + * @param AccountSignedPlayerInfo The OAuth token received from a Facebook Instant Game that may be decoded with the Application Secret (must be available with the nakama configuration) + * @param AccountVars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture AuthenticateFacebookInstantGame( + const FNakamaClientConfig& ClientConfig, + FNakamaOptionalBool Create, + const FString& Username, + const FString& AccountSignedPlayerInfo, + const TMap& AccountVars = {}, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Authenticate a user with Apple's GameCenter against the server. + * + * @param Config The client configuration. + * @param Create Register the account if the user does not already exist. + * @param Username Set the username on the account at register. Must be unique. + * @param AccountPlayerId Player ID (generated by GameCenter). + * @param AccountBundleId Bundle ID (generated by GameCenter). + * @param AccountTimestampSeconds Time since UNIX epoch when the signature was created. + * @param AccountSalt A random "NSString" used to compute the hash and keep it randomized. + * @param AccountSignature The verification signature data generated. + * @param AccountPublicKeyUrl The URL for the public encryption key. + * @param AccountVars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture AuthenticateGameCenter( + const FNakamaClientConfig& ClientConfig, + FNakamaOptionalBool Create, + const FString& Username, + const FString& AccountPlayerId, + const FString& AccountBundleId, + int64 AccountTimestampSeconds, + const FString& AccountSalt, + const FString& AccountSignature, + const FString& AccountPublicKeyUrl, + const TMap& AccountVars = {}, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Authenticate a user with Google against the server. + * + * @param Config The client configuration. + * @param Create Register the account if the user does not already exist. + * @param Username Set the username on the account at register. Must be unique. + * @param AccountToken The OAuth token received from Google to access their profile API. + * @param AccountVars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture AuthenticateGoogle( + const FNakamaClientConfig& ClientConfig, + FNakamaOptionalBool Create, + const FString& Username, + const FString& AccountToken, + const TMap& AccountVars = {}, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Authenticate a user with Steam against the server. + * + * @param Config The client configuration. + * @param Create Register the account if the user does not already exist. + * @param Username Set the username on the account at register. Must be unique. + * @param Sync Import Steam friends for the user. + * @param AccountToken The account token received from Steam to access their profile API. + * @param AccountVars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture AuthenticateSteam( + const FNakamaClientConfig& ClientConfig, + FNakamaOptionalBool Create, + const FString& Username, + FNakamaOptionalBool Sync, + const FString& AccountToken, + const TMap& AccountVars = {}, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Ban a set of users from a group. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param GroupId The group to ban users from. + * @param UserIds The users to ban. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture BanGroupUsers( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& GroupId, + const TArray& UserIds, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Ban a set of users from a group. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param GroupId The group to ban users from. + * @param UserIds The users to ban. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture BanGroupUsers( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& GroupId, + const TArray& UserIds, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Block one or more users by ID or username. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Ids The account id of a user. + * @param Usernames The account username of a user. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture BlockFriends( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& Ids, + const TArray& Usernames, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Block one or more users by ID or username. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Ids The account id of a user. + * @param Usernames The account username of a user. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture BlockFriends( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const TArray& Ids, + const TArray& Usernames, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Create a new group with the current user as the owner. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Name A unique name for the group. + * @param Description A description for the group. + * @param LangTag The language expected to be a tag which follows the BCP-47 spec. + * @param AvatarUrl A URL for an avatar image. + * @param Open Mark a group as open or not where only admins can accept members. + * @param MaxCount Maximum number of group members. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture CreateGroup( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Name, + const FString& Description, + const FString& LangTag, + const FString& AvatarUrl, + bool Open, + int32 MaxCount, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Create a new group with the current user as the owner. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Name A unique name for the group. + * @param Description A description for the group. + * @param LangTag The language expected to be a tag which follows the BCP-47 spec. + * @param AvatarUrl A URL for an avatar image. + * @param Open Mark a group as open or not where only admins can accept members. + * @param MaxCount Maximum number of group members. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture CreateGroup( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Name, + const FString& Description, + const FString& LangTag, + const FString& AvatarUrl, + bool Open, + int32 MaxCount, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Delete the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture DeleteAccount( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Delete the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture DeleteAccount( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Delete one or more users by ID or username. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Ids The account id of a user. + * @param Usernames The account username of a user. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture DeleteFriends( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& Ids, + const TArray& Usernames, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Delete one or more users by ID or username. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Ids The account id of a user. + * @param Usernames The account username of a user. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture DeleteFriends( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const TArray& Ids, + const TArray& Usernames, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Delete a group by ID. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param GroupId The id of a group. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture DeleteGroup( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& GroupId, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Delete a group by ID. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param GroupId The id of a group. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture DeleteGroup( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& GroupId, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Delete a leaderboard record. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param LeaderboardId The leaderboard ID to delete from. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture DeleteLeaderboardRecord( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& LeaderboardId, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Delete a leaderboard record. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param LeaderboardId The leaderboard ID to delete from. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture DeleteLeaderboardRecord( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& LeaderboardId, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Delete one or more notifications for the current user. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Ids The id of notifications. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture DeleteNotifications( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& Ids, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Delete one or more notifications for the current user. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Ids The id of notifications. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture DeleteNotifications( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const TArray& Ids, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Delete a tournament record. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param TournamentId The tournament ID to delete from. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture DeleteTournamentRecord( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& TournamentId, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Delete a tournament record. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param TournamentId The tournament ID to delete from. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture DeleteTournamentRecord( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& TournamentId, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Delete one or more objects by ID or username. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param ObjectIds Batch of storage objects. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture DeleteStorageObjects( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& ObjectIds, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Delete one or more objects by ID or username. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param ObjectIds Batch of storage objects. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture DeleteStorageObjects( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const TArray& ObjectIds, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Submit an event for processing in the server's registered runtime custom events handler. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Name An event name, type, category, or identifier. + * @param Timestamp The time when the event was triggered. + * @param External True if the event came directly from a client call, false otherwise. + * @param Properties Arbitrary event property values. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture Event( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Name, + const FDateTime& Timestamp, + bool External, + const TMap& Properties, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Submit an event for processing in the server's registered runtime custom events handler. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Name An event name, type, category, or identifier. + * @param Timestamp The time when the event was triggered. + * @param External True if the event came directly from a client call, false otherwise. + * @param Properties Arbitrary event property values. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture Event( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Name, + const FDateTime& Timestamp, + bool External, + const TMap& Properties, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Fetch the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture GetAccount( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Fetch the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture GetAccount( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Fetch zero or more users by ID and/or username. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Ids The account id of a user. + * @param Usernames The account username of a user. + * @param FacebookIds The Facebook ID of a user. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture GetUsers( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& Ids, + const TArray& Usernames, + const TArray& FacebookIds, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Fetch zero or more users by ID and/or username. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Ids The account id of a user. + * @param Usernames The account username of a user. + * @param FacebookIds The Facebook ID of a user. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture GetUsers( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const TArray& Ids, + const TArray& Usernames, + const TArray& FacebookIds, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Get subscription by product id. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param ProductId Product id of the subscription + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture GetSubscription( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& ProductId, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Get subscription by product id. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param ProductId Product id of the subscription + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture GetSubscription( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& ProductId, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Get matchmaker stats. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture GetMatchmakerStats( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Get matchmaker stats. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture GetMatchmakerStats( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * A healthcheck which load balancers can use to check the service. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture Healthcheck( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * A healthcheck which load balancers can use to check the service. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture Healthcheck( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Import Facebook friends and add them to a user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Reset Reset the current user's friends list. + * @param AccountToken The OAuth token received from Facebook to access their profile API. + * @param AccountVars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ImportFacebookFriends( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + FNakamaOptionalBool Reset, + const FString& AccountToken, + const TMap& AccountVars = {}, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Import Facebook friends and add them to a user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Reset Reset the current user's friends list. + * @param AccountToken The OAuth token received from Facebook to access their profile API. + * @param AccountVars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ImportFacebookFriends( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + FNakamaOptionalBool Reset, + const FString& AccountToken, + const TMap& AccountVars = {}, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Import Steam friends and add them to a user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Reset Reset the current user's friends list. + * @param AccountToken The account token received from Steam to access their profile API. + * @param AccountVars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ImportSteamFriends( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + FNakamaOptionalBool Reset, + const FString& AccountToken, + const TMap& AccountVars = {}, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Import Steam friends and add them to a user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Reset Reset the current user's friends list. + * @param AccountToken The account token received from Steam to access their profile API. + * @param AccountVars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ImportSteamFriends( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + FNakamaOptionalBool Reset, + const FString& AccountToken, + const TMap& AccountVars = {}, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Immediately join an open group, or request to join a closed one. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param GroupId The group ID to join. The group must already exist. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture JoinGroup( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& GroupId, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Immediately join an open group, or request to join a closed one. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param GroupId The group ID to join. The group must already exist. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture JoinGroup( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& GroupId, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Attempt to join an open and running tournament. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param TournamentId The ID of the tournament to join. The tournament must already exist. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture JoinTournament( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& TournamentId, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Attempt to join an open and running tournament. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param TournamentId The ID of the tournament to join. The tournament must already exist. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture JoinTournament( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& TournamentId, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Kick a set of users from a group. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param GroupId The group ID to kick from. + * @param UserIds The users to kick. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture KickGroupUsers( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& GroupId, + const TArray& UserIds, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Kick a set of users from a group. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param GroupId The group ID to kick from. + * @param UserIds The users to kick. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture KickGroupUsers( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& GroupId, + const TArray& UserIds, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Leave a group the user is a member of. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param GroupId The group ID to leave. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture LeaveGroup( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& GroupId, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Leave a group the user is a member of. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param GroupId The group ID to leave. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture LeaveGroup( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& GroupId, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Add an Apple ID to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Token The ID token received from Apple to validate. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture LinkApple( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Add an Apple ID to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Token The ID token received from Apple to validate. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture LinkApple( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Add a custom ID to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Id A custom identifier. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture LinkCustom( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Id, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Add a custom ID to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Id A custom identifier. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture LinkCustom( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Id, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Add a device ID to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Id A device identifier. Should be obtained by a platform-specific device API. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture LinkDevice( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Id, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Add a device ID to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Id A device identifier. Should be obtained by a platform-specific device API. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture LinkDevice( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Id, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Add an email+password to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Email A valid RFC-5322 email address. + * @param Password A password for the user account. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture LinkEmail( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Email, + const FString& Password, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Add an email+password to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Email A valid RFC-5322 email address. + * @param Password A password for the user account. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture LinkEmail( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Email, + const FString& Password, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Add Facebook to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Sync Import Facebook friends for the user. + * @param AccountToken The OAuth token received from Facebook to access their profile API. + * @param AccountVars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture LinkFacebook( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + FNakamaOptionalBool Sync, + const FString& AccountToken, + const TMap& AccountVars = {}, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Add Facebook to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Sync Import Facebook friends for the user. + * @param AccountToken The OAuth token received from Facebook to access their profile API. + * @param AccountVars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture LinkFacebook( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + FNakamaOptionalBool Sync, + const FString& AccountToken, + const TMap& AccountVars = {}, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Add Facebook Instant Game to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param SignedPlayerInfo The OAuth token received from a Facebook Instant Game that may be decoded with the Application Secret (must be available with the nakama configuration) + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture LinkFacebookInstantGame( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& SignedPlayerInfo, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Add Facebook Instant Game to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param SignedPlayerInfo The OAuth token received from a Facebook Instant Game that may be decoded with the Application Secret (must be available with the nakama configuration) + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture LinkFacebookInstantGame( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& SignedPlayerInfo, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Add Apple's GameCenter to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param PlayerId Player ID (generated by GameCenter). + * @param BundleId Bundle ID (generated by GameCenter). + * @param TimestampSeconds Time since UNIX epoch when the signature was created. + * @param Salt A random "NSString" used to compute the hash and keep it randomized. + * @param Signature The verification signature data generated. + * @param PublicKeyUrl The URL for the public encryption key. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture LinkGameCenter( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& PlayerId, + const FString& BundleId, + int64 TimestampSeconds, + const FString& Salt, + const FString& Signature, + const FString& PublicKeyUrl, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Add Apple's GameCenter to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param PlayerId Player ID (generated by GameCenter). + * @param BundleId Bundle ID (generated by GameCenter). + * @param TimestampSeconds Time since UNIX epoch when the signature was created. + * @param Salt A random "NSString" used to compute the hash and keep it randomized. + * @param Signature The verification signature data generated. + * @param PublicKeyUrl The URL for the public encryption key. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture LinkGameCenter( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& PlayerId, + const FString& BundleId, + int64 TimestampSeconds, + const FString& Salt, + const FString& Signature, + const FString& PublicKeyUrl, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Add Google to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Token The OAuth token received from Google to access their profile API. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture LinkGoogle( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Add Google to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Token The OAuth token received from Google to access their profile API. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture LinkGoogle( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Add Steam to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Sync Import Steam friends for the user. + * @param AccountToken The account token received from Steam to access their profile API. + * @param AccountVars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture LinkSteam( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + FNakamaOptionalBool Sync, + const FString& AccountToken, + const TMap& AccountVars = {}, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Add Steam to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Sync Import Steam friends for the user. + * @param AccountToken The account token received from Steam to access their profile API. + * @param AccountVars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture LinkSteam( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + FNakamaOptionalBool Sync, + const FString& AccountToken, + const TMap& AccountVars = {}, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List a channel's message history. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param ChannelId The channel ID to list from. + * @param Limit Max number of records to return. Between 1 and 100. + * @param Forward True if listing should be older messages to newer, false if reverse. + * @param Cursor A pagination cursor, if any. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListChannelMessages( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& ChannelId, + FNakamaOptionalInt32 Limit, + FNakamaOptionalBool Forward, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List a channel's message history. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param ChannelId The channel ID to list from. + * @param Limit Max number of records to return. Between 1 and 100. + * @param Forward True if listing should be older messages to newer, false if reverse. + * @param Cursor A pagination cursor, if any. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListChannelMessages( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& ChannelId, + FNakamaOptionalInt32 Limit, + FNakamaOptionalBool Forward, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List all friends for the current user. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Limit Max number of records to return. Between 1 and 1000. + * @param State The friend state to list. + * @param Cursor An optional next page cursor. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListFriends( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + FNakamaOptionalInt32 Limit, + FNakamaOptionalInt32 State, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List all friends for the current user. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Limit Max number of records to return. Between 1 and 1000. + * @param State The friend state to list. + * @param Cursor An optional next page cursor. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListFriends( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + FNakamaOptionalInt32 Limit, + FNakamaOptionalInt32 State, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List friends of friends for the current user. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Limit Max number of records to return. Between 1 and 100. + * @param Cursor An optional next page cursor. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListFriendsOfFriends( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List friends of friends for the current user. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Limit Max number of records to return. Between 1 and 100. + * @param Cursor An optional next page cursor. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListFriendsOfFriends( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List groups based on given filters. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Name List groups that contain this value in their names. + * @param Cursor Optional pagination cursor. + * @param Limit Max number of groups to return. Between 1 and 100. + * @param LangTag Language tag filter + * @param Members Number of group members + * @param Open Optional Open/Closed filter. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListGroups( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Name, + const FString& Cursor, + FNakamaOptionalInt32 Limit, + const FString& LangTag, + FNakamaOptionalInt32 Members, + FNakamaOptionalBool Open, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List groups based on given filters. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Name List groups that contain this value in their names. + * @param Cursor Optional pagination cursor. + * @param Limit Max number of groups to return. Between 1 and 100. + * @param LangTag Language tag filter + * @param Members Number of group members + * @param Open Optional Open/Closed filter. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListGroups( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Name, + const FString& Cursor, + FNakamaOptionalInt32 Limit, + const FString& LangTag, + FNakamaOptionalInt32 Members, + FNakamaOptionalBool Open, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List all users that are part of a group. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param GroupId The group ID to list from. + * @param Limit Max number of records to return. Between 1 and 100. + * @param State The group user state to list. + * @param Cursor An optional next page cursor. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListGroupUsers( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& GroupId, + FNakamaOptionalInt32 Limit, + FNakamaOptionalInt32 State, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List all users that are part of a group. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param GroupId The group ID to list from. + * @param Limit Max number of records to return. Between 1 and 100. + * @param State The group user state to list. + * @param Cursor An optional next page cursor. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListGroupUsers( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& GroupId, + FNakamaOptionalInt32 Limit, + FNakamaOptionalInt32 State, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List leaderboard records. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param LeaderboardId The ID of the leaderboard to list for. + * @param OwnerIds One or more owners to retrieve records for. + * @param Limit Max number of records to return. Between 1 and 100. + * @param Cursor A next or previous page cursor. + * @param Expiry Expiry in seconds (since epoch) to begin fetching records from. Optional. 0 means from current time. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListLeaderboardRecords( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& LeaderboardId, + const TArray& OwnerIds, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + FNakamaOptionalInt64 Expiry, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List leaderboard records. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param LeaderboardId The ID of the leaderboard to list for. + * @param OwnerIds One or more owners to retrieve records for. + * @param Limit Max number of records to return. Between 1 and 100. + * @param Cursor A next or previous page cursor. + * @param Expiry Expiry in seconds (since epoch) to begin fetching records from. Optional. 0 means from current time. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListLeaderboardRecords( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& LeaderboardId, + const TArray& OwnerIds, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + FNakamaOptionalInt64 Expiry, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List leaderboard records around the target ownerId. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param LeaderboardId The ID of the tournament to list for. + * @param Limit Max number of records to return. Between 1 and 100. + * @param OwnerId The owner to retrieve records around. + * @param Expiry Expiry in seconds (since epoch) to begin fetching records from. + * @param Cursor A next or previous page cursor. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListLeaderboardRecordsAroundOwner( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& LeaderboardId, + FNakamaOptionalInt32 Limit, + const FString& OwnerId, + FNakamaOptionalInt64 Expiry, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List leaderboard records around the target ownerId. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param LeaderboardId The ID of the tournament to list for. + * @param Limit Max number of records to return. Between 1 and 100. + * @param OwnerId The owner to retrieve records around. + * @param Expiry Expiry in seconds (since epoch) to begin fetching records from. + * @param Cursor A next or previous page cursor. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListLeaderboardRecordsAroundOwner( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& LeaderboardId, + FNakamaOptionalInt32 Limit, + const FString& OwnerId, + FNakamaOptionalInt64 Expiry, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List running matches and optionally filter by matching criteria. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Limit Limit the number of returned matches. + * @param Authoritative Authoritative or relayed matches. + * @param Label Label filter. + * @param MinSize Minimum user count. + * @param MaxSize Maximum user count. + * @param Query Arbitrary label query. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListMatches( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + FNakamaOptionalInt32 Limit, + FNakamaOptionalBool Authoritative, + const FString& Label, + FNakamaOptionalInt32 MinSize, + FNakamaOptionalInt32 MaxSize, + const FString& Query, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List running matches and optionally filter by matching criteria. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Limit Limit the number of returned matches. + * @param Authoritative Authoritative or relayed matches. + * @param Label Label filter. + * @param MinSize Minimum user count. + * @param MaxSize Maximum user count. + * @param Query Arbitrary label query. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListMatches( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + FNakamaOptionalInt32 Limit, + FNakamaOptionalBool Authoritative, + const FString& Label, + FNakamaOptionalInt32 MinSize, + FNakamaOptionalInt32 MaxSize, + const FString& Query, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List parties and optionally filter by matching criteria. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Limit Limit the number of returned parties. + * @param Open Optionally filter by open/closed parties. + * @param Query Arbitrary label query. + * @param Cursor Cursor for the next page of results, if any. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListParties( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + FNakamaOptionalInt32 Limit, + FNakamaOptionalBool Open, + const FString& Query, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List parties and optionally filter by matching criteria. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Limit Limit the number of returned parties. + * @param Open Optionally filter by open/closed parties. + * @param Query Arbitrary label query. + * @param Cursor Cursor for the next page of results, if any. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListParties( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + FNakamaOptionalInt32 Limit, + FNakamaOptionalBool Open, + const FString& Query, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Fetch list of notifications. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Limit The number of notifications to get. Between 1 and 100. + * @param CacheableCursor A cursor to page through notifications. May be cached by clients to get from point in time forwards. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListNotifications( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + FNakamaOptionalInt32 Limit, + const FString& CacheableCursor, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Fetch list of notifications. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Limit The number of notifications to get. Between 1 and 100. + * @param CacheableCursor A cursor to page through notifications. May be cached by clients to get from point in time forwards. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListNotifications( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + FNakamaOptionalInt32 Limit, + const FString& CacheableCursor, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List publicly readable storage objects in a given collection. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param UserId ID of the user. + * @param Collection The collection which stores the object. + * @param Limit The number of storage objects to list. Between 1 and 100. + * @param Cursor The cursor to page through results from. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListStorageObjects( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& UserId, + const FString& Collection, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List publicly readable storage objects in a given collection. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param UserId ID of the user. + * @param Collection The collection which stores the object. + * @param Limit The number of storage objects to list. Between 1 and 100. + * @param Cursor The cursor to page through results from. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListStorageObjects( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& UserId, + const FString& Collection, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List user's subscriptions. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Limit Max number of results per page + * @param Cursor Cursor to retrieve a page of records from + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListSubscriptions( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List user's subscriptions. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Limit Max number of results per page + * @param Cursor Cursor to retrieve a page of records from + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListSubscriptions( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List current or upcoming tournaments. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param CategoryStart The start of the categories to include. Defaults to 0. + * @param CategoryEnd The end of the categories to include. Defaults to 128. + * @param StartTime The start time for tournaments. Defaults to epoch. + * @param EndTime The end time for tournaments. Defaults to +1 year from current Unix time. + * @param Limit Max number of records to return. Between 1 and 100. + * @param Cursor A next page cursor for listings (optional). + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListTournaments( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + FNakamaOptionalInt32 CategoryStart, + FNakamaOptionalInt32 CategoryEnd, + FNakamaOptionalInt32 StartTime, + FNakamaOptionalInt32 EndTime, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List current or upcoming tournaments. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param CategoryStart The start of the categories to include. Defaults to 0. + * @param CategoryEnd The end of the categories to include. Defaults to 128. + * @param StartTime The start time for tournaments. Defaults to epoch. + * @param EndTime The end time for tournaments. Defaults to +1 year from current Unix time. + * @param Limit Max number of records to return. Between 1 and 100. + * @param Cursor A next page cursor for listings (optional). + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListTournaments( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + FNakamaOptionalInt32 CategoryStart, + FNakamaOptionalInt32 CategoryEnd, + FNakamaOptionalInt32 StartTime, + FNakamaOptionalInt32 EndTime, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List tournament records. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param TournamentId The ID of the tournament to list for. + * @param OwnerIds One or more owners to retrieve records for. + * @param Limit Max number of records to return. Between 1 and 100. + * @param Cursor A next or previous page cursor. + * @param Expiry Expiry in seconds (since epoch) to begin fetching records from. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListTournamentRecords( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& TournamentId, + const TArray& OwnerIds, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + FNakamaOptionalInt64 Expiry, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List tournament records. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param TournamentId The ID of the tournament to list for. + * @param OwnerIds One or more owners to retrieve records for. + * @param Limit Max number of records to return. Between 1 and 100. + * @param Cursor A next or previous page cursor. + * @param Expiry Expiry in seconds (since epoch) to begin fetching records from. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListTournamentRecords( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& TournamentId, + const TArray& OwnerIds, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + FNakamaOptionalInt64 Expiry, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List tournament records for a given owner. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param TournamentId The ID of the tournament to list for. + * @param Limit Max number of records to return. Between 1 and 100. + * @param OwnerId The owner to retrieve records around. + * @param Expiry Expiry in seconds (since epoch) to begin fetching records from. + * @param Cursor A next or previous page cursor. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListTournamentRecordsAroundOwner( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& TournamentId, + FNakamaOptionalInt32 Limit, + const FString& OwnerId, + FNakamaOptionalInt64 Expiry, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List tournament records for a given owner. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param TournamentId The ID of the tournament to list for. + * @param Limit Max number of records to return. Between 1 and 100. + * @param OwnerId The owner to retrieve records around. + * @param Expiry Expiry in seconds (since epoch) to begin fetching records from. + * @param Cursor A next or previous page cursor. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListTournamentRecordsAroundOwner( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& TournamentId, + FNakamaOptionalInt32 Limit, + const FString& OwnerId, + FNakamaOptionalInt64 Expiry, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List groups the current user belongs to. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param UserId ID of the user. + * @param Limit Max number of records to return. Between 1 and 100. + * @param State The user group state to list. + * @param Cursor An optional next page cursor. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListUserGroups( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& UserId, + FNakamaOptionalInt32 Limit, + FNakamaOptionalInt32 State, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List groups the current user belongs to. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param UserId ID of the user. + * @param Limit Max number of records to return. Between 1 and 100. + * @param State The user group state to list. + * @param Cursor An optional next page cursor. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ListUserGroups( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& UserId, + FNakamaOptionalInt32 Limit, + FNakamaOptionalInt32 State, + const FString& Cursor, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Promote a set of users in a group to the next role up. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param GroupId The group ID to promote in. + * @param UserIds The users to promote. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture PromoteGroupUsers( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& GroupId, + const TArray& UserIds, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Promote a set of users in a group to the next role up. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param GroupId The group ID to promote in. + * @param UserIds The users to promote. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture PromoteGroupUsers( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& GroupId, + const TArray& UserIds, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Demote a set of users in a group to the next role down. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param GroupId The group ID to demote in. + * @param UserIds The users to demote. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture DemoteGroupUsers( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& GroupId, + const TArray& UserIds, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Demote a set of users in a group to the next role down. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param GroupId The group ID to demote in. + * @param UserIds The users to demote. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture DemoteGroupUsers( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& GroupId, + const TArray& UserIds, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Get storage objects. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param ObjectIds Batch of storage objects. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ReadStorageObjects( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& ObjectIds, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Get storage objects. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param ObjectIds Batch of storage objects. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ReadStorageObjects( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const TArray& ObjectIds, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Execute a Lua function on the server. + * + * @param Config The client configuration. + * @param Id The identifier of the function. + * @param Payload The payload of the function which must be a JSON object. + * @param HttpKey The authentication key used when executed as a non-client HTTP request. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture RpcFunc( + const FNakamaClientConfig& ClientConfig, + const FString& Id, + const FString& Payload, + const FString& HttpKey, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Execute a Lua function on the server. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Id The identifier of the function. + * @param Payload The payload of the function which must be a JSON object. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture RpcFunc( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Id, + const FString& Payload, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Remove the Apple ID from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Token The ID token received from Apple to validate. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture UnlinkApple( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Remove the Apple ID from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Token The ID token received from Apple to validate. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture UnlinkApple( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Remove the custom ID from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Id A custom identifier. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture UnlinkCustom( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Id, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Remove the custom ID from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Id A custom identifier. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture UnlinkCustom( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Id, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Remove the device ID from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Id A device identifier. Should be obtained by a platform-specific device API. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture UnlinkDevice( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Id, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Remove the device ID from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Id A device identifier. Should be obtained by a platform-specific device API. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture UnlinkDevice( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Id, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Remove the email+password from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Email A valid RFC-5322 email address. + * @param Password A password for the user account. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture UnlinkEmail( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Email, + const FString& Password, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Remove the email+password from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Email A valid RFC-5322 email address. + * @param Password A password for the user account. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture UnlinkEmail( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Email, + const FString& Password, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Remove Facebook from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Token The OAuth token received from Facebook to access their profile API. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture UnlinkFacebook( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Remove Facebook from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Token The OAuth token received from Facebook to access their profile API. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture UnlinkFacebook( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Remove Facebook Instant Game profile from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param SignedPlayerInfo The OAuth token received from a Facebook Instant Game that may be decoded with the Application Secret (must be available with the nakama configuration) + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture UnlinkFacebookInstantGame( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& SignedPlayerInfo, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Remove Facebook Instant Game profile from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param SignedPlayerInfo The OAuth token received from a Facebook Instant Game that may be decoded with the Application Secret (must be available with the nakama configuration) + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture UnlinkFacebookInstantGame( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& SignedPlayerInfo, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Remove Apple's GameCenter from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param PlayerId Player ID (generated by GameCenter). + * @param BundleId Bundle ID (generated by GameCenter). + * @param TimestampSeconds Time since UNIX epoch when the signature was created. + * @param Salt A random "NSString" used to compute the hash and keep it randomized. + * @param Signature The verification signature data generated. + * @param PublicKeyUrl The URL for the public encryption key. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture UnlinkGameCenter( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& PlayerId, + const FString& BundleId, + int64 TimestampSeconds, + const FString& Salt, + const FString& Signature, + const FString& PublicKeyUrl, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Remove Apple's GameCenter from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param PlayerId Player ID (generated by GameCenter). + * @param BundleId Bundle ID (generated by GameCenter). + * @param TimestampSeconds Time since UNIX epoch when the signature was created. + * @param Salt A random "NSString" used to compute the hash and keep it randomized. + * @param Signature The verification signature data generated. + * @param PublicKeyUrl The URL for the public encryption key. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture UnlinkGameCenter( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& PlayerId, + const FString& BundleId, + int64 TimestampSeconds, + const FString& Salt, + const FString& Signature, + const FString& PublicKeyUrl, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Remove Google from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Token The OAuth token received from Google to access their profile API. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture UnlinkGoogle( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Remove Google from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Token The OAuth token received from Google to access their profile API. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture UnlinkGoogle( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Remove Steam from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Token The account token received from Steam to access their profile API. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture UnlinkSteam( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Remove Steam from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Token The account token received from Steam to access their profile API. + * @param Vars Extra information that will be bundled in the session token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture UnlinkSteam( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Token, + const TMap& Vars, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Update fields in the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Username The username of the user's account. + * @param DisplayName The display name of the user. + * @param AvatarUrl A URL for an avatar image. + * @param LangTag The language expected to be a tag which follows the BCP-47 spec. + * @param Location The location set by the user. + * @param Timezone The timezone set by the user. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture UpdateAccount( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Username, + const FString& DisplayName, + const FString& AvatarUrl, + const FString& LangTag, + const FString& Location, + const FString& Timezone, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Update fields in the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Username The username of the user's account. + * @param DisplayName The display name of the user. + * @param AvatarUrl A URL for an avatar image. + * @param LangTag The language expected to be a tag which follows the BCP-47 spec. + * @param Location The location set by the user. + * @param Timezone The timezone set by the user. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture UpdateAccount( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Username, + const FString& DisplayName, + const FString& AvatarUrl, + const FString& LangTag, + const FString& Location, + const FString& Timezone, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Update fields in a given group. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param GroupId The ID of the group to update. + * @param Name Name. + * @param Description Description string. + * @param LangTag Lang tag. + * @param AvatarUrl Avatar URL. + * @param Open Open is true if anyone should be allowed to join, or false if joins must be approved by a group admin. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture UpdateGroup( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& GroupId, + const FString& Name, + const FString& Description, + const FString& LangTag, + const FString& AvatarUrl, + FNakamaOptionalBool Open, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Update fields in a given group. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param GroupId The ID of the group to update. + * @param Name Name. + * @param Description Description string. + * @param LangTag Lang tag. + * @param AvatarUrl Avatar URL. + * @param Open Open is true if anyone should be allowed to join, or false if joins must be approved by a group admin. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture UpdateGroup( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& GroupId, + const FString& Name, + const FString& Description, + const FString& LangTag, + const FString& AvatarUrl, + FNakamaOptionalBool Open, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Validate Apple IAP Receipt + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Receipt Base64 encoded Apple receipt data payload. + * @param Persist Persist the purchase + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ValidatePurchaseApple( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Receipt, + FNakamaOptionalBool Persist, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Validate Apple IAP Receipt + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Receipt Base64 encoded Apple receipt data payload. + * @param Persist Persist the purchase + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ValidatePurchaseApple( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Receipt, + FNakamaOptionalBool Persist, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Validate Apple Subscription Receipt + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Receipt Base64 encoded Apple receipt data payload. + * @param Persist Persist the subscription. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ValidateSubscriptionApple( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Receipt, + FNakamaOptionalBool Persist, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Validate Apple Subscription Receipt + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Receipt Base64 encoded Apple receipt data payload. + * @param Persist Persist the subscription. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ValidateSubscriptionApple( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Receipt, + FNakamaOptionalBool Persist, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Validate Google IAP Receipt + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Purchase JSON encoded Google purchase payload. + * @param Persist Persist the purchase + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ValidatePurchaseGoogle( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Purchase, + FNakamaOptionalBool Persist, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Validate Google IAP Receipt + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Purchase JSON encoded Google purchase payload. + * @param Persist Persist the purchase + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ValidatePurchaseGoogle( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Purchase, + FNakamaOptionalBool Persist, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Validate Google Subscription Receipt + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Receipt JSON encoded Google purchase payload. + * @param Persist Persist the subscription. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ValidateSubscriptionGoogle( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Receipt, + FNakamaOptionalBool Persist, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Validate Google Subscription Receipt + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Receipt JSON encoded Google purchase payload. + * @param Persist Persist the subscription. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ValidateSubscriptionGoogle( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Receipt, + FNakamaOptionalBool Persist, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Validate Huawei IAP Receipt + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Purchase JSON encoded Huawei InAppPurchaseData. + * @param Signature InAppPurchaseData signature. + * @param Persist Persist the purchase + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ValidatePurchaseHuawei( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Purchase, + const FString& Signature, + FNakamaOptionalBool Persist, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Validate Huawei IAP Receipt + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Purchase JSON encoded Huawei InAppPurchaseData. + * @param Signature InAppPurchaseData signature. + * @param Persist Persist the purchase + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ValidatePurchaseHuawei( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& Purchase, + const FString& Signature, + FNakamaOptionalBool Persist, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Validate FB Instant IAP Receipt + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param SignedRequest Base64 encoded Facebook Instant signedRequest receipt data payload. + * @param Persist Persist the purchase + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ValidatePurchaseFacebookInstant( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& SignedRequest, + FNakamaOptionalBool Persist, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Validate FB Instant IAP Receipt + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param SignedRequest Base64 encoded Facebook Instant signedRequest receipt data payload. + * @param Persist Persist the purchase + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture ValidatePurchaseFacebookInstant( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& SignedRequest, + FNakamaOptionalBool Persist, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Write a record to a leaderboard. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param LeaderboardId The ID of the leaderboard to write to. + * @param RecordScore The score value to submit. + * @param RecordSubscore An optional secondary value. + * @param RecordMetadata Optional record metadata. + * @param RecordOperator Operator override. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture WriteLeaderboardRecord( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& LeaderboardId, + int64 RecordScore, + int64 RecordSubscore, + const FString& RecordMetadata, + ENakamaOperator RecordOperator, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Write a record to a leaderboard. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param LeaderboardId The ID of the leaderboard to write to. + * @param RecordScore The score value to submit. + * @param RecordSubscore An optional secondary value. + * @param RecordMetadata Optional record metadata. + * @param RecordOperator Operator override. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture WriteLeaderboardRecord( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& LeaderboardId, + int64 RecordScore, + int64 RecordSubscore, + const FString& RecordMetadata, + ENakamaOperator RecordOperator, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Write objects into the storage engine. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Objects The objects to store on the server. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture WriteStorageObjects( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& Objects, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Write objects into the storage engine. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Objects The objects to store on the server. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture WriteStorageObjects( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const TArray& Objects, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Write a record to a tournament. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param TournamentId The tournament ID to write the record for. + * @param RecordScore The score value to submit. + * @param RecordSubscore An optional secondary value. + * @param RecordMetadata A JSON object of additional properties (optional). + * @param RecordOperator Operator override. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture WriteTournamentRecord( + const FNakamaClientConfig& ClientConfig, + const FString& HttpKey, + const FString& TournamentId, + int64 RecordScore, + int64 RecordSubscore, + const FString& RecordMetadata, + ENakamaOperator RecordOperator, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Write a record to a tournament. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param TournamentId The tournament ID to write the record for. + * @param RecordScore The score value to submit. + * @param RecordSubscore An optional secondary value. + * @param RecordMetadata A JSON object of additional properties (optional). + * @param RecordOperator Operator override. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + NAKAMA_API TNakamaFuture WriteTournamentRecord( + const FNakamaClientConfig& ClientConfig, + const FNakamaSession& Session, + const FString& TournamentId, + int64 RecordScore, + int64 RecordSubscore, + const FString& RecordMetadata, + ENakamaOperator RecordOperator, + const FNakamaRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); +} diff --git a/Nakama/Source/Nakama/Public/NakamaClientConfig.h b/Nakama/Source/Nakama/Public/NakamaClientConfig.h new file mode 100644 index 000000000..243bd81f1 --- /dev/null +++ b/Nakama/Source/Nakama/Public/NakamaClientConfig.h @@ -0,0 +1,56 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "CoreMinimal.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "NakamaClientConfig.generated.h" + +/** Low-level API client configuration. */ +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaClientConfig +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nakama") + FString ServerKey; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nakama") + FString Host; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nakama") + int32 Port = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nakama") + bool bUseSSL = false; + + FString GetBaseUrl() const noexcept; +}; + +UCLASS() +class NAKAMA_API UNakamaClientConfigFunctions : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + + UFUNCTION(BlueprintPure, Category = "Nakama|ClientConfig") + static FString GetBaseUrl(const FNakamaClientConfig& Config) + { + return Config.GetBaseUrl(); + } +}; diff --git a/Nakama/Source/Nakama/Public/NakamaError.h b/Nakama/Source/Nakama/Public/NakamaError.h new file mode 100644 index 000000000..09433670e --- /dev/null +++ b/Nakama/Source/Nakama/Public/NakamaError.h @@ -0,0 +1,53 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "NakamaError.generated.h" + +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaError +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Message; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 Code = 0; +}; + +/** gRPC status codes returned by Nakama in FNakamaError::Code. */ +namespace NakamaErrorCode +{ + constexpr int32 OK = 0; + constexpr int32 Cancelled = 1; + constexpr int32 Unknown = 2; + constexpr int32 InvalidArgument = 3; + constexpr int32 DeadlineExceeded = 4; + constexpr int32 NotFound = 5; + constexpr int32 AlreadyExists = 6; + constexpr int32 PermissionDenied = 7; + constexpr int32 ResourceExhausted = 8; + constexpr int32 FailedPrecondition = 9; + constexpr int32 Aborted = 10; + constexpr int32 OutOfRange = 11; + constexpr int32 Unimplemented = 12; + constexpr int32 Internal = 13; + constexpr int32 Unavailable = 14; + constexpr int32 DataLoss = 15; + constexpr int32 Unauthenticated = 16; +} diff --git a/Nakama/Source/Nakama/Public/NakamaExtraEnums.h b/Nakama/Source/Nakama/Public/NakamaExtraEnums.h new file mode 100644 index 000000000..e490e88da --- /dev/null +++ b/Nakama/Source/Nakama/Public/NakamaExtraEnums.h @@ -0,0 +1,77 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "CoreMinimal.h" +#include "NakamaExtraEnums.generated.h" + +UENUM(BlueprintType) +enum class ENakamaChannelMessageCode : uint8 +{ + CHANNEL_MESSAGE_TYPE_CHAT = 0 UMETA(DisplayName = "Chat"), + CHANNEL_MESSAGE_TYPE_CHAT_UPDATE = 1 UMETA(DisplayName = "Chat Update"), + CHANNEL_MESSAGE_TYPE_CHAT_REMOVE = 2 UMETA(DisplayName = "Chat Remove"), + CHANNEL_MESSAGE_TYPE_GROUP_JOIN = 3 UMETA(DisplayName = "Group Join"), + CHANNEL_MESSAGE_TYPE_GROUP_ADD = 4 UMETA(DisplayName = "Group Add"), + CHANNEL_MESSAGE_TYPE_GROUP_LEAVE = 5 UMETA(DisplayName = "Group Leave"), + CHANNEL_MESSAGE_TYPE_GROUP_KICK = 6 UMETA(DisplayName = "Group Kick"), + CHANNEL_MESSAGE_TYPE_GROUP_PROMOTE = 7 UMETA(DisplayName = "Group Promote"), + CHANNEL_MESSAGE_TYPE_GROUP_BAN = 8 UMETA(DisplayName = "Group Ban"), + CHANNEL_MESSAGE_TYPE_GROUP_DEMOTE = 9 UMETA(DisplayName = "Group Demote"), +}; + +enum class ENakamaNotificationCode : int32 +{ + NOTIFICATION_CODE_UNKNOWN = 0, + NOTIFICATION_CODE_DM_REQUEST = -1, + NOTIFICATION_CODE_FRIEND_REQUEST = -2, + NOTIFICATION_CODE_FRIEND_ACCEPT = -3, + NOTIFICATION_CODE_GROUP_ADD = -4, + NOTIFICATION_CODE_GROUP_JOIN_REQUEST = -5, + NOTIFICATION_CODE_FRIEND_JOIN_GAME = -6, + NOTIFICATION_CODE_SINGLE_SOCKET = -7, + NOTIFICATION_CODE_USER_BANNED = -8, + NOTIFICATION_CODE_FRIEND_REMOVE = -9, +}; + +UENUM(BlueprintType) +enum class ENakamaStoragePermissionRead : uint8 +{ + STORAGE_PERMISSION_READ_NO_READ = 0 UMETA(DisplayName = "No Read"), + STORAGE_PERMISSION_READ_OWNER_READ = 1 UMETA(DisplayName = "Owner Read"), + STORAGE_PERMISSION_READ_PUBLIC_READ = 2 UMETA(DisplayName = "Public Read"), +}; + +UENUM(BlueprintType) +enum class ENakamaStoragePermissionWrite : uint8 +{ + STORAGE_PERMISSION_WRITE_NO_WRITE = 0 UMETA(DisplayName = "No Write"), + STORAGE_PERMISSION_WRITE_OWNER_WRITE = 1 UMETA(DisplayName = "Owner Write"), +}; + +UENUM(BlueprintType) +enum class ENakamaStreamMode : uint8 +{ + STREAM_MODE_NOTIFICATIONS = 0 UMETA(DisplayName = "Notifications"), + STREAM_MODE_STATUS = 1 UMETA(DisplayName = "Status"), + STREAM_MODE_CHANNEL = 2 UMETA(DisplayName = "Channel"), + STREAM_MODE_GROUP = 3 UMETA(DisplayName = "Group"), + STREAM_MODE_DM = 4 UMETA(DisplayName = "DM"), + STREAM_MODE_MATCH_RELAYED = 5 UMETA(DisplayName = "Match Relayed"), + STREAM_MODE_MATCH_AUTHORITATIVE = 6 UMETA(DisplayName = "Match Authoritative"), + STREAM_MODE_PARTY = 7 UMETA(DisplayName = "Party"), +}; diff --git a/Nakama/Source/Nakama/Public/NakamaFuture.h b/Nakama/Source/Nakama/Public/NakamaFuture.h new file mode 100644 index 000000000..cc373b253 --- /dev/null +++ b/Nakama/Source/Nakama/Public/NakamaFuture.h @@ -0,0 +1,40 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "AsyncFuture.h" + +/** + * Nakama-flavoured alias for TAsyncFuture. + * All three .Next() overloads are inherited from TAsyncFuture and dispatch + * user callbacks to the game thread. + */ +template +using TNakamaFuture = TAsyncFuture; + +/** Type trait for TNakamaFuture (delegates to TIsTAsyncFuture). */ +template using TIsTNakamaFuture = TIsTAsyncFuture; + +/** + * Create a pre-resolved TNakamaFuture. + * Thin wrapper around MakeCompletedAsyncFuture for backward compatibility. + */ +template +TNakamaFuture MakeCompletedFuture(ResultT Value) +{ + return MakeCompletedAsyncFuture(MoveTemp(Value)); +} diff --git a/Nakama/Source/Nakama/Public/NakamaHttpHelper.h b/Nakama/Source/Nakama/Public/NakamaHttpHelper.h new file mode 100644 index 000000000..436d54606 --- /dev/null +++ b/Nakama/Source/Nakama/Public/NakamaHttpHelper.h @@ -0,0 +1,254 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "CoreMinimal.h" +#include "Dom/JsonObject.h" +#include "HttpModule.h" +#include "Interfaces/IHttpRequest.h" +#include "Interfaces/IHttpResponse.h" +#include "Misc/Base64.h" +#include "Misc/DateTime.h" +#include "Serialization/JsonReader.h" +#include "Serialization/JsonSerializer.h" +#include "Serialization/JsonWriter.h" +#include "Serialization/MemoryWriter.h" +#include "Templates/Function.h" +#include "Templates/SharedPointer.h" +#include "NakamaError.h" +#include "NakamaClientConfig.h" +#include "NakamaTypes.h" + +/** + * Internal HTTP helpers for NakamaApi. + * All functions are typed against FNakamaClientConfig and ENakamaRequestAuth. + * + * Include this header in generated .cpp files only — not in public API headers. + */ +namespace NakamaHttpInternal +{ + +inline FString SerializeJsonToString(const TSharedPtr& Json) +{ + TArray JsonBytes; + JsonBytes.Reserve(1024); + FMemoryWriter Archive(JsonBytes); + TSharedRef>> Writer = + TJsonWriterFactory>::Create(&Archive); + FJsonSerializer::Serialize(Json.ToSharedRef(), Writer); + Writer->Close(); + FUTF8ToTCHAR Converter(reinterpret_cast(JsonBytes.GetData()), JsonBytes.Num()); + return FString(Converter.Length(), Converter.Get()); +} + +inline FString SerializeJsonEscaped(const TSharedPtr& Json) +{ + TArray JsonBytes; + JsonBytes.Reserve(2048); + FMemoryWriter Archive(JsonBytes); + TSharedRef>> Writer = + TJsonWriterFactory>::Create(&Archive); + FJsonSerializer::Serialize(Json.ToSharedRef(), Writer); + Writer->Close(); + FUTF8ToTCHAR Converter(reinterpret_cast(JsonBytes.GetData()), JsonBytes.Num()); + FString Condensed(Converter.Length(), Converter.Get()); + FString Escaped = Condensed.Replace(TEXT("\\"), TEXT("\\\\")).Replace(TEXT("\""), TEXT("\\\"")); + return TEXT("\"") + Escaped + TEXT("\""); +} + +inline void DoHttpRequest( + const FNakamaClientConfig& Config, + const FString& Endpoint, + const FString& Method, + const FString& BodyString, + ENakamaRequestAuth AuthType, + const FString& TokenString, + TFunction)> OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken) noexcept +{ + const FString Url = Config.GetBaseUrl() + Endpoint; + + TSharedRef Request = FHttpModule::Get().CreateRequest(); + Request->SetURL(Url); + Request->SetVerb(Method); + Request->SetHeader(TEXT("Content-Type"), TEXT("application/json")); + Request->SetHeader(TEXT("Accept"), TEXT("application/json")); + + switch (AuthType) + { + case ENakamaRequestAuth::Basic: + { + const FString Auth = FString::Printf(TEXT("%s:"), *Config.ServerKey); + Request->SetHeader(TEXT("Authorization"), FString::Printf(TEXT("Basic %s"), *FBase64::Encode(Auth))); + } + break; + case ENakamaRequestAuth::Bearer: + if (!TokenString.IsEmpty()) + { + Request->SetHeader(TEXT("Authorization"), FString::Printf(TEXT("Bearer %s"), *TokenString)); + } + break; + case ENakamaRequestAuth::HttpKey: + if (!TokenString.IsEmpty()) + { + const FString Auth = FString::Printf(TEXT("%s:"), *TokenString); + Request->SetHeader(TEXT("Authorization"), FString::Printf(TEXT("Basic %s"), *FBase64::Encode(Auth))); + } + break; + case ENakamaRequestAuth::None: + default: + break; + } + + if (!BodyString.IsEmpty() && Method != TEXT("GET")) + { + Request->SetContentAsString(BodyString); + } + + Request->SetTimeout(Timeout); + + Request->OnProcessRequestComplete().BindLambda( + [OnSuccess, OnError, CancellationToken](FHttpRequestPtr Req, FHttpResponsePtr Res, bool bSuccess) + { + if (CancellationToken->Load()) + { + if (OnError) + { + OnError(FNakamaError(TEXT("Request cancelled"), -1)); + } + return; + } + + if (!bSuccess || !Res.IsValid()) + { + if (OnError) + { + OnError(FNakamaError(TEXT("Connection failed"), 0)); + } + return; + } + + const int32 Code = Res->GetResponseCode(); + const FString Content = Res->GetContentAsString(); + + if (Code < 200 || Code >= 300) + { + FString ErrorMsg = FString::Printf(TEXT("HTTP %d"), Code); + int32 ErrorCode = Code; + TSharedPtr Json; + if (FJsonSerializer::Deserialize(TJsonReaderFactory<>::Create(Content), Json) && Json.IsValid()) + { + if (Json->HasField(TEXT("message"))) + { + ErrorMsg = Json->GetStringField(TEXT("message")); + } + if (Json->HasField(TEXT("code"))) + { + ErrorCode = static_cast(Json->GetNumberField(TEXT("code"))); + } + } + if (OnError) + { + OnError(FNakamaError(ErrorMsg, ErrorCode)); + } + return; + } + + TSharedPtr Json; + if (!Content.IsEmpty()) + { + if (!FJsonSerializer::Deserialize(TJsonReaderFactory<>::Create(Content), Json) || !Json.IsValid()) + { + if (OnError) + { + OnError(FNakamaError(TEXT("Invalid JSON response"), 500)); + } + return; + } + } + + if (OnSuccess) + { + OnSuccess(Json); + } + }); + + Request->ProcessRequest(); +} + +inline FString BuildQueryString(const TArray>& QueryParams) +{ + if (QueryParams.Num() == 0) return FString{}; + TArray Parts; + Parts.Reserve(QueryParams.Num()); + for (const auto& P : QueryParams) + { + Parts.Add(P.Key + TEXT("=") + FGenericPlatformHttp::UrlEncode(P.Value)); + } + return TEXT("?") + FString::Join(Parts, TEXT("&")); +} + +inline void SendRequest( + const FNakamaClientConfig& Config, + const FString& Endpoint, + const TArray>& QueryParams, + const FString& Method, + const FString& BodyString, + ENakamaRequestAuth AuthType, + const FString& BearerToken, + TFunction)> OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken) noexcept +{ + if (CancellationToken->Load()) + { + if (OnError) + { + OnError(FNakamaError(TEXT("Request cancelled"), -1)); + } + return; + } + + DoHttpRequest(Config, Endpoint + BuildQueryString(QueryParams), Method, BodyString, AuthType, BearerToken, OnSuccess, OnError, Timeout, CancellationToken); +} + +inline void MakeRequest( + const FNakamaClientConfig& Config, + const FString& Endpoint, + const TArray>& QueryParams, + const FString& Method, + const TSharedPtr& Body, + ENakamaRequestAuth AuthType, + const FString& BearerToken, + TFunction)> OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) noexcept +{ + FString BodyString; + if (Body.IsValid() && Method != TEXT("GET")) + { + BodyString = SerializeJsonToString(Body); + } + SendRequest(Config, Endpoint, QueryParams, Method, BodyString, AuthType, BearerToken, OnSuccess, OnError, Timeout, CancellationToken); +} + +} // namespace NakamaHttpInternal diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaUnreal.h b/Nakama/Source/Nakama/Public/NakamaModule.h similarity index 80% rename from Nakama/Source/NakamaUnreal/Public/NakamaUnreal.h rename to Nakama/Source/Nakama/Public/NakamaModule.h index 2ec15a0bc..6f052ca01 100644 --- a/Nakama/Source/NakamaUnreal/Public/NakamaUnreal.h +++ b/Nakama/Source/Nakama/Public/NakamaModule.h @@ -1,5 +1,5 @@ /* -* Copyright 2025 The Nakama Authors + * Copyright 2026 The Nakama Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,15 @@ #pragma once -#include "Modules/ModuleInterface.h" +#include "Modules/ModuleManager.h" -class FNakamaUnrealModule : public IModuleInterface +// +// Module implementation +class FNakamaModule : public IModuleInterface { public: - /** IModuleInterface implementation */ virtual void StartupModule() override; virtual void ShutdownModule() override; }; + + diff --git a/Nakama/Source/Nakama/Public/NakamaOptionals.h b/Nakama/Source/Nakama/Public/NakamaOptionals.h new file mode 100644 index 000000000..250a1bf75 --- /dev/null +++ b/Nakama/Source/Nakama/Public/NakamaOptionals.h @@ -0,0 +1,159 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "CoreMinimal.h" +#include "NakamaOptionals.generated.h" + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaOptionalBool +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Optional") + bool bIsSet = false; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Optional") + bool Value = false; + + FNakamaOptionalBool() = default; + FNakamaOptionalBool(bool InValue) : bIsSet(true), Value(InValue) {} + + bool IsSet() const { return bIsSet; } + bool IsEmpty() const { return !bIsSet; } + bool GetValue() const { return Value; } +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaOptionalInt32 +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Optional") + bool bIsSet = false; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Optional") + int32 Value = 0; + + FNakamaOptionalInt32() = default; + FNakamaOptionalInt32(int32 InValue) : bIsSet(true), Value(InValue) {} + FNakamaOptionalInt32(double InValue) : bIsSet(true), Value(static_cast(InValue)) {} + + bool IsSet() const { return bIsSet; } + bool IsEmpty() const { return !bIsSet; } + int32 GetValue() const { return Value; } +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaOptionalInt64 +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Optional") + bool bIsSet = false; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Optional") + int64 Value = 0; + + FNakamaOptionalInt64() = default; + FNakamaOptionalInt64(int32 InValue) : bIsSet(true), Value(static_cast(InValue)) {} + FNakamaOptionalInt64(int64 InValue) : bIsSet(true), Value(InValue) {} + FNakamaOptionalInt64(double InValue) : bIsSet(true), Value(static_cast(InValue)) {} + + bool IsSet() const { return bIsSet; } + bool IsEmpty() const { return !bIsSet; } + int64 GetValue() const { return Value; } +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaOptionalFloat +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Optional") + bool bIsSet = false; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Optional") + float Value = 0.f; + + FNakamaOptionalFloat() = default; + FNakamaOptionalFloat(float InValue) : bIsSet(true), Value(InValue) {} + FNakamaOptionalFloat(double InValue) : bIsSet(true), Value(static_cast(InValue)) {} + + bool IsSet() const { return bIsSet; } + bool IsEmpty() const { return !bIsSet; } + float GetValue() const { return Value; } +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaOptionalDouble +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Optional") + bool bIsSet = false; + + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Optional") + double Value = 0.0; + + FNakamaOptionalDouble() = default; + FNakamaOptionalDouble(double InValue) : bIsSet(true), Value(InValue) {} + + bool IsSet() const { return bIsSet; } + bool IsEmpty() const { return !bIsSet; } + double GetValue() const { return Value; } +}; + +// +// Aliases for Realtime +// + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaRtOptionalBool : public FNakamaOptionalBool +{ + GENERATED_BODY() + using FNakamaOptionalBool::FNakamaOptionalBool; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaRtOptionalInt32 : public FNakamaOptionalInt32 +{ + GENERATED_BODY() + using FNakamaOptionalInt32::FNakamaOptionalInt32; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaRtOptionalInt64 : public FNakamaOptionalInt64 +{ + GENERATED_BODY() + using FNakamaOptionalInt64::FNakamaOptionalInt64; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaRtOptionalFloat : public FNakamaOptionalFloat +{ + GENERATED_BODY() + using FNakamaOptionalFloat::FNakamaOptionalFloat; +}; + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaRtOptionalDouble : public FNakamaOptionalDouble +{ + GENERATED_BODY() + using FNakamaOptionalDouble::FNakamaOptionalDouble; +}; + diff --git a/Nakama/Source/Nakama/Public/NakamaRt.h b/Nakama/Source/Nakama/Public/NakamaRt.h new file mode 100644 index 000000000..5f4e00546 --- /dev/null +++ b/Nakama/Source/Nakama/Public/NakamaRt.h @@ -0,0 +1,406 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "CoreMinimal.h" +#include "NakamaRtConnection.h" +#include "NakamaRtTypes.h" + +namespace NakamaRt +{ + template + struct FNakamaRtResult + { + bool bIsSuccess = false; + + TOptional Data; + TOptional Error; + + static FNakamaRtResult Success(const T& InData) + { + FNakamaRtResult Result; + Result.bIsSuccess = true; + Result.Data = InData; + return Result; + } + static FNakamaRtResult Failure(const FNakamaRtError& InError) + { + FNakamaRtResult Result; + Result.bIsSuccess = false; + Result.Error = InError; + return Result; + } + }; + + /* + * Join a realtime chat channel. + * + * @param Target The user ID to DM with, group ID to chat with, or room channel name to join. + * @param Type The type of the chat channel. + * @param Persistence Whether messages sent on this channel should be persistent. + * @param Hidden Whether the user should appear in the channel's presence list and events. + */ + NAKAMA_API TNakamaFuture> + ChannelJoin( + const TSharedPtr& Connection + , const FString& Target + , int32 Type + , FNakamaRtOptionalBool Persistence + , FNakamaRtOptionalBool Hidden + ); + + /* + * Leave a realtime chat channel. + * + * @param ChannelId The ID of the channel to leave. + */ + NAKAMA_API TNakamaFuture> ChannelLeave( + const TSharedPtr& Connection + , const FString& ChannelId + ); + + /* + * Send a message to a realtime chat channel. + * + * @param ChannelId The channel to sent to. + * @param Content Message content. + */ + NAKAMA_API TNakamaFuture> ChannelMessageSend( + const TSharedPtr& Connection + , const FString& ChannelId + , const FString& Content + ); + + /* + * Update a message previously sent to a realtime chat channel. + * + * @param ChannelId The channel the message was sent to. + * @param MessageId The ID assigned to the message to update. + * @param Content New message content. + */ + NAKAMA_API TNakamaFuture> ChannelMessageUpdate( + const TSharedPtr& Connection + , const FString& ChannelId + , const FString& MessageId + , const FString& Content + ); + + /* + * Remove a message previously sent to a realtime chat channel. + * + * @param ChannelId The channel the message was sent to. + * @param MessageId The ID assigned to the message to update. + */ + NAKAMA_API TNakamaFuture> ChannelMessageRemove( + const TSharedPtr& Connection + , const FString& ChannelId + , const FString& MessageId + ); + + /* + * A client to server request to create a realtime match. + * + * @param Name Optional name to use when creating the match. + */ + NAKAMA_API TNakamaFuture> MatchCreate( + const TSharedPtr& Connection + , const FString& Name + ); + + /* + * A client to server request to send data to a realtime match. + * + * @param MatchId The match unique ID. + * @param OpCode Op code value. + * @param Data Data payload, if any. + * @param Presences List of presences in the match to deliver to, if filtering is required. Otherwise deliver to everyone in the match. + * @param Reliable True if the data should be sent reliably, false otherwise. + */ + NAKAMA_API TNakamaFuture> MatchDataSend( + const TSharedPtr& Connection + , const FString& MatchId + , int64 OpCode + , const TArray& Data + , const TArray& Presences + , bool Reliable + ); + + /* + * A client to server request to join a realtime match. + * + * @param MatchId The match unique ID. + * @param Token A matchmaking result token. + * @param Metadata An optional set of key-value metadata pairs to be passed to the match handler, if any. + */ + NAKAMA_API TNakamaFuture> MatchJoin( + const TSharedPtr& Connection + , const FString& MatchId + , const FString& Token + , const TMap& Metadata + ); + + /* + * A client to server request to leave a realtime match. + * + * @param MatchId The match unique ID. + */ + NAKAMA_API TNakamaFuture> MatchLeave( + const TSharedPtr& Connection + , const FString& MatchId + ); + + /* + * Submit a new matchmaking process request. + * + * @param MinCount Minimum total user count to match together. + * @param MaxCount Maximum total user count to match together. + * @param Query Filter query used to identify suitable users. + * @param CountMultiple Optional multiple of the count that must be satisfied. + * @param StringProperties String properties. + * @param NumericProperties Numeric properties. + */ + NAKAMA_API TNakamaFuture> MatchmakerAdd( + const TSharedPtr& Connection + , int32 MinCount + , int32 MaxCount + , const FString& Query + , FNakamaRtOptionalInt32 CountMultiple + , const TMap& StringProperties + , const TMap& NumericProperties + ); + + /* + * Cancel a matchmaking process using a ticket. + * + * @param Ticket The ticket to cancel. + */ + NAKAMA_API TNakamaFuture> MatchmakerRemove( + const TSharedPtr& Connection + , const FString& Ticket + ); + + /* + * RPC call or response. + * + * @param Id The identifier of the function. + * @param Payload The payload of the function which must be a JSON object. + * @param HttpKey The authentication key used when executed as a non-client HTTP request. + */ + NAKAMA_API TNakamaFuture> Rpc( + const TSharedPtr& Connection + , const FString& Id + , const FString& Payload + , const FString& HttpKey + ); + + /* + * Start following some set of users to receive their status updates. + * + * @param UserIds User IDs to follow. + * @param Usernames Usernames to follow. + */ + NAKAMA_API TNakamaFuture> StatusFollow( + const TSharedPtr& Connection + , const TArray& UserIds + , const TArray& Usernames + ); + + /* + * Stop following some set of users to no longer receive their status updates. + * + * @param UserIds Users to unfollow. + */ + NAKAMA_API TNakamaFuture> StatusUnfollow( + const TSharedPtr& Connection + , const TArray& UserIds + ); + + /* + * Set the user's own status. + * + * @param Status Status string to set, if not present the user will appear offline. + */ + NAKAMA_API TNakamaFuture> StatusUpdate( + const TSharedPtr& Connection + , const FString& Status + ); + + /* + * Application-level heartbeat and connection check. + * + */ + NAKAMA_API TNakamaFuture> Ping( + const TSharedPtr& Connection + ); + + /* + * Create a party. + * + * @param Open Whether or not the party will require join requests to be approved by the party leader. + * @param MaxSize Maximum number of party members. + * @param Label Label + * @param Hidden Whether the party is visible in party listings. + */ + NAKAMA_API TNakamaFuture> PartyCreate( + const TSharedPtr& Connection + , bool Open + , int32 MaxSize + , const FString& Label + , bool Hidden + ); + + /* + * Join a party, or request to join if the party is not open. + * + * @param PartyId Party ID to join. + */ + NAKAMA_API TNakamaFuture> PartyJoin( + const TSharedPtr& Connection + , const FString& PartyId + ); + + /* + * Leave a party. + * + * @param PartyId Party ID to leave. + */ + NAKAMA_API TNakamaFuture> PartyLeave( + const TSharedPtr& Connection + , const FString& PartyId + ); + + /* + * Promote a new party leader. + * + * @param PartyId Party ID to promote a new leader for. + * @param Presence The presence of an existing party member to promote as the new leader. + */ + NAKAMA_API TNakamaFuture> PartyPromote( + const TSharedPtr& Connection + , const FString& PartyId + , const FNakamaRtUserPresence& Presence + ); + + /* + * Accept a request to join. + * + * @param PartyId Party ID to accept a join request for. + * @param Presence The presence to accept as a party member. + */ + NAKAMA_API TNakamaFuture> PartyAccept( + const TSharedPtr& Connection + , const FString& PartyId + , const FNakamaRtUserPresence& Presence + ); + + /* + * Kick a party member, or decline a request to join. + * + * @param PartyId Party ID to remove/reject from. + * @param Presence The presence to remove or reject. + */ + NAKAMA_API TNakamaFuture> PartyRemove( + const TSharedPtr& Connection + , const FString& PartyId + , const FNakamaRtUserPresence& Presence + ); + + /* + * End a party, kicking all party members and closing it. + * + * @param PartyId Party ID to close. + */ + NAKAMA_API TNakamaFuture> PartyClose( + const TSharedPtr& Connection + , const FString& PartyId + ); + + /* + * Request a list of pending join requests for a party. + * + * @param PartyId Party ID to get a list of join requests for. + */ + NAKAMA_API TNakamaFuture> PartyJoinRequestList( + const TSharedPtr& Connection + , const FString& PartyId + ); + + /* + * Begin matchmaking as a party. + * + * @param PartyId Party ID. + * @param MinCount Minimum total user count to match together. + * @param MaxCount Maximum total user count to match together. + * @param Query Filter query used to identify suitable users. + * @param CountMultiple Optional multiple of the count that must be satisfied. + * @param StringProperties String properties. + * @param NumericProperties Numeric properties. + */ + NAKAMA_API TNakamaFuture> PartyMatchmakerAdd( + const TSharedPtr& Connection + , const FString& PartyId + , int32 MinCount + , int32 MaxCount + , const FString& Query + , FNakamaRtOptionalInt32 CountMultiple + , const TMap& StringProperties + , const TMap& NumericProperties + ); + + /* + * Cancel a party matchmaking process using a ticket. + * + * @param PartyId Party ID. + * @param Ticket The ticket to cancel. + */ + NAKAMA_API TNakamaFuture> PartyMatchmakerRemove( + const TSharedPtr& Connection + , const FString& PartyId + , const FString& Ticket + ); + + /* + * A client to server request to send data to a party. + * + * @param PartyId Party ID to send to. + * @param OpCode Op code value. + * @param Data Data payload, if any. + */ + NAKAMA_API TNakamaFuture> PartyDataSend( + const TSharedPtr& Connection + , const FString& PartyId + , int64 OpCode + , const TArray& Data + ); + + /* + * Update Party label and whether it's open or closed. + * + * @param PartyId Party ID. + * @param Label Label to set. + * @param Open Change the party to open or closed. + * @param Hidden Whether the party is visible in party listings. + */ + NAKAMA_API TNakamaFuture> PartyUpdate( + const TSharedPtr& Connection + , const FString& PartyId + , const FString& Label + , bool Open + , bool Hidden + ); +} + diff --git a/Nakama/Source/Nakama/Public/NakamaRtConnection.h b/Nakama/Source/Nakama/Public/NakamaRtConnection.h new file mode 100644 index 000000000..fde5ed8e8 --- /dev/null +++ b/Nakama/Source/Nakama/Public/NakamaRtConnection.h @@ -0,0 +1,195 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include "CoreMinimal.h" +#include "Templates/SharedPointer.h" +#include "IWebSocket.h" +#include "NakamaFuture.h" +#include "NakamaRtTypes.h" +#include "NakamaRtConnection.generated.h" + +UENUM(BlueprintType) +enum class ENakamaWebSocketError : uint8 +{ + None = 0, + ConnectionAlreadyInProgress = 1, + ConnectionFailed = 2, + ConnectionAborted = 3, + NotConnected = 4, + ConnectionClosed = 5, + ServerError = 6, +}; + +UENUM(BlueprintType) +enum class EWebSocketMessageError : uint8 +{ + WS_ERROR_NONE = 0, + WS_ERROR_MESSAGE_MALFORMED = 1, + WS_ERROR_MESSAGE_HASERROR = 2, + WS_ERROR_RESPONSE_NOCID = 3, +}; + +USTRUCT(BlueprintType) +struct FNakamaWebSocketConnectionResult +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadOnly, Category = "Nakama|Realtime") + bool bSuccess = false; + + UPROPERTY(BlueprintReadOnly, Category = "Nakama|Realtime") + ENakamaWebSocketError ErrorCode = ENakamaWebSocketError::None; +}; + +/** Result of a realtime send operation. Check ErrorCode before accessing Data — + * Data is nullptr when ErrorCode != ENakamaWebSocketError::None. */ +USTRUCT(BlueprintType) +struct FNakamaWebSocketResponse +{ + GENERATED_BODY() + + TSharedPtr Data; + + UPROPERTY(BlueprintReadOnly, Category = "Nakama|Realtime") + ENakamaWebSocketError ErrorCode = ENakamaWebSocketError::None; +}; + +USTRUCT(BlueprintType) +struct FNakamaWebSocketConnectionParams +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nakama|Realtime") + FString Host; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nakama|Realtime") + FString Token; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nakama|Realtime") + float PingIntervalSeconds = 2.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nakama|Realtime") + int32 Port = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nakama|Realtime") + bool bUseSSL = false; +}; + +DECLARE_MULTICAST_DELEGATE_OneParam(FDelegateServerResponseReceived, const FString&); +DECLARE_MULTICAST_DELEGATE_OneParam(FDelegateServerEventReceived, const TSharedPtr&); +DECLARE_MULTICAST_DELEGATE_OneParam(FDelegateMessageSent, const FString&); +DECLARE_MULTICAST_DELEGATE_TwoParams(FDelegateMessageError, EWebSocketMessageError, const FString&); +DECLARE_MULTICAST_DELEGATE_ThreeParams(FDelegateClosed, int32, const FString&, bool); + +// +// Delegates for event callbacks +DECLARE_MULTICAST_DELEGATE_OneParam(FDelegateChannelMessage, const FNakamaRtChannelMessage&); +DECLARE_MULTICAST_DELEGATE_OneParam(FDelegateChannelPresenceEvent, const FNakamaRtChannelPresenceEvent&); +DECLARE_MULTICAST_DELEGATE_OneParam(FDelegateMatchData, const FNakamaRtMatchData&); +DECLARE_MULTICAST_DELEGATE_OneParam(FDelegateMatchPresenceEvent, const FNakamaRtMatchPresenceEvent&); +DECLARE_MULTICAST_DELEGATE_OneParam(FDelegateMatchmakerMatched, const FNakamaRtMatchmakerMatched&); +DECLARE_MULTICAST_DELEGATE_OneParam(FDelegateNotifications, const FNakamaRtNotifications&); +DECLARE_MULTICAST_DELEGATE_OneParam(FDelegatePartyLeader, const FNakamaRtPartyLeader&); +DECLARE_MULTICAST_DELEGATE_OneParam(FDelegatePartyJoinRequest, const FNakamaRtPartyJoinRequest&); +DECLARE_MULTICAST_DELEGATE_OneParam(FDelegatePartyData, const FNakamaRtPartyData&); +DECLARE_MULTICAST_DELEGATE_OneParam(FDelegatePartyPresenceEvent, const FNakamaRtPartyPresenceEvent&); +DECLARE_MULTICAST_DELEGATE_OneParam(FDelegateStatusPresenceEvent, const FNakamaRtStatusPresenceEvent&); +DECLARE_MULTICAST_DELEGATE_OneParam(FDelegateStreamData, const FNakamaRtStreamData&); +DECLARE_MULTICAST_DELEGATE_OneParam(FDelegateStreamPresenceEvent, const FNakamaRtStreamPresenceEvent&); + +enum class ENakamaRtConnectionState : uint8 +{ + Disconnected, + ConnectionInProgress, + Connected, +}; + +class NAKAMA_API FNakamaRtConnection : public TSharedFromThis +{ +private: + // Current ongoing requests + TMap::FState>> Requests; + FCriticalSection RequestsLock; + + // Params for current connection + FNakamaWebSocketConnectionParams ConnectionParams; + + TSharedPtr WebSocket; + + FTSTicker::FDelegateHandle PingTimerHandle; + + TSharedPtr::FState> ConnectionPromise; + std::atomic ConnectionState { ENakamaRtConnectionState::Disconnected }; + +public: + FNakamaRtConnection(); + virtual ~FNakamaRtConnection(); + + FNakamaRtConnection(const FNakamaRtConnection& Connection) = delete; + FNakamaRtConnection(FNakamaRtConnection&& Connection) = delete; + FNakamaRtConnection& operator=(const FNakamaRtConnection& Connection) = delete; + FNakamaRtConnection& operator=(FNakamaRtConnection&& Connection) = delete; + + TNakamaFuture Connect(const FNakamaWebSocketConnectionParams& Params); + void Close(); + + TNakamaFuture Send(const FString& RequestName, const TSharedPtr& Data); + + int32 GetPendingRequestCount(); + + // + // Public Delegates + FDelegateServerResponseReceived ServerResponseReceived; + FDelegateServerEventReceived ServerEventReceived; + FDelegateMessageSent MessageSent; + FDelegateMessageError MessageError; + FDelegateClosed Closed; + + FDelegateChannelMessage ChannelMessage; + FDelegateChannelPresenceEvent ChannelPresenceEvent; + FDelegateMatchData MatchData; + FDelegateMatchPresenceEvent MatchPresenceEvent; + FDelegateMatchmakerMatched MatchmakerMatched; + FDelegateNotifications Notifications; + FDelegatePartyLeader PartyLeader; + FDelegatePartyJoinRequest PartyJoinRequest; + FDelegatePartyData PartyData; + FDelegatePartyPresenceEvent PartyPresenceEvent; + FDelegateStatusPresenceEvent StatusPresenceEvent; + FDelegateStreamData StreamData; + FDelegateStreamPresenceEvent StreamPresenceEvent; + +private: + // + // Server Ping-Ponging + void StartPingLoop(); + bool SendPing(); + void StopPingLoop(); + + // + // Web socket callbacks + void OnConnected(); + void OnConnectionError(const FString& Error); + void OnMessage(const FString& Message); + void OnMessageSent(const FString& Message); + void OnClosed(int32 StatusCode, const FString& Reason, bool bWasClean); + + void HandleServerEvent(const TSharedPtr& Envelope); +}; + diff --git a/Nakama/Source/Nakama/Public/NakamaSession.h b/Nakama/Source/Nakama/Public/NakamaSession.h new file mode 100644 index 000000000..ce4255dea --- /dev/null +++ b/Nakama/Source/Nakama/Public/NakamaSession.h @@ -0,0 +1,109 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "CoreMinimal.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "NakamaSession.generated.h" + +USTRUCT(BlueprintType) +struct NAKAMA_API FNakamaSession +{ + GENERATED_BODY() + + /** Session variables from the auth token JWT. */ + UPROPERTY(BlueprintReadOnly, Category = "Nakama") + TMap Vars; + + /** Authentication credentials. */ + UPROPERTY(BlueprintReadOnly, Category = "Nakama") + FString Token; + + /** Refresh token that can be used for session token renewal. */ + UPROPERTY(BlueprintReadOnly, Category = "Nakama") + FString RefreshToken; + + /** User ID parsed from the auth token JWT. */ + UPROPERTY(BlueprintReadOnly, Category = "Nakama") + FString UserId; + + /** Username parsed from the auth token JWT. */ + UPROPERTY(BlueprintReadOnly, Category = "Nakama") + FString Username; + + /** Auth token expiry (Unix timestamp) parsed from JWT. */ + UPROPERTY(BlueprintReadOnly, Category = "Nakama") + int64 TokenExpiresAt = 0; + + /** Auth token issued-at (Unix timestamp) parsed from JWT. */ + UPROPERTY(BlueprintReadOnly, Category = "Nakama") + int64 TokenIssuedAt = 0; + + /** Refresh token expiry (Unix timestamp) parsed from JWT. */ + UPROPERTY(BlueprintReadOnly, Category = "Nakama") + int64 RefreshTokenExpiresAt = 0; + + /** True if the corresponding account was just created, false otherwise. */ + UPROPERTY(BlueprintReadOnly, Category = "Nakama") + bool Created = false; + + /** True if the auth token expires within BufferSeconds from now. */ + bool IsExpired(int64 BufferSeconds = 0) const noexcept; + + /** True if the refresh token has expired (no buffer). */ + bool IsRefreshExpired(int64 BufferSeconds = 0) const noexcept; + + /** Replace tokens and re-parse JWT claims. */ + void Update(const FString& NewToken, const FString& NewRefreshToken) noexcept; + + static FNakamaSession FromJson(const TSharedPtr& Json) noexcept; + TSharedPtr ToJson() const noexcept; + +private: + static bool ParseJwtPayload(const FString& Jwt, TSharedPtr& Out) noexcept; + void ParseTokens() noexcept; +}; + +UCLASS() +class NAKAMA_API UNakamaSessionFunctions : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + + /** True if the auth token expires within BufferSeconds from now. */ + UFUNCTION(BlueprintPure, Category = "Nakama|Session") + static bool IsExpired(const FNakamaSession& Session, int64 BufferSeconds = 0) + { + return Session.IsExpired(BufferSeconds); + } + + /** True if the refresh token has expired (no buffer). */ + UFUNCTION(BlueprintPure, Category = "Nakama|Session") + static bool IsRefreshExpired(const FNakamaSession& Session, int64 BufferSeconds = 0) + { + return Session.IsRefreshExpired(BufferSeconds); + } + + /** Replace tokens and re-parse JWT claims. */ + UFUNCTION(BlueprintCallable, Category = "Nakama|Session") + static void UpdateSession(UPARAM(ref) FNakamaSession& Session, const FString& NewToken, const FString& NewRefreshToken) + { + Session.Update(NewToken, NewRefreshToken); + } +}; + diff --git a/Nakama/Source/NakamaUnreal/NakamaUnreal.Build.cs b/Nakama/Source/NakamaApi/NakamaApi.Build.cs similarity index 89% rename from Nakama/Source/NakamaUnreal/NakamaUnreal.Build.cs rename to Nakama/Source/NakamaApi/NakamaApi.Build.cs index 46b969a93..bcbd91294 100644 --- a/Nakama/Source/NakamaUnreal/NakamaUnreal.Build.cs +++ b/Nakama/Source/NakamaApi/NakamaApi.Build.cs @@ -17,9 +17,9 @@ using UnrealBuildTool; using System.IO; -public class NakamaUnreal : ModuleRules +public class NakamaApi : ModuleRules { - public NakamaUnreal(ReadOnlyTargetRules Target) : base(Target) + public NakamaApi(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; @@ -37,11 +37,10 @@ public NakamaUnreal(ReadOnlyTargetRules Target) : base(Target) ); - // NakamaUnreal no longer depends on NakamaCore PublicDependencyModuleNames.AddRange( new string[] { - "Core", "HTTP", "WebSockets", "JsonUtilities" + "Core", "HTTP", "JsonUtilities", "WebSockets" // ... add other public dependencies that you statically link with here ... } ); diff --git a/Nakama/Source/NakamaApi/Private/NakamaApi.cpp b/Nakama/Source/NakamaApi/Private/NakamaApi.cpp new file mode 100644 index 000000000..a963a9f85 --- /dev/null +++ b/Nakama/Source/NakamaApi/Private/NakamaApi.cpp @@ -0,0 +1,9399 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + +#include "NakamaApi.h" +#include "GenericPlatform/GenericPlatformHttp.h" +#include "NakamaHttpHelper.h" + +DEFINE_LOG_CATEGORY(LogNakama); + +using namespace NakamaHttpInternal; + +NAKAMAAPI_API void NakamaApi::AddFriends ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const TArray& Ids, + const TArray& Usernames, + const FString& Metadata, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/friend"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : Ids) + { + QueryParams.Add({TEXT("ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + for (const FString& Item : Usernames) + { + QueryParams.Add({TEXT("usernames"), FString::Printf(TEXT("%s"), *(Item))}); + } + if (Metadata.IsEmpty() == false) + { + QueryParams.Add({TEXT("metadata"), FString::Printf(TEXT("%s"), *(Metadata))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::AddFriends ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const TArray& Ids, + const TArray& Usernames, + const FString& Metadata, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/friend"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : Ids) + { + QueryParams.Add({TEXT("ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + for (const FString& Item : Usernames) + { + QueryParams.Add({TEXT("usernames"), FString::Printf(TEXT("%s"), *(Item))}); + } + if (Metadata.IsEmpty() == false) + { + QueryParams.Add({TEXT("metadata"), FString::Printf(TEXT("%s"), *(Metadata))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::AddGroupUsers ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& GroupId, + const TArray& UserIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/group/{group_id}/add"); + // + // Fill Path Params + const FString Encoded_GroupId = FGenericPlatformHttp::UrlEncode(GroupId); + Endpoint = Endpoint.Replace(TEXT("{group_id}"), *Encoded_GroupId); + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : UserIds) + { + QueryParams.Add({TEXT("user_ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::AddGroupUsers ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& GroupId, + const TArray& UserIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/group/{group_id}/add"); + // + // Fill Path Params + const FString Encoded_GroupId = FGenericPlatformHttp::UrlEncode(GroupId); + Endpoint = Endpoint.Replace(TEXT("{group_id}"), *Encoded_GroupId); + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : UserIds) + { + QueryParams.Add({TEXT("user_ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::SessionRefresh ( + const FNakamaClientConfig& Config, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/session/refresh"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Token.IsEmpty() == false) + { + Body->SetStringField(TEXT("token"), Token); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Basic, + TEXT(""), + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaSession Result = FNakamaSession::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::SessionLogout ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Token, + const FString& RefreshToken, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/session/logout"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Token.IsEmpty() == false) + { + Body->SetStringField(TEXT("token"), Token); + } + if (RefreshToken.IsEmpty() == false) + { + Body->SetStringField(TEXT("refresh_token"), RefreshToken); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::SessionLogout ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Token, + const FString& RefreshToken, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/session/logout"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Token.IsEmpty() == false) + { + Body->SetStringField(TEXT("token"), Token); + } + if (RefreshToken.IsEmpty() == false) + { + Body->SetStringField(TEXT("refresh_token"), RefreshToken); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::AuthenticateApple ( + const FNakamaClientConfig& Config, + const FNakamaAccountApple& Account, + FNakamaOptionalBool Create, + const FString& Username, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/authenticate/apple"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Create.IsEmpty() == false) + { + QueryParams.Add({TEXT("create"), FString::Printf(TEXT("%s"), *LexToString(Create.GetValue()))}); + } + if (Username.IsEmpty() == false) + { + QueryParams.Add({TEXT("username"), FString::Printf(TEXT("%s"), *(Username))}); + } + + // + // Fill Body Params + TSharedPtr Body; + Body = Account.ToJson(); + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Basic, + TEXT(""), + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaSession Result = FNakamaSession::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::AuthenticateCustom ( + const FNakamaClientConfig& Config, + const FNakamaAccountCustom& Account, + FNakamaOptionalBool Create, + const FString& Username, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/authenticate/custom"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Create.IsEmpty() == false) + { + QueryParams.Add({TEXT("create"), FString::Printf(TEXT("%s"), *LexToString(Create.GetValue()))}); + } + if (Username.IsEmpty() == false) + { + QueryParams.Add({TEXT("username"), FString::Printf(TEXT("%s"), *(Username))}); + } + + // + // Fill Body Params + TSharedPtr Body; + Body = Account.ToJson(); + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Basic, + TEXT(""), + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaSession Result = FNakamaSession::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::AuthenticateDevice ( + const FNakamaClientConfig& Config, + const FNakamaAccountDevice& Account, + FNakamaOptionalBool Create, + const FString& Username, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/authenticate/device"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Create.IsEmpty() == false) + { + QueryParams.Add({TEXT("create"), FString::Printf(TEXT("%s"), *LexToString(Create.GetValue()))}); + } + if (Username.IsEmpty() == false) + { + QueryParams.Add({TEXT("username"), FString::Printf(TEXT("%s"), *(Username))}); + } + + // + // Fill Body Params + TSharedPtr Body; + Body = Account.ToJson(); + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Basic, + TEXT(""), + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaSession Result = FNakamaSession::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::AuthenticateEmail ( + const FNakamaClientConfig& Config, + const FNakamaAccountEmail& Account, + FNakamaOptionalBool Create, + const FString& Username, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/authenticate/email"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Create.IsEmpty() == false) + { + QueryParams.Add({TEXT("create"), FString::Printf(TEXT("%s"), *LexToString(Create.GetValue()))}); + } + if (Username.IsEmpty() == false) + { + QueryParams.Add({TEXT("username"), FString::Printf(TEXT("%s"), *(Username))}); + } + + // + // Fill Body Params + TSharedPtr Body; + Body = Account.ToJson(); + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Basic, + TEXT(""), + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaSession Result = FNakamaSession::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::AuthenticateFacebook ( + const FNakamaClientConfig& Config, + const FNakamaAccountFacebook& Account, + FNakamaOptionalBool Create, + const FString& Username, + FNakamaOptionalBool Sync, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/authenticate/facebook"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Create.IsEmpty() == false) + { + QueryParams.Add({TEXT("create"), FString::Printf(TEXT("%s"), *LexToString(Create.GetValue()))}); + } + if (Username.IsEmpty() == false) + { + QueryParams.Add({TEXT("username"), FString::Printf(TEXT("%s"), *(Username))}); + } + if (Sync.IsEmpty() == false) + { + QueryParams.Add({TEXT("sync"), FString::Printf(TEXT("%s"), *LexToString(Sync.GetValue()))}); + } + + // + // Fill Body Params + TSharedPtr Body; + Body = Account.ToJson(); + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Basic, + TEXT(""), + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaSession Result = FNakamaSession::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::AuthenticateFacebookInstantGame ( + const FNakamaClientConfig& Config, + const FNakamaAccountFacebookInstantGame& Account, + FNakamaOptionalBool Create, + const FString& Username, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/authenticate/facebookinstantgame"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Create.IsEmpty() == false) + { + QueryParams.Add({TEXT("create"), FString::Printf(TEXT("%s"), *LexToString(Create.GetValue()))}); + } + if (Username.IsEmpty() == false) + { + QueryParams.Add({TEXT("username"), FString::Printf(TEXT("%s"), *(Username))}); + } + + // + // Fill Body Params + TSharedPtr Body; + Body = Account.ToJson(); + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Basic, + TEXT(""), + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaSession Result = FNakamaSession::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::AuthenticateGameCenter ( + const FNakamaClientConfig& Config, + const FNakamaAccountGameCenter& Account, + FNakamaOptionalBool Create, + const FString& Username, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/authenticate/gamecenter"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Create.IsEmpty() == false) + { + QueryParams.Add({TEXT("create"), FString::Printf(TEXT("%s"), *LexToString(Create.GetValue()))}); + } + if (Username.IsEmpty() == false) + { + QueryParams.Add({TEXT("username"), FString::Printf(TEXT("%s"), *(Username))}); + } + + // + // Fill Body Params + TSharedPtr Body; + Body = Account.ToJson(); + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Basic, + TEXT(""), + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaSession Result = FNakamaSession::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::AuthenticateGoogle ( + const FNakamaClientConfig& Config, + const FNakamaAccountGoogle& Account, + FNakamaOptionalBool Create, + const FString& Username, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/authenticate/google"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Create.IsEmpty() == false) + { + QueryParams.Add({TEXT("create"), FString::Printf(TEXT("%s"), *LexToString(Create.GetValue()))}); + } + if (Username.IsEmpty() == false) + { + QueryParams.Add({TEXT("username"), FString::Printf(TEXT("%s"), *(Username))}); + } + + // + // Fill Body Params + TSharedPtr Body; + Body = Account.ToJson(); + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Basic, + TEXT(""), + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaSession Result = FNakamaSession::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::AuthenticateSteam ( + const FNakamaClientConfig& Config, + const FNakamaAccountSteam& Account, + FNakamaOptionalBool Create, + const FString& Username, + FNakamaOptionalBool Sync, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/authenticate/steam"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Create.IsEmpty() == false) + { + QueryParams.Add({TEXT("create"), FString::Printf(TEXT("%s"), *LexToString(Create.GetValue()))}); + } + if (Username.IsEmpty() == false) + { + QueryParams.Add({TEXT("username"), FString::Printf(TEXT("%s"), *(Username))}); + } + if (Sync.IsEmpty() == false) + { + QueryParams.Add({TEXT("sync"), FString::Printf(TEXT("%s"), *LexToString(Sync.GetValue()))}); + } + + // + // Fill Body Params + TSharedPtr Body; + Body = Account.ToJson(); + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Basic, + TEXT(""), + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaSession Result = FNakamaSession::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::BanGroupUsers ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& GroupId, + const TArray& UserIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/group/{group_id}/ban"); + // + // Fill Path Params + const FString Encoded_GroupId = FGenericPlatformHttp::UrlEncode(GroupId); + Endpoint = Endpoint.Replace(TEXT("{group_id}"), *Encoded_GroupId); + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : UserIds) + { + QueryParams.Add({TEXT("user_ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::BanGroupUsers ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& GroupId, + const TArray& UserIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/group/{group_id}/ban"); + // + // Fill Path Params + const FString Encoded_GroupId = FGenericPlatformHttp::UrlEncode(GroupId); + Endpoint = Endpoint.Replace(TEXT("{group_id}"), *Encoded_GroupId); + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : UserIds) + { + QueryParams.Add({TEXT("user_ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::BlockFriends ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const TArray& Ids, + const TArray& Usernames, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/friend/block"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : Ids) + { + QueryParams.Add({TEXT("ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + for (const FString& Item : Usernames) + { + QueryParams.Add({TEXT("usernames"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::BlockFriends ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const TArray& Ids, + const TArray& Usernames, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/friend/block"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : Ids) + { + QueryParams.Add({TEXT("ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + for (const FString& Item : Usernames) + { + QueryParams.Add({TEXT("usernames"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::CreateGroup ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Name, + const FString& Description, + const FString& LangTag, + const FString& AvatarUrl, + bool Open, + int32 MaxCount, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/group"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Name.IsEmpty() == false) + { + Body->SetStringField(TEXT("name"), Name); + } + if (Description.IsEmpty() == false) + { + Body->SetStringField(TEXT("description"), Description); + } + if (LangTag.IsEmpty() == false) + { + Body->SetStringField(TEXT("lang_tag"), LangTag); + } + if (AvatarUrl.IsEmpty() == false) + { + Body->SetStringField(TEXT("avatar_url"), AvatarUrl); + } + + { + Body->SetBoolField(TEXT("open"), Open); + } + + { + Body->SetNumberField(TEXT("max_count"), MaxCount); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaGroup Result = FNakamaGroup::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::CreateGroup ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Name, + const FString& Description, + const FString& LangTag, + const FString& AvatarUrl, + bool Open, + int32 MaxCount, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/group"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Name.IsEmpty() == false) + { + Body->SetStringField(TEXT("name"), Name); + } + if (Description.IsEmpty() == false) + { + Body->SetStringField(TEXT("description"), Description); + } + if (LangTag.IsEmpty() == false) + { + Body->SetStringField(TEXT("lang_tag"), LangTag); + } + if (AvatarUrl.IsEmpty() == false) + { + Body->SetStringField(TEXT("avatar_url"), AvatarUrl); + } + + { + Body->SetBoolField(TEXT("open"), Open); + } + + { + Body->SetNumberField(TEXT("max_count"), MaxCount); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaGroup Result = FNakamaGroup::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::DeleteAccount ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("DELETE"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::DeleteAccount ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("DELETE"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::DeleteFriends ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const TArray& Ids, + const TArray& Usernames, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/friend"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : Ids) + { + QueryParams.Add({TEXT("ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + for (const FString& Item : Usernames) + { + QueryParams.Add({TEXT("usernames"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("DELETE"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::DeleteFriends ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const TArray& Ids, + const TArray& Usernames, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/friend"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : Ids) + { + QueryParams.Add({TEXT("ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + for (const FString& Item : Usernames) + { + QueryParams.Add({TEXT("usernames"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("DELETE"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::DeleteGroup ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& GroupId, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/group/{group_id}"); + // + // Fill Path Params + const FString Encoded_GroupId = FGenericPlatformHttp::UrlEncode(GroupId); + Endpoint = Endpoint.Replace(TEXT("{group_id}"), *Encoded_GroupId); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("DELETE"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::DeleteGroup ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& GroupId, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/group/{group_id}"); + // + // Fill Path Params + const FString Encoded_GroupId = FGenericPlatformHttp::UrlEncode(GroupId); + Endpoint = Endpoint.Replace(TEXT("{group_id}"), *Encoded_GroupId); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("DELETE"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::DeleteLeaderboardRecord ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& LeaderboardId, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/leaderboard/{leaderboard_id}"); + // + // Fill Path Params + const FString Encoded_LeaderboardId = FGenericPlatformHttp::UrlEncode(LeaderboardId); + Endpoint = Endpoint.Replace(TEXT("{leaderboard_id}"), *Encoded_LeaderboardId); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("DELETE"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::DeleteLeaderboardRecord ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& LeaderboardId, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/leaderboard/{leaderboard_id}"); + // + // Fill Path Params + const FString Encoded_LeaderboardId = FGenericPlatformHttp::UrlEncode(LeaderboardId); + Endpoint = Endpoint.Replace(TEXT("{leaderboard_id}"), *Encoded_LeaderboardId); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("DELETE"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::DeleteNotifications ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const TArray& Ids, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/notification"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : Ids) + { + QueryParams.Add({TEXT("ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("DELETE"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::DeleteNotifications ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const TArray& Ids, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/notification"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : Ids) + { + QueryParams.Add({TEXT("ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("DELETE"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::DeleteTournamentRecord ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& TournamentId, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/tournament/{tournament_id}"); + // + // Fill Path Params + const FString Encoded_TournamentId = FGenericPlatformHttp::UrlEncode(TournamentId); + Endpoint = Endpoint.Replace(TEXT("{tournament_id}"), *Encoded_TournamentId); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("DELETE"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::DeleteTournamentRecord ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& TournamentId, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/tournament/{tournament_id}"); + // + // Fill Path Params + const FString Encoded_TournamentId = FGenericPlatformHttp::UrlEncode(TournamentId); + Endpoint = Endpoint.Replace(TEXT("{tournament_id}"), *Encoded_TournamentId); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("DELETE"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::DeleteStorageObjects ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const TArray& ObjectIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/storage/delete"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (ObjectIds.Num() > 0) + { + TArray> Array; + for (const FNakamaDeleteStorageObjectId& Item : ObjectIds) + { + Array.Add(MakeShared(Item.ToJson())); + } + Body->SetArrayField(TEXT("object_ids"), Array); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("PUT"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::DeleteStorageObjects ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const TArray& ObjectIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/storage/delete"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (ObjectIds.Num() > 0) + { + TArray> Array; + for (const FNakamaDeleteStorageObjectId& Item : ObjectIds) + { + Array.Add(MakeShared(Item.ToJson())); + } + Body->SetArrayField(TEXT("object_ids"), Array); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("PUT"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::Event ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Name, + const FDateTime& Timestamp, + bool External, + const TMap& Properties, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/event"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Name.IsEmpty() == false) + { + Body->SetStringField(TEXT("name"), Name); + } + + { + Body->SetStringField(TEXT("timestamp"), Timestamp.ToIso8601()); + } + + { + Body->SetBoolField(TEXT("external"), External); + } + if (Properties.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Properties) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("properties"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::Event ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Name, + const FDateTime& Timestamp, + bool External, + const TMap& Properties, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/event"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Name.IsEmpty() == false) + { + Body->SetStringField(TEXT("name"), Name); + } + + { + Body->SetStringField(TEXT("timestamp"), Timestamp.ToIso8601()); + } + + { + Body->SetBoolField(TEXT("external"), External); + } + if (Properties.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Properties) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("properties"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::GetAccount ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaAccount Result = FNakamaAccount::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::GetAccount ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaAccount Result = FNakamaAccount::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::GetUsers ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const TArray& Ids, + const TArray& Usernames, + const TArray& FacebookIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/user"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : Ids) + { + QueryParams.Add({TEXT("ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + for (const FString& Item : Usernames) + { + QueryParams.Add({TEXT("usernames"), FString::Printf(TEXT("%s"), *(Item))}); + } + for (const FString& Item : FacebookIds) + { + QueryParams.Add({TEXT("facebook_ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaUsers Result = FNakamaUsers::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::GetUsers ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const TArray& Ids, + const TArray& Usernames, + const TArray& FacebookIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/user"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : Ids) + { + QueryParams.Add({TEXT("ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + for (const FString& Item : Usernames) + { + QueryParams.Add({TEXT("usernames"), FString::Printf(TEXT("%s"), *(Item))}); + } + for (const FString& Item : FacebookIds) + { + QueryParams.Add({TEXT("facebook_ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaUsers Result = FNakamaUsers::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::GetSubscription ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& ProductId, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/iap/subscription/{product_id}"); + // + // Fill Path Params + const FString Encoded_ProductId = FGenericPlatformHttp::UrlEncode(ProductId); + Endpoint = Endpoint.Replace(TEXT("{product_id}"), *Encoded_ProductId); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaValidatedSubscription Result = FNakamaValidatedSubscription::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::GetSubscription ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& ProductId, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/iap/subscription/{product_id}"); + // + // Fill Path Params + const FString Encoded_ProductId = FGenericPlatformHttp::UrlEncode(ProductId); + Endpoint = Endpoint.Replace(TEXT("{product_id}"), *Encoded_ProductId); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaValidatedSubscription Result = FNakamaValidatedSubscription::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::GetMatchmakerStats ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/matchmaker/stats"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaMatchmakerStats Result = FNakamaMatchmakerStats::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::GetMatchmakerStats ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/matchmaker/stats"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaMatchmakerStats Result = FNakamaMatchmakerStats::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::Healthcheck ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/healthcheck"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::Healthcheck ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/healthcheck"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ImportFacebookFriends ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FNakamaAccountFacebook& Account, + FNakamaOptionalBool Reset, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/friend/facebook"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Reset.IsEmpty() == false) + { + QueryParams.Add({TEXT("reset"), FString::Printf(TEXT("%s"), *LexToString(Reset.GetValue()))}); + } + + // + // Fill Body Params + TSharedPtr Body; + Body = Account.ToJson(); + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ImportFacebookFriends ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FNakamaAccountFacebook& Account, + FNakamaOptionalBool Reset, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/friend/facebook"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Reset.IsEmpty() == false) + { + QueryParams.Add({TEXT("reset"), FString::Printf(TEXT("%s"), *LexToString(Reset.GetValue()))}); + } + + // + // Fill Body Params + TSharedPtr Body; + Body = Account.ToJson(); + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ImportSteamFriends ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FNakamaAccountSteam& Account, + FNakamaOptionalBool Reset, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/friend/steam"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Reset.IsEmpty() == false) + { + QueryParams.Add({TEXT("reset"), FString::Printf(TEXT("%s"), *LexToString(Reset.GetValue()))}); + } + + // + // Fill Body Params + TSharedPtr Body; + Body = Account.ToJson(); + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ImportSteamFriends ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FNakamaAccountSteam& Account, + FNakamaOptionalBool Reset, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/friend/steam"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Reset.IsEmpty() == false) + { + QueryParams.Add({TEXT("reset"), FString::Printf(TEXT("%s"), *LexToString(Reset.GetValue()))}); + } + + // + // Fill Body Params + TSharedPtr Body; + Body = Account.ToJson(); + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::JoinGroup ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& GroupId, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/group/{group_id}/join"); + // + // Fill Path Params + const FString Encoded_GroupId = FGenericPlatformHttp::UrlEncode(GroupId); + Endpoint = Endpoint.Replace(TEXT("{group_id}"), *Encoded_GroupId); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::JoinGroup ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& GroupId, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/group/{group_id}/join"); + // + // Fill Path Params + const FString Encoded_GroupId = FGenericPlatformHttp::UrlEncode(GroupId); + Endpoint = Endpoint.Replace(TEXT("{group_id}"), *Encoded_GroupId); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::JoinTournament ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& TournamentId, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/tournament/{tournament_id}/join"); + // + // Fill Path Params + const FString Encoded_TournamentId = FGenericPlatformHttp::UrlEncode(TournamentId); + Endpoint = Endpoint.Replace(TEXT("{tournament_id}"), *Encoded_TournamentId); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::JoinTournament ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& TournamentId, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/tournament/{tournament_id}/join"); + // + // Fill Path Params + const FString Encoded_TournamentId = FGenericPlatformHttp::UrlEncode(TournamentId); + Endpoint = Endpoint.Replace(TEXT("{tournament_id}"), *Encoded_TournamentId); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::KickGroupUsers ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& GroupId, + const TArray& UserIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/group/{group_id}/kick"); + // + // Fill Path Params + const FString Encoded_GroupId = FGenericPlatformHttp::UrlEncode(GroupId); + Endpoint = Endpoint.Replace(TEXT("{group_id}"), *Encoded_GroupId); + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : UserIds) + { + QueryParams.Add({TEXT("user_ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::KickGroupUsers ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& GroupId, + const TArray& UserIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/group/{group_id}/kick"); + // + // Fill Path Params + const FString Encoded_GroupId = FGenericPlatformHttp::UrlEncode(GroupId); + Endpoint = Endpoint.Replace(TEXT("{group_id}"), *Encoded_GroupId); + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : UserIds) + { + QueryParams.Add({TEXT("user_ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::LeaveGroup ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& GroupId, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/group/{group_id}/leave"); + // + // Fill Path Params + const FString Encoded_GroupId = FGenericPlatformHttp::UrlEncode(GroupId); + Endpoint = Endpoint.Replace(TEXT("{group_id}"), *Encoded_GroupId); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::LeaveGroup ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& GroupId, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/group/{group_id}/leave"); + // + // Fill Path Params + const FString Encoded_GroupId = FGenericPlatformHttp::UrlEncode(GroupId); + Endpoint = Endpoint.Replace(TEXT("{group_id}"), *Encoded_GroupId); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::LinkApple ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/link/apple"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Token.IsEmpty() == false) + { + Body->SetStringField(TEXT("token"), Token); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::LinkApple ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/link/apple"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Token.IsEmpty() == false) + { + Body->SetStringField(TEXT("token"), Token); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::LinkCustom ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Id, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/link/custom"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Id.IsEmpty() == false) + { + Body->SetStringField(TEXT("id"), Id); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::LinkCustom ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Id, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/link/custom"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Id.IsEmpty() == false) + { + Body->SetStringField(TEXT("id"), Id); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::LinkDevice ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Id, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/link/device"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Id.IsEmpty() == false) + { + Body->SetStringField(TEXT("id"), Id); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::LinkDevice ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Id, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/link/device"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Id.IsEmpty() == false) + { + Body->SetStringField(TEXT("id"), Id); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::LinkEmail ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Email, + const FString& Password, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/link/email"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Email.IsEmpty() == false) + { + Body->SetStringField(TEXT("email"), Email); + } + if (Password.IsEmpty() == false) + { + Body->SetStringField(TEXT("password"), Password); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::LinkEmail ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Email, + const FString& Password, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/link/email"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Email.IsEmpty() == false) + { + Body->SetStringField(TEXT("email"), Email); + } + if (Password.IsEmpty() == false) + { + Body->SetStringField(TEXT("password"), Password); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::LinkFacebook ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FNakamaAccountFacebook& Account, + FNakamaOptionalBool Sync, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/link/facebook"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Sync.IsEmpty() == false) + { + QueryParams.Add({TEXT("sync"), FString::Printf(TEXT("%s"), *LexToString(Sync.GetValue()))}); + } + + // + // Fill Body Params + TSharedPtr Body; + Body = Account.ToJson(); + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::LinkFacebook ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FNakamaAccountFacebook& Account, + FNakamaOptionalBool Sync, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/link/facebook"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Sync.IsEmpty() == false) + { + QueryParams.Add({TEXT("sync"), FString::Printf(TEXT("%s"), *LexToString(Sync.GetValue()))}); + } + + // + // Fill Body Params + TSharedPtr Body; + Body = Account.ToJson(); + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::LinkFacebookInstantGame ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& SignedPlayerInfo, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/link/facebookinstantgame"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (SignedPlayerInfo.IsEmpty() == false) + { + Body->SetStringField(TEXT("signed_player_info"), SignedPlayerInfo); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::LinkFacebookInstantGame ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& SignedPlayerInfo, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/link/facebookinstantgame"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (SignedPlayerInfo.IsEmpty() == false) + { + Body->SetStringField(TEXT("signed_player_info"), SignedPlayerInfo); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::LinkGameCenter ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& PlayerId, + const FString& BundleId, + int64 TimestampSeconds, + const FString& Salt, + const FString& Signature, + const FString& PublicKeyUrl, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/link/gamecenter"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (PlayerId.IsEmpty() == false) + { + Body->SetStringField(TEXT("player_id"), PlayerId); + } + if (BundleId.IsEmpty() == false) + { + Body->SetStringField(TEXT("bundle_id"), BundleId); + } + + { + Body->SetNumberField(TEXT("timestamp_seconds"), TimestampSeconds); + } + if (Salt.IsEmpty() == false) + { + Body->SetStringField(TEXT("salt"), Salt); + } + if (Signature.IsEmpty() == false) + { + Body->SetStringField(TEXT("signature"), Signature); + } + if (PublicKeyUrl.IsEmpty() == false) + { + Body->SetStringField(TEXT("public_key_url"), PublicKeyUrl); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::LinkGameCenter ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& PlayerId, + const FString& BundleId, + int64 TimestampSeconds, + const FString& Salt, + const FString& Signature, + const FString& PublicKeyUrl, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/link/gamecenter"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (PlayerId.IsEmpty() == false) + { + Body->SetStringField(TEXT("player_id"), PlayerId); + } + if (BundleId.IsEmpty() == false) + { + Body->SetStringField(TEXT("bundle_id"), BundleId); + } + + { + Body->SetNumberField(TEXT("timestamp_seconds"), TimestampSeconds); + } + if (Salt.IsEmpty() == false) + { + Body->SetStringField(TEXT("salt"), Salt); + } + if (Signature.IsEmpty() == false) + { + Body->SetStringField(TEXT("signature"), Signature); + } + if (PublicKeyUrl.IsEmpty() == false) + { + Body->SetStringField(TEXT("public_key_url"), PublicKeyUrl); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::LinkGoogle ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/link/google"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Token.IsEmpty() == false) + { + Body->SetStringField(TEXT("token"), Token); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::LinkGoogle ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/link/google"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Token.IsEmpty() == false) + { + Body->SetStringField(TEXT("token"), Token); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::LinkSteam ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FNakamaAccountSteam& Account, + FNakamaOptionalBool Sync, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/link/steam"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + + { + Body->SetObjectField(TEXT("account"), Account.ToJson()); + } + if (Sync.IsEmpty() == false) + { + Body->SetBoolField(TEXT("sync"), Sync.GetValue()); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::LinkSteam ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FNakamaAccountSteam& Account, + FNakamaOptionalBool Sync, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/link/steam"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + + { + Body->SetObjectField(TEXT("account"), Account.ToJson()); + } + if (Sync.IsEmpty() == false) + { + Body->SetBoolField(TEXT("sync"), Sync.GetValue()); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListChannelMessages ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& ChannelId, + FNakamaOptionalInt32 Limit, + FNakamaOptionalBool Forward, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/channel/{channel_id}"); + // + // Fill Path Params + const FString Encoded_ChannelId = FGenericPlatformHttp::UrlEncode(ChannelId); + Endpoint = Endpoint.Replace(TEXT("{channel_id}"), *Encoded_ChannelId); + + // + // Fill Query Params + TArray> QueryParams; + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (Forward.IsEmpty() == false) + { + QueryParams.Add({TEXT("forward"), FString::Printf(TEXT("%s"), *LexToString(Forward.GetValue()))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaChannelMessageList Result = FNakamaChannelMessageList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListChannelMessages ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& ChannelId, + FNakamaOptionalInt32 Limit, + FNakamaOptionalBool Forward, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/channel/{channel_id}"); + // + // Fill Path Params + const FString Encoded_ChannelId = FGenericPlatformHttp::UrlEncode(ChannelId); + Endpoint = Endpoint.Replace(TEXT("{channel_id}"), *Encoded_ChannelId); + + // + // Fill Query Params + TArray> QueryParams; + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (Forward.IsEmpty() == false) + { + QueryParams.Add({TEXT("forward"), FString::Printf(TEXT("%s"), *LexToString(Forward.GetValue()))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaChannelMessageList Result = FNakamaChannelMessageList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListFriends ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + FNakamaOptionalInt32 Limit, + FNakamaOptionalInt32 State, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/friend"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (State.IsEmpty() == false) + { + QueryParams.Add({TEXT("state"), FString::Printf(TEXT("%d"), (State.GetValue()))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaFriendList Result = FNakamaFriendList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListFriends ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + FNakamaOptionalInt32 Limit, + FNakamaOptionalInt32 State, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/friend"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (State.IsEmpty() == false) + { + QueryParams.Add({TEXT("state"), FString::Printf(TEXT("%d"), (State.GetValue()))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaFriendList Result = FNakamaFriendList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListFriendsOfFriends ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/friend/friends"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaFriendsOfFriendsList Result = FNakamaFriendsOfFriendsList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListFriendsOfFriends ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/friend/friends"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaFriendsOfFriendsList Result = FNakamaFriendsOfFriendsList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListGroups ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Name, + const FString& Cursor, + FNakamaOptionalInt32 Limit, + const FString& LangTag, + FNakamaOptionalInt32 Members, + FNakamaOptionalBool Open, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/group"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Name.IsEmpty() == false) + { + QueryParams.Add({TEXT("name"), FString::Printf(TEXT("%s"), *(Name))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (LangTag.IsEmpty() == false) + { + QueryParams.Add({TEXT("lang_tag"), FString::Printf(TEXT("%s"), *(LangTag))}); + } + if (Members.IsEmpty() == false) + { + QueryParams.Add({TEXT("members"), FString::Printf(TEXT("%d"), (Members.GetValue()))}); + } + if (Open.IsEmpty() == false) + { + QueryParams.Add({TEXT("open"), FString::Printf(TEXT("%s"), *LexToString(Open.GetValue()))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaGroupList Result = FNakamaGroupList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListGroups ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Name, + const FString& Cursor, + FNakamaOptionalInt32 Limit, + const FString& LangTag, + FNakamaOptionalInt32 Members, + FNakamaOptionalBool Open, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/group"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Name.IsEmpty() == false) + { + QueryParams.Add({TEXT("name"), FString::Printf(TEXT("%s"), *(Name))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (LangTag.IsEmpty() == false) + { + QueryParams.Add({TEXT("lang_tag"), FString::Printf(TEXT("%s"), *(LangTag))}); + } + if (Members.IsEmpty() == false) + { + QueryParams.Add({TEXT("members"), FString::Printf(TEXT("%d"), (Members.GetValue()))}); + } + if (Open.IsEmpty() == false) + { + QueryParams.Add({TEXT("open"), FString::Printf(TEXT("%s"), *LexToString(Open.GetValue()))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaGroupList Result = FNakamaGroupList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListGroupUsers ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& GroupId, + FNakamaOptionalInt32 Limit, + FNakamaOptionalInt32 State, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/group/{group_id}/user"); + // + // Fill Path Params + const FString Encoded_GroupId = FGenericPlatformHttp::UrlEncode(GroupId); + Endpoint = Endpoint.Replace(TEXT("{group_id}"), *Encoded_GroupId); + + // + // Fill Query Params + TArray> QueryParams; + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (State.IsEmpty() == false) + { + QueryParams.Add({TEXT("state"), FString::Printf(TEXT("%d"), (State.GetValue()))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaGroupUserList Result = FNakamaGroupUserList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListGroupUsers ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& GroupId, + FNakamaOptionalInt32 Limit, + FNakamaOptionalInt32 State, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/group/{group_id}/user"); + // + // Fill Path Params + const FString Encoded_GroupId = FGenericPlatformHttp::UrlEncode(GroupId); + Endpoint = Endpoint.Replace(TEXT("{group_id}"), *Encoded_GroupId); + + // + // Fill Query Params + TArray> QueryParams; + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (State.IsEmpty() == false) + { + QueryParams.Add({TEXT("state"), FString::Printf(TEXT("%d"), (State.GetValue()))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaGroupUserList Result = FNakamaGroupUserList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListLeaderboardRecords ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& LeaderboardId, + const TArray& OwnerIds, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + FNakamaOptionalInt64 Expiry, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/leaderboard/{leaderboard_id}"); + // + // Fill Path Params + const FString Encoded_LeaderboardId = FGenericPlatformHttp::UrlEncode(LeaderboardId); + Endpoint = Endpoint.Replace(TEXT("{leaderboard_id}"), *Encoded_LeaderboardId); + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : OwnerIds) + { + QueryParams.Add({TEXT("owner_ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + if (Expiry.IsEmpty() == false) + { + QueryParams.Add({TEXT("expiry"), FString::Printf(TEXT("%lld"), (Expiry.GetValue()))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaLeaderboardRecordList Result = FNakamaLeaderboardRecordList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListLeaderboardRecords ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& LeaderboardId, + const TArray& OwnerIds, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + FNakamaOptionalInt64 Expiry, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/leaderboard/{leaderboard_id}"); + // + // Fill Path Params + const FString Encoded_LeaderboardId = FGenericPlatformHttp::UrlEncode(LeaderboardId); + Endpoint = Endpoint.Replace(TEXT("{leaderboard_id}"), *Encoded_LeaderboardId); + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : OwnerIds) + { + QueryParams.Add({TEXT("owner_ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + if (Expiry.IsEmpty() == false) + { + QueryParams.Add({TEXT("expiry"), FString::Printf(TEXT("%lld"), (Expiry.GetValue()))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaLeaderboardRecordList Result = FNakamaLeaderboardRecordList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListLeaderboardRecordsAroundOwner ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& LeaderboardId, + FNakamaOptionalInt32 Limit, + const FString& OwnerId, + FNakamaOptionalInt64 Expiry, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/leaderboard/{leaderboard_id}/owner/{owner_id}"); + // + // Fill Path Params + const FString Encoded_LeaderboardId = FGenericPlatformHttp::UrlEncode(LeaderboardId); + Endpoint = Endpoint.Replace(TEXT("{leaderboard_id}"), *Encoded_LeaderboardId); + const FString Encoded_OwnerId = FGenericPlatformHttp::UrlEncode(OwnerId); + Endpoint = Endpoint.Replace(TEXT("{owner_id}"), *Encoded_OwnerId); + + // + // Fill Query Params + TArray> QueryParams; + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (Expiry.IsEmpty() == false) + { + QueryParams.Add({TEXT("expiry"), FString::Printf(TEXT("%lld"), (Expiry.GetValue()))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaLeaderboardRecordList Result = FNakamaLeaderboardRecordList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListLeaderboardRecordsAroundOwner ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& LeaderboardId, + FNakamaOptionalInt32 Limit, + const FString& OwnerId, + FNakamaOptionalInt64 Expiry, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/leaderboard/{leaderboard_id}/owner/{owner_id}"); + // + // Fill Path Params + const FString Encoded_LeaderboardId = FGenericPlatformHttp::UrlEncode(LeaderboardId); + Endpoint = Endpoint.Replace(TEXT("{leaderboard_id}"), *Encoded_LeaderboardId); + const FString Encoded_OwnerId = FGenericPlatformHttp::UrlEncode(OwnerId); + Endpoint = Endpoint.Replace(TEXT("{owner_id}"), *Encoded_OwnerId); + + // + // Fill Query Params + TArray> QueryParams; + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (Expiry.IsEmpty() == false) + { + QueryParams.Add({TEXT("expiry"), FString::Printf(TEXT("%lld"), (Expiry.GetValue()))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaLeaderboardRecordList Result = FNakamaLeaderboardRecordList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListMatches ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + FNakamaOptionalInt32 Limit, + FNakamaOptionalBool Authoritative, + const FString& Label, + FNakamaOptionalInt32 MinSize, + FNakamaOptionalInt32 MaxSize, + const FString& Query, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/match"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (Authoritative.IsEmpty() == false) + { + QueryParams.Add({TEXT("authoritative"), FString::Printf(TEXT("%s"), *LexToString(Authoritative.GetValue()))}); + } + if (Label.IsEmpty() == false) + { + QueryParams.Add({TEXT("label"), FString::Printf(TEXT("%s"), *(Label))}); + } + if (MinSize.IsEmpty() == false) + { + QueryParams.Add({TEXT("min_size"), FString::Printf(TEXT("%d"), (MinSize.GetValue()))}); + } + if (MaxSize.IsEmpty() == false) + { + QueryParams.Add({TEXT("max_size"), FString::Printf(TEXT("%d"), (MaxSize.GetValue()))}); + } + if (Query.IsEmpty() == false) + { + QueryParams.Add({TEXT("query"), FString::Printf(TEXT("%s"), *(Query))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaMatchList Result = FNakamaMatchList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListMatches ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + FNakamaOptionalInt32 Limit, + FNakamaOptionalBool Authoritative, + const FString& Label, + FNakamaOptionalInt32 MinSize, + FNakamaOptionalInt32 MaxSize, + const FString& Query, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/match"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (Authoritative.IsEmpty() == false) + { + QueryParams.Add({TEXT("authoritative"), FString::Printf(TEXT("%s"), *LexToString(Authoritative.GetValue()))}); + } + if (Label.IsEmpty() == false) + { + QueryParams.Add({TEXT("label"), FString::Printf(TEXT("%s"), *(Label))}); + } + if (MinSize.IsEmpty() == false) + { + QueryParams.Add({TEXT("min_size"), FString::Printf(TEXT("%d"), (MinSize.GetValue()))}); + } + if (MaxSize.IsEmpty() == false) + { + QueryParams.Add({TEXT("max_size"), FString::Printf(TEXT("%d"), (MaxSize.GetValue()))}); + } + if (Query.IsEmpty() == false) + { + QueryParams.Add({TEXT("query"), FString::Printf(TEXT("%s"), *(Query))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaMatchList Result = FNakamaMatchList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListParties ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + FNakamaOptionalInt32 Limit, + FNakamaOptionalBool Open, + const FString& Query, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/party"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (Open.IsEmpty() == false) + { + QueryParams.Add({TEXT("open"), FString::Printf(TEXT("%s"), *LexToString(Open.GetValue()))}); + } + if (Query.IsEmpty() == false) + { + QueryParams.Add({TEXT("query"), FString::Printf(TEXT("%s"), *(Query))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaPartyList Result = FNakamaPartyList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListParties ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + FNakamaOptionalInt32 Limit, + FNakamaOptionalBool Open, + const FString& Query, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/party"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (Open.IsEmpty() == false) + { + QueryParams.Add({TEXT("open"), FString::Printf(TEXT("%s"), *LexToString(Open.GetValue()))}); + } + if (Query.IsEmpty() == false) + { + QueryParams.Add({TEXT("query"), FString::Printf(TEXT("%s"), *(Query))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaPartyList Result = FNakamaPartyList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListNotifications ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + FNakamaOptionalInt32 Limit, + const FString& CacheableCursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/notification"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (CacheableCursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cacheable_cursor"), FString::Printf(TEXT("%s"), *(CacheableCursor))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaNotificationList Result = FNakamaNotificationList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListNotifications ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + FNakamaOptionalInt32 Limit, + const FString& CacheableCursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/notification"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (CacheableCursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cacheable_cursor"), FString::Printf(TEXT("%s"), *(CacheableCursor))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaNotificationList Result = FNakamaNotificationList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListStorageObjects ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& UserId, + const FString& Collection, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/storage/{collection}"); + // + // Fill Path Params + const FString Encoded_Collection = FGenericPlatformHttp::UrlEncode(Collection); + Endpoint = Endpoint.Replace(TEXT("{collection}"), *Encoded_Collection); + + // + // Fill Query Params + TArray> QueryParams; + if (UserId.IsEmpty() == false) + { + QueryParams.Add({TEXT("user_id"), FString::Printf(TEXT("%s"), *(UserId))}); + } + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaStorageObjectList Result = FNakamaStorageObjectList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListStorageObjects ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& UserId, + const FString& Collection, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/storage/{collection}"); + // + // Fill Path Params + const FString Encoded_Collection = FGenericPlatformHttp::UrlEncode(Collection); + Endpoint = Endpoint.Replace(TEXT("{collection}"), *Encoded_Collection); + + // + // Fill Query Params + TArray> QueryParams; + if (UserId.IsEmpty() == false) + { + QueryParams.Add({TEXT("user_id"), FString::Printf(TEXT("%s"), *(UserId))}); + } + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaStorageObjectList Result = FNakamaStorageObjectList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListSubscriptions ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/iap/subscription"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Limit.IsEmpty() == false) + { + Body->SetNumberField(TEXT("limit"), Limit.GetValue()); + } + if (Cursor.IsEmpty() == false) + { + Body->SetStringField(TEXT("cursor"), Cursor); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaSubscriptionList Result = FNakamaSubscriptionList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListSubscriptions ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/iap/subscription"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Limit.IsEmpty() == false) + { + Body->SetNumberField(TEXT("limit"), Limit.GetValue()); + } + if (Cursor.IsEmpty() == false) + { + Body->SetStringField(TEXT("cursor"), Cursor); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaSubscriptionList Result = FNakamaSubscriptionList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListTournaments ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + FNakamaOptionalInt32 CategoryStart, + FNakamaOptionalInt32 CategoryEnd, + FNakamaOptionalInt32 StartTime, + FNakamaOptionalInt32 EndTime, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/tournament"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (CategoryStart.IsEmpty() == false) + { + QueryParams.Add({TEXT("category_start"), FString::Printf(TEXT("%d"), (CategoryStart.GetValue()))}); + } + if (CategoryEnd.IsEmpty() == false) + { + QueryParams.Add({TEXT("category_end"), FString::Printf(TEXT("%d"), (CategoryEnd.GetValue()))}); + } + if (StartTime.IsEmpty() == false) + { + QueryParams.Add({TEXT("start_time"), FString::Printf(TEXT("%d"), (StartTime.GetValue()))}); + } + if (EndTime.IsEmpty() == false) + { + QueryParams.Add({TEXT("end_time"), FString::Printf(TEXT("%d"), (EndTime.GetValue()))}); + } + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaTournamentList Result = FNakamaTournamentList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListTournaments ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + FNakamaOptionalInt32 CategoryStart, + FNakamaOptionalInt32 CategoryEnd, + FNakamaOptionalInt32 StartTime, + FNakamaOptionalInt32 EndTime, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/tournament"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + if (CategoryStart.IsEmpty() == false) + { + QueryParams.Add({TEXT("category_start"), FString::Printf(TEXT("%d"), (CategoryStart.GetValue()))}); + } + if (CategoryEnd.IsEmpty() == false) + { + QueryParams.Add({TEXT("category_end"), FString::Printf(TEXT("%d"), (CategoryEnd.GetValue()))}); + } + if (StartTime.IsEmpty() == false) + { + QueryParams.Add({TEXT("start_time"), FString::Printf(TEXT("%d"), (StartTime.GetValue()))}); + } + if (EndTime.IsEmpty() == false) + { + QueryParams.Add({TEXT("end_time"), FString::Printf(TEXT("%d"), (EndTime.GetValue()))}); + } + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaTournamentList Result = FNakamaTournamentList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListTournamentRecords ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& TournamentId, + const TArray& OwnerIds, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + FNakamaOptionalInt64 Expiry, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/tournament/{tournament_id}"); + // + // Fill Path Params + const FString Encoded_TournamentId = FGenericPlatformHttp::UrlEncode(TournamentId); + Endpoint = Endpoint.Replace(TEXT("{tournament_id}"), *Encoded_TournamentId); + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : OwnerIds) + { + QueryParams.Add({TEXT("owner_ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + if (Expiry.IsEmpty() == false) + { + QueryParams.Add({TEXT("expiry"), FString::Printf(TEXT("%lld"), (Expiry.GetValue()))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaTournamentRecordList Result = FNakamaTournamentRecordList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListTournamentRecords ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& TournamentId, + const TArray& OwnerIds, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + FNakamaOptionalInt64 Expiry, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/tournament/{tournament_id}"); + // + // Fill Path Params + const FString Encoded_TournamentId = FGenericPlatformHttp::UrlEncode(TournamentId); + Endpoint = Endpoint.Replace(TEXT("{tournament_id}"), *Encoded_TournamentId); + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : OwnerIds) + { + QueryParams.Add({TEXT("owner_ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + if (Expiry.IsEmpty() == false) + { + QueryParams.Add({TEXT("expiry"), FString::Printf(TEXT("%lld"), (Expiry.GetValue()))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaTournamentRecordList Result = FNakamaTournamentRecordList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListTournamentRecordsAroundOwner ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& TournamentId, + FNakamaOptionalInt32 Limit, + const FString& OwnerId, + FNakamaOptionalInt64 Expiry, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/tournament/{tournament_id}/owner/{owner_id}"); + // + // Fill Path Params + const FString Encoded_TournamentId = FGenericPlatformHttp::UrlEncode(TournamentId); + Endpoint = Endpoint.Replace(TEXT("{tournament_id}"), *Encoded_TournamentId); + const FString Encoded_OwnerId = FGenericPlatformHttp::UrlEncode(OwnerId); + Endpoint = Endpoint.Replace(TEXT("{owner_id}"), *Encoded_OwnerId); + + // + // Fill Query Params + TArray> QueryParams; + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (Expiry.IsEmpty() == false) + { + QueryParams.Add({TEXT("expiry"), FString::Printf(TEXT("%lld"), (Expiry.GetValue()))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaTournamentRecordList Result = FNakamaTournamentRecordList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListTournamentRecordsAroundOwner ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& TournamentId, + FNakamaOptionalInt32 Limit, + const FString& OwnerId, + FNakamaOptionalInt64 Expiry, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/tournament/{tournament_id}/owner/{owner_id}"); + // + // Fill Path Params + const FString Encoded_TournamentId = FGenericPlatformHttp::UrlEncode(TournamentId); + Endpoint = Endpoint.Replace(TEXT("{tournament_id}"), *Encoded_TournamentId); + const FString Encoded_OwnerId = FGenericPlatformHttp::UrlEncode(OwnerId); + Endpoint = Endpoint.Replace(TEXT("{owner_id}"), *Encoded_OwnerId); + + // + // Fill Query Params + TArray> QueryParams; + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (Expiry.IsEmpty() == false) + { + QueryParams.Add({TEXT("expiry"), FString::Printf(TEXT("%lld"), (Expiry.GetValue()))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaTournamentRecordList Result = FNakamaTournamentRecordList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListUserGroups ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& UserId, + FNakamaOptionalInt32 Limit, + FNakamaOptionalInt32 State, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/user/{user_id}/group"); + // + // Fill Path Params + const FString Encoded_UserId = FGenericPlatformHttp::UrlEncode(UserId); + Endpoint = Endpoint.Replace(TEXT("{user_id}"), *Encoded_UserId); + + // + // Fill Query Params + TArray> QueryParams; + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (State.IsEmpty() == false) + { + QueryParams.Add({TEXT("state"), FString::Printf(TEXT("%d"), (State.GetValue()))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaUserGroupList Result = FNakamaUserGroupList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ListUserGroups ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& UserId, + FNakamaOptionalInt32 Limit, + FNakamaOptionalInt32 State, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/user/{user_id}/group"); + // + // Fill Path Params + const FString Encoded_UserId = FGenericPlatformHttp::UrlEncode(UserId); + Endpoint = Endpoint.Replace(TEXT("{user_id}"), *Encoded_UserId); + + // + // Fill Query Params + TArray> QueryParams; + if (Limit.IsEmpty() == false) + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit.GetValue()))}); + } + if (State.IsEmpty() == false) + { + QueryParams.Add({TEXT("state"), FString::Printf(TEXT("%d"), (State.GetValue()))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaUserGroupList Result = FNakamaUserGroupList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::PromoteGroupUsers ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& GroupId, + const TArray& UserIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/group/{group_id}/promote"); + // + // Fill Path Params + const FString Encoded_GroupId = FGenericPlatformHttp::UrlEncode(GroupId); + Endpoint = Endpoint.Replace(TEXT("{group_id}"), *Encoded_GroupId); + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : UserIds) + { + QueryParams.Add({TEXT("user_ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::PromoteGroupUsers ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& GroupId, + const TArray& UserIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/group/{group_id}/promote"); + // + // Fill Path Params + const FString Encoded_GroupId = FGenericPlatformHttp::UrlEncode(GroupId); + Endpoint = Endpoint.Replace(TEXT("{group_id}"), *Encoded_GroupId); + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : UserIds) + { + QueryParams.Add({TEXT("user_ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::DemoteGroupUsers ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& GroupId, + const TArray& UserIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/group/{group_id}/demote"); + // + // Fill Path Params + const FString Encoded_GroupId = FGenericPlatformHttp::UrlEncode(GroupId); + Endpoint = Endpoint.Replace(TEXT("{group_id}"), *Encoded_GroupId); + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : UserIds) + { + QueryParams.Add({TEXT("user_ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::DemoteGroupUsers ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& GroupId, + const TArray& UserIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/group/{group_id}/demote"); + // + // Fill Path Params + const FString Encoded_GroupId = FGenericPlatformHttp::UrlEncode(GroupId); + Endpoint = Endpoint.Replace(TEXT("{group_id}"), *Encoded_GroupId); + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : UserIds) + { + QueryParams.Add({TEXT("user_ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ReadStorageObjects ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const TArray& ObjectIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/storage"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (ObjectIds.Num() > 0) + { + TArray> Array; + for (const FNakamaReadStorageObjectId& Item : ObjectIds) + { + Array.Add(MakeShared(Item.ToJson())); + } + Body->SetArrayField(TEXT("object_ids"), Array); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaStorageObjects Result = FNakamaStorageObjects::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ReadStorageObjects ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const TArray& ObjectIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/storage"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (ObjectIds.Num() > 0) + { + TArray> Array; + for (const FNakamaReadStorageObjectId& Item : ObjectIds) + { + Array.Add(MakeShared(Item.ToJson())); + } + Body->SetArrayField(TEXT("object_ids"), Array); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaStorageObjects Result = FNakamaStorageObjects::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::RpcFunc ( + const FNakamaClientConfig& Config, + const FString& Id, + const FString& Payload, + const FString& HttpKey, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/rpc/{id}"); + // + // Fill Path Params + const FString Encoded_Id = FGenericPlatformHttp::UrlEncode(Id); + Endpoint = Endpoint.Replace(TEXT("{id}"), *Encoded_Id); + + // + // Fill Query Params + TArray> QueryParams; + if (HttpKey.IsEmpty() == false) + { + QueryParams.Add({TEXT("http_key"), FString::Printf(TEXT("%s"), *(HttpKey))}); + } + + // + // Fill Body Params + FString RawBody; + if (Payload.IsEmpty() == false) + { + FString Escaped = Payload.Replace(TEXT("\\"), TEXT("\\\\")).Replace(TEXT("\""), TEXT("\\\"")).Replace(TEXT("\n"), TEXT("\\n")).Replace(TEXT("\r"), TEXT("\\r")).Replace(TEXT("\t"), TEXT("\\t")); + RawBody = TEXT("\"") + Escaped + TEXT("\""); + } + + // + // Make the request + SendRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + RawBody, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaRpc Result = FNakamaRpc::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::RpcFunc ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Id, + const FString& Payload, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/rpc/{id}"); + // + // Fill Path Params + const FString Encoded_Id = FGenericPlatformHttp::UrlEncode(Id); + Endpoint = Endpoint.Replace(TEXT("{id}"), *Encoded_Id); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + FString RawBody; + if (Payload.IsEmpty() == false) + { + FString Escaped = Payload.Replace(TEXT("\\"), TEXT("\\\\")).Replace(TEXT("\""), TEXT("\\\"")).Replace(TEXT("\n"), TEXT("\\n")).Replace(TEXT("\r"), TEXT("\\r")).Replace(TEXT("\t"), TEXT("\\t")); + RawBody = TEXT("\"") + Escaped + TEXT("\""); + } + + // + // Make the request + SendRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + RawBody, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaRpc Result = FNakamaRpc::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::UnlinkApple ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/unlink/apple"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Token.IsEmpty() == false) + { + Body->SetStringField(TEXT("token"), Token); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::UnlinkApple ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/unlink/apple"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Token.IsEmpty() == false) + { + Body->SetStringField(TEXT("token"), Token); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::UnlinkCustom ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Id, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/unlink/custom"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Id.IsEmpty() == false) + { + Body->SetStringField(TEXT("id"), Id); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::UnlinkCustom ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Id, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/unlink/custom"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Id.IsEmpty() == false) + { + Body->SetStringField(TEXT("id"), Id); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::UnlinkDevice ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Id, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/unlink/device"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Id.IsEmpty() == false) + { + Body->SetStringField(TEXT("id"), Id); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::UnlinkDevice ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Id, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/unlink/device"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Id.IsEmpty() == false) + { + Body->SetStringField(TEXT("id"), Id); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::UnlinkEmail ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Email, + const FString& Password, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/unlink/email"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Email.IsEmpty() == false) + { + Body->SetStringField(TEXT("email"), Email); + } + if (Password.IsEmpty() == false) + { + Body->SetStringField(TEXT("password"), Password); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::UnlinkEmail ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Email, + const FString& Password, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/unlink/email"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Email.IsEmpty() == false) + { + Body->SetStringField(TEXT("email"), Email); + } + if (Password.IsEmpty() == false) + { + Body->SetStringField(TEXT("password"), Password); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::UnlinkFacebook ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/unlink/facebook"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Token.IsEmpty() == false) + { + Body->SetStringField(TEXT("token"), Token); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::UnlinkFacebook ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/unlink/facebook"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Token.IsEmpty() == false) + { + Body->SetStringField(TEXT("token"), Token); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::UnlinkFacebookInstantGame ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& SignedPlayerInfo, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/unlink/facebookinstantgame"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (SignedPlayerInfo.IsEmpty() == false) + { + Body->SetStringField(TEXT("signed_player_info"), SignedPlayerInfo); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::UnlinkFacebookInstantGame ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& SignedPlayerInfo, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/unlink/facebookinstantgame"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (SignedPlayerInfo.IsEmpty() == false) + { + Body->SetStringField(TEXT("signed_player_info"), SignedPlayerInfo); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::UnlinkGameCenter ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& PlayerId, + const FString& BundleId, + int64 TimestampSeconds, + const FString& Salt, + const FString& Signature, + const FString& PublicKeyUrl, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/unlink/gamecenter"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (PlayerId.IsEmpty() == false) + { + Body->SetStringField(TEXT("player_id"), PlayerId); + } + if (BundleId.IsEmpty() == false) + { + Body->SetStringField(TEXT("bundle_id"), BundleId); + } + + { + Body->SetNumberField(TEXT("timestamp_seconds"), TimestampSeconds); + } + if (Salt.IsEmpty() == false) + { + Body->SetStringField(TEXT("salt"), Salt); + } + if (Signature.IsEmpty() == false) + { + Body->SetStringField(TEXT("signature"), Signature); + } + if (PublicKeyUrl.IsEmpty() == false) + { + Body->SetStringField(TEXT("public_key_url"), PublicKeyUrl); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::UnlinkGameCenter ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& PlayerId, + const FString& BundleId, + int64 TimestampSeconds, + const FString& Salt, + const FString& Signature, + const FString& PublicKeyUrl, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/unlink/gamecenter"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (PlayerId.IsEmpty() == false) + { + Body->SetStringField(TEXT("player_id"), PlayerId); + } + if (BundleId.IsEmpty() == false) + { + Body->SetStringField(TEXT("bundle_id"), BundleId); + } + + { + Body->SetNumberField(TEXT("timestamp_seconds"), TimestampSeconds); + } + if (Salt.IsEmpty() == false) + { + Body->SetStringField(TEXT("salt"), Salt); + } + if (Signature.IsEmpty() == false) + { + Body->SetStringField(TEXT("signature"), Signature); + } + if (PublicKeyUrl.IsEmpty() == false) + { + Body->SetStringField(TEXT("public_key_url"), PublicKeyUrl); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::UnlinkGoogle ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/unlink/google"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Token.IsEmpty() == false) + { + Body->SetStringField(TEXT("token"), Token); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::UnlinkGoogle ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/unlink/google"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Token.IsEmpty() == false) + { + Body->SetStringField(TEXT("token"), Token); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::UnlinkSteam ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/unlink/steam"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Token.IsEmpty() == false) + { + Body->SetStringField(TEXT("token"), Token); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::UnlinkSteam ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account/unlink/steam"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Token.IsEmpty() == false) + { + Body->SetStringField(TEXT("token"), Token); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("vars"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::UpdateAccount ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Username, + const FString& DisplayName, + const FString& AvatarUrl, + const FString& LangTag, + const FString& Location, + const FString& Timezone, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Username.IsEmpty() == false) + { + Body->SetStringField(TEXT("username"), Username); + } + if (DisplayName.IsEmpty() == false) + { + Body->SetStringField(TEXT("display_name"), DisplayName); + } + if (AvatarUrl.IsEmpty() == false) + { + Body->SetStringField(TEXT("avatar_url"), AvatarUrl); + } + if (LangTag.IsEmpty() == false) + { + Body->SetStringField(TEXT("lang_tag"), LangTag); + } + if (Location.IsEmpty() == false) + { + Body->SetStringField(TEXT("location"), Location); + } + if (Timezone.IsEmpty() == false) + { + Body->SetStringField(TEXT("timezone"), Timezone); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("PUT"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::UpdateAccount ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Username, + const FString& DisplayName, + const FString& AvatarUrl, + const FString& LangTag, + const FString& Location, + const FString& Timezone, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/account"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Username.IsEmpty() == false) + { + Body->SetStringField(TEXT("username"), Username); + } + if (DisplayName.IsEmpty() == false) + { + Body->SetStringField(TEXT("display_name"), DisplayName); + } + if (AvatarUrl.IsEmpty() == false) + { + Body->SetStringField(TEXT("avatar_url"), AvatarUrl); + } + if (LangTag.IsEmpty() == false) + { + Body->SetStringField(TEXT("lang_tag"), LangTag); + } + if (Location.IsEmpty() == false) + { + Body->SetStringField(TEXT("location"), Location); + } + if (Timezone.IsEmpty() == false) + { + Body->SetStringField(TEXT("timezone"), Timezone); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("PUT"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::UpdateGroup ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& GroupId, + const FString& Name, + const FString& Description, + const FString& LangTag, + const FString& AvatarUrl, + FNakamaOptionalBool Open, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/group/{group_id}"); + // + // Fill Path Params + const FString Encoded_GroupId = FGenericPlatformHttp::UrlEncode(GroupId); + Endpoint = Endpoint.Replace(TEXT("{group_id}"), *Encoded_GroupId); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Name.IsEmpty() == false) + { + Body->SetStringField(TEXT("name"), Name); + } + if (Description.IsEmpty() == false) + { + Body->SetStringField(TEXT("description"), Description); + } + if (LangTag.IsEmpty() == false) + { + Body->SetStringField(TEXT("lang_tag"), LangTag); + } + if (AvatarUrl.IsEmpty() == false) + { + Body->SetStringField(TEXT("avatar_url"), AvatarUrl); + } + if (Open.IsEmpty() == false) + { + Body->SetBoolField(TEXT("open"), Open.GetValue()); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("PUT"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::UpdateGroup ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& GroupId, + const FString& Name, + const FString& Description, + const FString& LangTag, + const FString& AvatarUrl, + FNakamaOptionalBool Open, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/group/{group_id}"); + // + // Fill Path Params + const FString Encoded_GroupId = FGenericPlatformHttp::UrlEncode(GroupId); + Endpoint = Endpoint.Replace(TEXT("{group_id}"), *Encoded_GroupId); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Name.IsEmpty() == false) + { + Body->SetStringField(TEXT("name"), Name); + } + if (Description.IsEmpty() == false) + { + Body->SetStringField(TEXT("description"), Description); + } + if (LangTag.IsEmpty() == false) + { + Body->SetStringField(TEXT("lang_tag"), LangTag); + } + if (AvatarUrl.IsEmpty() == false) + { + Body->SetStringField(TEXT("avatar_url"), AvatarUrl); + } + if (Open.IsEmpty() == false) + { + Body->SetBoolField(TEXT("open"), Open.GetValue()); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("PUT"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ValidatePurchaseApple ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Receipt, + FNakamaOptionalBool Persist, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/iap/purchase/apple"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Receipt.IsEmpty() == false) + { + Body->SetStringField(TEXT("receipt"), Receipt); + } + if (Persist.IsEmpty() == false) + { + Body->SetBoolField(TEXT("persist"), Persist.GetValue()); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaValidatePurchaseResponse Result = FNakamaValidatePurchaseResponse::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ValidatePurchaseApple ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Receipt, + FNakamaOptionalBool Persist, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/iap/purchase/apple"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Receipt.IsEmpty() == false) + { + Body->SetStringField(TEXT("receipt"), Receipt); + } + if (Persist.IsEmpty() == false) + { + Body->SetBoolField(TEXT("persist"), Persist.GetValue()); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaValidatePurchaseResponse Result = FNakamaValidatePurchaseResponse::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ValidateSubscriptionApple ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Receipt, + FNakamaOptionalBool Persist, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/iap/subscription/apple"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Receipt.IsEmpty() == false) + { + Body->SetStringField(TEXT("receipt"), Receipt); + } + if (Persist.IsEmpty() == false) + { + Body->SetBoolField(TEXT("persist"), Persist.GetValue()); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaValidateSubscriptionResponse Result = FNakamaValidateSubscriptionResponse::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ValidateSubscriptionApple ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Receipt, + FNakamaOptionalBool Persist, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/iap/subscription/apple"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Receipt.IsEmpty() == false) + { + Body->SetStringField(TEXT("receipt"), Receipt); + } + if (Persist.IsEmpty() == false) + { + Body->SetBoolField(TEXT("persist"), Persist.GetValue()); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaValidateSubscriptionResponse Result = FNakamaValidateSubscriptionResponse::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ValidatePurchaseGoogle ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Purchase, + FNakamaOptionalBool Persist, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/iap/purchase/google"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Purchase.IsEmpty() == false) + { + Body->SetStringField(TEXT("purchase"), Purchase); + } + if (Persist.IsEmpty() == false) + { + Body->SetBoolField(TEXT("persist"), Persist.GetValue()); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaValidatePurchaseResponse Result = FNakamaValidatePurchaseResponse::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ValidatePurchaseGoogle ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Purchase, + FNakamaOptionalBool Persist, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/iap/purchase/google"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Purchase.IsEmpty() == false) + { + Body->SetStringField(TEXT("purchase"), Purchase); + } + if (Persist.IsEmpty() == false) + { + Body->SetBoolField(TEXT("persist"), Persist.GetValue()); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaValidatePurchaseResponse Result = FNakamaValidatePurchaseResponse::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ValidateSubscriptionGoogle ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Receipt, + FNakamaOptionalBool Persist, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/iap/subscription/google"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Receipt.IsEmpty() == false) + { + Body->SetStringField(TEXT("receipt"), Receipt); + } + if (Persist.IsEmpty() == false) + { + Body->SetBoolField(TEXT("persist"), Persist.GetValue()); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaValidateSubscriptionResponse Result = FNakamaValidateSubscriptionResponse::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ValidateSubscriptionGoogle ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Receipt, + FNakamaOptionalBool Persist, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/iap/subscription/google"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Receipt.IsEmpty() == false) + { + Body->SetStringField(TEXT("receipt"), Receipt); + } + if (Persist.IsEmpty() == false) + { + Body->SetBoolField(TEXT("persist"), Persist.GetValue()); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaValidateSubscriptionResponse Result = FNakamaValidateSubscriptionResponse::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ValidatePurchaseHuawei ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Purchase, + const FString& Signature, + FNakamaOptionalBool Persist, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/iap/purchase/huawei"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Purchase.IsEmpty() == false) + { + Body->SetStringField(TEXT("purchase"), Purchase); + } + if (Signature.IsEmpty() == false) + { + Body->SetStringField(TEXT("signature"), Signature); + } + if (Persist.IsEmpty() == false) + { + Body->SetBoolField(TEXT("persist"), Persist.GetValue()); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaValidatePurchaseResponse Result = FNakamaValidatePurchaseResponse::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ValidatePurchaseHuawei ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Purchase, + const FString& Signature, + FNakamaOptionalBool Persist, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/iap/purchase/huawei"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Purchase.IsEmpty() == false) + { + Body->SetStringField(TEXT("purchase"), Purchase); + } + if (Signature.IsEmpty() == false) + { + Body->SetStringField(TEXT("signature"), Signature); + } + if (Persist.IsEmpty() == false) + { + Body->SetBoolField(TEXT("persist"), Persist.GetValue()); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaValidatePurchaseResponse Result = FNakamaValidatePurchaseResponse::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ValidatePurchaseFacebookInstant ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& SignedRequest, + FNakamaOptionalBool Persist, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/iap/purchase/facebookinstant"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (SignedRequest.IsEmpty() == false) + { + Body->SetStringField(TEXT("signed_request"), SignedRequest); + } + if (Persist.IsEmpty() == false) + { + Body->SetBoolField(TEXT("persist"), Persist.GetValue()); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaValidatePurchaseResponse Result = FNakamaValidatePurchaseResponse::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::ValidatePurchaseFacebookInstant ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& SignedRequest, + FNakamaOptionalBool Persist, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/iap/purchase/facebookinstant"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (SignedRequest.IsEmpty() == false) + { + Body->SetStringField(TEXT("signed_request"), SignedRequest); + } + if (Persist.IsEmpty() == false) + { + Body->SetBoolField(TEXT("persist"), Persist.GetValue()); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaValidatePurchaseResponse Result = FNakamaValidatePurchaseResponse::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::WriteLeaderboardRecord ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& LeaderboardId, + const FNakamaWriteLeaderboardRecordRequestLeaderboardRecordWrite& Record, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/leaderboard/{leaderboard_id}"); + // + // Fill Path Params + const FString Encoded_LeaderboardId = FGenericPlatformHttp::UrlEncode(LeaderboardId); + Endpoint = Endpoint.Replace(TEXT("{leaderboard_id}"), *Encoded_LeaderboardId); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = Record.ToJson(); + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaLeaderboardRecord Result = FNakamaLeaderboardRecord::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::WriteLeaderboardRecord ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& LeaderboardId, + const FNakamaWriteLeaderboardRecordRequestLeaderboardRecordWrite& Record, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/leaderboard/{leaderboard_id}"); + // + // Fill Path Params + const FString Encoded_LeaderboardId = FGenericPlatformHttp::UrlEncode(LeaderboardId); + Endpoint = Endpoint.Replace(TEXT("{leaderboard_id}"), *Encoded_LeaderboardId); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = Record.ToJson(); + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaLeaderboardRecord Result = FNakamaLeaderboardRecord::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::WriteStorageObjects ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const TArray& Objects, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/storage"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Objects.Num() > 0) + { + TArray> Array; + for (const FNakamaWriteStorageObject& Item : Objects) + { + Array.Add(MakeShared(Item.ToJson())); + } + Body->SetArrayField(TEXT("objects"), Array); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("PUT"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaStorageObjectAcks Result = FNakamaStorageObjectAcks::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::WriteStorageObjects ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const TArray& Objects, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/storage"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Objects.Num() > 0) + { + TArray> Array; + for (const FNakamaWriteStorageObject& Item : Objects) + { + Array.Add(MakeShared(Item.ToJson())); + } + Body->SetArrayField(TEXT("objects"), Array); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("PUT"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaStorageObjectAcks Result = FNakamaStorageObjectAcks::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::WriteTournamentRecord ( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& TournamentId, + const FNakamaWriteTournamentRecordRequestTournamentRecordWrite& Record, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/tournament/{tournament_id}"); + // + // Fill Path Params + const FString Encoded_TournamentId = FGenericPlatformHttp::UrlEncode(TournamentId); + Endpoint = Endpoint.Replace(TEXT("{tournament_id}"), *Encoded_TournamentId); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = Record.ToJson(); + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("PUT"), + Body, + ENakamaRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaLeaderboardRecord Result = FNakamaLeaderboardRecord::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +NAKAMAAPI_API void NakamaApi::WriteTournamentRecord ( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& TournamentId, + const FNakamaWriteTournamentRecordRequestTournamentRecordWrite& Record, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v2/tournament/{tournament_id}"); + // + // Fill Path Params + const FString Encoded_TournamentId = FGenericPlatformHttp::UrlEncode(TournamentId); + Endpoint = Endpoint.Replace(TEXT("{tournament_id}"), *Encoded_TournamentId); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = Record.ToJson(); + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("PUT"), + Body, + ENakamaRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FNakamaLeaderboardRecord Result = FNakamaLeaderboardRecord::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + diff --git a/Nakama/Source/NakamaApi/Private/NakamaApiModule.cpp b/Nakama/Source/NakamaApi/Private/NakamaApiModule.cpp new file mode 100644 index 000000000..a963e9142 --- /dev/null +++ b/Nakama/Source/NakamaApi/Private/NakamaApiModule.cpp @@ -0,0 +1,35 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NakamaApiModule.h" +#include "NakamaApi.h" + +void FNakamaApiModule::StartupModule() +{ + UE_LOG(LogNakama, Log, TEXT("NakamaApi module starting")); + + if (!FModuleManager::Get().IsModuleLoaded("WebSockets")) + { + FModuleManager::Get().LoadModule("WebSockets"); + } +} + +void FNakamaApiModule::ShutdownModule() +{ + UE_LOG(LogNakama, Log, TEXT("NakamaApi module shutting down")); +} + +IMPLEMENT_MODULE(FNakamaApiModule, NakamaApi) diff --git a/Nakama/Source/NakamaApi/Private/NakamaRtTypes.cpp b/Nakama/Source/NakamaApi/Private/NakamaRtTypes.cpp new file mode 100644 index 000000000..568b24626 --- /dev/null +++ b/Nakama/Source/NakamaApi/Private/NakamaRtTypes.cpp @@ -0,0 +1,2614 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + +#include "NakamaRtTypes.h" + +FNakamaRtUserPresence FNakamaRtUserPresence::FromJson(const TSharedPtr& Json) +{ + FNakamaRtUserPresence Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("user_id"))) + { + Result.UserId = Json->GetStringField(TEXT("user_id")); + } + if (Json->HasField(TEXT("session_id"))) + { + Result.SessionId = Json->GetStringField(TEXT("session_id")); + } + if (Json->HasField(TEXT("username"))) + { + Result.Username = Json->GetStringField(TEXT("username")); + } + if (Json->HasField(TEXT("persistence"))) + { + Result.Persistence = Json->GetBoolField(TEXT("persistence")); + } + if (Json->HasField(TEXT("status"))) + { + Result.Status = Json->GetStringField(TEXT("status")); + } + + return Result; +} + +TSharedPtr FNakamaRtUserPresence::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (UserId.IsEmpty() == false) + { + Json->SetStringField(TEXT("user_id"), UserId); + } + if (SessionId.IsEmpty() == false) + { + Json->SetStringField(TEXT("session_id"), SessionId); + } + if (Username.IsEmpty() == false) + { + Json->SetStringField(TEXT("username"), Username); + } + + { + Json->SetBoolField(TEXT("persistence"), Persistence); + } + if (Status.IsEmpty() == false) + { + Json->SetStringField(TEXT("status"), Status); + } + return Json; +} + +FNakamaRtChannel FNakamaRtChannel::FromJson(const TSharedPtr& Json) +{ + FNakamaRtChannel Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("id"))) + { + Result.Id = Json->GetStringField(TEXT("id")); + } + if (Json->HasField(TEXT("presences"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("presences"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Presences.Add(FNakamaRtUserPresence::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("self"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("self"), NestedObj)) + { + Result.Self_ = (FNakamaRtUserPresence::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("room_name"))) + { + Result.RoomName = Json->GetStringField(TEXT("room_name")); + } + if (Json->HasField(TEXT("group_id"))) + { + Result.GroupId = Json->GetStringField(TEXT("group_id")); + } + if (Json->HasField(TEXT("user_id_one"))) + { + Result.UserIdOne = Json->GetStringField(TEXT("user_id_one")); + } + if (Json->HasField(TEXT("user_id_two"))) + { + Result.UserIdTwo = Json->GetStringField(TEXT("user_id_two")); + } + + return Result; +} + +TSharedPtr FNakamaRtChannel::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Id.IsEmpty() == false) + { + Json->SetStringField(TEXT("id"), Id); + } + if (Presences.Num() > 0) + { + TArray> Array; + for (const auto& Item : Presences) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("presences"), Array); + } + + { + Json->SetObjectField(TEXT("self"), Self_.ToJson()); + } + if (RoomName.IsEmpty() == false) + { + Json->SetStringField(TEXT("room_name"), RoomName); + } + if (GroupId.IsEmpty() == false) + { + Json->SetStringField(TEXT("group_id"), GroupId); + } + if (UserIdOne.IsEmpty() == false) + { + Json->SetStringField(TEXT("user_id_one"), UserIdOne); + } + if (UserIdTwo.IsEmpty() == false) + { + Json->SetStringField(TEXT("user_id_two"), UserIdTwo); + } + return Json; +} + +FNakamaRtChannelJoin FNakamaRtChannelJoin::FromJson(const TSharedPtr& Json) +{ + FNakamaRtChannelJoin Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("target"))) + { + Result.Target = Json->GetStringField(TEXT("target")); + } + if (Json->HasField(TEXT("type"))) + { + Result.Type = Json->GetNumberField(TEXT("type")); + } + if (Json->HasField(TEXT("persistence"))) + { + Result.Persistence = Json->GetBoolField(TEXT("persistence")); + } + if (Json->HasField(TEXT("hidden"))) + { + Result.Hidden = Json->GetBoolField(TEXT("hidden")); + } + + return Result; +} + +TSharedPtr FNakamaRtChannelJoin::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Target.IsEmpty() == false) + { + Json->SetStringField(TEXT("target"), Target); + } + + { + Json->SetNumberField(TEXT("type"), Type); + } + if (Persistence.IsEmpty() == false) + { + Json->SetBoolField(TEXT("persistence"), Persistence.GetValue()); + } + if (Hidden.IsEmpty() == false) + { + Json->SetBoolField(TEXT("hidden"), Hidden.GetValue()); + } + return Json; +} + +FNakamaRtChannelLeave FNakamaRtChannelLeave::FromJson(const TSharedPtr& Json) +{ + FNakamaRtChannelLeave Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("channel_id"))) + { + Result.ChannelId = Json->GetStringField(TEXT("channel_id")); + } + + return Result; +} + +TSharedPtr FNakamaRtChannelLeave::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (ChannelId.IsEmpty() == false) + { + Json->SetStringField(TEXT("channel_id"), ChannelId); + } + return Json; +} + +FNakamaRtChannelMessageAck FNakamaRtChannelMessageAck::FromJson(const TSharedPtr& Json) +{ + FNakamaRtChannelMessageAck Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("channel_id"))) + { + Result.ChannelId = Json->GetStringField(TEXT("channel_id")); + } + if (Json->HasField(TEXT("message_id"))) + { + Result.MessageId = Json->GetStringField(TEXT("message_id")); + } + if (Json->HasField(TEXT("code"))) + { + Result.Code = Json->GetNumberField(TEXT("code")); + } + if (Json->HasField(TEXT("username"))) + { + Result.Username = Json->GetStringField(TEXT("username")); + } + if (Json->HasField(TEXT("create_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("create_time")), Result.CreateTime); + } + if (Json->HasField(TEXT("update_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("update_time")), Result.UpdateTime); + } + if (Json->HasField(TEXT("persistent"))) + { + Result.Persistent = Json->GetBoolField(TEXT("persistent")); + } + if (Json->HasField(TEXT("room_name"))) + { + Result.RoomName = Json->GetStringField(TEXT("room_name")); + } + if (Json->HasField(TEXT("group_id"))) + { + Result.GroupId = Json->GetStringField(TEXT("group_id")); + } + if (Json->HasField(TEXT("user_id_one"))) + { + Result.UserIdOne = Json->GetStringField(TEXT("user_id_one")); + } + if (Json->HasField(TEXT("user_id_two"))) + { + Result.UserIdTwo = Json->GetStringField(TEXT("user_id_two")); + } + + return Result; +} + +TSharedPtr FNakamaRtChannelMessageAck::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (ChannelId.IsEmpty() == false) + { + Json->SetStringField(TEXT("channel_id"), ChannelId); + } + if (MessageId.IsEmpty() == false) + { + Json->SetStringField(TEXT("message_id"), MessageId); + } + if (Code.IsEmpty() == false) + { + Json->SetNumberField(TEXT("code"), Code.GetValue()); + } + if (Username.IsEmpty() == false) + { + Json->SetStringField(TEXT("username"), Username); + } + + { + Json->SetStringField(TEXT("create_time"), CreateTime.ToIso8601()); + } + + { + Json->SetStringField(TEXT("update_time"), UpdateTime.ToIso8601()); + } + if (Persistent.IsEmpty() == false) + { + Json->SetBoolField(TEXT("persistent"), Persistent.GetValue()); + } + if (RoomName.IsEmpty() == false) + { + Json->SetStringField(TEXT("room_name"), RoomName); + } + if (GroupId.IsEmpty() == false) + { + Json->SetStringField(TEXT("group_id"), GroupId); + } + if (UserIdOne.IsEmpty() == false) + { + Json->SetStringField(TEXT("user_id_one"), UserIdOne); + } + if (UserIdTwo.IsEmpty() == false) + { + Json->SetStringField(TEXT("user_id_two"), UserIdTwo); + } + return Json; +} + +FNakamaRtChannelMessageSend FNakamaRtChannelMessageSend::FromJson(const TSharedPtr& Json) +{ + FNakamaRtChannelMessageSend Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("channel_id"))) + { + Result.ChannelId = Json->GetStringField(TEXT("channel_id")); + } + if (Json->HasField(TEXT("content"))) + { + Result.Content = Json->GetStringField(TEXT("content")); + } + + return Result; +} + +TSharedPtr FNakamaRtChannelMessageSend::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (ChannelId.IsEmpty() == false) + { + Json->SetStringField(TEXT("channel_id"), ChannelId); + } + if (Content.IsEmpty() == false) + { + Json->SetStringField(TEXT("content"), Content); + } + return Json; +} + +FNakamaRtChannelMessageUpdate FNakamaRtChannelMessageUpdate::FromJson(const TSharedPtr& Json) +{ + FNakamaRtChannelMessageUpdate Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("channel_id"))) + { + Result.ChannelId = Json->GetStringField(TEXT("channel_id")); + } + if (Json->HasField(TEXT("message_id"))) + { + Result.MessageId = Json->GetStringField(TEXT("message_id")); + } + if (Json->HasField(TEXT("content"))) + { + Result.Content = Json->GetStringField(TEXT("content")); + } + + return Result; +} + +TSharedPtr FNakamaRtChannelMessageUpdate::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (ChannelId.IsEmpty() == false) + { + Json->SetStringField(TEXT("channel_id"), ChannelId); + } + if (MessageId.IsEmpty() == false) + { + Json->SetStringField(TEXT("message_id"), MessageId); + } + if (Content.IsEmpty() == false) + { + Json->SetStringField(TEXT("content"), Content); + } + return Json; +} + +FNakamaRtChannelMessageRemove FNakamaRtChannelMessageRemove::FromJson(const TSharedPtr& Json) +{ + FNakamaRtChannelMessageRemove Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("channel_id"))) + { + Result.ChannelId = Json->GetStringField(TEXT("channel_id")); + } + if (Json->HasField(TEXT("message_id"))) + { + Result.MessageId = Json->GetStringField(TEXT("message_id")); + } + + return Result; +} + +TSharedPtr FNakamaRtChannelMessageRemove::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (ChannelId.IsEmpty() == false) + { + Json->SetStringField(TEXT("channel_id"), ChannelId); + } + if (MessageId.IsEmpty() == false) + { + Json->SetStringField(TEXT("message_id"), MessageId); + } + return Json; +} + +FNakamaRtChannelPresenceEvent FNakamaRtChannelPresenceEvent::FromJson(const TSharedPtr& Json) +{ + FNakamaRtChannelPresenceEvent Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("channel_id"))) + { + Result.ChannelId = Json->GetStringField(TEXT("channel_id")); + } + if (Json->HasField(TEXT("joins"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("joins"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Joins.Add(FNakamaRtUserPresence::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("leaves"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("leaves"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Leaves.Add(FNakamaRtUserPresence::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("room_name"))) + { + Result.RoomName = Json->GetStringField(TEXT("room_name")); + } + if (Json->HasField(TEXT("group_id"))) + { + Result.GroupId = Json->GetStringField(TEXT("group_id")); + } + if (Json->HasField(TEXT("user_id_one"))) + { + Result.UserIdOne = Json->GetStringField(TEXT("user_id_one")); + } + if (Json->HasField(TEXT("user_id_two"))) + { + Result.UserIdTwo = Json->GetStringField(TEXT("user_id_two")); + } + + return Result; +} + +TSharedPtr FNakamaRtChannelPresenceEvent::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (ChannelId.IsEmpty() == false) + { + Json->SetStringField(TEXT("channel_id"), ChannelId); + } + if (Joins.Num() > 0) + { + TArray> Array; + for (const auto& Item : Joins) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("joins"), Array); + } + if (Leaves.Num() > 0) + { + TArray> Array; + for (const auto& Item : Leaves) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("leaves"), Array); + } + if (RoomName.IsEmpty() == false) + { + Json->SetStringField(TEXT("room_name"), RoomName); + } + if (GroupId.IsEmpty() == false) + { + Json->SetStringField(TEXT("group_id"), GroupId); + } + if (UserIdOne.IsEmpty() == false) + { + Json->SetStringField(TEXT("user_id_one"), UserIdOne); + } + if (UserIdTwo.IsEmpty() == false) + { + Json->SetStringField(TEXT("user_id_two"), UserIdTwo); + } + return Json; +} + +FNakamaRtError FNakamaRtError::FromJson(const TSharedPtr& Json) +{ + FNakamaRtError Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("code"))) + { + Result.Code = Json->GetNumberField(TEXT("code")); + } + if (Json->HasField(TEXT("message"))) + { + Result.Message = Json->GetStringField(TEXT("message")); + } + if (Json->HasField(TEXT("context"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("context"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.Context.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + + return Result; +} + +TSharedPtr FNakamaRtError::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetNumberField(TEXT("code"), Code); + } + if (Message.IsEmpty() == false) + { + Json->SetStringField(TEXT("message"), Message); + } + if (Context.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Context) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("context"), MapObj); + } + return Json; +} + +FNakamaRtMatch FNakamaRtMatch::FromJson(const TSharedPtr& Json) +{ + FNakamaRtMatch Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("match_id"))) + { + Result.MatchId = Json->GetStringField(TEXT("match_id")); + } + if (Json->HasField(TEXT("authoritative"))) + { + Result.Authoritative = Json->GetBoolField(TEXT("authoritative")); + } + if (Json->HasField(TEXT("label"))) + { + Result.Label = Json->GetStringField(TEXT("label")); + } + if (Json->HasField(TEXT("size"))) + { + Result.Size = Json->GetNumberField(TEXT("size")); + } + if (Json->HasField(TEXT("presences"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("presences"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Presences.Add(FNakamaRtUserPresence::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("self"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("self"), NestedObj)) + { + Result.Self_ = (FNakamaRtUserPresence::FromJson(*NestedObj)); + } + } + + return Result; +} + +TSharedPtr FNakamaRtMatch::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (MatchId.IsEmpty() == false) + { + Json->SetStringField(TEXT("match_id"), MatchId); + } + + { + Json->SetBoolField(TEXT("authoritative"), Authoritative); + } + if (Label.IsEmpty() == false) + { + Json->SetStringField(TEXT("label"), Label); + } + + { + Json->SetNumberField(TEXT("size"), Size); + } + if (Presences.Num() > 0) + { + TArray> Array; + for (const auto& Item : Presences) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("presences"), Array); + } + + { + Json->SetObjectField(TEXT("self"), Self_.ToJson()); + } + return Json; +} + +FNakamaRtMatchCreate FNakamaRtMatchCreate::FromJson(const TSharedPtr& Json) +{ + FNakamaRtMatchCreate Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("name"))) + { + Result.Name = Json->GetStringField(TEXT("name")); + } + + return Result; +} + +TSharedPtr FNakamaRtMatchCreate::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Name.IsEmpty() == false) + { + Json->SetStringField(TEXT("name"), Name); + } + return Json; +} + +FNakamaRtMatchData FNakamaRtMatchData::FromJson(const TSharedPtr& Json) +{ + FNakamaRtMatchData Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("match_id"))) + { + Result.MatchId = Json->GetStringField(TEXT("match_id")); + } + if (Json->HasField(TEXT("presence"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("presence"), NestedObj)) + { + Result.Presence = (FNakamaRtUserPresence::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("op_code"))) + { + Result.OpCode = Json->GetNumberField(TEXT("op_code")); + } + if (Json->HasField(TEXT("data"))) + { + FBase64::Decode(Json->GetStringField(TEXT("data")), Result.Data); + } + if (Json->HasField(TEXT("reliable"))) + { + Result.Reliable = Json->GetBoolField(TEXT("reliable")); + } + + return Result; +} + +TSharedPtr FNakamaRtMatchData::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (MatchId.IsEmpty() == false) + { + Json->SetStringField(TEXT("match_id"), MatchId); + } + + { + Json->SetObjectField(TEXT("presence"), Presence.ToJson()); + } + + { + Json->SetNumberField(TEXT("op_code"), OpCode); + } + if (Data.IsEmpty() == false) + { + Json->SetStringField(TEXT("data"), FBase64::Encode(Data.GetData(), Data.Num())); + } + + { + Json->SetBoolField(TEXT("reliable"), Reliable); + } + return Json; +} + +FNakamaRtMatchDataSend FNakamaRtMatchDataSend::FromJson(const TSharedPtr& Json) +{ + FNakamaRtMatchDataSend Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("match_id"))) + { + Result.MatchId = Json->GetStringField(TEXT("match_id")); + } + if (Json->HasField(TEXT("op_code"))) + { + Result.OpCode = Json->GetNumberField(TEXT("op_code")); + } + if (Json->HasField(TEXT("data"))) + { + FBase64::Decode(Json->GetStringField(TEXT("data")), Result.Data); + } + if (Json->HasField(TEXT("presences"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("presences"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Presences.Add(FNakamaRtUserPresence::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("reliable"))) + { + Result.Reliable = Json->GetBoolField(TEXT("reliable")); + } + + return Result; +} + +TSharedPtr FNakamaRtMatchDataSend::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (MatchId.IsEmpty() == false) + { + Json->SetStringField(TEXT("match_id"), MatchId); + } + + { + Json->SetNumberField(TEXT("op_code"), OpCode); + } + if (Data.IsEmpty() == false) + { + Json->SetStringField(TEXT("data"), FBase64::Encode(Data.GetData(), Data.Num())); + } + if (Presences.Num() > 0) + { + TArray> Array; + for (const auto& Item : Presences) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("presences"), Array); + } + + { + Json->SetBoolField(TEXT("reliable"), Reliable); + } + return Json; +} + +FNakamaRtMatchJoin FNakamaRtMatchJoin::FromJson(const TSharedPtr& Json) +{ + FNakamaRtMatchJoin Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("metadata"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("metadata"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.Metadata.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + if (Json->HasField(TEXT("match_id"))) + { + Result.MatchId = Json->GetStringField(TEXT("match_id")); + } + if (Json->HasField(TEXT("token"))) + { + Result.Token = Json->GetStringField(TEXT("token")); + } + + return Result; +} + +TSharedPtr FNakamaRtMatchJoin::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Metadata.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Metadata) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("metadata"), MapObj); + } + if (MatchId.IsEmpty() == false) + { + Json->SetStringField(TEXT("match_id"), MatchId); + } + if (Token.IsEmpty() == false) + { + Json->SetStringField(TEXT("token"), Token); + } + return Json; +} + +FNakamaRtMatchLeave FNakamaRtMatchLeave::FromJson(const TSharedPtr& Json) +{ + FNakamaRtMatchLeave Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("match_id"))) + { + Result.MatchId = Json->GetStringField(TEXT("match_id")); + } + + return Result; +} + +TSharedPtr FNakamaRtMatchLeave::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (MatchId.IsEmpty() == false) + { + Json->SetStringField(TEXT("match_id"), MatchId); + } + return Json; +} + +FNakamaRtMatchPresenceEvent FNakamaRtMatchPresenceEvent::FromJson(const TSharedPtr& Json) +{ + FNakamaRtMatchPresenceEvent Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("match_id"))) + { + Result.MatchId = Json->GetStringField(TEXT("match_id")); + } + if (Json->HasField(TEXT("joins"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("joins"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Joins.Add(FNakamaRtUserPresence::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("leaves"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("leaves"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Leaves.Add(FNakamaRtUserPresence::FromJson(*ItemObj)); + } + } + } + } + + return Result; +} + +TSharedPtr FNakamaRtMatchPresenceEvent::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (MatchId.IsEmpty() == false) + { + Json->SetStringField(TEXT("match_id"), MatchId); + } + if (Joins.Num() > 0) + { + TArray> Array; + for (const auto& Item : Joins) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("joins"), Array); + } + if (Leaves.Num() > 0) + { + TArray> Array; + for (const auto& Item : Leaves) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("leaves"), Array); + } + return Json; +} + +FNakamaRtMatchmakerAdd FNakamaRtMatchmakerAdd::FromJson(const TSharedPtr& Json) +{ + FNakamaRtMatchmakerAdd Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("min_count"))) + { + Result.MinCount = Json->GetNumberField(TEXT("min_count")); + } + if (Json->HasField(TEXT("max_count"))) + { + Result.MaxCount = Json->GetNumberField(TEXT("max_count")); + } + if (Json->HasField(TEXT("query"))) + { + Result.Query = Json->GetStringField(TEXT("query")); + } + if (Json->HasField(TEXT("count_multiple"))) + { + Result.CountMultiple = Json->GetNumberField(TEXT("count_multiple")); + } + if (Json->HasField(TEXT("string_properties"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("string_properties"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.StringProperties.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + if (Json->HasField(TEXT("numeric_properties"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("numeric_properties"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.NumericProperties.Add(Pair.Key, Pair.Value->AsNumber()); + } + } + } + + return Result; +} + +TSharedPtr FNakamaRtMatchmakerAdd::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetNumberField(TEXT("min_count"), MinCount); + } + + { + Json->SetNumberField(TEXT("max_count"), MaxCount); + } + if (Query.IsEmpty() == false) + { + Json->SetStringField(TEXT("query"), Query); + } + if (CountMultiple.IsEmpty() == false) + { + Json->SetNumberField(TEXT("count_multiple"), CountMultiple.GetValue()); + } + if (StringProperties.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : StringProperties) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("string_properties"), MapObj); + } + if (NumericProperties.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : NumericProperties) + { + MapObj->SetNumberField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("numeric_properties"), MapObj); + } + return Json; +} + +FNakamaRtMatchmakerMatchedMatchmakerUser FNakamaRtMatchmakerMatchedMatchmakerUser::FromJson(const TSharedPtr& Json) +{ + FNakamaRtMatchmakerMatchedMatchmakerUser Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("presence"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("presence"), NestedObj)) + { + Result.Presence = (FNakamaRtUserPresence::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("party_id"))) + { + Result.PartyId = Json->GetStringField(TEXT("party_id")); + } + if (Json->HasField(TEXT("string_properties"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("string_properties"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.StringProperties.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + if (Json->HasField(TEXT("numeric_properties"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("numeric_properties"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.NumericProperties.Add(Pair.Key, Pair.Value->AsNumber()); + } + } + } + + return Result; +} + +TSharedPtr FNakamaRtMatchmakerMatchedMatchmakerUser::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetObjectField(TEXT("presence"), Presence.ToJson()); + } + if (PartyId.IsEmpty() == false) + { + Json->SetStringField(TEXT("party_id"), PartyId); + } + if (StringProperties.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : StringProperties) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("string_properties"), MapObj); + } + if (NumericProperties.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : NumericProperties) + { + MapObj->SetNumberField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("numeric_properties"), MapObj); + } + return Json; +} + +FNakamaRtMatchmakerMatched FNakamaRtMatchmakerMatched::FromJson(const TSharedPtr& Json) +{ + FNakamaRtMatchmakerMatched Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("ticket"))) + { + Result.Ticket = Json->GetStringField(TEXT("ticket")); + } + if (Json->HasField(TEXT("users"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("users"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Users.Add(FNakamaRtMatchmakerMatchedMatchmakerUser::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("self"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("self"), NestedObj)) + { + Result.Self_ = (FNakamaRtMatchmakerMatchedMatchmakerUser::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("match_id"))) + { + Result.MatchId = Json->GetStringField(TEXT("match_id")); + } + if (Json->HasField(TEXT("token"))) + { + Result.Token = Json->GetStringField(TEXT("token")); + } + + return Result; +} + +TSharedPtr FNakamaRtMatchmakerMatched::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Ticket.IsEmpty() == false) + { + Json->SetStringField(TEXT("ticket"), Ticket); + } + if (Users.Num() > 0) + { + TArray> Array; + for (const auto& Item : Users) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("users"), Array); + } + + { + Json->SetObjectField(TEXT("self"), Self_.ToJson()); + } + if (MatchId.IsEmpty() == false) + { + Json->SetStringField(TEXT("match_id"), MatchId); + } + if (Token.IsEmpty() == false) + { + Json->SetStringField(TEXT("token"), Token); + } + return Json; +} + +FNakamaRtMatchmakerRemove FNakamaRtMatchmakerRemove::FromJson(const TSharedPtr& Json) +{ + FNakamaRtMatchmakerRemove Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("ticket"))) + { + Result.Ticket = Json->GetStringField(TEXT("ticket")); + } + + return Result; +} + +TSharedPtr FNakamaRtMatchmakerRemove::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Ticket.IsEmpty() == false) + { + Json->SetStringField(TEXT("ticket"), Ticket); + } + return Json; +} + +FNakamaRtMatchmakerTicket FNakamaRtMatchmakerTicket::FromJson(const TSharedPtr& Json) +{ + FNakamaRtMatchmakerTicket Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("ticket"))) + { + Result.Ticket = Json->GetStringField(TEXT("ticket")); + } + + return Result; +} + +TSharedPtr FNakamaRtMatchmakerTicket::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Ticket.IsEmpty() == false) + { + Json->SetStringField(TEXT("ticket"), Ticket); + } + return Json; +} + +FNakamaRtNotifications FNakamaRtNotifications::FromJson(const TSharedPtr& Json) +{ + FNakamaRtNotifications Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("notifications"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("notifications"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Notifications.Add(FNakamaRtNotification::FromJson(*ItemObj)); + } + } + } + } + + return Result; +} + +TSharedPtr FNakamaRtNotifications::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Notifications.Num() > 0) + { + TArray> Array; + for (const auto& Item : Notifications) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("notifications"), Array); + } + return Json; +} + +FNakamaRtParty FNakamaRtParty::FromJson(const TSharedPtr& Json) +{ + FNakamaRtParty Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("party_id"))) + { + Result.PartyId = Json->GetStringField(TEXT("party_id")); + } + if (Json->HasField(TEXT("open"))) + { + Result.Open = Json->GetBoolField(TEXT("open")); + } + if (Json->HasField(TEXT("hidden"))) + { + Result.Hidden = Json->GetBoolField(TEXT("hidden")); + } + if (Json->HasField(TEXT("max_size"))) + { + Result.MaxSize = Json->GetNumberField(TEXT("max_size")); + } + if (Json->HasField(TEXT("self"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("self"), NestedObj)) + { + Result.Self_ = (FNakamaRtUserPresence::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("leader"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("leader"), NestedObj)) + { + Result.Leader = (FNakamaRtUserPresence::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("presences"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("presences"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Presences.Add(FNakamaRtUserPresence::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("label"))) + { + Result.Label = Json->GetStringField(TEXT("label")); + } + + return Result; +} + +TSharedPtr FNakamaRtParty::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (PartyId.IsEmpty() == false) + { + Json->SetStringField(TEXT("party_id"), PartyId); + } + + { + Json->SetBoolField(TEXT("open"), Open); + } + + { + Json->SetBoolField(TEXT("hidden"), Hidden); + } + + { + Json->SetNumberField(TEXT("max_size"), MaxSize); + } + + { + Json->SetObjectField(TEXT("self"), Self_.ToJson()); + } + + { + Json->SetObjectField(TEXT("leader"), Leader.ToJson()); + } + if (Presences.Num() > 0) + { + TArray> Array; + for (const auto& Item : Presences) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("presences"), Array); + } + if (Label.IsEmpty() == false) + { + Json->SetStringField(TEXT("label"), Label); + } + return Json; +} + +FNakamaRtPartyCreate FNakamaRtPartyCreate::FromJson(const TSharedPtr& Json) +{ + FNakamaRtPartyCreate Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("open"))) + { + Result.Open = Json->GetBoolField(TEXT("open")); + } + if (Json->HasField(TEXT("max_size"))) + { + Result.MaxSize = Json->GetNumberField(TEXT("max_size")); + } + if (Json->HasField(TEXT("label"))) + { + Result.Label = Json->GetStringField(TEXT("label")); + } + if (Json->HasField(TEXT("hidden"))) + { + Result.Hidden = Json->GetBoolField(TEXT("hidden")); + } + + return Result; +} + +TSharedPtr FNakamaRtPartyCreate::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetBoolField(TEXT("open"), Open); + } + + { + Json->SetNumberField(TEXT("max_size"), MaxSize); + } + if (Label.IsEmpty() == false) + { + Json->SetStringField(TEXT("label"), Label); + } + + { + Json->SetBoolField(TEXT("hidden"), Hidden); + } + return Json; +} + +FNakamaRtPartyUpdate FNakamaRtPartyUpdate::FromJson(const TSharedPtr& Json) +{ + FNakamaRtPartyUpdate Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("party_id"))) + { + Result.PartyId = Json->GetStringField(TEXT("party_id")); + } + if (Json->HasField(TEXT("label"))) + { + Result.Label = Json->GetStringField(TEXT("label")); + } + if (Json->HasField(TEXT("open"))) + { + Result.Open = Json->GetBoolField(TEXT("open")); + } + if (Json->HasField(TEXT("hidden"))) + { + Result.Hidden = Json->GetBoolField(TEXT("hidden")); + } + + return Result; +} + +TSharedPtr FNakamaRtPartyUpdate::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (PartyId.IsEmpty() == false) + { + Json->SetStringField(TEXT("party_id"), PartyId); + } + if (Label.IsEmpty() == false) + { + Json->SetStringField(TEXT("label"), Label); + } + + { + Json->SetBoolField(TEXT("open"), Open); + } + + { + Json->SetBoolField(TEXT("hidden"), Hidden); + } + return Json; +} + +FNakamaRtPartyJoin FNakamaRtPartyJoin::FromJson(const TSharedPtr& Json) +{ + FNakamaRtPartyJoin Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("party_id"))) + { + Result.PartyId = Json->GetStringField(TEXT("party_id")); + } + + return Result; +} + +TSharedPtr FNakamaRtPartyJoin::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (PartyId.IsEmpty() == false) + { + Json->SetStringField(TEXT("party_id"), PartyId); + } + return Json; +} + +FNakamaRtPartyLeave FNakamaRtPartyLeave::FromJson(const TSharedPtr& Json) +{ + FNakamaRtPartyLeave Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("party_id"))) + { + Result.PartyId = Json->GetStringField(TEXT("party_id")); + } + + return Result; +} + +TSharedPtr FNakamaRtPartyLeave::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (PartyId.IsEmpty() == false) + { + Json->SetStringField(TEXT("party_id"), PartyId); + } + return Json; +} + +FNakamaRtPartyPromote FNakamaRtPartyPromote::FromJson(const TSharedPtr& Json) +{ + FNakamaRtPartyPromote Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("party_id"))) + { + Result.PartyId = Json->GetStringField(TEXT("party_id")); + } + if (Json->HasField(TEXT("presence"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("presence"), NestedObj)) + { + Result.Presence = (FNakamaRtUserPresence::FromJson(*NestedObj)); + } + } + + return Result; +} + +TSharedPtr FNakamaRtPartyPromote::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (PartyId.IsEmpty() == false) + { + Json->SetStringField(TEXT("party_id"), PartyId); + } + + { + Json->SetObjectField(TEXT("presence"), Presence.ToJson()); + } + return Json; +} + +FNakamaRtPartyLeader FNakamaRtPartyLeader::FromJson(const TSharedPtr& Json) +{ + FNakamaRtPartyLeader Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("party_id"))) + { + Result.PartyId = Json->GetStringField(TEXT("party_id")); + } + if (Json->HasField(TEXT("presence"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("presence"), NestedObj)) + { + Result.Presence = (FNakamaRtUserPresence::FromJson(*NestedObj)); + } + } + + return Result; +} + +TSharedPtr FNakamaRtPartyLeader::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (PartyId.IsEmpty() == false) + { + Json->SetStringField(TEXT("party_id"), PartyId); + } + + { + Json->SetObjectField(TEXT("presence"), Presence.ToJson()); + } + return Json; +} + +FNakamaRtPartyAccept FNakamaRtPartyAccept::FromJson(const TSharedPtr& Json) +{ + FNakamaRtPartyAccept Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("party_id"))) + { + Result.PartyId = Json->GetStringField(TEXT("party_id")); + } + if (Json->HasField(TEXT("presence"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("presence"), NestedObj)) + { + Result.Presence = (FNakamaRtUserPresence::FromJson(*NestedObj)); + } + } + + return Result; +} + +TSharedPtr FNakamaRtPartyAccept::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (PartyId.IsEmpty() == false) + { + Json->SetStringField(TEXT("party_id"), PartyId); + } + + { + Json->SetObjectField(TEXT("presence"), Presence.ToJson()); + } + return Json; +} + +FNakamaRtPartyRemove FNakamaRtPartyRemove::FromJson(const TSharedPtr& Json) +{ + FNakamaRtPartyRemove Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("party_id"))) + { + Result.PartyId = Json->GetStringField(TEXT("party_id")); + } + if (Json->HasField(TEXT("presence"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("presence"), NestedObj)) + { + Result.Presence = (FNakamaRtUserPresence::FromJson(*NestedObj)); + } + } + + return Result; +} + +TSharedPtr FNakamaRtPartyRemove::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (PartyId.IsEmpty() == false) + { + Json->SetStringField(TEXT("party_id"), PartyId); + } + + { + Json->SetObjectField(TEXT("presence"), Presence.ToJson()); + } + return Json; +} + +FNakamaRtPartyClose FNakamaRtPartyClose::FromJson(const TSharedPtr& Json) +{ + FNakamaRtPartyClose Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("party_id"))) + { + Result.PartyId = Json->GetStringField(TEXT("party_id")); + } + + return Result; +} + +TSharedPtr FNakamaRtPartyClose::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (PartyId.IsEmpty() == false) + { + Json->SetStringField(TEXT("party_id"), PartyId); + } + return Json; +} + +FNakamaRtPartyJoinRequestList FNakamaRtPartyJoinRequestList::FromJson(const TSharedPtr& Json) +{ + FNakamaRtPartyJoinRequestList Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("party_id"))) + { + Result.PartyId = Json->GetStringField(TEXT("party_id")); + } + + return Result; +} + +TSharedPtr FNakamaRtPartyJoinRequestList::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (PartyId.IsEmpty() == false) + { + Json->SetStringField(TEXT("party_id"), PartyId); + } + return Json; +} + +FNakamaRtPartyJoinRequest FNakamaRtPartyJoinRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaRtPartyJoinRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("party_id"))) + { + Result.PartyId = Json->GetStringField(TEXT("party_id")); + } + if (Json->HasField(TEXT("presences"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("presences"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Presences.Add(FNakamaRtUserPresence::FromJson(*ItemObj)); + } + } + } + } + + return Result; +} + +TSharedPtr FNakamaRtPartyJoinRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (PartyId.IsEmpty() == false) + { + Json->SetStringField(TEXT("party_id"), PartyId); + } + if (Presences.Num() > 0) + { + TArray> Array; + for (const auto& Item : Presences) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("presences"), Array); + } + return Json; +} + +FNakamaRtPartyMatchmakerAdd FNakamaRtPartyMatchmakerAdd::FromJson(const TSharedPtr& Json) +{ + FNakamaRtPartyMatchmakerAdd Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("party_id"))) + { + Result.PartyId = Json->GetStringField(TEXT("party_id")); + } + if (Json->HasField(TEXT("min_count"))) + { + Result.MinCount = Json->GetNumberField(TEXT("min_count")); + } + if (Json->HasField(TEXT("max_count"))) + { + Result.MaxCount = Json->GetNumberField(TEXT("max_count")); + } + if (Json->HasField(TEXT("query"))) + { + Result.Query = Json->GetStringField(TEXT("query")); + } + if (Json->HasField(TEXT("count_multiple"))) + { + Result.CountMultiple = Json->GetNumberField(TEXT("count_multiple")); + } + if (Json->HasField(TEXT("string_properties"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("string_properties"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.StringProperties.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + if (Json->HasField(TEXT("numeric_properties"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("numeric_properties"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.NumericProperties.Add(Pair.Key, Pair.Value->AsNumber()); + } + } + } + + return Result; +} + +TSharedPtr FNakamaRtPartyMatchmakerAdd::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (PartyId.IsEmpty() == false) + { + Json->SetStringField(TEXT("party_id"), PartyId); + } + + { + Json->SetNumberField(TEXT("min_count"), MinCount); + } + + { + Json->SetNumberField(TEXT("max_count"), MaxCount); + } + if (Query.IsEmpty() == false) + { + Json->SetStringField(TEXT("query"), Query); + } + if (CountMultiple.IsEmpty() == false) + { + Json->SetNumberField(TEXT("count_multiple"), CountMultiple.GetValue()); + } + if (StringProperties.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : StringProperties) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("string_properties"), MapObj); + } + if (NumericProperties.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : NumericProperties) + { + MapObj->SetNumberField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("numeric_properties"), MapObj); + } + return Json; +} + +FNakamaRtPartyMatchmakerRemove FNakamaRtPartyMatchmakerRemove::FromJson(const TSharedPtr& Json) +{ + FNakamaRtPartyMatchmakerRemove Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("party_id"))) + { + Result.PartyId = Json->GetStringField(TEXT("party_id")); + } + if (Json->HasField(TEXT("ticket"))) + { + Result.Ticket = Json->GetStringField(TEXT("ticket")); + } + + return Result; +} + +TSharedPtr FNakamaRtPartyMatchmakerRemove::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (PartyId.IsEmpty() == false) + { + Json->SetStringField(TEXT("party_id"), PartyId); + } + if (Ticket.IsEmpty() == false) + { + Json->SetStringField(TEXT("ticket"), Ticket); + } + return Json; +} + +FNakamaRtPartyMatchmakerTicket FNakamaRtPartyMatchmakerTicket::FromJson(const TSharedPtr& Json) +{ + FNakamaRtPartyMatchmakerTicket Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("party_id"))) + { + Result.PartyId = Json->GetStringField(TEXT("party_id")); + } + if (Json->HasField(TEXT("ticket"))) + { + Result.Ticket = Json->GetStringField(TEXT("ticket")); + } + + return Result; +} + +TSharedPtr FNakamaRtPartyMatchmakerTicket::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (PartyId.IsEmpty() == false) + { + Json->SetStringField(TEXT("party_id"), PartyId); + } + if (Ticket.IsEmpty() == false) + { + Json->SetStringField(TEXT("ticket"), Ticket); + } + return Json; +} + +FNakamaRtPartyData FNakamaRtPartyData::FromJson(const TSharedPtr& Json) +{ + FNakamaRtPartyData Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("party_id"))) + { + Result.PartyId = Json->GetStringField(TEXT("party_id")); + } + if (Json->HasField(TEXT("presence"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("presence"), NestedObj)) + { + Result.Presence = (FNakamaRtUserPresence::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("op_code"))) + { + Result.OpCode = Json->GetNumberField(TEXT("op_code")); + } + if (Json->HasField(TEXT("data"))) + { + FBase64::Decode(Json->GetStringField(TEXT("data")), Result.Data); + } + + return Result; +} + +TSharedPtr FNakamaRtPartyData::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (PartyId.IsEmpty() == false) + { + Json->SetStringField(TEXT("party_id"), PartyId); + } + + { + Json->SetObjectField(TEXT("presence"), Presence.ToJson()); + } + + { + Json->SetNumberField(TEXT("op_code"), OpCode); + } + if (Data.IsEmpty() == false) + { + Json->SetStringField(TEXT("data"), FBase64::Encode(Data.GetData(), Data.Num())); + } + return Json; +} + +FNakamaRtPartyDataSend FNakamaRtPartyDataSend::FromJson(const TSharedPtr& Json) +{ + FNakamaRtPartyDataSend Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("party_id"))) + { + Result.PartyId = Json->GetStringField(TEXT("party_id")); + } + if (Json->HasField(TEXT("op_code"))) + { + Result.OpCode = Json->GetNumberField(TEXT("op_code")); + } + if (Json->HasField(TEXT("data"))) + { + FBase64::Decode(Json->GetStringField(TEXT("data")), Result.Data); + } + + return Result; +} + +TSharedPtr FNakamaRtPartyDataSend::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (PartyId.IsEmpty() == false) + { + Json->SetStringField(TEXT("party_id"), PartyId); + } + + { + Json->SetNumberField(TEXT("op_code"), OpCode); + } + if (Data.IsEmpty() == false) + { + Json->SetStringField(TEXT("data"), FBase64::Encode(Data.GetData(), Data.Num())); + } + return Json; +} + +FNakamaRtPartyPresenceEvent FNakamaRtPartyPresenceEvent::FromJson(const TSharedPtr& Json) +{ + FNakamaRtPartyPresenceEvent Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("party_id"))) + { + Result.PartyId = Json->GetStringField(TEXT("party_id")); + } + if (Json->HasField(TEXT("joins"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("joins"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Joins.Add(FNakamaRtUserPresence::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("leaves"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("leaves"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Leaves.Add(FNakamaRtUserPresence::FromJson(*ItemObj)); + } + } + } + } + + return Result; +} + +TSharedPtr FNakamaRtPartyPresenceEvent::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (PartyId.IsEmpty() == false) + { + Json->SetStringField(TEXT("party_id"), PartyId); + } + if (Joins.Num() > 0) + { + TArray> Array; + for (const auto& Item : Joins) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("joins"), Array); + } + if (Leaves.Num() > 0) + { + TArray> Array; + for (const auto& Item : Leaves) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("leaves"), Array); + } + return Json; +} + +FNakamaRtPing FNakamaRtPing::FromJson(const TSharedPtr& Json) +{ + FNakamaRtPing Result; + if (!Json.IsValid()) + { + return Result; + } + + return Result; +} + +TSharedPtr FNakamaRtPing::ToJson() const +{ + TSharedPtr Json = MakeShared(); + return Json; +} + +FNakamaRtPong FNakamaRtPong::FromJson(const TSharedPtr& Json) +{ + FNakamaRtPong Result; + if (!Json.IsValid()) + { + return Result; + } + + return Result; +} + +TSharedPtr FNakamaRtPong::ToJson() const +{ + TSharedPtr Json = MakeShared(); + return Json; +} + +FNakamaRtStatus FNakamaRtStatus::FromJson(const TSharedPtr& Json) +{ + FNakamaRtStatus Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("presences"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("presences"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Presences.Add(FNakamaRtUserPresence::FromJson(*ItemObj)); + } + } + } + } + + return Result; +} + +TSharedPtr FNakamaRtStatus::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Presences.Num() > 0) + { + TArray> Array; + for (const auto& Item : Presences) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("presences"), Array); + } + return Json; +} + +FNakamaRtStatusFollow FNakamaRtStatusFollow::FromJson(const TSharedPtr& Json) +{ + FNakamaRtStatusFollow Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("user_ids"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("user_ids"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.UserIds.Add((Item->AsString())); + } + } + } + if (Json->HasField(TEXT("usernames"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("usernames"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.Usernames.Add((Item->AsString())); + } + } + } + + return Result; +} + +TSharedPtr FNakamaRtStatusFollow::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (UserIds.Num() > 0) + { + TArray> Array; + for (const auto& Item : UserIds) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("user_ids"), Array); + } + if (Usernames.Num() > 0) + { + TArray> Array; + for (const auto& Item : Usernames) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("usernames"), Array); + } + return Json; +} + +FNakamaRtStatusPresenceEvent FNakamaRtStatusPresenceEvent::FromJson(const TSharedPtr& Json) +{ + FNakamaRtStatusPresenceEvent Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("joins"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("joins"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Joins.Add(FNakamaRtUserPresence::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("leaves"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("leaves"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Leaves.Add(FNakamaRtUserPresence::FromJson(*ItemObj)); + } + } + } + } + + return Result; +} + +TSharedPtr FNakamaRtStatusPresenceEvent::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Joins.Num() > 0) + { + TArray> Array; + for (const auto& Item : Joins) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("joins"), Array); + } + if (Leaves.Num() > 0) + { + TArray> Array; + for (const auto& Item : Leaves) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("leaves"), Array); + } + return Json; +} + +FNakamaRtStatusUnfollow FNakamaRtStatusUnfollow::FromJson(const TSharedPtr& Json) +{ + FNakamaRtStatusUnfollow Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("user_ids"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("user_ids"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.UserIds.Add((Item->AsString())); + } + } + } + + return Result; +} + +TSharedPtr FNakamaRtStatusUnfollow::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (UserIds.Num() > 0) + { + TArray> Array; + for (const auto& Item : UserIds) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("user_ids"), Array); + } + return Json; +} + +FNakamaRtStatusUpdate FNakamaRtStatusUpdate::FromJson(const TSharedPtr& Json) +{ + FNakamaRtStatusUpdate Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("status"))) + { + Result.Status = Json->GetStringField(TEXT("status")); + } + + return Result; +} + +TSharedPtr FNakamaRtStatusUpdate::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Status.IsEmpty() == false) + { + Json->SetStringField(TEXT("status"), Status); + } + return Json; +} + +FNakamaRtStream FNakamaRtStream::FromJson(const TSharedPtr& Json) +{ + FNakamaRtStream Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("mode"))) + { + Result.Mode = Json->GetNumberField(TEXT("mode")); + } + if (Json->HasField(TEXT("subject"))) + { + Result.Subject = Json->GetStringField(TEXT("subject")); + } + if (Json->HasField(TEXT("subcontext"))) + { + Result.Subcontext = Json->GetStringField(TEXT("subcontext")); + } + if (Json->HasField(TEXT("label"))) + { + Result.Label = Json->GetStringField(TEXT("label")); + } + + return Result; +} + +TSharedPtr FNakamaRtStream::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetNumberField(TEXT("mode"), Mode); + } + if (Subject.IsEmpty() == false) + { + Json->SetStringField(TEXT("subject"), Subject); + } + if (Subcontext.IsEmpty() == false) + { + Json->SetStringField(TEXT("subcontext"), Subcontext); + } + if (Label.IsEmpty() == false) + { + Json->SetStringField(TEXT("label"), Label); + } + return Json; +} + +FNakamaRtStreamData FNakamaRtStreamData::FromJson(const TSharedPtr& Json) +{ + FNakamaRtStreamData Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("stream"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("stream"), NestedObj)) + { + Result.Stream = (FNakamaRtStream::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("sender"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("sender"), NestedObj)) + { + Result.Sender = (FNakamaRtUserPresence::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("data"))) + { + Result.Data = Json->GetStringField(TEXT("data")); + } + if (Json->HasField(TEXT("reliable"))) + { + Result.Reliable = Json->GetBoolField(TEXT("reliable")); + } + + return Result; +} + +TSharedPtr FNakamaRtStreamData::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetObjectField(TEXT("stream"), Stream.ToJson()); + } + + { + Json->SetObjectField(TEXT("sender"), Sender.ToJson()); + } + if (Data.IsEmpty() == false) + { + Json->SetStringField(TEXT("data"), Data); + } + + { + Json->SetBoolField(TEXT("reliable"), Reliable); + } + return Json; +} + +FNakamaRtStreamPresenceEvent FNakamaRtStreamPresenceEvent::FromJson(const TSharedPtr& Json) +{ + FNakamaRtStreamPresenceEvent Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("stream"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("stream"), NestedObj)) + { + Result.Stream = (FNakamaRtStream::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("joins"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("joins"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Joins.Add(FNakamaRtUserPresence::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("leaves"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("leaves"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Leaves.Add(FNakamaRtUserPresence::FromJson(*ItemObj)); + } + } + } + } + + return Result; +} + +TSharedPtr FNakamaRtStreamPresenceEvent::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetObjectField(TEXT("stream"), Stream.ToJson()); + } + if (Joins.Num() > 0) + { + TArray> Array; + for (const auto& Item : Joins) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("joins"), Array); + } + if (Leaves.Num() > 0) + { + TArray> Array; + for (const auto& Item : Leaves) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("leaves"), Array); + } + return Json; +} diff --git a/Nakama/Source/NakamaApi/Private/NakamaTypes.cpp b/Nakama/Source/NakamaApi/Private/NakamaTypes.cpp new file mode 100644 index 000000000..b7d0de19f --- /dev/null +++ b/Nakama/Source/NakamaApi/Private/NakamaTypes.cpp @@ -0,0 +1,6149 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + +#include "NakamaTypes.h" + +#include "JsonObjectConverter.h" + +FNakamaUser FNakamaUser::FromJson(const TSharedPtr& Json) +{ + FNakamaUser Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create FNakamaUser from JSON")); + } + + return Result; +} + +TSharedPtr FNakamaUser::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +FNakamaAccountDevice FNakamaAccountDevice::FromJson(const TSharedPtr& Json) +{ + FNakamaAccountDevice Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("id"))) + { + Result.Id = Json->GetStringField(TEXT("id")); + } + if (Json->HasField(TEXT("vars"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("vars"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.Vars.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + return Result; +} + +TSharedPtr FNakamaAccountDevice::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Id.IsEmpty() == false) + { + Json->SetStringField(TEXT("id"), Id); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("vars"), MapObj); + } + return Json; +} + +FNakamaAccount FNakamaAccount::FromJson(const TSharedPtr& Json) +{ + FNakamaAccount Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("user"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("user"), NestedObj)) + { + Result.User = (FNakamaUser::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("wallet"))) + { + Result.Wallet = Json->GetStringField(TEXT("wallet")); + } + if (Json->HasField(TEXT("email"))) + { + Result.Email = Json->GetStringField(TEXT("email")); + } + if (Json->HasField(TEXT("devices"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("devices"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Devices.Add(FNakamaAccountDevice::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("custom_id"))) + { + Result.CustomId = Json->GetStringField(TEXT("custom_id")); + } + if (Json->HasField(TEXT("verify_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("verify_time")), Result.VerifyTime); + } + if (Json->HasField(TEXT("disable_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("disable_time")), Result.DisableTime); + } + return Result; +} + +TSharedPtr FNakamaAccount::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetObjectField(TEXT("user"), User.ToJson()); + } + if (Wallet.IsEmpty() == false) + { + Json->SetStringField(TEXT("wallet"), Wallet); + } + if (Email.IsEmpty() == false) + { + Json->SetStringField(TEXT("email"), Email); + } + if (Devices.Num() > 0) + { + TArray> Array; + for (const auto& Item : Devices) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("devices"), Array); + } + if (CustomId.IsEmpty() == false) + { + Json->SetStringField(TEXT("custom_id"), CustomId); + } + + { + Json->SetStringField(TEXT("verify_time"), VerifyTime.ToIso8601()); + } + + { + Json->SetStringField(TEXT("disable_time"), DisableTime.ToIso8601()); + } + return Json; +} + +FNakamaAccountRefresh FNakamaAccountRefresh::FromJson(const TSharedPtr& Json) +{ + FNakamaAccountRefresh Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("token"))) + { + Result.Token = Json->GetStringField(TEXT("token")); + } + if (Json->HasField(TEXT("vars"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("vars"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.Vars.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + return Result; +} + +TSharedPtr FNakamaAccountRefresh::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Token.IsEmpty() == false) + { + Json->SetStringField(TEXT("token"), Token); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("vars"), MapObj); + } + return Json; +} + +FNakamaAccountApple FNakamaAccountApple::FromJson(const TSharedPtr& Json) +{ + FNakamaAccountApple Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("token"))) + { + Result.Token = Json->GetStringField(TEXT("token")); + } + if (Json->HasField(TEXT("vars"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("vars"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.Vars.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + return Result; +} + +TSharedPtr FNakamaAccountApple::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Token.IsEmpty() == false) + { + Json->SetStringField(TEXT("token"), Token); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("vars"), MapObj); + } + return Json; +} + +FNakamaAccountCustom FNakamaAccountCustom::FromJson(const TSharedPtr& Json) +{ + FNakamaAccountCustom Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("id"))) + { + Result.Id = Json->GetStringField(TEXT("id")); + } + if (Json->HasField(TEXT("vars"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("vars"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.Vars.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + return Result; +} + +TSharedPtr FNakamaAccountCustom::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Id.IsEmpty() == false) + { + Json->SetStringField(TEXT("id"), Id); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("vars"), MapObj); + } + return Json; +} + +FNakamaAccountEmail FNakamaAccountEmail::FromJson(const TSharedPtr& Json) +{ + FNakamaAccountEmail Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("email"))) + { + Result.Email = Json->GetStringField(TEXT("email")); + } + if (Json->HasField(TEXT("password"))) + { + Result.Password = Json->GetStringField(TEXT("password")); + } + if (Json->HasField(TEXT("vars"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("vars"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.Vars.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + return Result; +} + +TSharedPtr FNakamaAccountEmail::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Email.IsEmpty() == false) + { + Json->SetStringField(TEXT("email"), Email); + } + if (Password.IsEmpty() == false) + { + Json->SetStringField(TEXT("password"), Password); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("vars"), MapObj); + } + return Json; +} + +FNakamaAccountFacebook FNakamaAccountFacebook::FromJson(const TSharedPtr& Json) +{ + FNakamaAccountFacebook Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("token"))) + { + Result.Token = Json->GetStringField(TEXT("token")); + } + if (Json->HasField(TEXT("vars"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("vars"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.Vars.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + return Result; +} + +TSharedPtr FNakamaAccountFacebook::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Token.IsEmpty() == false) + { + Json->SetStringField(TEXT("token"), Token); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("vars"), MapObj); + } + return Json; +} + +FNakamaAccountFacebookInstantGame FNakamaAccountFacebookInstantGame::FromJson(const TSharedPtr& Json) +{ + FNakamaAccountFacebookInstantGame Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("signed_player_info"))) + { + Result.SignedPlayerInfo = Json->GetStringField(TEXT("signed_player_info")); + } + if (Json->HasField(TEXT("vars"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("vars"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.Vars.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + return Result; +} + +TSharedPtr FNakamaAccountFacebookInstantGame::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (SignedPlayerInfo.IsEmpty() == false) + { + Json->SetStringField(TEXT("signed_player_info"), SignedPlayerInfo); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("vars"), MapObj); + } + return Json; +} + +FNakamaAccountGameCenter FNakamaAccountGameCenter::FromJson(const TSharedPtr& Json) +{ + FNakamaAccountGameCenter Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("player_id"))) + { + Result.PlayerId = Json->GetStringField(TEXT("player_id")); + } + if (Json->HasField(TEXT("bundle_id"))) + { + Result.BundleId = Json->GetStringField(TEXT("bundle_id")); + } + if (Json->HasField(TEXT("timestamp_seconds"))) + { + Result.TimestampSeconds = Json->GetNumberField(TEXT("timestamp_seconds")); + } + if (Json->HasField(TEXT("salt"))) + { + Result.Salt = Json->GetStringField(TEXT("salt")); + } + if (Json->HasField(TEXT("signature"))) + { + Result.Signature = Json->GetStringField(TEXT("signature")); + } + if (Json->HasField(TEXT("public_key_url"))) + { + Result.PublicKeyUrl = Json->GetStringField(TEXT("public_key_url")); + } + if (Json->HasField(TEXT("vars"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("vars"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.Vars.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + return Result; +} + +TSharedPtr FNakamaAccountGameCenter::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (PlayerId.IsEmpty() == false) + { + Json->SetStringField(TEXT("player_id"), PlayerId); + } + if (BundleId.IsEmpty() == false) + { + Json->SetStringField(TEXT("bundle_id"), BundleId); + } + + { + Json->SetNumberField(TEXT("timestamp_seconds"), TimestampSeconds); + } + if (Salt.IsEmpty() == false) + { + Json->SetStringField(TEXT("salt"), Salt); + } + if (Signature.IsEmpty() == false) + { + Json->SetStringField(TEXT("signature"), Signature); + } + if (PublicKeyUrl.IsEmpty() == false) + { + Json->SetStringField(TEXT("public_key_url"), PublicKeyUrl); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("vars"), MapObj); + } + return Json; +} + +FNakamaAccountGoogle FNakamaAccountGoogle::FromJson(const TSharedPtr& Json) +{ + FNakamaAccountGoogle Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("token"))) + { + Result.Token = Json->GetStringField(TEXT("token")); + } + if (Json->HasField(TEXT("vars"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("vars"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.Vars.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + return Result; +} + +TSharedPtr FNakamaAccountGoogle::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Token.IsEmpty() == false) + { + Json->SetStringField(TEXT("token"), Token); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("vars"), MapObj); + } + return Json; +} + +FNakamaAccountSteam FNakamaAccountSteam::FromJson(const TSharedPtr& Json) +{ + FNakamaAccountSteam Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("token"))) + { + Result.Token = Json->GetStringField(TEXT("token")); + } + if (Json->HasField(TEXT("vars"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("vars"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.Vars.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + return Result; +} + +TSharedPtr FNakamaAccountSteam::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Token.IsEmpty() == false) + { + Json->SetStringField(TEXT("token"), Token); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("vars"), MapObj); + } + return Json; +} + +FNakamaAddFriendsRequest FNakamaAddFriendsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaAddFriendsRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("ids"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("ids"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.Ids.Add((Item->AsString())); + } + } + } + if (Json->HasField(TEXT("usernames"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("usernames"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.Usernames.Add((Item->AsString())); + } + } + } + if (Json->HasField(TEXT("metadata"))) + { + Result.Metadata = Json->GetStringField(TEXT("metadata")); + } + return Result; +} + +TSharedPtr FNakamaAddFriendsRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Ids.Num() > 0) + { + TArray> Array; + for (const auto& Item : Ids) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("ids"), Array); + } + if (Usernames.Num() > 0) + { + TArray> Array; + for (const auto& Item : Usernames) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("usernames"), Array); + } + if (Metadata.IsEmpty() == false) + { + Json->SetStringField(TEXT("metadata"), Metadata); + } + return Json; +} + +FNakamaAddGroupUsersRequest FNakamaAddGroupUsersRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaAddGroupUsersRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("group_id"))) + { + Result.GroupId = Json->GetStringField(TEXT("group_id")); + } + if (Json->HasField(TEXT("user_ids"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("user_ids"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.UserIds.Add((Item->AsString())); + } + } + } + return Result; +} + +TSharedPtr FNakamaAddGroupUsersRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (GroupId.IsEmpty() == false) + { + Json->SetStringField(TEXT("group_id"), GroupId); + } + if (UserIds.Num() > 0) + { + TArray> Array; + for (const auto& Item : UserIds) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("user_ids"), Array); + } + return Json; +} + +FNakamaSessionRefreshRequest FNakamaSessionRefreshRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaSessionRefreshRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("token"))) + { + Result.Token = Json->GetStringField(TEXT("token")); + } + if (Json->HasField(TEXT("vars"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("vars"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.Vars.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + return Result; +} + +TSharedPtr FNakamaSessionRefreshRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Token.IsEmpty() == false) + { + Json->SetStringField(TEXT("token"), Token); + } + if (Vars.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Vars) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("vars"), MapObj); + } + return Json; +} + +FNakamaSessionLogoutRequest FNakamaSessionLogoutRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaSessionLogoutRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("token"))) + { + Result.Token = Json->GetStringField(TEXT("token")); + } + if (Json->HasField(TEXT("refresh_token"))) + { + Result.RefreshToken = Json->GetStringField(TEXT("refresh_token")); + } + return Result; +} + +TSharedPtr FNakamaSessionLogoutRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Token.IsEmpty() == false) + { + Json->SetStringField(TEXT("token"), Token); + } + if (RefreshToken.IsEmpty() == false) + { + Json->SetStringField(TEXT("refresh_token"), RefreshToken); + } + return Json; +} + +FNakamaAuthenticateAppleRequest FNakamaAuthenticateAppleRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaAuthenticateAppleRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("account"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("account"), NestedObj)) + { + Result.Account = (FNakamaAccountApple::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("create"))) + { + Result.Create = Json->GetBoolField(TEXT("create")); + } + if (Json->HasField(TEXT("username"))) + { + Result.Username = Json->GetStringField(TEXT("username")); + } + return Result; +} + +TSharedPtr FNakamaAuthenticateAppleRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetObjectField(TEXT("account"), Account.ToJson()); + } + if (Create.IsEmpty() == false) + { + Json->SetBoolField(TEXT("create"), Create.GetValue()); + } + if (Username.IsEmpty() == false) + { + Json->SetStringField(TEXT("username"), Username); + } + return Json; +} + +FNakamaAuthenticateCustomRequest FNakamaAuthenticateCustomRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaAuthenticateCustomRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("account"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("account"), NestedObj)) + { + Result.Account = (FNakamaAccountCustom::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("create"))) + { + Result.Create = Json->GetBoolField(TEXT("create")); + } + if (Json->HasField(TEXT("username"))) + { + Result.Username = Json->GetStringField(TEXT("username")); + } + return Result; +} + +TSharedPtr FNakamaAuthenticateCustomRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetObjectField(TEXT("account"), Account.ToJson()); + } + if (Create.IsEmpty() == false) + { + Json->SetBoolField(TEXT("create"), Create.GetValue()); + } + if (Username.IsEmpty() == false) + { + Json->SetStringField(TEXT("username"), Username); + } + return Json; +} + +FNakamaAuthenticateDeviceRequest FNakamaAuthenticateDeviceRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaAuthenticateDeviceRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("account"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("account"), NestedObj)) + { + Result.Account = (FNakamaAccountDevice::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("create"))) + { + Result.Create = Json->GetBoolField(TEXT("create")); + } + if (Json->HasField(TEXT("username"))) + { + Result.Username = Json->GetStringField(TEXT("username")); + } + return Result; +} + +TSharedPtr FNakamaAuthenticateDeviceRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetObjectField(TEXT("account"), Account.ToJson()); + } + if (Create.IsEmpty() == false) + { + Json->SetBoolField(TEXT("create"), Create.GetValue()); + } + if (Username.IsEmpty() == false) + { + Json->SetStringField(TEXT("username"), Username); + } + return Json; +} + +FNakamaAuthenticateEmailRequest FNakamaAuthenticateEmailRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaAuthenticateEmailRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("account"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("account"), NestedObj)) + { + Result.Account = (FNakamaAccountEmail::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("create"))) + { + Result.Create = Json->GetBoolField(TEXT("create")); + } + if (Json->HasField(TEXT("username"))) + { + Result.Username = Json->GetStringField(TEXT("username")); + } + return Result; +} + +TSharedPtr FNakamaAuthenticateEmailRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetObjectField(TEXT("account"), Account.ToJson()); + } + if (Create.IsEmpty() == false) + { + Json->SetBoolField(TEXT("create"), Create.GetValue()); + } + if (Username.IsEmpty() == false) + { + Json->SetStringField(TEXT("username"), Username); + } + return Json; +} + +FNakamaAuthenticateFacebookRequest FNakamaAuthenticateFacebookRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaAuthenticateFacebookRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("account"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("account"), NestedObj)) + { + Result.Account = (FNakamaAccountFacebook::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("create"))) + { + Result.Create = Json->GetBoolField(TEXT("create")); + } + if (Json->HasField(TEXT("username"))) + { + Result.Username = Json->GetStringField(TEXT("username")); + } + if (Json->HasField(TEXT("sync"))) + { + Result.Sync = Json->GetBoolField(TEXT("sync")); + } + return Result; +} + +TSharedPtr FNakamaAuthenticateFacebookRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetObjectField(TEXT("account"), Account.ToJson()); + } + if (Create.IsEmpty() == false) + { + Json->SetBoolField(TEXT("create"), Create.GetValue()); + } + if (Username.IsEmpty() == false) + { + Json->SetStringField(TEXT("username"), Username); + } + if (Sync.IsEmpty() == false) + { + Json->SetBoolField(TEXT("sync"), Sync.GetValue()); + } + return Json; +} + +FNakamaAuthenticateFacebookInstantGameRequest FNakamaAuthenticateFacebookInstantGameRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaAuthenticateFacebookInstantGameRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("account"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("account"), NestedObj)) + { + Result.Account = (FNakamaAccountFacebookInstantGame::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("create"))) + { + Result.Create = Json->GetBoolField(TEXT("create")); + } + if (Json->HasField(TEXT("username"))) + { + Result.Username = Json->GetStringField(TEXT("username")); + } + return Result; +} + +TSharedPtr FNakamaAuthenticateFacebookInstantGameRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetObjectField(TEXT("account"), Account.ToJson()); + } + if (Create.IsEmpty() == false) + { + Json->SetBoolField(TEXT("create"), Create.GetValue()); + } + if (Username.IsEmpty() == false) + { + Json->SetStringField(TEXT("username"), Username); + } + return Json; +} + +FNakamaAuthenticateGameCenterRequest FNakamaAuthenticateGameCenterRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaAuthenticateGameCenterRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("account"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("account"), NestedObj)) + { + Result.Account = (FNakamaAccountGameCenter::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("create"))) + { + Result.Create = Json->GetBoolField(TEXT("create")); + } + if (Json->HasField(TEXT("username"))) + { + Result.Username = Json->GetStringField(TEXT("username")); + } + return Result; +} + +TSharedPtr FNakamaAuthenticateGameCenterRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetObjectField(TEXT("account"), Account.ToJson()); + } + if (Create.IsEmpty() == false) + { + Json->SetBoolField(TEXT("create"), Create.GetValue()); + } + if (Username.IsEmpty() == false) + { + Json->SetStringField(TEXT("username"), Username); + } + return Json; +} + +FNakamaAuthenticateGoogleRequest FNakamaAuthenticateGoogleRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaAuthenticateGoogleRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("account"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("account"), NestedObj)) + { + Result.Account = (FNakamaAccountGoogle::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("create"))) + { + Result.Create = Json->GetBoolField(TEXT("create")); + } + if (Json->HasField(TEXT("username"))) + { + Result.Username = Json->GetStringField(TEXT("username")); + } + return Result; +} + +TSharedPtr FNakamaAuthenticateGoogleRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetObjectField(TEXT("account"), Account.ToJson()); + } + if (Create.IsEmpty() == false) + { + Json->SetBoolField(TEXT("create"), Create.GetValue()); + } + if (Username.IsEmpty() == false) + { + Json->SetStringField(TEXT("username"), Username); + } + return Json; +} + +FNakamaAuthenticateSteamRequest FNakamaAuthenticateSteamRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaAuthenticateSteamRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("account"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("account"), NestedObj)) + { + Result.Account = (FNakamaAccountSteam::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("create"))) + { + Result.Create = Json->GetBoolField(TEXT("create")); + } + if (Json->HasField(TEXT("username"))) + { + Result.Username = Json->GetStringField(TEXT("username")); + } + if (Json->HasField(TEXT("sync"))) + { + Result.Sync = Json->GetBoolField(TEXT("sync")); + } + return Result; +} + +TSharedPtr FNakamaAuthenticateSteamRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetObjectField(TEXT("account"), Account.ToJson()); + } + if (Create.IsEmpty() == false) + { + Json->SetBoolField(TEXT("create"), Create.GetValue()); + } + if (Username.IsEmpty() == false) + { + Json->SetStringField(TEXT("username"), Username); + } + if (Sync.IsEmpty() == false) + { + Json->SetBoolField(TEXT("sync"), Sync.GetValue()); + } + return Json; +} + +FNakamaBanGroupUsersRequest FNakamaBanGroupUsersRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaBanGroupUsersRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("group_id"))) + { + Result.GroupId = Json->GetStringField(TEXT("group_id")); + } + if (Json->HasField(TEXT("user_ids"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("user_ids"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.UserIds.Add((Item->AsString())); + } + } + } + return Result; +} + +TSharedPtr FNakamaBanGroupUsersRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (GroupId.IsEmpty() == false) + { + Json->SetStringField(TEXT("group_id"), GroupId); + } + if (UserIds.Num() > 0) + { + TArray> Array; + for (const auto& Item : UserIds) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("user_ids"), Array); + } + return Json; +} + +FNakamaBlockFriendsRequest FNakamaBlockFriendsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaBlockFriendsRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("ids"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("ids"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.Ids.Add((Item->AsString())); + } + } + } + if (Json->HasField(TEXT("usernames"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("usernames"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.Usernames.Add((Item->AsString())); + } + } + } + return Result; +} + +TSharedPtr FNakamaBlockFriendsRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Ids.Num() > 0) + { + TArray> Array; + for (const auto& Item : Ids) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("ids"), Array); + } + if (Usernames.Num() > 0) + { + TArray> Array; + for (const auto& Item : Usernames) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("usernames"), Array); + } + return Json; +} + +FNakamaChannelMessage FNakamaChannelMessage::FromJson(const TSharedPtr& Json) +{ + FNakamaChannelMessage Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("channel_id"))) + { + Result.ChannelId = Json->GetStringField(TEXT("channel_id")); + } + if (Json->HasField(TEXT("message_id"))) + { + Result.MessageId = Json->GetStringField(TEXT("message_id")); + } + if (Json->HasField(TEXT("code"))) + { + Result.Code = Json->GetNumberField(TEXT("code")); + } + if (Json->HasField(TEXT("sender_id"))) + { + Result.SenderId = Json->GetStringField(TEXT("sender_id")); + } + if (Json->HasField(TEXT("username"))) + { + Result.Username = Json->GetStringField(TEXT("username")); + } + if (Json->HasField(TEXT("content"))) + { + Result.Content = Json->GetStringField(TEXT("content")); + } + if (Json->HasField(TEXT("create_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("create_time")), Result.CreateTime); + } + if (Json->HasField(TEXT("update_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("update_time")), Result.UpdateTime); + } + if (Json->HasField(TEXT("persistent"))) + { + Result.Persistent = Json->GetBoolField(TEXT("persistent")); + } + if (Json->HasField(TEXT("room_name"))) + { + Result.RoomName = Json->GetStringField(TEXT("room_name")); + } + if (Json->HasField(TEXT("group_id"))) + { + Result.GroupId = Json->GetStringField(TEXT("group_id")); + } + if (Json->HasField(TEXT("user_id_one"))) + { + Result.UserIdOne = Json->GetStringField(TEXT("user_id_one")); + } + if (Json->HasField(TEXT("user_id_two"))) + { + Result.UserIdTwo = Json->GetStringField(TEXT("user_id_two")); + } + return Result; +} + +TSharedPtr FNakamaChannelMessage::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (ChannelId.IsEmpty() == false) + { + Json->SetStringField(TEXT("channel_id"), ChannelId); + } + if (MessageId.IsEmpty() == false) + { + Json->SetStringField(TEXT("message_id"), MessageId); + } + if (Code.IsEmpty() == false) + { + Json->SetNumberField(TEXT("code"), Code.GetValue()); + } + if (SenderId.IsEmpty() == false) + { + Json->SetStringField(TEXT("sender_id"), SenderId); + } + if (Username.IsEmpty() == false) + { + Json->SetStringField(TEXT("username"), Username); + } + if (Content.IsEmpty() == false) + { + Json->SetStringField(TEXT("content"), Content); + } + + { + Json->SetStringField(TEXT("create_time"), CreateTime.ToIso8601()); + } + + { + Json->SetStringField(TEXT("update_time"), UpdateTime.ToIso8601()); + } + if (Persistent.IsEmpty() == false) + { + Json->SetBoolField(TEXT("persistent"), Persistent.GetValue()); + } + if (RoomName.IsEmpty() == false) + { + Json->SetStringField(TEXT("room_name"), RoomName); + } + if (GroupId.IsEmpty() == false) + { + Json->SetStringField(TEXT("group_id"), GroupId); + } + if (UserIdOne.IsEmpty() == false) + { + Json->SetStringField(TEXT("user_id_one"), UserIdOne); + } + if (UserIdTwo.IsEmpty() == false) + { + Json->SetStringField(TEXT("user_id_two"), UserIdTwo); + } + return Json; +} + +FNakamaChannelMessageList FNakamaChannelMessageList::FromJson(const TSharedPtr& Json) +{ + FNakamaChannelMessageList Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("messages"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("messages"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Messages.Add(FNakamaChannelMessage::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("next_cursor"))) + { + Result.NextCursor = Json->GetStringField(TEXT("next_cursor")); + } + if (Json->HasField(TEXT("prev_cursor"))) + { + Result.PrevCursor = Json->GetStringField(TEXT("prev_cursor")); + } + if (Json->HasField(TEXT("cacheable_cursor"))) + { + Result.CacheableCursor = Json->GetStringField(TEXT("cacheable_cursor")); + } + return Result; +} + +TSharedPtr FNakamaChannelMessageList::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Messages.Num() > 0) + { + TArray> Array; + for (const auto& Item : Messages) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("messages"), Array); + } + if (NextCursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("next_cursor"), NextCursor); + } + if (PrevCursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("prev_cursor"), PrevCursor); + } + if (CacheableCursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cacheable_cursor"), CacheableCursor); + } + return Json; +} + +FNakamaCreateGroupRequest FNakamaCreateGroupRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaCreateGroupRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("name"))) + { + Result.Name = Json->GetStringField(TEXT("name")); + } + if (Json->HasField(TEXT("description"))) + { + Result.Description = Json->GetStringField(TEXT("description")); + } + if (Json->HasField(TEXT("lang_tag"))) + { + Result.LangTag = Json->GetStringField(TEXT("lang_tag")); + } + if (Json->HasField(TEXT("avatar_url"))) + { + Result.AvatarUrl = Json->GetStringField(TEXT("avatar_url")); + } + if (Json->HasField(TEXT("open"))) + { + Result.Open = Json->GetBoolField(TEXT("open")); + } + if (Json->HasField(TEXT("max_count"))) + { + Result.MaxCount = Json->GetNumberField(TEXT("max_count")); + } + return Result; +} + +TSharedPtr FNakamaCreateGroupRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Name.IsEmpty() == false) + { + Json->SetStringField(TEXT("name"), Name); + } + if (Description.IsEmpty() == false) + { + Json->SetStringField(TEXT("description"), Description); + } + if (LangTag.IsEmpty() == false) + { + Json->SetStringField(TEXT("lang_tag"), LangTag); + } + if (AvatarUrl.IsEmpty() == false) + { + Json->SetStringField(TEXT("avatar_url"), AvatarUrl); + } + + { + Json->SetBoolField(TEXT("open"), Open); + } + + { + Json->SetNumberField(TEXT("max_count"), MaxCount); + } + return Json; +} + +FNakamaDeleteFriendsRequest FNakamaDeleteFriendsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaDeleteFriendsRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("ids"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("ids"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.Ids.Add((Item->AsString())); + } + } + } + if (Json->HasField(TEXT("usernames"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("usernames"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.Usernames.Add((Item->AsString())); + } + } + } + return Result; +} + +TSharedPtr FNakamaDeleteFriendsRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Ids.Num() > 0) + { + TArray> Array; + for (const auto& Item : Ids) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("ids"), Array); + } + if (Usernames.Num() > 0) + { + TArray> Array; + for (const auto& Item : Usernames) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("usernames"), Array); + } + return Json; +} + +FNakamaDeleteGroupRequest FNakamaDeleteGroupRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaDeleteGroupRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("group_id"))) + { + Result.GroupId = Json->GetStringField(TEXT("group_id")); + } + return Result; +} + +TSharedPtr FNakamaDeleteGroupRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (GroupId.IsEmpty() == false) + { + Json->SetStringField(TEXT("group_id"), GroupId); + } + return Json; +} + +FNakamaDeleteLeaderboardRecordRequest FNakamaDeleteLeaderboardRecordRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaDeleteLeaderboardRecordRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("leaderboard_id"))) + { + Result.LeaderboardId = Json->GetStringField(TEXT("leaderboard_id")); + } + return Result; +} + +TSharedPtr FNakamaDeleteLeaderboardRecordRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (LeaderboardId.IsEmpty() == false) + { + Json->SetStringField(TEXT("leaderboard_id"), LeaderboardId); + } + return Json; +} + +FNakamaDeleteNotificationsRequest FNakamaDeleteNotificationsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaDeleteNotificationsRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("ids"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("ids"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.Ids.Add((Item->AsString())); + } + } + } + return Result; +} + +TSharedPtr FNakamaDeleteNotificationsRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Ids.Num() > 0) + { + TArray> Array; + for (const auto& Item : Ids) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("ids"), Array); + } + return Json; +} + +FNakamaDeleteTournamentRecordRequest FNakamaDeleteTournamentRecordRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaDeleteTournamentRecordRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("tournament_id"))) + { + Result.TournamentId = Json->GetStringField(TEXT("tournament_id")); + } + return Result; +} + +TSharedPtr FNakamaDeleteTournamentRecordRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (TournamentId.IsEmpty() == false) + { + Json->SetStringField(TEXT("tournament_id"), TournamentId); + } + return Json; +} + +FNakamaDeleteStorageObjectId FNakamaDeleteStorageObjectId::FromJson(const TSharedPtr& Json) +{ + FNakamaDeleteStorageObjectId Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("collection"))) + { + Result.Collection = Json->GetStringField(TEXT("collection")); + } + if (Json->HasField(TEXT("key"))) + { + Result.Key = Json->GetStringField(TEXT("key")); + } + if (Json->HasField(TEXT("version"))) + { + Result.Version = Json->GetStringField(TEXT("version")); + } + return Result; +} + +TSharedPtr FNakamaDeleteStorageObjectId::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Collection.IsEmpty() == false) + { + Json->SetStringField(TEXT("collection"), Collection); + } + if (Key.IsEmpty() == false) + { + Json->SetStringField(TEXT("key"), Key); + } + if (Version.IsEmpty() == false) + { + Json->SetStringField(TEXT("version"), Version); + } + return Json; +} + +FNakamaDeleteStorageObjectsRequest FNakamaDeleteStorageObjectsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaDeleteStorageObjectsRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("object_ids"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("object_ids"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.ObjectIds.Add(FNakamaDeleteStorageObjectId::FromJson(*ItemObj)); + } + } + } + } + return Result; +} + +TSharedPtr FNakamaDeleteStorageObjectsRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (ObjectIds.Num() > 0) + { + TArray> Array; + for (const auto& Item : ObjectIds) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("object_ids"), Array); + } + return Json; +} + +FNakamaEvent FNakamaEvent::FromJson(const TSharedPtr& Json) +{ + FNakamaEvent Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("name"))) + { + Result.Name = Json->GetStringField(TEXT("name")); + } + if (Json->HasField(TEXT("timestamp"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("timestamp")), Result.Timestamp); + } + if (Json->HasField(TEXT("external"))) + { + Result.External = Json->GetBoolField(TEXT("external")); + } + if (Json->HasField(TEXT("properties"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("properties"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.Properties.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + return Result; +} + +TSharedPtr FNakamaEvent::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Name.IsEmpty() == false) + { + Json->SetStringField(TEXT("name"), Name); + } + + { + Json->SetStringField(TEXT("timestamp"), Timestamp.ToIso8601()); + } + + { + Json->SetBoolField(TEXT("external"), External); + } + if (Properties.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Properties) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("properties"), MapObj); + } + return Json; +} + +FNakamaFriend FNakamaFriend::FromJson(const TSharedPtr& Json) +{ + FNakamaFriend Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("user"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("user"), NestedObj)) + { + Result.User = (FNakamaUser::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("state"))) + { + Result.State = Json->GetNumberField(TEXT("state")); + } + if (Json->HasField(TEXT("update_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("update_time")), Result.UpdateTime); + } + if (Json->HasField(TEXT("metadata"))) + { + Result.Metadata = Json->GetStringField(TEXT("metadata")); + } + return Result; +} + +TSharedPtr FNakamaFriend::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetObjectField(TEXT("user"), User.ToJson()); + } + if (State.IsEmpty() == false) + { + Json->SetNumberField(TEXT("state"), State.GetValue()); + } + + { + Json->SetStringField(TEXT("update_time"), UpdateTime.ToIso8601()); + } + if (Metadata.IsEmpty() == false) + { + Json->SetStringField(TEXT("metadata"), Metadata); + } + return Json; +} + +FNakamaFriendList FNakamaFriendList::FromJson(const TSharedPtr& Json) +{ + FNakamaFriendList Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("friends"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("friends"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Friends.Add(FNakamaFriend::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + return Result; +} + +TSharedPtr FNakamaFriendList::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Friends.Num() > 0) + { + TArray> Array; + for (const auto& Item : Friends) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("friends"), Array); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + return Json; +} + +FNakamaFriendsOfFriendsListFriendOfFriend FNakamaFriendsOfFriendsListFriendOfFriend::FromJson(const TSharedPtr& Json) +{ + FNakamaFriendsOfFriendsListFriendOfFriend Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("referrer"))) + { + Result.Referrer = Json->GetStringField(TEXT("referrer")); + } + if (Json->HasField(TEXT("user"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("user"), NestedObj)) + { + Result.User = (FNakamaUser::FromJson(*NestedObj)); + } + } + return Result; +} + +TSharedPtr FNakamaFriendsOfFriendsListFriendOfFriend::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Referrer.IsEmpty() == false) + { + Json->SetStringField(TEXT("referrer"), Referrer); + } + + { + Json->SetObjectField(TEXT("user"), User.ToJson()); + } + return Json; +} + +FNakamaFriendsOfFriendsList FNakamaFriendsOfFriendsList::FromJson(const TSharedPtr& Json) +{ + FNakamaFriendsOfFriendsList Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("friends_of_friends"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("friends_of_friends"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.FriendsOfFriends.Add(FNakamaFriendsOfFriendsListFriendOfFriend::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + return Result; +} + +TSharedPtr FNakamaFriendsOfFriendsList::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (FriendsOfFriends.Num() > 0) + { + TArray> Array; + for (const auto& Item : FriendsOfFriends) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("friends_of_friends"), Array); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + return Json; +} + +FNakamaGetUsersRequest FNakamaGetUsersRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaGetUsersRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("ids"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("ids"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.Ids.Add((Item->AsString())); + } + } + } + if (Json->HasField(TEXT("usernames"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("usernames"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.Usernames.Add((Item->AsString())); + } + } + } + if (Json->HasField(TEXT("facebook_ids"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("facebook_ids"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.FacebookIds.Add((Item->AsString())); + } + } + } + return Result; +} + +TSharedPtr FNakamaGetUsersRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Ids.Num() > 0) + { + TArray> Array; + for (const auto& Item : Ids) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("ids"), Array); + } + if (Usernames.Num() > 0) + { + TArray> Array; + for (const auto& Item : Usernames) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("usernames"), Array); + } + if (FacebookIds.Num() > 0) + { + TArray> Array; + for (const auto& Item : FacebookIds) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("facebook_ids"), Array); + } + return Json; +} + +FNakamaGetSubscriptionRequest FNakamaGetSubscriptionRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaGetSubscriptionRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("product_id"))) + { + Result.ProductId = Json->GetStringField(TEXT("product_id")); + } + return Result; +} + +TSharedPtr FNakamaGetSubscriptionRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (ProductId.IsEmpty() == false) + { + Json->SetStringField(TEXT("product_id"), ProductId); + } + return Json; +} + +FNakamaGroup FNakamaGroup::FromJson(const TSharedPtr& Json) +{ + FNakamaGroup Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("id"))) + { + Result.Id = Json->GetStringField(TEXT("id")); + } + if (Json->HasField(TEXT("creator_id"))) + { + Result.CreatorId = Json->GetStringField(TEXT("creator_id")); + } + if (Json->HasField(TEXT("name"))) + { + Result.Name = Json->GetStringField(TEXT("name")); + } + if (Json->HasField(TEXT("description"))) + { + Result.Description = Json->GetStringField(TEXT("description")); + } + if (Json->HasField(TEXT("lang_tag"))) + { + Result.LangTag = Json->GetStringField(TEXT("lang_tag")); + } + if (Json->HasField(TEXT("metadata"))) + { + Result.Metadata = Json->GetStringField(TEXT("metadata")); + } + if (Json->HasField(TEXT("avatar_url"))) + { + Result.AvatarUrl = Json->GetStringField(TEXT("avatar_url")); + } + if (Json->HasField(TEXT("open"))) + { + Result.Open = Json->GetBoolField(TEXT("open")); + } + if (Json->HasField(TEXT("edge_count"))) + { + Result.EdgeCount = Json->GetNumberField(TEXT("edge_count")); + } + if (Json->HasField(TEXT("max_count"))) + { + Result.MaxCount = Json->GetNumberField(TEXT("max_count")); + } + if (Json->HasField(TEXT("create_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("create_time")), Result.CreateTime); + } + if (Json->HasField(TEXT("update_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("update_time")), Result.UpdateTime); + } + return Result; +} + +TSharedPtr FNakamaGroup::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Id.IsEmpty() == false) + { + Json->SetStringField(TEXT("id"), Id); + } + if (CreatorId.IsEmpty() == false) + { + Json->SetStringField(TEXT("creator_id"), CreatorId); + } + if (Name.IsEmpty() == false) + { + Json->SetStringField(TEXT("name"), Name); + } + if (Description.IsEmpty() == false) + { + Json->SetStringField(TEXT("description"), Description); + } + if (LangTag.IsEmpty() == false) + { + Json->SetStringField(TEXT("lang_tag"), LangTag); + } + if (Metadata.IsEmpty() == false) + { + Json->SetStringField(TEXT("metadata"), Metadata); + } + if (AvatarUrl.IsEmpty() == false) + { + Json->SetStringField(TEXT("avatar_url"), AvatarUrl); + } + if (Open.IsEmpty() == false) + { + Json->SetBoolField(TEXT("open"), Open.GetValue()); + } + + { + Json->SetNumberField(TEXT("edge_count"), EdgeCount); + } + + { + Json->SetNumberField(TEXT("max_count"), MaxCount); + } + + { + Json->SetStringField(TEXT("create_time"), CreateTime.ToIso8601()); + } + + { + Json->SetStringField(TEXT("update_time"), UpdateTime.ToIso8601()); + } + return Json; +} + +FNakamaGroupList FNakamaGroupList::FromJson(const TSharedPtr& Json) +{ + FNakamaGroupList Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("groups"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("groups"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Groups.Add(FNakamaGroup::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + return Result; +} + +TSharedPtr FNakamaGroupList::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Groups.Num() > 0) + { + TArray> Array; + for (const auto& Item : Groups) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("groups"), Array); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + return Json; +} + +FNakamaGroupUserListGroupUser FNakamaGroupUserListGroupUser::FromJson(const TSharedPtr& Json) +{ + FNakamaGroupUserListGroupUser Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("user"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("user"), NestedObj)) + { + Result.User = (FNakamaUser::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("state"))) + { + Result.State = Json->GetNumberField(TEXT("state")); + } + return Result; +} + +TSharedPtr FNakamaGroupUserListGroupUser::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetObjectField(TEXT("user"), User.ToJson()); + } + if (State.IsEmpty() == false) + { + Json->SetNumberField(TEXT("state"), State.GetValue()); + } + return Json; +} + +FNakamaGroupUserList FNakamaGroupUserList::FromJson(const TSharedPtr& Json) +{ + FNakamaGroupUserList Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("group_users"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("group_users"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.GroupUsers.Add(FNakamaGroupUserListGroupUser::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + return Result; +} + +TSharedPtr FNakamaGroupUserList::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (GroupUsers.Num() > 0) + { + TArray> Array; + for (const auto& Item : GroupUsers) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("group_users"), Array); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + return Json; +} + +FNakamaImportFacebookFriendsRequest FNakamaImportFacebookFriendsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaImportFacebookFriendsRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("account"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("account"), NestedObj)) + { + Result.Account = (FNakamaAccountFacebook::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("reset"))) + { + Result.Reset = Json->GetBoolField(TEXT("reset")); + } + return Result; +} + +TSharedPtr FNakamaImportFacebookFriendsRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetObjectField(TEXT("account"), Account.ToJson()); + } + if (Reset.IsEmpty() == false) + { + Json->SetBoolField(TEXT("reset"), Reset.GetValue()); + } + return Json; +} + +FNakamaImportSteamFriendsRequest FNakamaImportSteamFriendsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaImportSteamFriendsRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("account"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("account"), NestedObj)) + { + Result.Account = (FNakamaAccountSteam::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("reset"))) + { + Result.Reset = Json->GetBoolField(TEXT("reset")); + } + return Result; +} + +TSharedPtr FNakamaImportSteamFriendsRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetObjectField(TEXT("account"), Account.ToJson()); + } + if (Reset.IsEmpty() == false) + { + Json->SetBoolField(TEXT("reset"), Reset.GetValue()); + } + return Json; +} + +FNakamaJoinGroupRequest FNakamaJoinGroupRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaJoinGroupRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("group_id"))) + { + Result.GroupId = Json->GetStringField(TEXT("group_id")); + } + return Result; +} + +TSharedPtr FNakamaJoinGroupRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (GroupId.IsEmpty() == false) + { + Json->SetStringField(TEXT("group_id"), GroupId); + } + return Json; +} + +FNakamaJoinTournamentRequest FNakamaJoinTournamentRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaJoinTournamentRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("tournament_id"))) + { + Result.TournamentId = Json->GetStringField(TEXT("tournament_id")); + } + return Result; +} + +TSharedPtr FNakamaJoinTournamentRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (TournamentId.IsEmpty() == false) + { + Json->SetStringField(TEXT("tournament_id"), TournamentId); + } + return Json; +} + +FNakamaKickGroupUsersRequest FNakamaKickGroupUsersRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaKickGroupUsersRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("group_id"))) + { + Result.GroupId = Json->GetStringField(TEXT("group_id")); + } + if (Json->HasField(TEXT("user_ids"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("user_ids"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.UserIds.Add((Item->AsString())); + } + } + } + return Result; +} + +TSharedPtr FNakamaKickGroupUsersRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (GroupId.IsEmpty() == false) + { + Json->SetStringField(TEXT("group_id"), GroupId); + } + if (UserIds.Num() > 0) + { + TArray> Array; + for (const auto& Item : UserIds) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("user_ids"), Array); + } + return Json; +} + +FNakamaLeaderboard FNakamaLeaderboard::FromJson(const TSharedPtr& Json) +{ + FNakamaLeaderboard Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("id"))) + { + Result.Id = Json->GetStringField(TEXT("id")); + } + if (Json->HasField(TEXT("sort_order"))) + { + Result.SortOrder = Json->GetNumberField(TEXT("sort_order")); + } + if (Json->HasField(TEXT("operator"))) + { + Result.Operator = static_cast(Json->GetNumberField(TEXT("operator"))); + } + if (Json->HasField(TEXT("prev_reset"))) + { + Result.PrevReset = Json->GetNumberField(TEXT("prev_reset")); + } + if (Json->HasField(TEXT("next_reset"))) + { + Result.NextReset = Json->GetNumberField(TEXT("next_reset")); + } + if (Json->HasField(TEXT("metadata"))) + { + Result.Metadata = Json->GetStringField(TEXT("metadata")); + } + if (Json->HasField(TEXT("create_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("create_time")), Result.CreateTime); + } + if (Json->HasField(TEXT("authoritative"))) + { + Result.Authoritative = Json->GetBoolField(TEXT("authoritative")); + } + return Result; +} + +TSharedPtr FNakamaLeaderboard::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Id.IsEmpty() == false) + { + Json->SetStringField(TEXT("id"), Id); + } + + { + Json->SetNumberField(TEXT("sort_order"), SortOrder); + } + + { + Json->SetNumberField(TEXT("operator"), static_cast(Operator)); + } + + { + Json->SetNumberField(TEXT("prev_reset"), PrevReset); + } + + { + Json->SetNumberField(TEXT("next_reset"), NextReset); + } + if (Metadata.IsEmpty() == false) + { + Json->SetStringField(TEXT("metadata"), Metadata); + } + + { + Json->SetStringField(TEXT("create_time"), CreateTime.ToIso8601()); + } + + { + Json->SetBoolField(TEXT("authoritative"), Authoritative); + } + return Json; +} + +FNakamaLeaderboardList FNakamaLeaderboardList::FromJson(const TSharedPtr& Json) +{ + FNakamaLeaderboardList Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("leaderboards"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("leaderboards"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Leaderboards.Add(FNakamaLeaderboard::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + return Result; +} + +TSharedPtr FNakamaLeaderboardList::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Leaderboards.Num() > 0) + { + TArray> Array; + for (const auto& Item : Leaderboards) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("leaderboards"), Array); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + return Json; +} + +FNakamaLeaderboardRecord FNakamaLeaderboardRecord::FromJson(const TSharedPtr& Json) +{ + FNakamaLeaderboardRecord Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("leaderboard_id"))) + { + Result.LeaderboardId = Json->GetStringField(TEXT("leaderboard_id")); + } + if (Json->HasField(TEXT("owner_id"))) + { + Result.OwnerId = Json->GetStringField(TEXT("owner_id")); + } + if (Json->HasField(TEXT("username"))) + { + Result.Username = Json->GetStringField(TEXT("username")); + } + if (Json->HasField(TEXT("score"))) + { + Result.Score = Json->GetNumberField(TEXT("score")); + } + if (Json->HasField(TEXT("subscore"))) + { + Result.Subscore = Json->GetNumberField(TEXT("subscore")); + } + if (Json->HasField(TEXT("num_score"))) + { + Result.NumScore = Json->GetNumberField(TEXT("num_score")); + } + if (Json->HasField(TEXT("metadata"))) + { + Result.Metadata = Json->GetStringField(TEXT("metadata")); + } + if (Json->HasField(TEXT("create_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("create_time")), Result.CreateTime); + } + if (Json->HasField(TEXT("update_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("update_time")), Result.UpdateTime); + } + if (Json->HasField(TEXT("expiry_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("expiry_time")), Result.ExpiryTime); + } + if (Json->HasField(TEXT("rank"))) + { + Result.Rank = Json->GetNumberField(TEXT("rank")); + } + if (Json->HasField(TEXT("max_num_score"))) + { + Result.MaxNumScore = Json->GetNumberField(TEXT("max_num_score")); + } + return Result; +} + +TSharedPtr FNakamaLeaderboardRecord::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (LeaderboardId.IsEmpty() == false) + { + Json->SetStringField(TEXT("leaderboard_id"), LeaderboardId); + } + if (OwnerId.IsEmpty() == false) + { + Json->SetStringField(TEXT("owner_id"), OwnerId); + } + if (Username.IsEmpty() == false) + { + Json->SetStringField(TEXT("username"), Username); + } + + { + Json->SetNumberField(TEXT("score"), Score); + } + + { + Json->SetNumberField(TEXT("subscore"), Subscore); + } + + { + Json->SetNumberField(TEXT("num_score"), NumScore); + } + if (Metadata.IsEmpty() == false) + { + Json->SetStringField(TEXT("metadata"), Metadata); + } + + { + Json->SetStringField(TEXT("create_time"), CreateTime.ToIso8601()); + } + + { + Json->SetStringField(TEXT("update_time"), UpdateTime.ToIso8601()); + } + + { + Json->SetStringField(TEXT("expiry_time"), ExpiryTime.ToIso8601()); + } + + { + Json->SetNumberField(TEXT("rank"), Rank); + } + + { + Json->SetNumberField(TEXT("max_num_score"), MaxNumScore); + } + return Json; +} + +FNakamaLeaderboardRecordList FNakamaLeaderboardRecordList::FromJson(const TSharedPtr& Json) +{ + FNakamaLeaderboardRecordList Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("records"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("records"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Records.Add(FNakamaLeaderboardRecord::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("owner_records"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("owner_records"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.OwnerRecords.Add(FNakamaLeaderboardRecord::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("next_cursor"))) + { + Result.NextCursor = Json->GetStringField(TEXT("next_cursor")); + } + if (Json->HasField(TEXT("prev_cursor"))) + { + Result.PrevCursor = Json->GetStringField(TEXT("prev_cursor")); + } + if (Json->HasField(TEXT("rank_count"))) + { + Result.RankCount = Json->GetNumberField(TEXT("rank_count")); + } + return Result; +} + +TSharedPtr FNakamaLeaderboardRecordList::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Records.Num() > 0) + { + TArray> Array; + for (const auto& Item : Records) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("records"), Array); + } + if (OwnerRecords.Num() > 0) + { + TArray> Array; + for (const auto& Item : OwnerRecords) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("owner_records"), Array); + } + if (NextCursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("next_cursor"), NextCursor); + } + if (PrevCursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("prev_cursor"), PrevCursor); + } + + { + Json->SetNumberField(TEXT("rank_count"), RankCount); + } + return Json; +} + +FNakamaLeaveGroupRequest FNakamaLeaveGroupRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaLeaveGroupRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("group_id"))) + { + Result.GroupId = Json->GetStringField(TEXT("group_id")); + } + return Result; +} + +TSharedPtr FNakamaLeaveGroupRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (GroupId.IsEmpty() == false) + { + Json->SetStringField(TEXT("group_id"), GroupId); + } + return Json; +} + +FNakamaLinkFacebookRequest FNakamaLinkFacebookRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaLinkFacebookRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("account"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("account"), NestedObj)) + { + Result.Account = (FNakamaAccountFacebook::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("sync"))) + { + Result.Sync = Json->GetBoolField(TEXT("sync")); + } + return Result; +} + +TSharedPtr FNakamaLinkFacebookRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetObjectField(TEXT("account"), Account.ToJson()); + } + if (Sync.IsEmpty() == false) + { + Json->SetBoolField(TEXT("sync"), Sync.GetValue()); + } + return Json; +} + +FNakamaLinkSteamRequest FNakamaLinkSteamRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaLinkSteamRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("account"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("account"), NestedObj)) + { + Result.Account = (FNakamaAccountSteam::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("sync"))) + { + Result.Sync = Json->GetBoolField(TEXT("sync")); + } + return Result; +} + +TSharedPtr FNakamaLinkSteamRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetObjectField(TEXT("account"), Account.ToJson()); + } + if (Sync.IsEmpty() == false) + { + Json->SetBoolField(TEXT("sync"), Sync.GetValue()); + } + return Json; +} + +FNakamaListChannelMessagesRequest FNakamaListChannelMessagesRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListChannelMessagesRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("channel_id"))) + { + Result.ChannelId = Json->GetStringField(TEXT("channel_id")); + } + if (Json->HasField(TEXT("limit"))) + { + Result.Limit = Json->GetNumberField(TEXT("limit")); + } + if (Json->HasField(TEXT("forward"))) + { + Result.Forward = Json->GetBoolField(TEXT("forward")); + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + return Result; +} + +TSharedPtr FNakamaListChannelMessagesRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (ChannelId.IsEmpty() == false) + { + Json->SetStringField(TEXT("channel_id"), ChannelId); + } + if (Limit.IsEmpty() == false) + { + Json->SetNumberField(TEXT("limit"), Limit.GetValue()); + } + if (Forward.IsEmpty() == false) + { + Json->SetBoolField(TEXT("forward"), Forward.GetValue()); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + return Json; +} + +FNakamaListFriendsRequest FNakamaListFriendsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListFriendsRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("limit"))) + { + Result.Limit = Json->GetNumberField(TEXT("limit")); + } + if (Json->HasField(TEXT("state"))) + { + Result.State = Json->GetNumberField(TEXT("state")); + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + return Result; +} + +TSharedPtr FNakamaListFriendsRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Limit.IsEmpty() == false) + { + Json->SetNumberField(TEXT("limit"), Limit.GetValue()); + } + if (State.IsEmpty() == false) + { + Json->SetNumberField(TEXT("state"), State.GetValue()); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + return Json; +} + +FNakamaListFriendsOfFriendsRequest FNakamaListFriendsOfFriendsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListFriendsOfFriendsRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("limit"))) + { + Result.Limit = Json->GetNumberField(TEXT("limit")); + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + return Result; +} + +TSharedPtr FNakamaListFriendsOfFriendsRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Limit.IsEmpty() == false) + { + Json->SetNumberField(TEXT("limit"), Limit.GetValue()); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + return Json; +} + +FNakamaListGroupsRequest FNakamaListGroupsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListGroupsRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("name"))) + { + Result.Name = Json->GetStringField(TEXT("name")); + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + if (Json->HasField(TEXT("limit"))) + { + Result.Limit = Json->GetNumberField(TEXT("limit")); + } + if (Json->HasField(TEXT("lang_tag"))) + { + Result.LangTag = Json->GetStringField(TEXT("lang_tag")); + } + if (Json->HasField(TEXT("members"))) + { + Result.Members = Json->GetNumberField(TEXT("members")); + } + if (Json->HasField(TEXT("open"))) + { + Result.Open = Json->GetBoolField(TEXT("open")); + } + return Result; +} + +TSharedPtr FNakamaListGroupsRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Name.IsEmpty() == false) + { + Json->SetStringField(TEXT("name"), Name); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + if (Limit.IsEmpty() == false) + { + Json->SetNumberField(TEXT("limit"), Limit.GetValue()); + } + if (LangTag.IsEmpty() == false) + { + Json->SetStringField(TEXT("lang_tag"), LangTag); + } + if (Members.IsEmpty() == false) + { + Json->SetNumberField(TEXT("members"), Members.GetValue()); + } + if (Open.IsEmpty() == false) + { + Json->SetBoolField(TEXT("open"), Open.GetValue()); + } + return Json; +} + +FNakamaListGroupUsersRequest FNakamaListGroupUsersRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListGroupUsersRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("group_id"))) + { + Result.GroupId = Json->GetStringField(TEXT("group_id")); + } + if (Json->HasField(TEXT("limit"))) + { + Result.Limit = Json->GetNumberField(TEXT("limit")); + } + if (Json->HasField(TEXT("state"))) + { + Result.State = Json->GetNumberField(TEXT("state")); + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + return Result; +} + +TSharedPtr FNakamaListGroupUsersRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (GroupId.IsEmpty() == false) + { + Json->SetStringField(TEXT("group_id"), GroupId); + } + if (Limit.IsEmpty() == false) + { + Json->SetNumberField(TEXT("limit"), Limit.GetValue()); + } + if (State.IsEmpty() == false) + { + Json->SetNumberField(TEXT("state"), State.GetValue()); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + return Json; +} + +FNakamaListLeaderboardRecordsAroundOwnerRequest FNakamaListLeaderboardRecordsAroundOwnerRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListLeaderboardRecordsAroundOwnerRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("leaderboard_id"))) + { + Result.LeaderboardId = Json->GetStringField(TEXT("leaderboard_id")); + } + if (Json->HasField(TEXT("limit"))) + { + Result.Limit = Json->GetNumberField(TEXT("limit")); + } + if (Json->HasField(TEXT("owner_id"))) + { + Result.OwnerId = Json->GetStringField(TEXT("owner_id")); + } + if (Json->HasField(TEXT("expiry"))) + { + Result.Expiry = Json->GetNumberField(TEXT("expiry")); + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + return Result; +} + +TSharedPtr FNakamaListLeaderboardRecordsAroundOwnerRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (LeaderboardId.IsEmpty() == false) + { + Json->SetStringField(TEXT("leaderboard_id"), LeaderboardId); + } + if (Limit.IsEmpty() == false) + { + Json->SetNumberField(TEXT("limit"), Limit.GetValue()); + } + if (OwnerId.IsEmpty() == false) + { + Json->SetStringField(TEXT("owner_id"), OwnerId); + } + if (Expiry.IsEmpty() == false) + { + Json->SetNumberField(TEXT("expiry"), Expiry.GetValue()); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + return Json; +} + +FNakamaListLeaderboardRecordsRequest FNakamaListLeaderboardRecordsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListLeaderboardRecordsRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("leaderboard_id"))) + { + Result.LeaderboardId = Json->GetStringField(TEXT("leaderboard_id")); + } + if (Json->HasField(TEXT("owner_ids"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("owner_ids"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.OwnerIds.Add((Item->AsString())); + } + } + } + if (Json->HasField(TEXT("limit"))) + { + Result.Limit = Json->GetNumberField(TEXT("limit")); + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + if (Json->HasField(TEXT("expiry"))) + { + Result.Expiry = Json->GetNumberField(TEXT("expiry")); + } + return Result; +} + +TSharedPtr FNakamaListLeaderboardRecordsRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (LeaderboardId.IsEmpty() == false) + { + Json->SetStringField(TEXT("leaderboard_id"), LeaderboardId); + } + if (OwnerIds.Num() > 0) + { + TArray> Array; + for (const auto& Item : OwnerIds) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("owner_ids"), Array); + } + if (Limit.IsEmpty() == false) + { + Json->SetNumberField(TEXT("limit"), Limit.GetValue()); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + if (Expiry.IsEmpty() == false) + { + Json->SetNumberField(TEXT("expiry"), Expiry.GetValue()); + } + return Json; +} + +FNakamaListMatchesRequest FNakamaListMatchesRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListMatchesRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("limit"))) + { + Result.Limit = Json->GetNumberField(TEXT("limit")); + } + if (Json->HasField(TEXT("authoritative"))) + { + Result.Authoritative = Json->GetBoolField(TEXT("authoritative")); + } + if (Json->HasField(TEXT("label"))) + { + Result.Label = Json->GetStringField(TEXT("label")); + } + if (Json->HasField(TEXT("min_size"))) + { + Result.MinSize = Json->GetNumberField(TEXT("min_size")); + } + if (Json->HasField(TEXT("max_size"))) + { + Result.MaxSize = Json->GetNumberField(TEXT("max_size")); + } + if (Json->HasField(TEXT("query"))) + { + Result.Query = Json->GetStringField(TEXT("query")); + } + return Result; +} + +TSharedPtr FNakamaListMatchesRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Limit.IsEmpty() == false) + { + Json->SetNumberField(TEXT("limit"), Limit.GetValue()); + } + if (Authoritative.IsEmpty() == false) + { + Json->SetBoolField(TEXT("authoritative"), Authoritative.GetValue()); + } + if (Label.IsEmpty() == false) + { + Json->SetStringField(TEXT("label"), Label); + } + if (MinSize.IsEmpty() == false) + { + Json->SetNumberField(TEXT("min_size"), MinSize.GetValue()); + } + if (MaxSize.IsEmpty() == false) + { + Json->SetNumberField(TEXT("max_size"), MaxSize.GetValue()); + } + if (Query.IsEmpty() == false) + { + Json->SetStringField(TEXT("query"), Query); + } + return Json; +} + +FNakamaListNotificationsRequest FNakamaListNotificationsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListNotificationsRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("limit"))) + { + Result.Limit = Json->GetNumberField(TEXT("limit")); + } + if (Json->HasField(TEXT("cacheable_cursor"))) + { + Result.CacheableCursor = Json->GetStringField(TEXT("cacheable_cursor")); + } + return Result; +} + +TSharedPtr FNakamaListNotificationsRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Limit.IsEmpty() == false) + { + Json->SetNumberField(TEXT("limit"), Limit.GetValue()); + } + if (CacheableCursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cacheable_cursor"), CacheableCursor); + } + return Json; +} + +FNakamaListStorageObjectsRequest FNakamaListStorageObjectsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListStorageObjectsRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("user_id"))) + { + Result.UserId = Json->GetStringField(TEXT("user_id")); + } + if (Json->HasField(TEXT("collection"))) + { + Result.Collection = Json->GetStringField(TEXT("collection")); + } + if (Json->HasField(TEXT("limit"))) + { + Result.Limit = Json->GetNumberField(TEXT("limit")); + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + return Result; +} + +TSharedPtr FNakamaListStorageObjectsRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (UserId.IsEmpty() == false) + { + Json->SetStringField(TEXT("user_id"), UserId); + } + if (Collection.IsEmpty() == false) + { + Json->SetStringField(TEXT("collection"), Collection); + } + if (Limit.IsEmpty() == false) + { + Json->SetNumberField(TEXT("limit"), Limit.GetValue()); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + return Json; +} + +FNakamaListSubscriptionsRequest FNakamaListSubscriptionsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListSubscriptionsRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("limit"))) + { + Result.Limit = Json->GetNumberField(TEXT("limit")); + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + return Result; +} + +TSharedPtr FNakamaListSubscriptionsRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Limit.IsEmpty() == false) + { + Json->SetNumberField(TEXT("limit"), Limit.GetValue()); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + return Json; +} + +FNakamaListTournamentRecordsAroundOwnerRequest FNakamaListTournamentRecordsAroundOwnerRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListTournamentRecordsAroundOwnerRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("tournament_id"))) + { + Result.TournamentId = Json->GetStringField(TEXT("tournament_id")); + } + if (Json->HasField(TEXT("limit"))) + { + Result.Limit = Json->GetNumberField(TEXT("limit")); + } + if (Json->HasField(TEXT("owner_id"))) + { + Result.OwnerId = Json->GetStringField(TEXT("owner_id")); + } + if (Json->HasField(TEXT("expiry"))) + { + Result.Expiry = Json->GetNumberField(TEXT("expiry")); + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + return Result; +} + +TSharedPtr FNakamaListTournamentRecordsAroundOwnerRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (TournamentId.IsEmpty() == false) + { + Json->SetStringField(TEXT("tournament_id"), TournamentId); + } + if (Limit.IsEmpty() == false) + { + Json->SetNumberField(TEXT("limit"), Limit.GetValue()); + } + if (OwnerId.IsEmpty() == false) + { + Json->SetStringField(TEXT("owner_id"), OwnerId); + } + if (Expiry.IsEmpty() == false) + { + Json->SetNumberField(TEXT("expiry"), Expiry.GetValue()); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + return Json; +} + +FNakamaListTournamentRecordsRequest FNakamaListTournamentRecordsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListTournamentRecordsRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("tournament_id"))) + { + Result.TournamentId = Json->GetStringField(TEXT("tournament_id")); + } + if (Json->HasField(TEXT("owner_ids"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("owner_ids"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.OwnerIds.Add((Item->AsString())); + } + } + } + if (Json->HasField(TEXT("limit"))) + { + Result.Limit = Json->GetNumberField(TEXT("limit")); + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + if (Json->HasField(TEXT("expiry"))) + { + Result.Expiry = Json->GetNumberField(TEXT("expiry")); + } + return Result; +} + +TSharedPtr FNakamaListTournamentRecordsRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (TournamentId.IsEmpty() == false) + { + Json->SetStringField(TEXT("tournament_id"), TournamentId); + } + if (OwnerIds.Num() > 0) + { + TArray> Array; + for (const auto& Item : OwnerIds) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("owner_ids"), Array); + } + if (Limit.IsEmpty() == false) + { + Json->SetNumberField(TEXT("limit"), Limit.GetValue()); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + if (Expiry.IsEmpty() == false) + { + Json->SetNumberField(TEXT("expiry"), Expiry.GetValue()); + } + return Json; +} + +FNakamaListTournamentsRequest FNakamaListTournamentsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListTournamentsRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("category_start"))) + { + Result.CategoryStart = Json->GetNumberField(TEXT("category_start")); + } + if (Json->HasField(TEXT("category_end"))) + { + Result.CategoryEnd = Json->GetNumberField(TEXT("category_end")); + } + if (Json->HasField(TEXT("start_time"))) + { + Result.StartTime = Json->GetNumberField(TEXT("start_time")); + } + if (Json->HasField(TEXT("end_time"))) + { + Result.EndTime = Json->GetNumberField(TEXT("end_time")); + } + if (Json->HasField(TEXT("limit"))) + { + Result.Limit = Json->GetNumberField(TEXT("limit")); + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + return Result; +} + +TSharedPtr FNakamaListTournamentsRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (CategoryStart.IsEmpty() == false) + { + Json->SetNumberField(TEXT("category_start"), CategoryStart.GetValue()); + } + if (CategoryEnd.IsEmpty() == false) + { + Json->SetNumberField(TEXT("category_end"), CategoryEnd.GetValue()); + } + if (StartTime.IsEmpty() == false) + { + Json->SetNumberField(TEXT("start_time"), StartTime.GetValue()); + } + if (EndTime.IsEmpty() == false) + { + Json->SetNumberField(TEXT("end_time"), EndTime.GetValue()); + } + if (Limit.IsEmpty() == false) + { + Json->SetNumberField(TEXT("limit"), Limit.GetValue()); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + return Json; +} + +FNakamaListUserGroupsRequest FNakamaListUserGroupsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListUserGroupsRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("user_id"))) + { + Result.UserId = Json->GetStringField(TEXT("user_id")); + } + if (Json->HasField(TEXT("limit"))) + { + Result.Limit = Json->GetNumberField(TEXT("limit")); + } + if (Json->HasField(TEXT("state"))) + { + Result.State = Json->GetNumberField(TEXT("state")); + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + return Result; +} + +TSharedPtr FNakamaListUserGroupsRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (UserId.IsEmpty() == false) + { + Json->SetStringField(TEXT("user_id"), UserId); + } + if (Limit.IsEmpty() == false) + { + Json->SetNumberField(TEXT("limit"), Limit.GetValue()); + } + if (State.IsEmpty() == false) + { + Json->SetNumberField(TEXT("state"), State.GetValue()); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + return Json; +} + +FNakamaMatch FNakamaMatch::FromJson(const TSharedPtr& Json) +{ + FNakamaMatch Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("match_id"))) + { + Result.MatchId = Json->GetStringField(TEXT("match_id")); + } + if (Json->HasField(TEXT("authoritative"))) + { + Result.Authoritative = Json->GetBoolField(TEXT("authoritative")); + } + if (Json->HasField(TEXT("label"))) + { + Result.Label = Json->GetStringField(TEXT("label")); + } + if (Json->HasField(TEXT("size"))) + { + Result.Size = Json->GetNumberField(TEXT("size")); + } + if (Json->HasField(TEXT("tick_rate"))) + { + Result.TickRate = Json->GetNumberField(TEXT("tick_rate")); + } + if (Json->HasField(TEXT("handler_name"))) + { + Result.HandlerName = Json->GetStringField(TEXT("handler_name")); + } + return Result; +} + +TSharedPtr FNakamaMatch::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (MatchId.IsEmpty() == false) + { + Json->SetStringField(TEXT("match_id"), MatchId); + } + + { + Json->SetBoolField(TEXT("authoritative"), Authoritative); + } + if (Label.IsEmpty() == false) + { + Json->SetStringField(TEXT("label"), Label); + } + + { + Json->SetNumberField(TEXT("size"), Size); + } + + { + Json->SetNumberField(TEXT("tick_rate"), TickRate); + } + if (HandlerName.IsEmpty() == false) + { + Json->SetStringField(TEXT("handler_name"), HandlerName); + } + return Json; +} + +FNakamaMatchList FNakamaMatchList::FromJson(const TSharedPtr& Json) +{ + FNakamaMatchList Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("matches"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("matches"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Matches.Add(FNakamaMatch::FromJson(*ItemObj)); + } + } + } + } + return Result; +} + +TSharedPtr FNakamaMatchList::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Matches.Num() > 0) + { + TArray> Array; + for (const auto& Item : Matches) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("matches"), Array); + } + return Json; +} + +FNakamaMatchmakerCompletionStats FNakamaMatchmakerCompletionStats::FromJson(const TSharedPtr& Json) +{ + FNakamaMatchmakerCompletionStats Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("create_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("create_time")), Result.CreateTime); + } + if (Json->HasField(TEXT("complete_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("complete_time")), Result.CompleteTime); + } + return Result; +} + +TSharedPtr FNakamaMatchmakerCompletionStats::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetStringField(TEXT("create_time"), CreateTime.ToIso8601()); + } + + { + Json->SetStringField(TEXT("complete_time"), CompleteTime.ToIso8601()); + } + return Json; +} + +FNakamaMatchmakerStats FNakamaMatchmakerStats::FromJson(const TSharedPtr& Json) +{ + FNakamaMatchmakerStats Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("ticket_count"))) + { + Result.TicketCount = Json->GetNumberField(TEXT("ticket_count")); + } + if (Json->HasField(TEXT("oldest_ticket_create_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("oldest_ticket_create_time")), Result.OldestTicketCreateTime); + } + if (Json->HasField(TEXT("completions"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("completions"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Completions.Add(FNakamaMatchmakerCompletionStats::FromJson(*ItemObj)); + } + } + } + } + return Result; +} + +TSharedPtr FNakamaMatchmakerStats::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetNumberField(TEXT("ticket_count"), TicketCount); + } + + { + Json->SetStringField(TEXT("oldest_ticket_create_time"), OldestTicketCreateTime.ToIso8601()); + } + if (Completions.Num() > 0) + { + TArray> Array; + for (const auto& Item : Completions) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("completions"), Array); + } + return Json; +} + +FNakamaNotification FNakamaNotification::FromJson(const TSharedPtr& Json) +{ + FNakamaNotification Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("id"))) + { + Result.Id = Json->GetStringField(TEXT("id")); + } + if (Json->HasField(TEXT("subject"))) + { + Result.Subject = Json->GetStringField(TEXT("subject")); + } + if (Json->HasField(TEXT("content"))) + { + Result.Content = Json->GetStringField(TEXT("content")); + } + if (Json->HasField(TEXT("code"))) + { + Result.Code = Json->GetNumberField(TEXT("code")); + } + if (Json->HasField(TEXT("sender_id"))) + { + Result.SenderId = Json->GetStringField(TEXT("sender_id")); + } + if (Json->HasField(TEXT("create_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("create_time")), Result.CreateTime); + } + if (Json->HasField(TEXT("persistent"))) + { + Result.Persistent = Json->GetBoolField(TEXT("persistent")); + } + return Result; +} + +TSharedPtr FNakamaNotification::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Id.IsEmpty() == false) + { + Json->SetStringField(TEXT("id"), Id); + } + if (Subject.IsEmpty() == false) + { + Json->SetStringField(TEXT("subject"), Subject); + } + if (Content.IsEmpty() == false) + { + Json->SetStringField(TEXT("content"), Content); + } + + { + Json->SetNumberField(TEXT("code"), Code); + } + if (SenderId.IsEmpty() == false) + { + Json->SetStringField(TEXT("sender_id"), SenderId); + } + + { + Json->SetStringField(TEXT("create_time"), CreateTime.ToIso8601()); + } + + { + Json->SetBoolField(TEXT("persistent"), Persistent); + } + return Json; +} + +FNakamaNotificationList FNakamaNotificationList::FromJson(const TSharedPtr& Json) +{ + FNakamaNotificationList Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("notifications"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("notifications"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Notifications.Add(FNakamaNotification::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("cacheable_cursor"))) + { + Result.CacheableCursor = Json->GetStringField(TEXT("cacheable_cursor")); + } + return Result; +} + +TSharedPtr FNakamaNotificationList::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Notifications.Num() > 0) + { + TArray> Array; + for (const auto& Item : Notifications) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("notifications"), Array); + } + if (CacheableCursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cacheable_cursor"), CacheableCursor); + } + return Json; +} + +FNakamaPromoteGroupUsersRequest FNakamaPromoteGroupUsersRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaPromoteGroupUsersRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("group_id"))) + { + Result.GroupId = Json->GetStringField(TEXT("group_id")); + } + if (Json->HasField(TEXT("user_ids"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("user_ids"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.UserIds.Add((Item->AsString())); + } + } + } + return Result; +} + +TSharedPtr FNakamaPromoteGroupUsersRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (GroupId.IsEmpty() == false) + { + Json->SetStringField(TEXT("group_id"), GroupId); + } + if (UserIds.Num() > 0) + { + TArray> Array; + for (const auto& Item : UserIds) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("user_ids"), Array); + } + return Json; +} + +FNakamaDemoteGroupUsersRequest FNakamaDemoteGroupUsersRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaDemoteGroupUsersRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("group_id"))) + { + Result.GroupId = Json->GetStringField(TEXT("group_id")); + } + if (Json->HasField(TEXT("user_ids"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("user_ids"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.UserIds.Add((Item->AsString())); + } + } + } + return Result; +} + +TSharedPtr FNakamaDemoteGroupUsersRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (GroupId.IsEmpty() == false) + { + Json->SetStringField(TEXT("group_id"), GroupId); + } + if (UserIds.Num() > 0) + { + TArray> Array; + for (const auto& Item : UserIds) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("user_ids"), Array); + } + return Json; +} + +FNakamaReadStorageObjectId FNakamaReadStorageObjectId::FromJson(const TSharedPtr& Json) +{ + FNakamaReadStorageObjectId Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("collection"))) + { + Result.Collection = Json->GetStringField(TEXT("collection")); + } + if (Json->HasField(TEXT("key"))) + { + Result.Key = Json->GetStringField(TEXT("key")); + } + if (Json->HasField(TEXT("user_id"))) + { + Result.UserId = Json->GetStringField(TEXT("user_id")); + } + return Result; +} + +TSharedPtr FNakamaReadStorageObjectId::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Collection.IsEmpty() == false) + { + Json->SetStringField(TEXT("collection"), Collection); + } + if (Key.IsEmpty() == false) + { + Json->SetStringField(TEXT("key"), Key); + } + if (UserId.IsEmpty() == false) + { + Json->SetStringField(TEXT("user_id"), UserId); + } + return Json; +} + +FNakamaReadStorageObjectsRequest FNakamaReadStorageObjectsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaReadStorageObjectsRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("object_ids"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("object_ids"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.ObjectIds.Add(FNakamaReadStorageObjectId::FromJson(*ItemObj)); + } + } + } + } + return Result; +} + +TSharedPtr FNakamaReadStorageObjectsRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (ObjectIds.Num() > 0) + { + TArray> Array; + for (const auto& Item : ObjectIds) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("object_ids"), Array); + } + return Json; +} + +FNakamaRpc FNakamaRpc::FromJson(const TSharedPtr& Json) +{ + FNakamaRpc Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("id"))) + { + Result.Id = Json->GetStringField(TEXT("id")); + } + if (Json->HasField(TEXT("payload"))) + { + Result.Payload = Json->GetStringField(TEXT("payload")); + } + if (Json->HasField(TEXT("http_key"))) + { + Result.HttpKey = Json->GetStringField(TEXT("http_key")); + } + return Result; +} + +TSharedPtr FNakamaRpc::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Id.IsEmpty() == false) + { + Json->SetStringField(TEXT("id"), Id); + } + if (Payload.IsEmpty() == false) + { + Json->SetStringField(TEXT("payload"), Payload); + } + if (HttpKey.IsEmpty() == false) + { + Json->SetStringField(TEXT("http_key"), HttpKey); + } + return Json; +} + +FNakamaStorageObject FNakamaStorageObject::FromJson(const TSharedPtr& Json) +{ + FNakamaStorageObject Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("collection"))) + { + Result.Collection = Json->GetStringField(TEXT("collection")); + } + if (Json->HasField(TEXT("key"))) + { + Result.Key = Json->GetStringField(TEXT("key")); + } + if (Json->HasField(TEXT("user_id"))) + { + Result.UserId = Json->GetStringField(TEXT("user_id")); + } + if (Json->HasField(TEXT("value"))) + { + Result.Value = Json->GetStringField(TEXT("value")); + } + if (Json->HasField(TEXT("version"))) + { + Result.Version = Json->GetStringField(TEXT("version")); + } + if (Json->HasField(TEXT("permission_read"))) + { + Result.PermissionRead = Json->GetNumberField(TEXT("permission_read")); + } + if (Json->HasField(TEXT("permission_write"))) + { + Result.PermissionWrite = Json->GetNumberField(TEXT("permission_write")); + } + if (Json->HasField(TEXT("create_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("create_time")), Result.CreateTime); + } + if (Json->HasField(TEXT("update_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("update_time")), Result.UpdateTime); + } + return Result; +} + +TSharedPtr FNakamaStorageObject::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Collection.IsEmpty() == false) + { + Json->SetStringField(TEXT("collection"), Collection); + } + if (Key.IsEmpty() == false) + { + Json->SetStringField(TEXT("key"), Key); + } + if (UserId.IsEmpty() == false) + { + Json->SetStringField(TEXT("user_id"), UserId); + } + if (Value.IsEmpty() == false) + { + Json->SetStringField(TEXT("value"), Value); + } + if (Version.IsEmpty() == false) + { + Json->SetStringField(TEXT("version"), Version); + } + + { + Json->SetNumberField(TEXT("permission_read"), PermissionRead); + } + + { + Json->SetNumberField(TEXT("permission_write"), PermissionWrite); + } + + { + Json->SetStringField(TEXT("create_time"), CreateTime.ToIso8601()); + } + + { + Json->SetStringField(TEXT("update_time"), UpdateTime.ToIso8601()); + } + return Json; +} + +FNakamaStorageObjectAck FNakamaStorageObjectAck::FromJson(const TSharedPtr& Json) +{ + FNakamaStorageObjectAck Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("collection"))) + { + Result.Collection = Json->GetStringField(TEXT("collection")); + } + if (Json->HasField(TEXT("key"))) + { + Result.Key = Json->GetStringField(TEXT("key")); + } + if (Json->HasField(TEXT("version"))) + { + Result.Version = Json->GetStringField(TEXT("version")); + } + if (Json->HasField(TEXT("user_id"))) + { + Result.UserId = Json->GetStringField(TEXT("user_id")); + } + if (Json->HasField(TEXT("create_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("create_time")), Result.CreateTime); + } + if (Json->HasField(TEXT("update_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("update_time")), Result.UpdateTime); + } + return Result; +} + +TSharedPtr FNakamaStorageObjectAck::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Collection.IsEmpty() == false) + { + Json->SetStringField(TEXT("collection"), Collection); + } + if (Key.IsEmpty() == false) + { + Json->SetStringField(TEXT("key"), Key); + } + if (Version.IsEmpty() == false) + { + Json->SetStringField(TEXT("version"), Version); + } + if (UserId.IsEmpty() == false) + { + Json->SetStringField(TEXT("user_id"), UserId); + } + + { + Json->SetStringField(TEXT("create_time"), CreateTime.ToIso8601()); + } + + { + Json->SetStringField(TEXT("update_time"), UpdateTime.ToIso8601()); + } + return Json; +} + +FNakamaStorageObjectAcks FNakamaStorageObjectAcks::FromJson(const TSharedPtr& Json) +{ + FNakamaStorageObjectAcks Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("acks"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("acks"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Acks.Add(FNakamaStorageObjectAck::FromJson(*ItemObj)); + } + } + } + } + return Result; +} + +TSharedPtr FNakamaStorageObjectAcks::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Acks.Num() > 0) + { + TArray> Array; + for (const auto& Item : Acks) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("acks"), Array); + } + return Json; +} + +FNakamaStorageObjects FNakamaStorageObjects::FromJson(const TSharedPtr& Json) +{ + FNakamaStorageObjects Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("objects"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("objects"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Objects.Add(FNakamaStorageObject::FromJson(*ItemObj)); + } + } + } + } + return Result; +} + +TSharedPtr FNakamaStorageObjects::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Objects.Num() > 0) + { + TArray> Array; + for (const auto& Item : Objects) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("objects"), Array); + } + return Json; +} + +FNakamaStorageObjectList FNakamaStorageObjectList::FromJson(const TSharedPtr& Json) +{ + FNakamaStorageObjectList Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("objects"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("objects"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Objects.Add(FNakamaStorageObject::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + return Result; +} + +TSharedPtr FNakamaStorageObjectList::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Objects.Num() > 0) + { + TArray> Array; + for (const auto& Item : Objects) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("objects"), Array); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + return Json; +} + +FNakamaTournament FNakamaTournament::FromJson(const TSharedPtr& Json) +{ + FNakamaTournament Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("id"))) + { + Result.Id = Json->GetStringField(TEXT("id")); + } + if (Json->HasField(TEXT("title"))) + { + Result.Title = Json->GetStringField(TEXT("title")); + } + if (Json->HasField(TEXT("description"))) + { + Result.Description = Json->GetStringField(TEXT("description")); + } + if (Json->HasField(TEXT("category"))) + { + Result.Category = Json->GetNumberField(TEXT("category")); + } + if (Json->HasField(TEXT("sort_order"))) + { + Result.SortOrder = Json->GetNumberField(TEXT("sort_order")); + } + if (Json->HasField(TEXT("size"))) + { + Result.Size = Json->GetNumberField(TEXT("size")); + } + if (Json->HasField(TEXT("max_size"))) + { + Result.MaxSize = Json->GetNumberField(TEXT("max_size")); + } + if (Json->HasField(TEXT("max_num_score"))) + { + Result.MaxNumScore = Json->GetNumberField(TEXT("max_num_score")); + } + if (Json->HasField(TEXT("can_enter"))) + { + Result.CanEnter = Json->GetBoolField(TEXT("can_enter")); + } + if (Json->HasField(TEXT("end_active"))) + { + Result.EndActive = Json->GetNumberField(TEXT("end_active")); + } + if (Json->HasField(TEXT("next_reset"))) + { + Result.NextReset = Json->GetNumberField(TEXT("next_reset")); + } + if (Json->HasField(TEXT("metadata"))) + { + Result.Metadata = Json->GetStringField(TEXT("metadata")); + } + if (Json->HasField(TEXT("create_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("create_time")), Result.CreateTime); + } + if (Json->HasField(TEXT("start_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("start_time")), Result.StartTime); + } + if (Json->HasField(TEXT("end_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("end_time")), Result.EndTime); + } + if (Json->HasField(TEXT("duration"))) + { + Result.Duration = Json->GetNumberField(TEXT("duration")); + } + if (Json->HasField(TEXT("start_active"))) + { + Result.StartActive = Json->GetNumberField(TEXT("start_active")); + } + if (Json->HasField(TEXT("prev_reset"))) + { + Result.PrevReset = Json->GetNumberField(TEXT("prev_reset")); + } + if (Json->HasField(TEXT("operator"))) + { + Result.Operator = static_cast(Json->GetNumberField(TEXT("operator"))); + } + if (Json->HasField(TEXT("authoritative"))) + { + Result.Authoritative = Json->GetBoolField(TEXT("authoritative")); + } + if (Json->HasField(TEXT("join_required"))) + { + Result.JoinRequired = Json->GetBoolField(TEXT("join_required")); + } + return Result; +} + +TSharedPtr FNakamaTournament::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Id.IsEmpty() == false) + { + Json->SetStringField(TEXT("id"), Id); + } + if (Title.IsEmpty() == false) + { + Json->SetStringField(TEXT("title"), Title); + } + if (Description.IsEmpty() == false) + { + Json->SetStringField(TEXT("description"), Description); + } + + { + Json->SetNumberField(TEXT("category"), Category); + } + + { + Json->SetNumberField(TEXT("sort_order"), SortOrder); + } + + { + Json->SetNumberField(TEXT("size"), Size); + } + + { + Json->SetNumberField(TEXT("max_size"), MaxSize); + } + + { + Json->SetNumberField(TEXT("max_num_score"), MaxNumScore); + } + + { + Json->SetBoolField(TEXT("can_enter"), CanEnter); + } + + { + Json->SetNumberField(TEXT("end_active"), EndActive); + } + + { + Json->SetNumberField(TEXT("next_reset"), NextReset); + } + if (Metadata.IsEmpty() == false) + { + Json->SetStringField(TEXT("metadata"), Metadata); + } + + { + Json->SetStringField(TEXT("create_time"), CreateTime.ToIso8601()); + } + + { + Json->SetStringField(TEXT("start_time"), StartTime.ToIso8601()); + } + + { + Json->SetStringField(TEXT("end_time"), EndTime.ToIso8601()); + } + + { + Json->SetNumberField(TEXT("duration"), Duration); + } + + { + Json->SetNumberField(TEXT("start_active"), StartActive); + } + + { + Json->SetNumberField(TEXT("prev_reset"), PrevReset); + } + + { + Json->SetNumberField(TEXT("operator"), static_cast(Operator)); + } + + { + Json->SetBoolField(TEXT("authoritative"), Authoritative); + } + + { + Json->SetBoolField(TEXT("join_required"), JoinRequired); + } + return Json; +} + +FNakamaTournamentList FNakamaTournamentList::FromJson(const TSharedPtr& Json) +{ + FNakamaTournamentList Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("tournaments"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("tournaments"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Tournaments.Add(FNakamaTournament::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + return Result; +} + +TSharedPtr FNakamaTournamentList::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Tournaments.Num() > 0) + { + TArray> Array; + for (const auto& Item : Tournaments) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("tournaments"), Array); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + return Json; +} + +FNakamaTournamentRecordList FNakamaTournamentRecordList::FromJson(const TSharedPtr& Json) +{ + FNakamaTournamentRecordList Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("records"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("records"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Records.Add(FNakamaLeaderboardRecord::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("owner_records"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("owner_records"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.OwnerRecords.Add(FNakamaLeaderboardRecord::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("next_cursor"))) + { + Result.NextCursor = Json->GetStringField(TEXT("next_cursor")); + } + if (Json->HasField(TEXT("prev_cursor"))) + { + Result.PrevCursor = Json->GetStringField(TEXT("prev_cursor")); + } + if (Json->HasField(TEXT("rank_count"))) + { + Result.RankCount = Json->GetNumberField(TEXT("rank_count")); + } + return Result; +} + +TSharedPtr FNakamaTournamentRecordList::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Records.Num() > 0) + { + TArray> Array; + for (const auto& Item : Records) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("records"), Array); + } + if (OwnerRecords.Num() > 0) + { + TArray> Array; + for (const auto& Item : OwnerRecords) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("owner_records"), Array); + } + if (NextCursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("next_cursor"), NextCursor); + } + if (PrevCursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("prev_cursor"), PrevCursor); + } + + { + Json->SetNumberField(TEXT("rank_count"), RankCount); + } + return Json; +} + +FNakamaUpdateAccountRequest FNakamaUpdateAccountRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaUpdateAccountRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("username"))) + { + Result.Username = Json->GetStringField(TEXT("username")); + } + if (Json->HasField(TEXT("display_name"))) + { + Result.DisplayName = Json->GetStringField(TEXT("display_name")); + } + if (Json->HasField(TEXT("avatar_url"))) + { + Result.AvatarUrl = Json->GetStringField(TEXT("avatar_url")); + } + if (Json->HasField(TEXT("lang_tag"))) + { + Result.LangTag = Json->GetStringField(TEXT("lang_tag")); + } + if (Json->HasField(TEXT("location"))) + { + Result.Location = Json->GetStringField(TEXT("location")); + } + if (Json->HasField(TEXT("timezone"))) + { + Result.Timezone = Json->GetStringField(TEXT("timezone")); + } + return Result; +} + +TSharedPtr FNakamaUpdateAccountRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Username.IsEmpty() == false) + { + Json->SetStringField(TEXT("username"), Username); + } + if (DisplayName.IsEmpty() == false) + { + Json->SetStringField(TEXT("display_name"), DisplayName); + } + if (AvatarUrl.IsEmpty() == false) + { + Json->SetStringField(TEXT("avatar_url"), AvatarUrl); + } + if (LangTag.IsEmpty() == false) + { + Json->SetStringField(TEXT("lang_tag"), LangTag); + } + if (Location.IsEmpty() == false) + { + Json->SetStringField(TEXT("location"), Location); + } + if (Timezone.IsEmpty() == false) + { + Json->SetStringField(TEXT("timezone"), Timezone); + } + return Json; +} + +FNakamaUpdateGroupRequest FNakamaUpdateGroupRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaUpdateGroupRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("group_id"))) + { + Result.GroupId = Json->GetStringField(TEXT("group_id")); + } + if (Json->HasField(TEXT("name"))) + { + Result.Name = Json->GetStringField(TEXT("name")); + } + if (Json->HasField(TEXT("description"))) + { + Result.Description = Json->GetStringField(TEXT("description")); + } + if (Json->HasField(TEXT("lang_tag"))) + { + Result.LangTag = Json->GetStringField(TEXT("lang_tag")); + } + if (Json->HasField(TEXT("avatar_url"))) + { + Result.AvatarUrl = Json->GetStringField(TEXT("avatar_url")); + } + if (Json->HasField(TEXT("open"))) + { + Result.Open = Json->GetBoolField(TEXT("open")); + } + return Result; +} + +TSharedPtr FNakamaUpdateGroupRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (GroupId.IsEmpty() == false) + { + Json->SetStringField(TEXT("group_id"), GroupId); + } + if (Name.IsEmpty() == false) + { + Json->SetStringField(TEXT("name"), Name); + } + if (Description.IsEmpty() == false) + { + Json->SetStringField(TEXT("description"), Description); + } + if (LangTag.IsEmpty() == false) + { + Json->SetStringField(TEXT("lang_tag"), LangTag); + } + if (AvatarUrl.IsEmpty() == false) + { + Json->SetStringField(TEXT("avatar_url"), AvatarUrl); + } + if (Open.IsEmpty() == false) + { + Json->SetBoolField(TEXT("open"), Open.GetValue()); + } + return Json; +} + +FNakamaUserGroupListUserGroup FNakamaUserGroupListUserGroup::FromJson(const TSharedPtr& Json) +{ + FNakamaUserGroupListUserGroup Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("group"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("group"), NestedObj)) + { + Result.Group = (FNakamaGroup::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("state"))) + { + Result.State = Json->GetNumberField(TEXT("state")); + } + return Result; +} + +TSharedPtr FNakamaUserGroupListUserGroup::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetObjectField(TEXT("group"), Group.ToJson()); + } + if (State.IsEmpty() == false) + { + Json->SetNumberField(TEXT("state"), State.GetValue()); + } + return Json; +} + +FNakamaUserGroupList FNakamaUserGroupList::FromJson(const TSharedPtr& Json) +{ + FNakamaUserGroupList Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("user_groups"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("user_groups"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.UserGroups.Add(FNakamaUserGroupListUserGroup::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + return Result; +} + +TSharedPtr FNakamaUserGroupList::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (UserGroups.Num() > 0) + { + TArray> Array; + for (const auto& Item : UserGroups) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("user_groups"), Array); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + return Json; +} + +FNakamaUsers FNakamaUsers::FromJson(const TSharedPtr& Json) +{ + FNakamaUsers Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("users"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("users"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Users.Add(FNakamaUser::FromJson(*ItemObj)); + } + } + } + } + return Result; +} + +TSharedPtr FNakamaUsers::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Users.Num() > 0) + { + TArray> Array; + for (const auto& Item : Users) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("users"), Array); + } + return Json; +} + +FNakamaValidatePurchaseAppleRequest FNakamaValidatePurchaseAppleRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaValidatePurchaseAppleRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("receipt"))) + { + Result.Receipt = Json->GetStringField(TEXT("receipt")); + } + if (Json->HasField(TEXT("persist"))) + { + Result.Persist = Json->GetBoolField(TEXT("persist")); + } + return Result; +} + +TSharedPtr FNakamaValidatePurchaseAppleRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Receipt.IsEmpty() == false) + { + Json->SetStringField(TEXT("receipt"), Receipt); + } + if (Persist.IsEmpty() == false) + { + Json->SetBoolField(TEXT("persist"), Persist.GetValue()); + } + return Json; +} + +FNakamaValidateSubscriptionAppleRequest FNakamaValidateSubscriptionAppleRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaValidateSubscriptionAppleRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("receipt"))) + { + Result.Receipt = Json->GetStringField(TEXT("receipt")); + } + if (Json->HasField(TEXT("persist"))) + { + Result.Persist = Json->GetBoolField(TEXT("persist")); + } + return Result; +} + +TSharedPtr FNakamaValidateSubscriptionAppleRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Receipt.IsEmpty() == false) + { + Json->SetStringField(TEXT("receipt"), Receipt); + } + if (Persist.IsEmpty() == false) + { + Json->SetBoolField(TEXT("persist"), Persist.GetValue()); + } + return Json; +} + +FNakamaValidatePurchaseGoogleRequest FNakamaValidatePurchaseGoogleRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaValidatePurchaseGoogleRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("purchase"))) + { + Result.Purchase = Json->GetStringField(TEXT("purchase")); + } + if (Json->HasField(TEXT("persist"))) + { + Result.Persist = Json->GetBoolField(TEXT("persist")); + } + return Result; +} + +TSharedPtr FNakamaValidatePurchaseGoogleRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Purchase.IsEmpty() == false) + { + Json->SetStringField(TEXT("purchase"), Purchase); + } + if (Persist.IsEmpty() == false) + { + Json->SetBoolField(TEXT("persist"), Persist.GetValue()); + } + return Json; +} + +FNakamaValidateSubscriptionGoogleRequest FNakamaValidateSubscriptionGoogleRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaValidateSubscriptionGoogleRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("receipt"))) + { + Result.Receipt = Json->GetStringField(TEXT("receipt")); + } + if (Json->HasField(TEXT("persist"))) + { + Result.Persist = Json->GetBoolField(TEXT("persist")); + } + return Result; +} + +TSharedPtr FNakamaValidateSubscriptionGoogleRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Receipt.IsEmpty() == false) + { + Json->SetStringField(TEXT("receipt"), Receipt); + } + if (Persist.IsEmpty() == false) + { + Json->SetBoolField(TEXT("persist"), Persist.GetValue()); + } + return Json; +} + +FNakamaValidatePurchaseHuaweiRequest FNakamaValidatePurchaseHuaweiRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaValidatePurchaseHuaweiRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("purchase"))) + { + Result.Purchase = Json->GetStringField(TEXT("purchase")); + } + if (Json->HasField(TEXT("signature"))) + { + Result.Signature = Json->GetStringField(TEXT("signature")); + } + if (Json->HasField(TEXT("persist"))) + { + Result.Persist = Json->GetBoolField(TEXT("persist")); + } + return Result; +} + +TSharedPtr FNakamaValidatePurchaseHuaweiRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Purchase.IsEmpty() == false) + { + Json->SetStringField(TEXT("purchase"), Purchase); + } + if (Signature.IsEmpty() == false) + { + Json->SetStringField(TEXT("signature"), Signature); + } + if (Persist.IsEmpty() == false) + { + Json->SetBoolField(TEXT("persist"), Persist.GetValue()); + } + return Json; +} + +FNakamaValidatePurchaseFacebookInstantRequest FNakamaValidatePurchaseFacebookInstantRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaValidatePurchaseFacebookInstantRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("signed_request"))) + { + Result.SignedRequest = Json->GetStringField(TEXT("signed_request")); + } + if (Json->HasField(TEXT("persist"))) + { + Result.Persist = Json->GetBoolField(TEXT("persist")); + } + return Result; +} + +TSharedPtr FNakamaValidatePurchaseFacebookInstantRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (SignedRequest.IsEmpty() == false) + { + Json->SetStringField(TEXT("signed_request"), SignedRequest); + } + if (Persist.IsEmpty() == false) + { + Json->SetBoolField(TEXT("persist"), Persist.GetValue()); + } + return Json; +} + +FNakamaValidatedPurchase FNakamaValidatedPurchase::FromJson(const TSharedPtr& Json) +{ + FNakamaValidatedPurchase Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("user_id"))) + { + Result.UserId = Json->GetStringField(TEXT("user_id")); + } + if (Json->HasField(TEXT("product_id"))) + { + Result.ProductId = Json->GetStringField(TEXT("product_id")); + } + if (Json->HasField(TEXT("transaction_id"))) + { + Result.TransactionId = Json->GetStringField(TEXT("transaction_id")); + } + if (Json->HasField(TEXT("store"))) + { + Result.Store = static_cast(Json->GetNumberField(TEXT("store"))); + } + if (Json->HasField(TEXT("purchase_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("purchase_time")), Result.PurchaseTime); + } + if (Json->HasField(TEXT("create_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("create_time")), Result.CreateTime); + } + if (Json->HasField(TEXT("update_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("update_time")), Result.UpdateTime); + } + if (Json->HasField(TEXT("refund_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("refund_time")), Result.RefundTime); + } + if (Json->HasField(TEXT("provider_response"))) + { + Result.ProviderResponse = Json->GetStringField(TEXT("provider_response")); + } + if (Json->HasField(TEXT("environment"))) + { + Result.Environment = static_cast(Json->GetNumberField(TEXT("environment"))); + } + if (Json->HasField(TEXT("seen_before"))) + { + Result.SeenBefore = Json->GetBoolField(TEXT("seen_before")); + } + return Result; +} + +TSharedPtr FNakamaValidatedPurchase::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (UserId.IsEmpty() == false) + { + Json->SetStringField(TEXT("user_id"), UserId); + } + if (ProductId.IsEmpty() == false) + { + Json->SetStringField(TEXT("product_id"), ProductId); + } + if (TransactionId.IsEmpty() == false) + { + Json->SetStringField(TEXT("transaction_id"), TransactionId); + } + + { + Json->SetNumberField(TEXT("store"), static_cast(Store)); + } + + { + Json->SetStringField(TEXT("purchase_time"), PurchaseTime.ToIso8601()); + } + + { + Json->SetStringField(TEXT("create_time"), CreateTime.ToIso8601()); + } + + { + Json->SetStringField(TEXT("update_time"), UpdateTime.ToIso8601()); + } + + { + Json->SetStringField(TEXT("refund_time"), RefundTime.ToIso8601()); + } + if (ProviderResponse.IsEmpty() == false) + { + Json->SetStringField(TEXT("provider_response"), ProviderResponse); + } + + { + Json->SetNumberField(TEXT("environment"), static_cast(Environment)); + } + + { + Json->SetBoolField(TEXT("seen_before"), SeenBefore); + } + return Json; +} + +FNakamaValidatePurchaseResponse FNakamaValidatePurchaseResponse::FromJson(const TSharedPtr& Json) +{ + FNakamaValidatePurchaseResponse Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("validated_purchases"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("validated_purchases"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.ValidatedPurchases.Add(FNakamaValidatedPurchase::FromJson(*ItemObj)); + } + } + } + } + return Result; +} + +TSharedPtr FNakamaValidatePurchaseResponse::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (ValidatedPurchases.Num() > 0) + { + TArray> Array; + for (const auto& Item : ValidatedPurchases) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("validated_purchases"), Array); + } + return Json; +} + +FNakamaValidatedSubscription FNakamaValidatedSubscription::FromJson(const TSharedPtr& Json) +{ + FNakamaValidatedSubscription Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("user_id"))) + { + Result.UserId = Json->GetStringField(TEXT("user_id")); + } + if (Json->HasField(TEXT("product_id"))) + { + Result.ProductId = Json->GetStringField(TEXT("product_id")); + } + if (Json->HasField(TEXT("original_transaction_id"))) + { + Result.OriginalTransactionId = Json->GetStringField(TEXT("original_transaction_id")); + } + if (Json->HasField(TEXT("store"))) + { + Result.Store = static_cast(Json->GetNumberField(TEXT("store"))); + } + if (Json->HasField(TEXT("purchase_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("purchase_time")), Result.PurchaseTime); + } + if (Json->HasField(TEXT("create_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("create_time")), Result.CreateTime); + } + if (Json->HasField(TEXT("update_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("update_time")), Result.UpdateTime); + } + if (Json->HasField(TEXT("environment"))) + { + Result.Environment = static_cast(Json->GetNumberField(TEXT("environment"))); + } + if (Json->HasField(TEXT("expiry_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("expiry_time")), Result.ExpiryTime); + } + if (Json->HasField(TEXT("refund_time"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("refund_time")), Result.RefundTime); + } + if (Json->HasField(TEXT("provider_response"))) + { + Result.ProviderResponse = Json->GetStringField(TEXT("provider_response")); + } + if (Json->HasField(TEXT("provider_notification"))) + { + Result.ProviderNotification = Json->GetStringField(TEXT("provider_notification")); + } + if (Json->HasField(TEXT("active"))) + { + Result.Active = Json->GetBoolField(TEXT("active")); + } + return Result; +} + +TSharedPtr FNakamaValidatedSubscription::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (UserId.IsEmpty() == false) + { + Json->SetStringField(TEXT("user_id"), UserId); + } + if (ProductId.IsEmpty() == false) + { + Json->SetStringField(TEXT("product_id"), ProductId); + } + if (OriginalTransactionId.IsEmpty() == false) + { + Json->SetStringField(TEXT("original_transaction_id"), OriginalTransactionId); + } + + { + Json->SetNumberField(TEXT("store"), static_cast(Store)); + } + + { + Json->SetStringField(TEXT("purchase_time"), PurchaseTime.ToIso8601()); + } + + { + Json->SetStringField(TEXT("create_time"), CreateTime.ToIso8601()); + } + + { + Json->SetStringField(TEXT("update_time"), UpdateTime.ToIso8601()); + } + + { + Json->SetNumberField(TEXT("environment"), static_cast(Environment)); + } + + { + Json->SetStringField(TEXT("expiry_time"), ExpiryTime.ToIso8601()); + } + + { + Json->SetStringField(TEXT("refund_time"), RefundTime.ToIso8601()); + } + if (ProviderResponse.IsEmpty() == false) + { + Json->SetStringField(TEXT("provider_response"), ProviderResponse); + } + if (ProviderNotification.IsEmpty() == false) + { + Json->SetStringField(TEXT("provider_notification"), ProviderNotification); + } + + { + Json->SetBoolField(TEXT("active"), Active); + } + return Json; +} + +FNakamaValidateSubscriptionResponse FNakamaValidateSubscriptionResponse::FromJson(const TSharedPtr& Json) +{ + FNakamaValidateSubscriptionResponse Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("validated_subscription"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("validated_subscription"), NestedObj)) + { + Result.ValidatedSubscription = (FNakamaValidatedSubscription::FromJson(*NestedObj)); + } + } + return Result; +} + +TSharedPtr FNakamaValidateSubscriptionResponse::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetObjectField(TEXT("validated_subscription"), ValidatedSubscription.ToJson()); + } + return Json; +} + +FNakamaPurchaseList FNakamaPurchaseList::FromJson(const TSharedPtr& Json) +{ + FNakamaPurchaseList Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("validated_purchases"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("validated_purchases"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.ValidatedPurchases.Add(FNakamaValidatedPurchase::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + if (Json->HasField(TEXT("prev_cursor"))) + { + Result.PrevCursor = Json->GetStringField(TEXT("prev_cursor")); + } + return Result; +} + +TSharedPtr FNakamaPurchaseList::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (ValidatedPurchases.Num() > 0) + { + TArray> Array; + for (const auto& Item : ValidatedPurchases) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("validated_purchases"), Array); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + if (PrevCursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("prev_cursor"), PrevCursor); + } + return Json; +} + +FNakamaSubscriptionList FNakamaSubscriptionList::FromJson(const TSharedPtr& Json) +{ + FNakamaSubscriptionList Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("validated_subscriptions"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("validated_subscriptions"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.ValidatedSubscriptions.Add(FNakamaValidatedSubscription::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + if (Json->HasField(TEXT("prev_cursor"))) + { + Result.PrevCursor = Json->GetStringField(TEXT("prev_cursor")); + } + return Result; +} + +TSharedPtr FNakamaSubscriptionList::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (ValidatedSubscriptions.Num() > 0) + { + TArray> Array; + for (const auto& Item : ValidatedSubscriptions) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("validated_subscriptions"), Array); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + if (PrevCursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("prev_cursor"), PrevCursor); + } + return Json; +} + +FNakamaWriteLeaderboardRecordRequestLeaderboardRecordWrite FNakamaWriteLeaderboardRecordRequestLeaderboardRecordWrite::FromJson(const TSharedPtr& Json) +{ + FNakamaWriteLeaderboardRecordRequestLeaderboardRecordWrite Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("score"))) + { + Result.Score = Json->GetNumberField(TEXT("score")); + } + if (Json->HasField(TEXT("subscore"))) + { + Result.Subscore = Json->GetNumberField(TEXT("subscore")); + } + if (Json->HasField(TEXT("metadata"))) + { + Result.Metadata = Json->GetStringField(TEXT("metadata")); + } + if (Json->HasField(TEXT("operator"))) + { + Result.Operator = static_cast(Json->GetNumberField(TEXT("operator"))); + } + return Result; +} + +TSharedPtr FNakamaWriteLeaderboardRecordRequestLeaderboardRecordWrite::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetNumberField(TEXT("score"), Score); + } + + { + Json->SetNumberField(TEXT("subscore"), Subscore); + } + if (Metadata.IsEmpty() == false) + { + Json->SetStringField(TEXT("metadata"), Metadata); + } + + { + Json->SetNumberField(TEXT("operator"), static_cast(Operator)); + } + return Json; +} + +FNakamaWriteLeaderboardRecordRequest FNakamaWriteLeaderboardRecordRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaWriteLeaderboardRecordRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("leaderboard_id"))) + { + Result.LeaderboardId = Json->GetStringField(TEXT("leaderboard_id")); + } + if (Json->HasField(TEXT("record"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("record"), NestedObj)) + { + Result.Record = (FNakamaWriteLeaderboardRecordRequestLeaderboardRecordWrite::FromJson(*NestedObj)); + } + } + return Result; +} + +TSharedPtr FNakamaWriteLeaderboardRecordRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (LeaderboardId.IsEmpty() == false) + { + Json->SetStringField(TEXT("leaderboard_id"), LeaderboardId); + } + + { + Json->SetObjectField(TEXT("record"), Record.ToJson()); + } + return Json; +} + +FNakamaWriteStorageObject FNakamaWriteStorageObject::FromJson(const TSharedPtr& Json) +{ + FNakamaWriteStorageObject Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("collection"))) + { + Result.Collection = Json->GetStringField(TEXT("collection")); + } + if (Json->HasField(TEXT("key"))) + { + Result.Key = Json->GetStringField(TEXT("key")); + } + if (Json->HasField(TEXT("value"))) + { + Result.Value = Json->GetStringField(TEXT("value")); + } + if (Json->HasField(TEXT("version"))) + { + Result.Version = Json->GetStringField(TEXT("version")); + } + if (Json->HasField(TEXT("permission_read"))) + { + Result.PermissionRead = Json->GetNumberField(TEXT("permission_read")); + } + if (Json->HasField(TEXT("permission_write"))) + { + Result.PermissionWrite = Json->GetNumberField(TEXT("permission_write")); + } + return Result; +} + +TSharedPtr FNakamaWriteStorageObject::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Collection.IsEmpty() == false) + { + Json->SetStringField(TEXT("collection"), Collection); + } + if (Key.IsEmpty() == false) + { + Json->SetStringField(TEXT("key"), Key); + } + if (Value.IsEmpty() == false) + { + Json->SetStringField(TEXT("value"), Value); + } + if (Version.IsEmpty() == false) + { + Json->SetStringField(TEXT("version"), Version); + } + if (PermissionRead.IsEmpty() == false) + { + Json->SetNumberField(TEXT("permission_read"), PermissionRead.GetValue()); + } + if (PermissionWrite.IsEmpty() == false) + { + Json->SetNumberField(TEXT("permission_write"), PermissionWrite.GetValue()); + } + return Json; +} + +FNakamaWriteStorageObjectsRequest FNakamaWriteStorageObjectsRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaWriteStorageObjectsRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("objects"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("objects"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Objects.Add(FNakamaWriteStorageObject::FromJson(*ItemObj)); + } + } + } + } + return Result; +} + +TSharedPtr FNakamaWriteStorageObjectsRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Objects.Num() > 0) + { + TArray> Array; + for (const auto& Item : Objects) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("objects"), Array); + } + return Json; +} + +FNakamaWriteTournamentRecordRequestTournamentRecordWrite FNakamaWriteTournamentRecordRequestTournamentRecordWrite::FromJson(const TSharedPtr& Json) +{ + FNakamaWriteTournamentRecordRequestTournamentRecordWrite Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("score"))) + { + Result.Score = Json->GetNumberField(TEXT("score")); + } + if (Json->HasField(TEXT("subscore"))) + { + Result.Subscore = Json->GetNumberField(TEXT("subscore")); + } + if (Json->HasField(TEXT("metadata"))) + { + Result.Metadata = Json->GetStringField(TEXT("metadata")); + } + if (Json->HasField(TEXT("operator"))) + { + Result.Operator = static_cast(Json->GetNumberField(TEXT("operator"))); + } + return Result; +} + +TSharedPtr FNakamaWriteTournamentRecordRequestTournamentRecordWrite::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetNumberField(TEXT("score"), Score); + } + + { + Json->SetNumberField(TEXT("subscore"), Subscore); + } + if (Metadata.IsEmpty() == false) + { + Json->SetStringField(TEXT("metadata"), Metadata); + } + + { + Json->SetNumberField(TEXT("operator"), static_cast(Operator)); + } + return Json; +} + +FNakamaWriteTournamentRecordRequest FNakamaWriteTournamentRecordRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaWriteTournamentRecordRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("tournament_id"))) + { + Result.TournamentId = Json->GetStringField(TEXT("tournament_id")); + } + if (Json->HasField(TEXT("record"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("record"), NestedObj)) + { + Result.Record = (FNakamaWriteTournamentRecordRequestTournamentRecordWrite::FromJson(*NestedObj)); + } + } + return Result; +} + +TSharedPtr FNakamaWriteTournamentRecordRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (TournamentId.IsEmpty() == false) + { + Json->SetStringField(TEXT("tournament_id"), TournamentId); + } + + { + Json->SetObjectField(TEXT("record"), Record.ToJson()); + } + return Json; +} + +FNakamaListPartiesRequest FNakamaListPartiesRequest::FromJson(const TSharedPtr& Json) +{ + FNakamaListPartiesRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("limit"))) + { + Result.Limit = Json->GetNumberField(TEXT("limit")); + } + if (Json->HasField(TEXT("open"))) + { + Result.Open = Json->GetBoolField(TEXT("open")); + } + if (Json->HasField(TEXT("query"))) + { + Result.Query = Json->GetStringField(TEXT("query")); + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + return Result; +} + +TSharedPtr FNakamaListPartiesRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Limit.IsEmpty() == false) + { + Json->SetNumberField(TEXT("limit"), Limit.GetValue()); + } + if (Open.IsEmpty() == false) + { + Json->SetBoolField(TEXT("open"), Open.GetValue()); + } + if (Query.IsEmpty() == false) + { + Json->SetStringField(TEXT("query"), Query); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + return Json; +} + +FNakamaParty FNakamaParty::FromJson(const TSharedPtr& Json) +{ + FNakamaParty Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("party_id"))) + { + Result.PartyId = Json->GetStringField(TEXT("party_id")); + } + if (Json->HasField(TEXT("open"))) + { + Result.Open = Json->GetBoolField(TEXT("open")); + } + if (Json->HasField(TEXT("hidden"))) + { + Result.Hidden = Json->GetBoolField(TEXT("hidden")); + } + if (Json->HasField(TEXT("max_size"))) + { + Result.MaxSize = Json->GetNumberField(TEXT("max_size")); + } + if (Json->HasField(TEXT("label"))) + { + Result.Label = Json->GetStringField(TEXT("label")); + } + return Result; +} + +TSharedPtr FNakamaParty::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (PartyId.IsEmpty() == false) + { + Json->SetStringField(TEXT("party_id"), PartyId); + } + + { + Json->SetBoolField(TEXT("open"), Open); + } + + { + Json->SetBoolField(TEXT("hidden"), Hidden); + } + + { + Json->SetNumberField(TEXT("max_size"), MaxSize); + } + if (Label.IsEmpty() == false) + { + Json->SetStringField(TEXT("label"), Label); + } + return Json; +} + +FNakamaPartyList FNakamaPartyList::FromJson(const TSharedPtr& Json) +{ + FNakamaPartyList Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("parties"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("parties"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Parties.Add(FNakamaParty::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + return Result; +} + +TSharedPtr FNakamaPartyList::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Parties.Num() > 0) + { + TArray> Array; + for (const auto& Item : Parties) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("parties"), Array); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + return Json; +} diff --git a/Nakama/Source/NakamaApi/Public/NakamaApi.h b/Nakama/Source/NakamaApi/Public/NakamaApi.h new file mode 100644 index 000000000..5f9612ad6 --- /dev/null +++ b/Nakama/Source/NakamaApi/Public/NakamaApi.h @@ -0,0 +1,3853 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + +#pragma once + +#include +#include "CoreMinimal.h" +#include "Templates/Function.h" +#include "Templates/SharedPointer.h" +#include "Dom/JsonObject.h" +#include "HAL/ThreadSafeBool.h" +#include "NakamaError.h" +#include "NakamaClientConfig.h" +#include "NakamaSession.h" +#include "NakamaTypes.h" + +NAKAMAAPI_API DECLARE_LOG_CATEGORY_EXTERN(LogNakama, Log, All); + +namespace NakamaApi +{ + + /* + * Add friends by ID or username to a user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Ids The account id of a user. + * @param Usernames The account username of a user. + * @param Metadata Optional metadata to add to friends. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void AddFriends( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const TArray& Ids, + const TArray& Usernames, + const FString& Metadata, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Add friends by ID or username to a user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Ids The account id of a user. + * @param Usernames The account username of a user. + * @param Metadata Optional metadata to add to friends. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void AddFriends( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const TArray& Ids, + const TArray& Usernames, + const FString& Metadata, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Add users to a group. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param GroupId The group to add users to. + * @param UserIds The users to add. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void AddGroupUsers( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& GroupId, + const TArray& UserIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Add users to a group. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param GroupId The group to add users to. + * @param UserIds The users to add. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void AddGroupUsers( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& GroupId, + const TArray& UserIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Refresh a user's session using a refresh token retrieved from a previous authentication request. + * + * @param Config The client configuration. + * @param Token Refresh token. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void SessionRefresh( + const FNakamaClientConfig& Config, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Log out a session, invalidate a refresh token, or log out all sessions/refresh tokens for a user. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Token Session token to log out. + * @param RefreshToken Refresh token to invalidate. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void SessionLogout( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Token, + const FString& RefreshToken, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Log out a session, invalidate a refresh token, or log out all sessions/refresh tokens for a user. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Token Session token to log out. + * @param RefreshToken Refresh token to invalidate. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void SessionLogout( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Token, + const FString& RefreshToken, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Authenticate a user with an Apple ID against the server. + * + * @param Config The client configuration. + * @param Account The Apple account details. + * @param Create Register the account if the user does not already exist. + * @param Username Set the username on the account at register. Must be unique. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void AuthenticateApple( + const FNakamaClientConfig& Config, + const FNakamaAccountApple& Account, + FNakamaOptionalBool Create, + const FString& Username, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Authenticate a user with a custom id against the server. + * + * @param Config The client configuration. + * @param Account The custom account details. + * @param Create Register the account if the user does not already exist. + * @param Username Set the username on the account at register. Must be unique. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void AuthenticateCustom( + const FNakamaClientConfig& Config, + const FNakamaAccountCustom& Account, + FNakamaOptionalBool Create, + const FString& Username, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Authenticate a user with a device id against the server. + * + * @param Config The client configuration. + * @param Account The device account details. + * @param Create Register the account if the user does not already exist. + * @param Username Set the username on the account at register. Must be unique. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void AuthenticateDevice( + const FNakamaClientConfig& Config, + const FNakamaAccountDevice& Account, + FNakamaOptionalBool Create, + const FString& Username, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Authenticate a user with an email+password against the server. + * + * @param Config The client configuration. + * @param Account The email account details. + * @param Create Register the account if the user does not already exist. + * @param Username Set the username on the account at register. Must be unique. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void AuthenticateEmail( + const FNakamaClientConfig& Config, + const FNakamaAccountEmail& Account, + FNakamaOptionalBool Create, + const FString& Username, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Authenticate a user with a Facebook OAuth token against the server. + * + * @param Config The client configuration. + * @param Account The Facebook account details. + * @param Create Register the account if the user does not already exist. + * @param Username Set the username on the account at register. Must be unique. + * @param Sync Import Facebook friends for the user. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void AuthenticateFacebook( + const FNakamaClientConfig& Config, + const FNakamaAccountFacebook& Account, + FNakamaOptionalBool Create, + const FString& Username, + FNakamaOptionalBool Sync, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Authenticate a user with a Facebook Instant Game token against the server. + * + * @param Config The client configuration. + * @param Account The Facebook Instant Game account details. + * @param Create Register the account if the user does not already exist. + * @param Username Set the username on the account at register. Must be unique. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void AuthenticateFacebookInstantGame( + const FNakamaClientConfig& Config, + const FNakamaAccountFacebookInstantGame& Account, + FNakamaOptionalBool Create, + const FString& Username, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Authenticate a user with Apple's GameCenter against the server. + * + * @param Config The client configuration. + * @param Account The Game Center account details. + * @param Create Register the account if the user does not already exist. + * @param Username Set the username on the account at register. Must be unique. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void AuthenticateGameCenter( + const FNakamaClientConfig& Config, + const FNakamaAccountGameCenter& Account, + FNakamaOptionalBool Create, + const FString& Username, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Authenticate a user with Google against the server. + * + * @param Config The client configuration. + * @param Account The Google account details. + * @param Create Register the account if the user does not already exist. + * @param Username Set the username on the account at register. Must be unique. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void AuthenticateGoogle( + const FNakamaClientConfig& Config, + const FNakamaAccountGoogle& Account, + FNakamaOptionalBool Create, + const FString& Username, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Authenticate a user with Steam against the server. + * + * @param Config The client configuration. + * @param Account The Steam account details. + * @param Create Register the account if the user does not already exist. + * @param Username Set the username on the account at register. Must be unique. + * @param Sync Import Steam friends for the user. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void AuthenticateSteam( + const FNakamaClientConfig& Config, + const FNakamaAccountSteam& Account, + FNakamaOptionalBool Create, + const FString& Username, + FNakamaOptionalBool Sync, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Ban a set of users from a group. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param GroupId The group to ban users from. + * @param UserIds The users to ban. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void BanGroupUsers( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& GroupId, + const TArray& UserIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Ban a set of users from a group. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param GroupId The group to ban users from. + * @param UserIds The users to ban. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void BanGroupUsers( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& GroupId, + const TArray& UserIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Block one or more users by ID or username. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Ids The account id of a user. + * @param Usernames The account username of a user. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void BlockFriends( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const TArray& Ids, + const TArray& Usernames, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Block one or more users by ID or username. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Ids The account id of a user. + * @param Usernames The account username of a user. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void BlockFriends( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const TArray& Ids, + const TArray& Usernames, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Create a new group with the current user as the owner. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Name A unique name for the group. + * @param Description A description for the group. + * @param LangTag The language expected to be a tag which follows the BCP-47 spec. + * @param AvatarUrl A URL for an avatar image. + * @param Open Mark a group as open or not where only admins can accept members. + * @param MaxCount Maximum number of group members. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void CreateGroup( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Name, + const FString& Description, + const FString& LangTag, + const FString& AvatarUrl, + bool Open, + int32 MaxCount, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Create a new group with the current user as the owner. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Name A unique name for the group. + * @param Description A description for the group. + * @param LangTag The language expected to be a tag which follows the BCP-47 spec. + * @param AvatarUrl A URL for an avatar image. + * @param Open Mark a group as open or not where only admins can accept members. + * @param MaxCount Maximum number of group members. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void CreateGroup( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Name, + const FString& Description, + const FString& LangTag, + const FString& AvatarUrl, + bool Open, + int32 MaxCount, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Delete the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void DeleteAccount( + const FNakamaClientConfig& Config, + const FString& HttpKey, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Delete the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void DeleteAccount( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Delete one or more users by ID or username. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Ids The account id of a user. + * @param Usernames The account username of a user. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void DeleteFriends( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const TArray& Ids, + const TArray& Usernames, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Delete one or more users by ID or username. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Ids The account id of a user. + * @param Usernames The account username of a user. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void DeleteFriends( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const TArray& Ids, + const TArray& Usernames, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Delete a group by ID. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param GroupId The id of a group. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void DeleteGroup( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& GroupId, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Delete a group by ID. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param GroupId The id of a group. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void DeleteGroup( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& GroupId, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Delete a leaderboard record. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param LeaderboardId The leaderboard ID to delete from. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void DeleteLeaderboardRecord( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& LeaderboardId, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Delete a leaderboard record. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param LeaderboardId The leaderboard ID to delete from. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void DeleteLeaderboardRecord( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& LeaderboardId, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Delete one or more notifications for the current user. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Ids The id of notifications. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void DeleteNotifications( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const TArray& Ids, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Delete one or more notifications for the current user. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Ids The id of notifications. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void DeleteNotifications( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const TArray& Ids, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Delete a tournament record. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param TournamentId The tournament ID to delete from. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void DeleteTournamentRecord( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& TournamentId, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Delete a tournament record. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param TournamentId The tournament ID to delete from. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void DeleteTournamentRecord( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& TournamentId, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Delete one or more objects by ID or username. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param ObjectIds Batch of storage objects. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void DeleteStorageObjects( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const TArray& ObjectIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Delete one or more objects by ID or username. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param ObjectIds Batch of storage objects. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void DeleteStorageObjects( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const TArray& ObjectIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Submit an event for processing in the server's registered runtime custom events handler. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Name An event name, type, category, or identifier. + * @param Timestamp The time when the event was triggered. + * @param External True if the event came directly from a client call, false otherwise. + * @param Properties Arbitrary event property values. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void Event( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Name, + const FDateTime& Timestamp, + bool External, + const TMap& Properties, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Submit an event for processing in the server's registered runtime custom events handler. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Name An event name, type, category, or identifier. + * @param Timestamp The time when the event was triggered. + * @param External True if the event came directly from a client call, false otherwise. + * @param Properties Arbitrary event property values. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void Event( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Name, + const FDateTime& Timestamp, + bool External, + const TMap& Properties, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Fetch the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void GetAccount( + const FNakamaClientConfig& Config, + const FString& HttpKey, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Fetch the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void GetAccount( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Fetch zero or more users by ID and/or username. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Ids The account id of a user. + * @param Usernames The account username of a user. + * @param FacebookIds The Facebook ID of a user. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void GetUsers( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const TArray& Ids, + const TArray& Usernames, + const TArray& FacebookIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Fetch zero or more users by ID and/or username. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Ids The account id of a user. + * @param Usernames The account username of a user. + * @param FacebookIds The Facebook ID of a user. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void GetUsers( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const TArray& Ids, + const TArray& Usernames, + const TArray& FacebookIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Get subscription by product id. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param ProductId Product id of the subscription + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void GetSubscription( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& ProductId, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Get subscription by product id. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param ProductId Product id of the subscription + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void GetSubscription( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& ProductId, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Get matchmaker stats. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void GetMatchmakerStats( + const FNakamaClientConfig& Config, + const FString& HttpKey, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Get matchmaker stats. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void GetMatchmakerStats( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * A healthcheck which load balancers can use to check the service. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void Healthcheck( + const FNakamaClientConfig& Config, + const FString& HttpKey, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * A healthcheck which load balancers can use to check the service. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void Healthcheck( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Import Facebook friends and add them to a user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Account The Facebook account details. + * @param Reset Reset the current user's friends list. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ImportFacebookFriends( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FNakamaAccountFacebook& Account, + FNakamaOptionalBool Reset, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Import Facebook friends and add them to a user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Account The Facebook account details. + * @param Reset Reset the current user's friends list. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ImportFacebookFriends( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FNakamaAccountFacebook& Account, + FNakamaOptionalBool Reset, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Import Steam friends and add them to a user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Account The Facebook account details. + * @param Reset Reset the current user's friends list. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ImportSteamFriends( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FNakamaAccountSteam& Account, + FNakamaOptionalBool Reset, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Import Steam friends and add them to a user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Account The Facebook account details. + * @param Reset Reset the current user's friends list. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ImportSteamFriends( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FNakamaAccountSteam& Account, + FNakamaOptionalBool Reset, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Immediately join an open group, or request to join a closed one. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param GroupId The group ID to join. The group must already exist. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void JoinGroup( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& GroupId, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Immediately join an open group, or request to join a closed one. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param GroupId The group ID to join. The group must already exist. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void JoinGroup( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& GroupId, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Attempt to join an open and running tournament. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param TournamentId The ID of the tournament to join. The tournament must already exist. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void JoinTournament( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& TournamentId, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Attempt to join an open and running tournament. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param TournamentId The ID of the tournament to join. The tournament must already exist. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void JoinTournament( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& TournamentId, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Kick a set of users from a group. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param GroupId The group ID to kick from. + * @param UserIds The users to kick. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void KickGroupUsers( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& GroupId, + const TArray& UserIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Kick a set of users from a group. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param GroupId The group ID to kick from. + * @param UserIds The users to kick. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void KickGroupUsers( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& GroupId, + const TArray& UserIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Leave a group the user is a member of. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param GroupId The group ID to leave. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void LeaveGroup( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& GroupId, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Leave a group the user is a member of. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param GroupId The group ID to leave. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void LeaveGroup( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& GroupId, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Add an Apple ID to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Token The ID token received from Apple to validate. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void LinkApple( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Add an Apple ID to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Token The ID token received from Apple to validate. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void LinkApple( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Add a custom ID to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Id A custom identifier. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void LinkCustom( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Id, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Add a custom ID to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Id A custom identifier. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void LinkCustom( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Id, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Add a device ID to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Id A device identifier. Should be obtained by a platform-specific device API. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void LinkDevice( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Id, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Add a device ID to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Id A device identifier. Should be obtained by a platform-specific device API. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void LinkDevice( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Id, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Add an email+password to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Email A valid RFC-5322 email address. + * @param Password A password for the user account. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void LinkEmail( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Email, + const FString& Password, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Add an email+password to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Email A valid RFC-5322 email address. + * @param Password A password for the user account. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void LinkEmail( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Email, + const FString& Password, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Add Facebook to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Account The Facebook account details. + * @param Sync Import Facebook friends for the user. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void LinkFacebook( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FNakamaAccountFacebook& Account, + FNakamaOptionalBool Sync, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Add Facebook to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Account The Facebook account details. + * @param Sync Import Facebook friends for the user. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void LinkFacebook( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FNakamaAccountFacebook& Account, + FNakamaOptionalBool Sync, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Add Facebook Instant Game to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param SignedPlayerInfo The OAuth token received from a Facebook Instant Game that may be decoded with the Application Secret (must be available with the nakama configuration) + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void LinkFacebookInstantGame( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& SignedPlayerInfo, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Add Facebook Instant Game to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param SignedPlayerInfo The OAuth token received from a Facebook Instant Game that may be decoded with the Application Secret (must be available with the nakama configuration) + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void LinkFacebookInstantGame( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& SignedPlayerInfo, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Add Apple's GameCenter to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param PlayerId Player ID (generated by GameCenter). + * @param BundleId Bundle ID (generated by GameCenter). + * @param TimestampSeconds Time since UNIX epoch when the signature was created. + * @param Salt A random "NSString" used to compute the hash and keep it randomized. + * @param Signature The verification signature data generated. + * @param PublicKeyUrl The URL for the public encryption key. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void LinkGameCenter( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& PlayerId, + const FString& BundleId, + int64 TimestampSeconds, + const FString& Salt, + const FString& Signature, + const FString& PublicKeyUrl, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Add Apple's GameCenter to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param PlayerId Player ID (generated by GameCenter). + * @param BundleId Bundle ID (generated by GameCenter). + * @param TimestampSeconds Time since UNIX epoch when the signature was created. + * @param Salt A random "NSString" used to compute the hash and keep it randomized. + * @param Signature The verification signature data generated. + * @param PublicKeyUrl The URL for the public encryption key. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void LinkGameCenter( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& PlayerId, + const FString& BundleId, + int64 TimestampSeconds, + const FString& Salt, + const FString& Signature, + const FString& PublicKeyUrl, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Add Google to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Token The OAuth token received from Google to access their profile API. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void LinkGoogle( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Add Google to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Token The OAuth token received from Google to access their profile API. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void LinkGoogle( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Add Steam to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Account The Facebook account details. + * @param Sync Import Steam friends for the user. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void LinkSteam( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FNakamaAccountSteam& Account, + FNakamaOptionalBool Sync, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Add Steam to the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Account The Facebook account details. + * @param Sync Import Steam friends for the user. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void LinkSteam( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FNakamaAccountSteam& Account, + FNakamaOptionalBool Sync, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List a channel's message history. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param ChannelId The channel ID to list from. + * @param Limit Max number of records to return. Between 1 and 100. + * @param Forward True if listing should be older messages to newer, false if reverse. + * @param Cursor A pagination cursor, if any. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListChannelMessages( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& ChannelId, + FNakamaOptionalInt32 Limit, + FNakamaOptionalBool Forward, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List a channel's message history. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param ChannelId The channel ID to list from. + * @param Limit Max number of records to return. Between 1 and 100. + * @param Forward True if listing should be older messages to newer, false if reverse. + * @param Cursor A pagination cursor, if any. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListChannelMessages( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& ChannelId, + FNakamaOptionalInt32 Limit, + FNakamaOptionalBool Forward, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List all friends for the current user. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Limit Max number of records to return. Between 1 and 1000. + * @param State The friend state to list. + * @param Cursor An optional next page cursor. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListFriends( + const FNakamaClientConfig& Config, + const FString& HttpKey, + FNakamaOptionalInt32 Limit, + FNakamaOptionalInt32 State, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List all friends for the current user. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Limit Max number of records to return. Between 1 and 1000. + * @param State The friend state to list. + * @param Cursor An optional next page cursor. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListFriends( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + FNakamaOptionalInt32 Limit, + FNakamaOptionalInt32 State, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List friends of friends for the current user. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Limit Max number of records to return. Between 1 and 100. + * @param Cursor An optional next page cursor. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListFriendsOfFriends( + const FNakamaClientConfig& Config, + const FString& HttpKey, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List friends of friends for the current user. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Limit Max number of records to return. Between 1 and 100. + * @param Cursor An optional next page cursor. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListFriendsOfFriends( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List groups based on given filters. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Name List groups that contain this value in their names. + * @param Cursor Optional pagination cursor. + * @param Limit Max number of groups to return. Between 1 and 100. + * @param LangTag Language tag filter + * @param Members Number of group members + * @param Open Optional Open/Closed filter. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListGroups( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Name, + const FString& Cursor, + FNakamaOptionalInt32 Limit, + const FString& LangTag, + FNakamaOptionalInt32 Members, + FNakamaOptionalBool Open, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List groups based on given filters. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Name List groups that contain this value in their names. + * @param Cursor Optional pagination cursor. + * @param Limit Max number of groups to return. Between 1 and 100. + * @param LangTag Language tag filter + * @param Members Number of group members + * @param Open Optional Open/Closed filter. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListGroups( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Name, + const FString& Cursor, + FNakamaOptionalInt32 Limit, + const FString& LangTag, + FNakamaOptionalInt32 Members, + FNakamaOptionalBool Open, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List all users that are part of a group. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param GroupId The group ID to list from. + * @param Limit Max number of records to return. Between 1 and 100. + * @param State The group user state to list. + * @param Cursor An optional next page cursor. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListGroupUsers( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& GroupId, + FNakamaOptionalInt32 Limit, + FNakamaOptionalInt32 State, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List all users that are part of a group. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param GroupId The group ID to list from. + * @param Limit Max number of records to return. Between 1 and 100. + * @param State The group user state to list. + * @param Cursor An optional next page cursor. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListGroupUsers( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& GroupId, + FNakamaOptionalInt32 Limit, + FNakamaOptionalInt32 State, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List leaderboard records. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param LeaderboardId The ID of the leaderboard to list for. + * @param OwnerIds One or more owners to retrieve records for. + * @param Limit Max number of records to return. Between 1 and 100. + * @param Cursor A next or previous page cursor. + * @param Expiry Expiry in seconds (since epoch) to begin fetching records from. Optional. 0 means from current time. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListLeaderboardRecords( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& LeaderboardId, + const TArray& OwnerIds, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + FNakamaOptionalInt64 Expiry, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List leaderboard records. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param LeaderboardId The ID of the leaderboard to list for. + * @param OwnerIds One or more owners to retrieve records for. + * @param Limit Max number of records to return. Between 1 and 100. + * @param Cursor A next or previous page cursor. + * @param Expiry Expiry in seconds (since epoch) to begin fetching records from. Optional. 0 means from current time. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListLeaderboardRecords( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& LeaderboardId, + const TArray& OwnerIds, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + FNakamaOptionalInt64 Expiry, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List leaderboard records around the target ownerId. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param LeaderboardId The ID of the tournament to list for. + * @param Limit Max number of records to return. Between 1 and 100. + * @param OwnerId The owner to retrieve records around. + * @param Expiry Expiry in seconds (since epoch) to begin fetching records from. + * @param Cursor A next or previous page cursor. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListLeaderboardRecordsAroundOwner( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& LeaderboardId, + FNakamaOptionalInt32 Limit, + const FString& OwnerId, + FNakamaOptionalInt64 Expiry, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List leaderboard records around the target ownerId. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param LeaderboardId The ID of the tournament to list for. + * @param Limit Max number of records to return. Between 1 and 100. + * @param OwnerId The owner to retrieve records around. + * @param Expiry Expiry in seconds (since epoch) to begin fetching records from. + * @param Cursor A next or previous page cursor. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListLeaderboardRecordsAroundOwner( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& LeaderboardId, + FNakamaOptionalInt32 Limit, + const FString& OwnerId, + FNakamaOptionalInt64 Expiry, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List running matches and optionally filter by matching criteria. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Limit Limit the number of returned matches. + * @param Authoritative Authoritative or relayed matches. + * @param Label Label filter. + * @param MinSize Minimum user count. + * @param MaxSize Maximum user count. + * @param Query Arbitrary label query. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListMatches( + const FNakamaClientConfig& Config, + const FString& HttpKey, + FNakamaOptionalInt32 Limit, + FNakamaOptionalBool Authoritative, + const FString& Label, + FNakamaOptionalInt32 MinSize, + FNakamaOptionalInt32 MaxSize, + const FString& Query, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List running matches and optionally filter by matching criteria. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Limit Limit the number of returned matches. + * @param Authoritative Authoritative or relayed matches. + * @param Label Label filter. + * @param MinSize Minimum user count. + * @param MaxSize Maximum user count. + * @param Query Arbitrary label query. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListMatches( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + FNakamaOptionalInt32 Limit, + FNakamaOptionalBool Authoritative, + const FString& Label, + FNakamaOptionalInt32 MinSize, + FNakamaOptionalInt32 MaxSize, + const FString& Query, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List parties and optionally filter by matching criteria. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Limit Limit the number of returned parties. + * @param Open Optionally filter by open/closed parties. + * @param Query Arbitrary label query. + * @param Cursor Cursor for the next page of results, if any. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListParties( + const FNakamaClientConfig& Config, + const FString& HttpKey, + FNakamaOptionalInt32 Limit, + FNakamaOptionalBool Open, + const FString& Query, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List parties and optionally filter by matching criteria. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Limit Limit the number of returned parties. + * @param Open Optionally filter by open/closed parties. + * @param Query Arbitrary label query. + * @param Cursor Cursor for the next page of results, if any. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListParties( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + FNakamaOptionalInt32 Limit, + FNakamaOptionalBool Open, + const FString& Query, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Fetch list of notifications. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Limit The number of notifications to get. Between 1 and 100. + * @param CacheableCursor A cursor to page through notifications. May be cached by clients to get from point in time forwards. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListNotifications( + const FNakamaClientConfig& Config, + const FString& HttpKey, + FNakamaOptionalInt32 Limit, + const FString& CacheableCursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Fetch list of notifications. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Limit The number of notifications to get. Between 1 and 100. + * @param CacheableCursor A cursor to page through notifications. May be cached by clients to get from point in time forwards. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListNotifications( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + FNakamaOptionalInt32 Limit, + const FString& CacheableCursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List publicly readable storage objects in a given collection. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param UserId ID of the user. + * @param Collection The collection which stores the object. + * @param Limit The number of storage objects to list. Between 1 and 100. + * @param Cursor The cursor to page through results from. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListStorageObjects( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& UserId, + const FString& Collection, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List publicly readable storage objects in a given collection. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param UserId ID of the user. + * @param Collection The collection which stores the object. + * @param Limit The number of storage objects to list. Between 1 and 100. + * @param Cursor The cursor to page through results from. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListStorageObjects( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& UserId, + const FString& Collection, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List user's subscriptions. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Limit Max number of results per page + * @param Cursor Cursor to retrieve a page of records from + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListSubscriptions( + const FNakamaClientConfig& Config, + const FString& HttpKey, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List user's subscriptions. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Limit Max number of results per page + * @param Cursor Cursor to retrieve a page of records from + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListSubscriptions( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List current or upcoming tournaments. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param CategoryStart The start of the categories to include. Defaults to 0. + * @param CategoryEnd The end of the categories to include. Defaults to 128. + * @param StartTime The start time for tournaments. Defaults to epoch. + * @param EndTime The end time for tournaments. Defaults to +1 year from current Unix time. + * @param Limit Max number of records to return. Between 1 and 100. + * @param Cursor A next page cursor for listings (optional). + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListTournaments( + const FNakamaClientConfig& Config, + const FString& HttpKey, + FNakamaOptionalInt32 CategoryStart, + FNakamaOptionalInt32 CategoryEnd, + FNakamaOptionalInt32 StartTime, + FNakamaOptionalInt32 EndTime, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List current or upcoming tournaments. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param CategoryStart The start of the categories to include. Defaults to 0. + * @param CategoryEnd The end of the categories to include. Defaults to 128. + * @param StartTime The start time for tournaments. Defaults to epoch. + * @param EndTime The end time for tournaments. Defaults to +1 year from current Unix time. + * @param Limit Max number of records to return. Between 1 and 100. + * @param Cursor A next page cursor for listings (optional). + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListTournaments( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + FNakamaOptionalInt32 CategoryStart, + FNakamaOptionalInt32 CategoryEnd, + FNakamaOptionalInt32 StartTime, + FNakamaOptionalInt32 EndTime, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List tournament records. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param TournamentId The ID of the tournament to list for. + * @param OwnerIds One or more owners to retrieve records for. + * @param Limit Max number of records to return. Between 1 and 100. + * @param Cursor A next or previous page cursor. + * @param Expiry Expiry in seconds (since epoch) to begin fetching records from. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListTournamentRecords( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& TournamentId, + const TArray& OwnerIds, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + FNakamaOptionalInt64 Expiry, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List tournament records. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param TournamentId The ID of the tournament to list for. + * @param OwnerIds One or more owners to retrieve records for. + * @param Limit Max number of records to return. Between 1 and 100. + * @param Cursor A next or previous page cursor. + * @param Expiry Expiry in seconds (since epoch) to begin fetching records from. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListTournamentRecords( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& TournamentId, + const TArray& OwnerIds, + FNakamaOptionalInt32 Limit, + const FString& Cursor, + FNakamaOptionalInt64 Expiry, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List tournament records for a given owner. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param TournamentId The ID of the tournament to list for. + * @param Limit Max number of records to return. Between 1 and 100. + * @param OwnerId The owner to retrieve records around. + * @param Expiry Expiry in seconds (since epoch) to begin fetching records from. + * @param Cursor A next or previous page cursor. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListTournamentRecordsAroundOwner( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& TournamentId, + FNakamaOptionalInt32 Limit, + const FString& OwnerId, + FNakamaOptionalInt64 Expiry, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List tournament records for a given owner. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param TournamentId The ID of the tournament to list for. + * @param Limit Max number of records to return. Between 1 and 100. + * @param OwnerId The owner to retrieve records around. + * @param Expiry Expiry in seconds (since epoch) to begin fetching records from. + * @param Cursor A next or previous page cursor. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListTournamentRecordsAroundOwner( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& TournamentId, + FNakamaOptionalInt32 Limit, + const FString& OwnerId, + FNakamaOptionalInt64 Expiry, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List groups the current user belongs to. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param UserId ID of the user. + * @param Limit Max number of records to return. Between 1 and 100. + * @param State The user group state to list. + * @param Cursor An optional next page cursor. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListUserGroups( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& UserId, + FNakamaOptionalInt32 Limit, + FNakamaOptionalInt32 State, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List groups the current user belongs to. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param UserId ID of the user. + * @param Limit Max number of records to return. Between 1 and 100. + * @param State The user group state to list. + * @param Cursor An optional next page cursor. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ListUserGroups( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& UserId, + FNakamaOptionalInt32 Limit, + FNakamaOptionalInt32 State, + const FString& Cursor, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Promote a set of users in a group to the next role up. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param GroupId The group ID to promote in. + * @param UserIds The users to promote. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void PromoteGroupUsers( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& GroupId, + const TArray& UserIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Promote a set of users in a group to the next role up. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param GroupId The group ID to promote in. + * @param UserIds The users to promote. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void PromoteGroupUsers( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& GroupId, + const TArray& UserIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Demote a set of users in a group to the next role down. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param GroupId The group ID to demote in. + * @param UserIds The users to demote. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void DemoteGroupUsers( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& GroupId, + const TArray& UserIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Demote a set of users in a group to the next role down. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param GroupId The group ID to demote in. + * @param UserIds The users to demote. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void DemoteGroupUsers( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& GroupId, + const TArray& UserIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Get storage objects. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param ObjectIds Batch of storage objects. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ReadStorageObjects( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const TArray& ObjectIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Get storage objects. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param ObjectIds Batch of storage objects. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ReadStorageObjects( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const TArray& ObjectIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Execute a Lua function on the server. + * + * @param Config The client configuration. + * @param Id The identifier of the function. + * @param Payload The payload of the function which must be a JSON object. + * @param HttpKey The authentication key used when executed as a non-client HTTP request. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void RpcFunc( + const FNakamaClientConfig& Config, + const FString& Id, + const FString& Payload, + const FString& HttpKey, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Execute a Lua function on the server. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Id The identifier of the function. + * @param Payload The payload of the function which must be a JSON object. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void RpcFunc( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Id, + const FString& Payload, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Remove the Apple ID from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Token The ID token received from Apple to validate. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void UnlinkApple( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Remove the Apple ID from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Token The ID token received from Apple to validate. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void UnlinkApple( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Remove the custom ID from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Id A custom identifier. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void UnlinkCustom( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Id, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Remove the custom ID from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Id A custom identifier. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void UnlinkCustom( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Id, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Remove the device ID from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Id A device identifier. Should be obtained by a platform-specific device API. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void UnlinkDevice( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Id, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Remove the device ID from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Id A device identifier. Should be obtained by a platform-specific device API. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void UnlinkDevice( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Id, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Remove the email+password from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Email A valid RFC-5322 email address. + * @param Password A password for the user account. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void UnlinkEmail( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Email, + const FString& Password, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Remove the email+password from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Email A valid RFC-5322 email address. + * @param Password A password for the user account. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void UnlinkEmail( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Email, + const FString& Password, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Remove Facebook from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Token The OAuth token received from Facebook to access their profile API. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void UnlinkFacebook( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Remove Facebook from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Token The OAuth token received from Facebook to access their profile API. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void UnlinkFacebook( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Remove Facebook Instant Game profile from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param SignedPlayerInfo The OAuth token received from a Facebook Instant Game that may be decoded with the Application Secret (must be available with the nakama configuration) + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void UnlinkFacebookInstantGame( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& SignedPlayerInfo, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Remove Facebook Instant Game profile from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param SignedPlayerInfo The OAuth token received from a Facebook Instant Game that may be decoded with the Application Secret (must be available with the nakama configuration) + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void UnlinkFacebookInstantGame( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& SignedPlayerInfo, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Remove Apple's GameCenter from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param PlayerId Player ID (generated by GameCenter). + * @param BundleId Bundle ID (generated by GameCenter). + * @param TimestampSeconds Time since UNIX epoch when the signature was created. + * @param Salt A random "NSString" used to compute the hash and keep it randomized. + * @param Signature The verification signature data generated. + * @param PublicKeyUrl The URL for the public encryption key. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void UnlinkGameCenter( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& PlayerId, + const FString& BundleId, + int64 TimestampSeconds, + const FString& Salt, + const FString& Signature, + const FString& PublicKeyUrl, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Remove Apple's GameCenter from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param PlayerId Player ID (generated by GameCenter). + * @param BundleId Bundle ID (generated by GameCenter). + * @param TimestampSeconds Time since UNIX epoch when the signature was created. + * @param Salt A random "NSString" used to compute the hash and keep it randomized. + * @param Signature The verification signature data generated. + * @param PublicKeyUrl The URL for the public encryption key. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void UnlinkGameCenter( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& PlayerId, + const FString& BundleId, + int64 TimestampSeconds, + const FString& Salt, + const FString& Signature, + const FString& PublicKeyUrl, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Remove Google from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Token The OAuth token received from Google to access their profile API. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void UnlinkGoogle( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Remove Google from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Token The OAuth token received from Google to access their profile API. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void UnlinkGoogle( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Remove Steam from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Token The account token received from Steam to access their profile API. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void UnlinkSteam( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Remove Steam from the social profiles on the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Token The account token received from Steam to access their profile API. + * @param Vars Extra information that will be bundled in the session token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void UnlinkSteam( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Token, + const TMap& Vars, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Update fields in the current user's account. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Username The username of the user's account. + * @param DisplayName The display name of the user. + * @param AvatarUrl A URL for an avatar image. + * @param LangTag The language expected to be a tag which follows the BCP-47 spec. + * @param Location The location set by the user. + * @param Timezone The timezone set by the user. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void UpdateAccount( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Username, + const FString& DisplayName, + const FString& AvatarUrl, + const FString& LangTag, + const FString& Location, + const FString& Timezone, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Update fields in the current user's account. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Username The username of the user's account. + * @param DisplayName The display name of the user. + * @param AvatarUrl A URL for an avatar image. + * @param LangTag The language expected to be a tag which follows the BCP-47 spec. + * @param Location The location set by the user. + * @param Timezone The timezone set by the user. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void UpdateAccount( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Username, + const FString& DisplayName, + const FString& AvatarUrl, + const FString& LangTag, + const FString& Location, + const FString& Timezone, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Update fields in a given group. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param GroupId The ID of the group to update. + * @param Name Name. + * @param Description Description string. + * @param LangTag Lang tag. + * @param AvatarUrl Avatar URL. + * @param Open Open is true if anyone should be allowed to join, or false if joins must be approved by a group admin. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void UpdateGroup( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& GroupId, + const FString& Name, + const FString& Description, + const FString& LangTag, + const FString& AvatarUrl, + FNakamaOptionalBool Open, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Update fields in a given group. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param GroupId The ID of the group to update. + * @param Name Name. + * @param Description Description string. + * @param LangTag Lang tag. + * @param AvatarUrl Avatar URL. + * @param Open Open is true if anyone should be allowed to join, or false if joins must be approved by a group admin. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void UpdateGroup( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& GroupId, + const FString& Name, + const FString& Description, + const FString& LangTag, + const FString& AvatarUrl, + FNakamaOptionalBool Open, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Validate Apple IAP Receipt + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Receipt Base64 encoded Apple receipt data payload. + * @param Persist Persist the purchase + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ValidatePurchaseApple( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Receipt, + FNakamaOptionalBool Persist, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Validate Apple IAP Receipt + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Receipt Base64 encoded Apple receipt data payload. + * @param Persist Persist the purchase + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ValidatePurchaseApple( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Receipt, + FNakamaOptionalBool Persist, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Validate Apple Subscription Receipt + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Receipt Base64 encoded Apple receipt data payload. + * @param Persist Persist the subscription. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ValidateSubscriptionApple( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Receipt, + FNakamaOptionalBool Persist, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Validate Apple Subscription Receipt + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Receipt Base64 encoded Apple receipt data payload. + * @param Persist Persist the subscription. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ValidateSubscriptionApple( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Receipt, + FNakamaOptionalBool Persist, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Validate Google IAP Receipt + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Purchase JSON encoded Google purchase payload. + * @param Persist Persist the purchase + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ValidatePurchaseGoogle( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Purchase, + FNakamaOptionalBool Persist, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Validate Google IAP Receipt + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Purchase JSON encoded Google purchase payload. + * @param Persist Persist the purchase + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ValidatePurchaseGoogle( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Purchase, + FNakamaOptionalBool Persist, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Validate Google Subscription Receipt + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Receipt JSON encoded Google purchase payload. + * @param Persist Persist the subscription. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ValidateSubscriptionGoogle( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Receipt, + FNakamaOptionalBool Persist, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Validate Google Subscription Receipt + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Receipt JSON encoded Google purchase payload. + * @param Persist Persist the subscription. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ValidateSubscriptionGoogle( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Receipt, + FNakamaOptionalBool Persist, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Validate Huawei IAP Receipt + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Purchase JSON encoded Huawei InAppPurchaseData. + * @param Signature InAppPurchaseData signature. + * @param Persist Persist the purchase + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ValidatePurchaseHuawei( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& Purchase, + const FString& Signature, + FNakamaOptionalBool Persist, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Validate Huawei IAP Receipt + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Purchase JSON encoded Huawei InAppPurchaseData. + * @param Signature InAppPurchaseData signature. + * @param Persist Persist the purchase + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ValidatePurchaseHuawei( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& Purchase, + const FString& Signature, + FNakamaOptionalBool Persist, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Validate FB Instant IAP Receipt + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param SignedRequest Base64 encoded Facebook Instant signedRequest receipt data payload. + * @param Persist Persist the purchase + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ValidatePurchaseFacebookInstant( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& SignedRequest, + FNakamaOptionalBool Persist, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Validate FB Instant IAP Receipt + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param SignedRequest Base64 encoded Facebook Instant signedRequest receipt data payload. + * @param Persist Persist the purchase + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void ValidatePurchaseFacebookInstant( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& SignedRequest, + FNakamaOptionalBool Persist, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Write a record to a leaderboard. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param LeaderboardId The ID of the leaderboard to write to. + * @param Record Record input. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void WriteLeaderboardRecord( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& LeaderboardId, + const FNakamaWriteLeaderboardRecordRequestLeaderboardRecordWrite& Record, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Write a record to a leaderboard. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param LeaderboardId The ID of the leaderboard to write to. + * @param Record Record input. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void WriteLeaderboardRecord( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& LeaderboardId, + const FNakamaWriteLeaderboardRecordRequestLeaderboardRecordWrite& Record, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Write objects into the storage engine. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Objects The objects to store on the server. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void WriteStorageObjects( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const TArray& Objects, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Write objects into the storage engine. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Objects The objects to store on the server. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void WriteStorageObjects( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const TArray& Objects, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Write a record to a tournament. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param TournamentId The tournament ID to write the record for. + * @param Record Record input. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void WriteTournamentRecord( + const FNakamaClientConfig& Config, + const FString& HttpKey, + const FString& TournamentId, + const FNakamaWriteTournamentRecordRequestTournamentRecordWrite& Record, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Write a record to a tournament. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param TournamentId The tournament ID to write the record for. + * @param Record Record input. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + NAKAMAAPI_API void WriteTournamentRecord( + const FNakamaClientConfig& Config, + const FNakamaSession& Session, + const FString& TournamentId, + const FNakamaWriteTournamentRecordRequestTournamentRecordWrite& Record, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + +} diff --git a/Satori/Source/SatoriUnreal/Public/SatoriUnreal.h b/Nakama/Source/NakamaApi/Public/NakamaApiModule.h similarity index 80% rename from Satori/Source/SatoriUnreal/Public/SatoriUnreal.h rename to Nakama/Source/NakamaApi/Public/NakamaApiModule.h index cc30865f5..6ef79df71 100644 --- a/Satori/Source/SatoriUnreal/Public/SatoriUnreal.h +++ b/Nakama/Source/NakamaApi/Public/NakamaApiModule.h @@ -1,5 +1,5 @@ /* -* Copyright 2025 The Nakama Authors + * Copyright 2026 The Nakama Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,11 @@ #pragma once -#include "Modules/ModuleInterface.h" +#include "Modules/ModuleManager.h" -class FSatoriUnrealModule : public IModuleInterface +class FNakamaApiModule : public IModuleInterface { public: - /** IModuleInterface implementation */ virtual void StartupModule() override; virtual void ShutdownModule() override; }; diff --git a/Nakama/Source/NakamaApi/Public/NakamaRtTypes.h b/Nakama/Source/NakamaApi/Public/NakamaRtTypes.h new file mode 100644 index 000000000..795810d30 --- /dev/null +++ b/Nakama/Source/NakamaApi/Public/NakamaRtTypes.h @@ -0,0 +1,1335 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + +#pragma once + +#include "CoreMinimal.h" +#include "NakamaOptionals.h" +#include "NakamaTypes.h" +#include "NakamaRtTypes.generated.h" + +// +// Shadow these from the other Nakama Types. +USTRUCT(BlueprintType) +struct FNakamaRtNotification : public FNakamaNotification +{ + GENERATED_BODY() + + static FNakamaRtNotification FromJson(const TSharedPtr& Shared) + { + return static_cast(FNakamaNotification::FromJson(Shared)); + } +}; +USTRUCT(BlueprintType) +struct FNakamaRtRpc : public FNakamaRpc +{ + GENERATED_BODY() + + static FNakamaRtRpc FromJson(const TSharedPtr& Shared) + { + return static_cast(FNakamaRpc::FromJson(Shared)); + } +}; +USTRUCT(BlueprintType) +struct FNakamaRtChannelMessage : public FNakamaChannelMessage +{ + GENERATED_BODY() + static FNakamaRtChannelMessage FromJson(const TSharedPtr& Shared) + { + return static_cast(FNakamaChannelMessage::FromJson(Shared)); + } +}; + +USTRUCT(BlueprintType) +struct FNakamaRtEmptyResponse +{ + GENERATED_BODY(); +}; +/* +* +*/ +UENUM(BlueprintType) +enum class ENakamaRtMessageCategory : uint8 +{ + CATEGORY_UNKNOWN = 0 // + , REQUEST = 1 // + , RESPONSE = 2 // + , EVENT = 3 // +}; +/* +* The type of chat channel. +*/ +UENUM(BlueprintType) +enum class ENakamaRtChannelJoinType : uint8 +{ + TYPE_UNSPECIFIED = 0 // Default case. Assumed as ROOM type. + , ROOM = 1 // A room which anyone can join to chat. + , DIRECT_MESSAGE = 2 // A private channel for 1-on-1 chat. + , GROUP = 3 // A channel for group chat. +}; +/* +* The selection of possible error codes. +*/ +UENUM(BlueprintType) +enum class ENakamaRtErrorCode : uint8 +{ + RUNTIME_EXCEPTION = 0 // An unexpected result from the server. + , UNRECOGNIZED_PAYLOAD = 1 // The server received a message which is not recognised. + , MISSING_PAYLOAD = 2 // A message was expected but contains no content. + , BAD_INPUT = 3 // Fields in the message have an invalid format. + , MATCH_NOT_FOUND = 4 // The match id was not found. + , MATCH_JOIN_REJECTED = 5 // The match join was rejected. + , RUNTIME_FUNCTION_NOT_FOUND = 6 // The runtime function does not exist on the server. + , RUNTIME_FUNCTION_EXCEPTION = 7 // The runtime function executed with an error. +}; + +/* +* A user session associated to a stream, usually through a list operation or a join/leave event. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtUserPresence +{ + GENERATED_BODY() + + /** The user this presence belongs to. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString UserId; + + /** A unique session ID identifying the particular connection, because the user may have many. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString SessionId; + + /** The username for display purposes. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Username; + + /** Whether this presence generates persistent data/messages, if applicable for the stream type. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + bool Persistence = false; + + /** A user-set status message for this stream, if applicable. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Status; + + static FNakamaRtUserPresence FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* A realtime chat channel. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtChannel +{ + GENERATED_BODY() + + /** The ID of the channel. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Id; + + /** The users currently in the channel. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TArray Presences; + + /** A reference to the current user's presence in the channel. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FNakamaRtUserPresence Self_; + + /** The name of the chat room, or an empty string if this message was not sent through a chat room. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString RoomName; + + /** The ID of the group, or an empty string if this message was not sent through a group channel. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString GroupId; + + /** The ID of the first DM user, or an empty string if this message was not sent through a DM chat. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString UserIdOne; + + /** The ID of the second DM user, or an empty string if this message was not sent through a DM chat. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString UserIdTwo; + + static FNakamaRtChannel FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Join operation for a realtime chat channel. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtChannelJoin +{ + GENERATED_BODY() + + /** The user ID to DM with, group ID to chat with, or room channel name to join. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Target; + + /** The type of the chat channel. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + int32 Type = 0; + + /** Whether messages sent on this channel should be persistent. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FNakamaRtOptionalBool Persistence; + + /** Whether the user should appear in the channel's presence list and events. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FNakamaRtOptionalBool Hidden; + + static FNakamaRtChannelJoin FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Leave a realtime channel. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtChannelLeave +{ + GENERATED_BODY() + + /** The ID of the channel to leave. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString ChannelId; + + static FNakamaRtChannelLeave FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* A receipt reply from a channel message send operation. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtChannelMessageAck +{ + GENERATED_BODY() + + /** The channel the message was sent to. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString ChannelId; + + /** The unique ID assigned to the message. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString MessageId; + + /** The code representing a message type or category. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FNakamaRtOptionalInt32 Code; + + /** Username of the message sender. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Username; + + /** The UNIX time (for gRPC clients) or ISO string (for REST clients) when the message was created. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FDateTime CreateTime; + + /** The UNIX time (for gRPC clients) or ISO string (for REST clients) when the message was last updated. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FDateTime UpdateTime; + + /** True if the message was persisted to the channel's history, false otherwise. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FNakamaRtOptionalBool Persistent; + + /** The name of the chat room, or an empty string if this message was not sent through a chat room. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString RoomName; + + /** The ID of the group, or an empty string if this message was not sent through a group channel. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString GroupId; + + /** The ID of the first DM user, or an empty string if this message was not sent through a DM chat. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString UserIdOne; + + /** The ID of the second DM user, or an empty string if this message was not sent through a DM chat. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString UserIdTwo; + + static FNakamaRtChannelMessageAck FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Send a message to a realtime channel. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtChannelMessageSend +{ + GENERATED_BODY() + + /** The channel to sent to. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString ChannelId; + + /** Message content. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Content; + + static FNakamaRtChannelMessageSend FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Update a message previously sent to a realtime channel. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtChannelMessageUpdate +{ + GENERATED_BODY() + + /** The channel the message was sent to. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString ChannelId; + + /** The ID assigned to the message to update. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString MessageId; + + /** New message content. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Content; + + static FNakamaRtChannelMessageUpdate FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Remove a message previously sent to a realtime channel. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtChannelMessageRemove +{ + GENERATED_BODY() + + /** The channel the message was sent to. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString ChannelId; + + /** The ID assigned to the message to update. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString MessageId; + + static FNakamaRtChannelMessageRemove FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* A set of joins and leaves on a particular channel. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtChannelPresenceEvent +{ + GENERATED_BODY() + + /** The channel identifier this event is for. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString ChannelId; + + /** Presences joining the channel as part of this event, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TArray Joins; + + /** Presences leaving the channel as part of this event, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TArray Leaves; + + /** The name of the chat room, or an empty string if this message was not sent through a chat room. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString RoomName; + + /** The ID of the group, or an empty string if this message was not sent through a group channel. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString GroupId; + + /** The ID of the first DM user, or an empty string if this message was not sent through a DM chat. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString UserIdOne; + + /** The ID of the second DM user, or an empty string if this message was not sent through a DM chat. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString UserIdTwo; + + static FNakamaRtChannelPresenceEvent FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* A logical error which may occur on the server. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtError +{ + GENERATED_BODY() + + /** The error code which should be one of "Error.Code" enums. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + int32 Code = 0; + + /** A message in English to help developers debug the response. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Message; + + /** Additional error details which may be different for each response. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TMap Context; + + static FNakamaRtError FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* A realtime match. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtMatch +{ + GENERATED_BODY() + + /** The match unique ID. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString MatchId; + + /** True if it's an server-managed authoritative match, false otherwise. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + bool Authoritative = false; + + /** Match label, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Label; + + /** The number of users currently in the match. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + int32 Size = 0; + + /** The users currently in the match. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TArray Presences; + + /** A reference to the current user's presence in the match. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FNakamaRtUserPresence Self_; + + static FNakamaRtMatch FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Create a new realtime match. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtMatchCreate +{ + GENERATED_BODY() + + /** Optional name to use when creating the match. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Name; + + static FNakamaRtMatchCreate FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Realtime match data received from the server. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtMatchData +{ + GENERATED_BODY() + + /** The match unique ID. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString MatchId; + + /** A reference to the user presence that sent this data, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FNakamaRtUserPresence Presence; + + /** Op code value. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + int64 OpCode = 0; + + /** Data payload, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TArray Data; + + /** True if this data was delivered reliably, false otherwise. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + bool Reliable = false; + + static FNakamaRtMatchData FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Send realtime match data to the server. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtMatchDataSend +{ + GENERATED_BODY() + + /** The match unique ID. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString MatchId; + + /** Op code value. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + int64 OpCode = 0; + + /** Data payload, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TArray Data; + + /** List of presences in the match to deliver to, if filtering is required. Otherwise deliver to everyone in the match. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TArray Presences; + + /** True if the data should be sent reliably, false otherwise. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + bool Reliable = false; + + static FNakamaRtMatchDataSend FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Join an existing realtime match. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtMatchJoin +{ + GENERATED_BODY() + + /** An optional set of key-value metadata pairs to be passed to the match handler, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TMap Metadata; + + /** The match unique ID. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString MatchId; + + /** A matchmaking result token. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Token; + + static FNakamaRtMatchJoin FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Leave a realtime match. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtMatchLeave +{ + GENERATED_BODY() + + /** The match unique ID. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString MatchId; + + static FNakamaRtMatchLeave FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* A set of joins and leaves on a particular realtime match. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtMatchPresenceEvent +{ + GENERATED_BODY() + + /** The match unique ID. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString MatchId; + + /** User presences that have just joined the match. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TArray Joins; + + /** User presences that have just left the match. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TArray Leaves; + + static FNakamaRtMatchPresenceEvent FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Start a new matchmaking process. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtMatchmakerAdd +{ + GENERATED_BODY() + + /** Minimum total user count to match together. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + int32 MinCount = 0; + + /** Maximum total user count to match together. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + int32 MaxCount = 0; + + /** Filter query used to identify suitable users. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Query; + + /** Optional multiple of the count that must be satisfied. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FNakamaRtOptionalInt32 CountMultiple; + + /** String properties. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TMap StringProperties; + + /** Numeric properties. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TMap NumericProperties; + + static FNakamaRtMatchmakerAdd FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtMatchmakerMatchedMatchmakerUser +{ + GENERATED_BODY() + + /** User info. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FNakamaRtUserPresence Presence; + + /** Party identifier, if this user was matched as a party member. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString PartyId; + + /** String properties. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TMap StringProperties; + + /** Numeric properties. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TMap NumericProperties; + + static FNakamaRtMatchmakerMatchedMatchmakerUser FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* A successful matchmaking result. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtMatchmakerMatched +{ + GENERATED_BODY() + + /** The matchmaking ticket that has completed. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Ticket; + + /** The users that have been matched together, and information about their matchmaking data. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TArray Users; + + /** A reference to the current user and their properties. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FNakamaRtMatchmakerMatchedMatchmakerUser Self_; + + /** Match ID. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString MatchId; + + /** Match join token. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Token; + + static FNakamaRtMatchmakerMatched FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Cancel an existing ongoing matchmaking process. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtMatchmakerRemove +{ + GENERATED_BODY() + + /** The ticket to cancel. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Ticket; + + static FNakamaRtMatchmakerRemove FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* A ticket representing a new matchmaking process. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtMatchmakerTicket +{ + GENERATED_BODY() + + /** The ticket that can be used to cancel matchmaking. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Ticket; + + static FNakamaRtMatchmakerTicket FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* A collection of zero or more notifications. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtNotifications +{ + GENERATED_BODY() + + /** Collection of notifications. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TArray Notifications; + + static FNakamaRtNotifications FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Incoming information about a party. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtParty +{ + GENERATED_BODY() + + /** Unique party identifier. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString PartyId; + + /** Open flag. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + bool Open = false; + + /** Hidden flag. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + bool Hidden = false; + + /** Maximum number of party members. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + int32 MaxSize = 0; + + /** Self. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FNakamaRtUserPresence Self_; + + /** Leader. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FNakamaRtUserPresence Leader; + + /** All current party members. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TArray Presences; + + /** Label for party listing. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Label; + + static FNakamaRtParty FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Create a party. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtPartyCreate +{ + GENERATED_BODY() + + /** Whether or not the party will require join requests to be approved by the party leader. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + bool Open = false; + + /** Maximum number of party members. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + int32 MaxSize = 0; + + /** Label */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Label; + + /** Whether the party is visible in party listings. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + bool Hidden = false; + + static FNakamaRtPartyCreate FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Update a party label. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtPartyUpdate +{ + GENERATED_BODY() + + /** Party ID. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString PartyId; + + /** Label to set. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Label; + + /** Change the party to open or closed. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + bool Open = false; + + /** Whether the party is visible in party listings. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + bool Hidden = false; + + static FNakamaRtPartyUpdate FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Join a party, or request to join if the party is not open. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtPartyJoin +{ + GENERATED_BODY() + + /** Party ID to join. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString PartyId; + + static FNakamaRtPartyJoin FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Leave a party. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtPartyLeave +{ + GENERATED_BODY() + + /** Party ID to leave. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString PartyId; + + static FNakamaRtPartyLeave FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Promote a new party leader. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtPartyPromote +{ + GENERATED_BODY() + + /** Party ID to promote a new leader for. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString PartyId; + + /** The presence of an existing party member to promote as the new leader. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FNakamaRtUserPresence Presence; + + static FNakamaRtPartyPromote FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Announcement of a new party leader. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtPartyLeader +{ + GENERATED_BODY() + + /** Party ID to announce the new leader for. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString PartyId; + + /** The presence of the new party leader. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FNakamaRtUserPresence Presence; + + static FNakamaRtPartyLeader FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Accept a request to join. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtPartyAccept +{ + GENERATED_BODY() + + /** Party ID to accept a join request for. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString PartyId; + + /** The presence to accept as a party member. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FNakamaRtUserPresence Presence; + + static FNakamaRtPartyAccept FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Kick a party member, or decline a request to join. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtPartyRemove +{ + GENERATED_BODY() + + /** Party ID to remove/reject from. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString PartyId; + + /** The presence to remove or reject. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FNakamaRtUserPresence Presence; + + static FNakamaRtPartyRemove FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* End a party, kicking all party members and closing it. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtPartyClose +{ + GENERATED_BODY() + + /** Party ID to close. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString PartyId; + + static FNakamaRtPartyClose FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Request a list of pending join requests for a party. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtPartyJoinRequestList +{ + GENERATED_BODY() + + /** Party ID to get a list of join requests for. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString PartyId; + + static FNakamaRtPartyJoinRequestList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Incoming notification for one or more new presences attempting to join the party. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtPartyJoinRequest +{ + GENERATED_BODY() + + /** Party ID these presences are attempting to join. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString PartyId; + + /** Presences attempting to join. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TArray Presences; + + static FNakamaRtPartyJoinRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Begin matchmaking as a party. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtPartyMatchmakerAdd +{ + GENERATED_BODY() + + /** Party ID. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString PartyId; + + /** Minimum total user count to match together. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + int32 MinCount = 0; + + /** Maximum total user count to match together. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + int32 MaxCount = 0; + + /** Filter query used to identify suitable users. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Query; + + /** Optional multiple of the count that must be satisfied. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FNakamaRtOptionalInt32 CountMultiple; + + /** String properties. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TMap StringProperties; + + /** Numeric properties. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TMap NumericProperties; + + static FNakamaRtPartyMatchmakerAdd FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Cancel a party matchmaking process using a ticket. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtPartyMatchmakerRemove +{ + GENERATED_BODY() + + /** Party ID. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString PartyId; + + /** The ticket to cancel. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Ticket; + + static FNakamaRtPartyMatchmakerRemove FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* A response from starting a new party matchmaking process. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtPartyMatchmakerTicket +{ + GENERATED_BODY() + + /** Party ID. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString PartyId; + + /** The ticket that can be used to cancel matchmaking. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Ticket; + + static FNakamaRtPartyMatchmakerTicket FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Incoming party data delivered from the server. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtPartyData +{ + GENERATED_BODY() + + /** The party ID. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString PartyId; + + /** A reference to the user presence that sent this data, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FNakamaRtUserPresence Presence; + + /** Op code value. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + int64 OpCode = 0; + + /** Data payload, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TArray Data; + + static FNakamaRtPartyData FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Send data to a party. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtPartyDataSend +{ + GENERATED_BODY() + + /** Party ID to send to. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString PartyId; + + /** Op code value. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + int64 OpCode = 0; + + /** Data payload, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TArray Data; + + static FNakamaRtPartyDataSend FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Presence update for a particular party. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtPartyPresenceEvent +{ + GENERATED_BODY() + + /** The party ID. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString PartyId; + + /** User presences that have just joined the party. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TArray Joins; + + /** User presences that have just left the party. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TArray Leaves; + + static FNakamaRtPartyPresenceEvent FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Application-level heartbeat and connection check. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtPing +{ + GENERATED_BODY() + + static FNakamaRtPing FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Application-level heartbeat and connection check response. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtPong +{ + GENERATED_BODY() + + static FNakamaRtPong FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* A snapshot of statuses for some set of users. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtStatus +{ + GENERATED_BODY() + + /** User statuses. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TArray Presences; + + static FNakamaRtStatus FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Start receiving status updates for some set of users. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtStatusFollow +{ + GENERATED_BODY() + + /** User IDs to follow. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TArray UserIds; + + /** Usernames to follow. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TArray Usernames; + + static FNakamaRtStatusFollow FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* A batch of status updates for a given user. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtStatusPresenceEvent +{ + GENERATED_BODY() + + /** New statuses for the user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TArray Joins; + + /** Previous statuses for the user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TArray Leaves; + + static FNakamaRtStatusPresenceEvent FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Stop receiving status updates for some set of users. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtStatusUnfollow +{ + GENERATED_BODY() + + /** Users to unfollow. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TArray UserIds; + + static FNakamaRtStatusUnfollow FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Set the user's own status. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtStatusUpdate +{ + GENERATED_BODY() + + /** Status string to set, if not present the user will appear offline. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Status; + + static FNakamaRtStatusUpdate FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* Represents identifying information for a stream. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtStream +{ + GENERATED_BODY() + + /** Mode identifies the type of stream. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + int32 Mode = 0; + + /** Subject is the primary identifier, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Subject; + + /** Subcontext is a secondary identifier, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Subcontext; + + /** The label is an arbitrary identifying string, if the stream has one. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Label; + + static FNakamaRtStream FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* A data message delivered over a stream. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtStreamData +{ + GENERATED_BODY() + + /** The stream this data message relates to. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FNakamaRtStream Stream; + + /** The sender, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FNakamaRtUserPresence Sender; + + /** Arbitrary contents of the data message. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FString Data; + + /** True if this data was delivered reliably, false otherwise. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + bool Reliable = false; + + static FNakamaRtStreamData FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/* +* A set of joins and leaves on a particular stream. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRtStreamPresenceEvent +{ + GENERATED_BODY() + + /** The stream this event relates to. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + FNakamaRtStream Stream; + + /** Presences joining the stream as part of this event, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TArray Joins; + + /** Presences leaving the stream as part of this event, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") + TArray Leaves; + + static FNakamaRtStreamPresenceEvent FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; diff --git a/Nakama/Source/NakamaApi/Public/NakamaTypes.h b/Nakama/Source/NakamaApi/Public/NakamaTypes.h new file mode 100644 index 000000000..3cf5edb70 --- /dev/null +++ b/Nakama/Source/NakamaApi/Public/NakamaTypes.h @@ -0,0 +1,3288 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + +#pragma once + +#include "CoreMinimal.h" +#include "NakamaOptionals.h" +#include "NakamaTypes.generated.h" + +enum class ENakamaRequestAuth : uint8 +{ + None, + Basic, + Bearer, + HttpKey +}; + +/* +* The friendship status. +*/ +UENUM(BlueprintType) +enum class ENakamaFriendState : uint8 +{ + FRIEND = 0 // The user is a friend of the current user. + , INVITE_SENT = 1 // The current user has sent an invite to the user. + , INVITE_RECEIVED = 2 // The current user has received an invite from this user. + , BLOCKED = 3 // The current user has blocked this user. +}; + +/* +* The group role status. +*/ +UENUM(BlueprintType) +enum class ENakamaGroupUserListGroupUserState : uint8 +{ + SUPERADMIN = 0 // The user is a superadmin with full control of the group. + , ADMIN = 1 // The user is an admin with additional privileges. + , MEMBER = 2 // The user is a regular member. + , JOIN_REQUEST = 3 // The user has requested to join the group +}; + +/* +* The group role status. +*/ +UENUM(BlueprintType) +enum class ENakamaUserGroupListUserGroupState : uint8 +{ + SUPERADMIN = 0 // The user is a superadmin with full control of the group. + , ADMIN = 1 // The user is an admin with additional privileges. + , MEMBER = 2 // The user is a regular member. + , JOIN_REQUEST = 3 // The user has requested to join the group +}; + +/* +* Validation Provider, +*/ +UENUM(BlueprintType) +enum class ENakamaStoreProvider : uint8 +{ + APPLE_APP_STORE = 0 // Apple App Store + , GOOGLE_PLAY_STORE = 1 // Google Play Store + , HUAWEI_APP_GALLERY = 2 // Huawei App Gallery + , FACEBOOK_INSTANT_STORE = 3 // Facebook Instant Store +}; + +/* +* Environment where a purchase/subscription took place, +*/ +UENUM(BlueprintType) +enum class ENakamaStoreEnvironment : uint8 +{ + UNKNOWN = 0 // Unknown environment. + , SANDBOX = 1 // Sandbox/test environment. + , PRODUCTION = 2 // Production environment. +}; + +/* +* Operator that can be used to override the one set in the leaderboard. +*/ +UENUM(BlueprintType) +enum class ENakamaOperator : uint8 +{ + NO_OVERRIDE = 0 // Do not override the leaderboard operator. + , BEST = 1 // Override the leaderboard operator with BEST. + , SET = 2 // Override the leaderboard operator with SET. + , INCREMENT = 3 // Override the leaderboard operator with INCREMENT. + , DECREMENT = 4 // Override the leaderboard operator with DECREMENT. +}; + +/** +* A user in the server. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaUser +{ + GENERATED_BODY() + + /** The id of the user's account. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Id; + + /** The username of the user's account. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Username; + + /** The display name of the user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString DisplayName; + + /** A URL for an avatar image. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString AvatarUrl; + + /** The language expected to be a tag which follows the BCP-47 spec. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString LangTag; + + /** The location set by the user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Location; + + /** The timezone set by the user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Timezone; + + /** Additional information stored as a JSON object. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Metadata; + + /** The Facebook id in the user's account. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString FacebookId; + + /** The Google id in the user's account. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString GoogleId; + + /** The Apple Game Center in of the user's account. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString GamecenterId; + + /** The Steam id in the user's account. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString SteamId; + + /** Indicates whether the user is currently online. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + bool Online = false; + + /** Number of related edges to this user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 EdgeCount = 0; + + /** The UNIX time (for gRPC clients) or ISO string (for REST clients) when the user was created. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime CreateTime; + + /** The UNIX time (for gRPC clients) or ISO string (for REST clients) when the user was last updated. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime UpdateTime; + + /** The Facebook Instant Game ID in the user's account. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString FacebookInstantGameId; + + /** The Apple Sign In ID in the user's account. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString AppleId; + + static FNakamaUser FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Send a device to the server. Used with authenticate/link/unlink and user. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaAccountDevice +{ + GENERATED_BODY() + + /** A device identifier. Should be obtained by a platform-specific device API. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Id; + + /** Extra information that will be bundled in the session token. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TMap Vars; + + static FNakamaAccountDevice FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A user with additional account details. Always the current user. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaAccount +{ + GENERATED_BODY() + + /** The user object. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaUser User; + + /** The user's wallet data. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Wallet; + + /** The email address of the user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Email; + + /** The devices which belong to the user's account. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Devices; + + /** The custom id in the user's account. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString CustomId; + + /** The UNIX time (for gRPC clients) or ISO string (for REST clients) when the user's email was verified. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime VerifyTime; + + /** The UNIX time (for gRPC clients) or ISO string (for REST clients) when the user's account was disabled/banned. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime DisableTime; + + static FNakamaAccount FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Obtain a new authentication token using a refresh token. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaAccountRefresh +{ + GENERATED_BODY() + + /** Refresh token. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Token; + + /** Extra information that will be bundled in the session token. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TMap Vars; + + static FNakamaAccountRefresh FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Send a Apple Sign In token to the server. Used with authenticate/link/unlink. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaAccountApple +{ + GENERATED_BODY() + + /** The ID token received from Apple to validate. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Token; + + /** Extra information that will be bundled in the session token. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TMap Vars; + + static FNakamaAccountApple FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Send a custom ID to the server. Used with authenticate/link/unlink. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaAccountCustom +{ + GENERATED_BODY() + + /** A custom identifier. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Id; + + /** Extra information that will be bundled in the session token. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TMap Vars; + + static FNakamaAccountCustom FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Send an email with password to the server. Used with authenticate/link/unlink. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaAccountEmail +{ + GENERATED_BODY() + + /** A valid RFC-5322 email address. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Email; + + /** A password for the user account. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Password; + + /** Extra information that will be bundled in the session token. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TMap Vars; + + static FNakamaAccountEmail FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Send a Facebook token to the server. Used with authenticate/link/unlink. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaAccountFacebook +{ + GENERATED_BODY() + + /** The OAuth token received from Facebook to access their profile API. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Token; + + /** Extra information that will be bundled in the session token. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TMap Vars; + + static FNakamaAccountFacebook FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Send a Facebook Instant Game token to the server. Used with authenticate/link/unlink. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaAccountFacebookInstantGame +{ + GENERATED_BODY() + + /** The OAuth token received from a Facebook Instant Game that may be decoded with the Application Secret (must be available with the nakama configuration) */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString SignedPlayerInfo; + + /** Extra information that will be bundled in the session token. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TMap Vars; + + static FNakamaAccountFacebookInstantGame FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Send Apple's Game Center account credentials to the server. Used with authenticate/link/unlink. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaAccountGameCenter +{ + GENERATED_BODY() + + /** Player ID (generated by GameCenter). */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString PlayerId; + + /** Bundle ID (generated by GameCenter). */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString BundleId; + + /** Time since UNIX epoch when the signature was created. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int64 TimestampSeconds = 0; + + /** A random "NSString" used to compute the hash and keep it randomized. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Salt; + + /** The verification signature data generated. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Signature; + + /** The URL for the public encryption key. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString PublicKeyUrl; + + /** Extra information that will be bundled in the session token. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TMap Vars; + + static FNakamaAccountGameCenter FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Send a Google token to the server. Used with authenticate/link/unlink. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaAccountGoogle +{ + GENERATED_BODY() + + /** The OAuth token received from Google to access their profile API. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Token; + + /** Extra information that will be bundled in the session token. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TMap Vars; + + static FNakamaAccountGoogle FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Send a Steam token to the server. Used with authenticate/link/unlink. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaAccountSteam +{ + GENERATED_BODY() + + /** The account token received from Steam to access their profile API. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Token; + + /** Extra information that will be bundled in the session token. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TMap Vars; + + static FNakamaAccountSteam FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Add one or more friends to the current user. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaAddFriendsRequest +{ + GENERATED_BODY() + + /** The account id of a user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Ids; + + /** The account username of a user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Usernames; + + /** Optional metadata to add to friends. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Metadata; + + static FNakamaAddFriendsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Add users to a group. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaAddGroupUsersRequest +{ + GENERATED_BODY() + + /** The group to add users to. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString GroupId; + + /** The users to add. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray UserIds; + + static FNakamaAddGroupUsersRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Authenticate against the server with a refresh token. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaSessionRefreshRequest +{ + GENERATED_BODY() + + /** Refresh token. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Token; + + /** Extra information that will be bundled in the session token. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TMap Vars; + + static FNakamaSessionRefreshRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Log out a session, invalidate a refresh token, or log out all sessions/refresh tokens for a user. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaSessionLogoutRequest +{ + GENERATED_BODY() + + /** Session token to log out. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Token; + + /** Refresh token to invalidate. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString RefreshToken; + + static FNakamaSessionLogoutRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Authenticate against the server with Apple Sign In. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaAuthenticateAppleRequest +{ + GENERATED_BODY() + + /** The Apple account details. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaAccountApple Account; + + /** Register the account if the user does not already exist. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Create; + + /** Set the username on the account at register. Must be unique. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Username; + + static FNakamaAuthenticateAppleRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Authenticate against the server with a custom ID. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaAuthenticateCustomRequest +{ + GENERATED_BODY() + + /** The custom account details. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaAccountCustom Account; + + /** Register the account if the user does not already exist. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Create; + + /** Set the username on the account at register. Must be unique. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Username; + + static FNakamaAuthenticateCustomRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Authenticate against the server with a device ID. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaAuthenticateDeviceRequest +{ + GENERATED_BODY() + + /** The device account details. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaAccountDevice Account; + + /** Register the account if the user does not already exist. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Create; + + /** Set the username on the account at register. Must be unique. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Username; + + static FNakamaAuthenticateDeviceRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Authenticate against the server with email+password. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaAuthenticateEmailRequest +{ + GENERATED_BODY() + + /** The email account details. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaAccountEmail Account; + + /** Register the account if the user does not already exist. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Create; + + /** Set the username on the account at register. Must be unique. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Username; + + static FNakamaAuthenticateEmailRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Authenticate against the server with Facebook. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaAuthenticateFacebookRequest +{ + GENERATED_BODY() + + /** The Facebook account details. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaAccountFacebook Account; + + /** Register the account if the user does not already exist. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Create; + + /** Set the username on the account at register. Must be unique. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Username; + + /** Import Facebook friends for the user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Sync; + + static FNakamaAuthenticateFacebookRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Authenticate against the server with Facebook Instant Game token. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaAuthenticateFacebookInstantGameRequest +{ + GENERATED_BODY() + + /** The Facebook Instant Game account details. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaAccountFacebookInstantGame Account; + + /** Register the account if the user does not already exist. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Create; + + /** Set the username on the account at register. Must be unique. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Username; + + static FNakamaAuthenticateFacebookInstantGameRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Authenticate against the server with Apple's Game Center. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaAuthenticateGameCenterRequest +{ + GENERATED_BODY() + + /** The Game Center account details. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaAccountGameCenter Account; + + /** Register the account if the user does not already exist. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Create; + + /** Set the username on the account at register. Must be unique. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Username; + + static FNakamaAuthenticateGameCenterRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Authenticate against the server with Google. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaAuthenticateGoogleRequest +{ + GENERATED_BODY() + + /** The Google account details. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaAccountGoogle Account; + + /** Register the account if the user does not already exist. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Create; + + /** Set the username on the account at register. Must be unique. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Username; + + static FNakamaAuthenticateGoogleRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Authenticate against the server with Steam. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaAuthenticateSteamRequest +{ + GENERATED_BODY() + + /** The Steam account details. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaAccountSteam Account; + + /** Register the account if the user does not already exist. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Create; + + /** Set the username on the account at register. Must be unique. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Username; + + /** Import Steam friends for the user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Sync; + + static FNakamaAuthenticateSteamRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Ban users from a group. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaBanGroupUsersRequest +{ + GENERATED_BODY() + + /** The group to ban users from. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString GroupId; + + /** The users to ban. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray UserIds; + + static FNakamaBanGroupUsersRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Block one or more friends for the current user. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaBlockFriendsRequest +{ + GENERATED_BODY() + + /** The account id of a user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Ids; + + /** The account username of a user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Usernames; + + static FNakamaBlockFriendsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A message sent on a channel. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaChannelMessage +{ + GENERATED_BODY() + + /** The channel this message belongs to. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString ChannelId; + + /** The unique ID of this message. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString MessageId; + + /** The code representing a message type or category. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 Code; + + /** Message sender, usually a user ID. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString SenderId; + + /** The username of the message sender, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Username; + + /** The content payload. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Content; + + /** The UNIX time (for gRPC clients) or ISO string (for REST clients) when the message was created. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime CreateTime; + + /** The UNIX time (for gRPC clients) or ISO string (for REST clients) when the message was last updated. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime UpdateTime; + + /** True if the message was persisted to the channel's history, false otherwise. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Persistent; + + /** The name of the chat room, or an empty string if this message was not sent through a chat room. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString RoomName; + + /** The ID of the group, or an empty string if this message was not sent through a group channel. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString GroupId; + + /** The ID of the first DM user, or an empty string if this message was not sent through a DM chat. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString UserIdOne; + + /** The ID of the second DM user, or an empty string if this message was not sent through a DM chat. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString UserIdTwo; + + static FNakamaChannelMessage FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A list of channel messages, usually a result of a list operation. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaChannelMessageList +{ + GENERATED_BODY() + + /** A list of messages. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Messages; + + /** The cursor to send when retrieving the next page, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString NextCursor; + + /** The cursor to send when retrieving the previous page, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString PrevCursor; + + /** Cacheable cursor to list newer messages. Durable and designed to be stored, unlike next/prev cursors. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString CacheableCursor; + + static FNakamaChannelMessageList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Create a group with the current user as owner. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaCreateGroupRequest +{ + GENERATED_BODY() + + /** A unique name for the group. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Name; + + /** A description for the group. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Description; + + /** The language expected to be a tag which follows the BCP-47 spec. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString LangTag; + + /** A URL for an avatar image. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString AvatarUrl; + + /** Mark a group as open or not where only admins can accept members. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + bool Open = false; + + /** Maximum number of group members. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 MaxCount = 0; + + static FNakamaCreateGroupRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Delete one or more friends for the current user. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaDeleteFriendsRequest +{ + GENERATED_BODY() + + /** The account id of a user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Ids; + + /** The account username of a user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Usernames; + + static FNakamaDeleteFriendsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Delete a group the user has access to. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaDeleteGroupRequest +{ + GENERATED_BODY() + + /** The id of a group. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString GroupId; + + static FNakamaDeleteGroupRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Delete a leaderboard record. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaDeleteLeaderboardRecordRequest +{ + GENERATED_BODY() + + /** The leaderboard ID to delete from. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString LeaderboardId; + + static FNakamaDeleteLeaderboardRecordRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Delete one or more notifications for the current user. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaDeleteNotificationsRequest +{ + GENERATED_BODY() + + /** The id of notifications. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Ids; + + static FNakamaDeleteNotificationsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Delete a leaderboard record. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaDeleteTournamentRecordRequest +{ + GENERATED_BODY() + + /** The tournament ID to delete from. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString TournamentId; + + static FNakamaDeleteTournamentRecordRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Storage objects to delete. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaDeleteStorageObjectId +{ + GENERATED_BODY() + + /** The collection which stores the object. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Collection; + + /** The key of the object within the collection. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Key; + + /** The version hash of the object. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Version; + + static FNakamaDeleteStorageObjectId FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Batch delete storage objects. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaDeleteStorageObjectsRequest +{ + GENERATED_BODY() + + /** Batch of storage objects. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray ObjectIds; + + static FNakamaDeleteStorageObjectsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Represents an event to be passed through the server to registered event handlers. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaEvent +{ + GENERATED_BODY() + + /** An event name, type, category, or identifier. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Name; + + /** The time when the event was triggered. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime Timestamp; + + /** True if the event came directly from a client call, false otherwise. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + bool External = false; + + /** Arbitrary event property values. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TMap Properties; + + static FNakamaEvent FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A friend of a user. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaFriend +{ + GENERATED_BODY() + + /** The user object. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaUser User; + + /** The friend status. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 State; + + /** Time of the latest relationship update. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime UpdateTime; + + /** Metadata. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Metadata; + + static FNakamaFriend FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A collection of zero or more friends of the user. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaFriendList +{ + GENERATED_BODY() + + /** The Friend objects. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Friends; + + /** Cursor for the next page of results, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Cursor; + + static FNakamaFriendList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A friend of a friend. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaFriendsOfFriendsListFriendOfFriend +{ + GENERATED_BODY() + + /** The user who referred its friend. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Referrer; + + /** User. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaUser User; + + static FNakamaFriendsOfFriendsListFriendOfFriend FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A List of friends of friends +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaFriendsOfFriendsList +{ + GENERATED_BODY() + + /** User friends of friends. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray FriendsOfFriends; + + /** Cursor for the next page of results, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Cursor; + + static FNakamaFriendsOfFriendsList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Fetch a batch of zero or more users from the server. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaGetUsersRequest +{ + GENERATED_BODY() + + /** The account id of a user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Ids; + + /** The account username of a user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Usernames; + + /** The Facebook ID of a user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray FacebookIds; + + static FNakamaGetUsersRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Fetch a subscription by product id. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaGetSubscriptionRequest +{ + GENERATED_BODY() + + /** Product id of the subscription */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString ProductId; + + static FNakamaGetSubscriptionRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A group in the server. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaGroup +{ + GENERATED_BODY() + + /** The id of a group. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Id; + + /** The id of the user who created the group. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString CreatorId; + + /** The unique name of the group. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Name; + + /** A description for the group. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Description; + + /** The language expected to be a tag which follows the BCP-47 spec. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString LangTag; + + /** Additional information stored as a JSON object. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Metadata; + + /** A URL for an avatar image. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString AvatarUrl; + + /** Anyone can join open groups, otherwise only admins can accept members. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Open; + + /** The current count of all members in the group. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 EdgeCount = 0; + + /** The maximum number of members allowed. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 MaxCount = 0; + + /** The UNIX time (for gRPC clients) or ISO string (for REST clients) when the group was created. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime CreateTime; + + /** The UNIX time (for gRPC clients) or ISO string (for REST clients) when the group was last updated. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime UpdateTime; + + static FNakamaGroup FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* One or more groups returned from a listing operation. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaGroupList +{ + GENERATED_BODY() + + /** One or more groups. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Groups; + + /** A cursor used to get the next page. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Cursor; + + static FNakamaGroupList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaGroupUserListGroupUser +{ + GENERATED_BODY() + + /** User. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaUser User; + + /** Their relationship to the group. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 State; + + static FNakamaGroupUserListGroupUser FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A list of users belonging to a group, along with their role. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaGroupUserList +{ + GENERATED_BODY() + + /** User-role pairs for a group. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray GroupUsers; + + /** Cursor for the next page of results, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Cursor; + + static FNakamaGroupUserList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Import Facebook friends into the current user's account. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaImportFacebookFriendsRequest +{ + GENERATED_BODY() + + /** The Facebook account details. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaAccountFacebook Account; + + /** Reset the current user's friends list. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Reset; + + static FNakamaImportFacebookFriendsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Import Facebook friends into the current user's account. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaImportSteamFriendsRequest +{ + GENERATED_BODY() + + /** The Facebook account details. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaAccountSteam Account; + + /** Reset the current user's friends list. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Reset; + + static FNakamaImportSteamFriendsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Immediately join an open group, or request to join a closed one. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaJoinGroupRequest +{ + GENERATED_BODY() + + /** The group ID to join. The group must already exist. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString GroupId; + + static FNakamaJoinGroupRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* The request to join a tournament. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaJoinTournamentRequest +{ + GENERATED_BODY() + + /** The ID of the tournament to join. The tournament must already exist. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString TournamentId; + + static FNakamaJoinTournamentRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Kick a set of users from a group. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaKickGroupUsersRequest +{ + GENERATED_BODY() + + /** The group ID to kick from. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString GroupId; + + /** The users to kick. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray UserIds; + + static FNakamaKickGroupUsersRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A leaderboard on the server. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaLeaderboard +{ + GENERATED_BODY() + + /** The ID of the leaderboard. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Id; + + /** ASC(0) or DESC(1) sort mode of scores in the leaderboard. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 SortOrder = 0; + + /** BEST, SET, INCREMENT or DECREMENT operator mode of the leaderboard. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + ENakamaOperator Operator = static_cast(0); + + /** The UNIX time when the leaderboard was previously reset. A computed value. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 PrevReset = 0; + + /** The UNIX time when the leaderboard is next playable. A computed value. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 NextReset = 0; + + /** Additional information stored as a JSON object. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Metadata; + + /** The UNIX time (for gRPC clients) or ISO string (for REST clients) when the leaderboard was created. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime CreateTime; + + /** Whether the leaderboard was created authoritatively or not. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + bool Authoritative = false; + + static FNakamaLeaderboard FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A list of leaderboards +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaLeaderboardList +{ + GENERATED_BODY() + + /** The list of leaderboards returned. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Leaderboards; + + /** A pagination cursor (optional). */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Cursor; + + static FNakamaLeaderboardList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Represents a complete leaderboard record with all scores and associated metadata. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaLeaderboardRecord +{ + GENERATED_BODY() + + /** The ID of the leaderboard this score belongs to. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString LeaderboardId; + + /** The ID of the score owner, usually a user or group. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString OwnerId; + + /** The username of the score owner, if the owner is a user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Username; + + /** The score value. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int64 Score = 0; + + /** An optional subscore value. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int64 Subscore = 0; + + /** The number of submissions to this score record. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 NumScore = 0; + + /** Metadata. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Metadata; + + /** The UNIX time (for gRPC clients) or ISO string (for REST clients) when the leaderboard record was created. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime CreateTime; + + /** The UNIX time (for gRPC clients) or ISO string (for REST clients) when the leaderboard record was updated. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime UpdateTime; + + /** The UNIX time (for gRPC clients) or ISO string (for REST clients) when the leaderboard record expires. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime ExpiryTime; + + /** The rank of this record. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int64 Rank = 0; + + /** The maximum number of score updates allowed by the owner. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 MaxNumScore = 0; + + static FNakamaLeaderboardRecord FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A set of leaderboard records, may be part of a leaderboard records page or a batch of individual records. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaLeaderboardRecordList +{ + GENERATED_BODY() + + /** A list of leaderboard records. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Records; + + /** A batched set of leaderboard records belonging to specified owners. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray OwnerRecords; + + /** The cursor to send when retrieving the next page, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString NextCursor; + + /** The cursor to send when retrieving the previous page, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString PrevCursor; + + /** The total number of ranks available. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int64 RankCount = 0; + + static FNakamaLeaderboardRecordList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Leave a group. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaLeaveGroupRequest +{ + GENERATED_BODY() + + /** The group ID to leave. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString GroupId; + + static FNakamaLeaveGroupRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Link Facebook to the current user's account. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaLinkFacebookRequest +{ + GENERATED_BODY() + + /** The Facebook account details. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaAccountFacebook Account; + + /** Import Facebook friends for the user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Sync; + + static FNakamaLinkFacebookRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Link Steam to the current user's account. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaLinkSteamRequest +{ + GENERATED_BODY() + + /** The Facebook account details. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaAccountSteam Account; + + /** Import Steam friends for the user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Sync; + + static FNakamaLinkSteamRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* List a channel's message history. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaListChannelMessagesRequest +{ + GENERATED_BODY() + + /** The channel ID to list from. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString ChannelId; + + /** Max number of records to return. Between 1 and 100. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 Limit; + + /** True if listing should be older messages to newer, false if reverse. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Forward; + + /** A pagination cursor, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Cursor; + + static FNakamaListChannelMessagesRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* List friends for a user. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaListFriendsRequest +{ + GENERATED_BODY() + + /** Max number of records to return. Between 1 and 1000. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 Limit; + + /** The friend state to list. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 State; + + /** An optional next page cursor. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Cursor; + + static FNakamaListFriendsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaListFriendsOfFriendsRequest +{ + GENERATED_BODY() + + /** Max number of records to return. Between 1 and 100. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 Limit; + + /** An optional next page cursor. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Cursor; + + static FNakamaListFriendsOfFriendsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* List groups based on given filters. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaListGroupsRequest +{ + GENERATED_BODY() + + /** List groups that contain this value in their names. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Name; + + /** Optional pagination cursor. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Cursor; + + /** Max number of groups to return. Between 1 and 100. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 Limit; + + /** Language tag filter */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString LangTag; + + /** Number of group members */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 Members; + + /** Optional Open/Closed filter. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Open; + + static FNakamaListGroupsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* List all users that are part of a group. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaListGroupUsersRequest +{ + GENERATED_BODY() + + /** The group ID to list from. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString GroupId; + + /** Max number of records to return. Between 1 and 100. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 Limit; + + /** The group user state to list. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 State; + + /** An optional next page cursor. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Cursor; + + static FNakamaListGroupUsersRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* List leaerboard records from a given leaderboard around the owner. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaListLeaderboardRecordsAroundOwnerRequest +{ + GENERATED_BODY() + + /** The ID of the tournament to list for. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString LeaderboardId; + + /** Max number of records to return. Between 1 and 100. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 Limit; + + /** The owner to retrieve records around. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString OwnerId; + + /** Expiry in seconds (since epoch) to begin fetching records from. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt64 Expiry; + + /** A next or previous page cursor. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Cursor; + + static FNakamaListLeaderboardRecordsAroundOwnerRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* List leaderboard records from a given leaderboard. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaListLeaderboardRecordsRequest +{ + GENERATED_BODY() + + /** The ID of the leaderboard to list for. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString LeaderboardId; + + /** One or more owners to retrieve records for. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray OwnerIds; + + /** Max number of records to return. Between 1 and 100. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 Limit; + + /** A next or previous page cursor. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Cursor; + + /** Expiry in seconds (since epoch) to begin fetching records from. Optional. 0 means from current time. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt64 Expiry; + + static FNakamaListLeaderboardRecordsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* List realtime matches. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaListMatchesRequest +{ + GENERATED_BODY() + + /** Limit the number of returned matches. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 Limit; + + /** Authoritative or relayed matches. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Authoritative; + + /** Label filter. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Label; + + /** Minimum user count. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 MinSize; + + /** Maximum user count. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 MaxSize; + + /** Arbitrary label query. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Query; + + static FNakamaListMatchesRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Get a list of unexpired notifications. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaListNotificationsRequest +{ + GENERATED_BODY() + + /** The number of notifications to get. Between 1 and 100. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 Limit; + + /** A cursor to page through notifications. May be cached by clients to get from point in time forwards. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString CacheableCursor; + + static FNakamaListNotificationsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* List publicly readable storage objects in a given collection. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaListStorageObjectsRequest +{ + GENERATED_BODY() + + /** ID of the user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString UserId; + + /** The collection which stores the object. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Collection; + + /** The number of storage objects to list. Between 1 and 100. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 Limit; + + /** The cursor to page through results from. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Cursor; + + static FNakamaListStorageObjectsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* List user subscriptions. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaListSubscriptionsRequest +{ + GENERATED_BODY() + + /** Max number of results per page */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 Limit; + + /** Cursor to retrieve a page of records from */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Cursor; + + static FNakamaListSubscriptionsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* List tournament records from a given tournament around the owner. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaListTournamentRecordsAroundOwnerRequest +{ + GENERATED_BODY() + + /** The ID of the tournament to list for. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString TournamentId; + + /** Max number of records to return. Between 1 and 100. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 Limit; + + /** The owner to retrieve records around. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString OwnerId; + + /** Expiry in seconds (since epoch) to begin fetching records from. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt64 Expiry; + + /** A next or previous page cursor. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Cursor; + + static FNakamaListTournamentRecordsAroundOwnerRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* List tournament records from a given tournament. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaListTournamentRecordsRequest +{ + GENERATED_BODY() + + /** The ID of the tournament to list for. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString TournamentId; + + /** One or more owners to retrieve records for. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray OwnerIds; + + /** Max number of records to return. Between 1 and 100. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 Limit; + + /** A next or previous page cursor. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Cursor; + + /** Expiry in seconds (since epoch) to begin fetching records from. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt64 Expiry; + + static FNakamaListTournamentRecordsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* List active/upcoming tournaments based on given filters. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaListTournamentsRequest +{ + GENERATED_BODY() + + /** The start of the categories to include. Defaults to 0. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 CategoryStart; + + /** The end of the categories to include. Defaults to 128. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 CategoryEnd; + + /** The start time for tournaments. Defaults to epoch. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 StartTime; + + /** The end time for tournaments. Defaults to +1 year from current Unix time. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 EndTime; + + /** Max number of records to return. Between 1 and 100. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 Limit; + + /** A next page cursor for listings (optional). */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Cursor; + + static FNakamaListTournamentsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* List the groups a user is part of, and their relationship to each. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaListUserGroupsRequest +{ + GENERATED_BODY() + + /** ID of the user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString UserId; + + /** Max number of records to return. Between 1 and 100. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 Limit; + + /** The user group state to list. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 State; + + /** An optional next page cursor. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Cursor; + + static FNakamaListUserGroupsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Represents a realtime match. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaMatch +{ + GENERATED_BODY() + + /** The ID of the match, can be used to join. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString MatchId; + + /** True if it's an server-managed authoritative match, false otherwise. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + bool Authoritative = false; + + /** Match label, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Label; + + /** Current number of users in the match. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 Size = 0; + + /** Tick Rate */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 TickRate = 0; + + /** Handler name */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString HandlerName; + + static FNakamaMatch FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A list of realtime matches. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaMatchList +{ + GENERATED_BODY() + + /** A number of matches corresponding to a list operation. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Matches; + + static FNakamaMatchList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Matchmaker ticket completion stats +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaMatchmakerCompletionStats +{ + GENERATED_BODY() + + /** */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime CreateTime; + + /** */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime CompleteTime; + + static FNakamaMatchmakerCompletionStats FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Matchmaker stats +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaMatchmakerStats +{ + GENERATED_BODY() + + /** */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 TicketCount = 0; + + /** */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime OldestTicketCreateTime; + + /** */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Completions; + + static FNakamaMatchmakerStats FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A notification in the server. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaNotification +{ + GENERATED_BODY() + + /** ID of the Notification. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Id; + + /** Subject of the notification. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Subject; + + /** Content of the notification in JSON. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Content; + + /** Category code for this notification. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 Code = 0; + + /** ID of the sender, if a user. Otherwise 'null'. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString SenderId; + + /** The UNIX time (for gRPC clients) or ISO string (for REST clients) when the notification was created. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime CreateTime; + + /** True if this notification was persisted to the database. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + bool Persistent = false; + + static FNakamaNotification FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A collection of zero or more notifications. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaNotificationList +{ + GENERATED_BODY() + + /** Collection of notifications. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Notifications; + + /** Use this cursor to paginate notifications. Cache this to catch up to new notifications. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString CacheableCursor; + + static FNakamaNotificationList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Promote a set of users in a group to the next role up. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaPromoteGroupUsersRequest +{ + GENERATED_BODY() + + /** The group ID to promote in. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString GroupId; + + /** The users to promote. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray UserIds; + + static FNakamaPromoteGroupUsersRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Demote a set of users in a group to the next role down. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaDemoteGroupUsersRequest +{ + GENERATED_BODY() + + /** The group ID to demote in. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString GroupId; + + /** The users to demote. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray UserIds; + + static FNakamaDemoteGroupUsersRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Storage objects to get. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaReadStorageObjectId +{ + GENERATED_BODY() + + /** The collection which stores the object. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Collection; + + /** The key of the object within the collection. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Key; + + /** The user owner of the object. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString UserId; + + static FNakamaReadStorageObjectId FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Batch get storage objects. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaReadStorageObjectsRequest +{ + GENERATED_BODY() + + /** Batch of storage objects. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray ObjectIds; + + static FNakamaReadStorageObjectsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Execute an Lua function on the server. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaRpc +{ + GENERATED_BODY() + + /** The identifier of the function. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Id; + + /** The payload of the function which must be a JSON object. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Payload; + + /** The authentication key used when executed as a non-client HTTP request. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString HttpKey; + + static FNakamaRpc FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* An object within the storage engine. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaStorageObject +{ + GENERATED_BODY() + + /** The collection which stores the object. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Collection; + + /** The key of the object within the collection. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Key; + + /** The user owner of the object. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString UserId; + + /** The value of the object. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Value; + + /** The version hash of the object. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Version; + + /** The read access permissions for the object. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 PermissionRead = 0; + + /** The write access permissions for the object. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 PermissionWrite = 0; + + /** The UNIX time (for gRPC clients) or ISO string (for REST clients) when the object was created. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime CreateTime; + + /** The UNIX time (for gRPC clients) or ISO string (for REST clients) when the object was last updated. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime UpdateTime; + + static FNakamaStorageObject FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A storage acknowledgement. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaStorageObjectAck +{ + GENERATED_BODY() + + /** The collection which stores the object. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Collection; + + /** The key of the object within the collection. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Key; + + /** The version hash of the object. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Version; + + /** The owner of the object. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString UserId; + + /** The UNIX time (for gRPC clients) or ISO string (for REST clients) when the object was created. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime CreateTime; + + /** The UNIX time (for gRPC clients) or ISO string (for REST clients) when the object was last updated. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime UpdateTime; + + static FNakamaStorageObjectAck FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Batch of acknowledgements for the storage object write. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaStorageObjectAcks +{ + GENERATED_BODY() + + /** Batch of storage write acknowledgements. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Acks; + + static FNakamaStorageObjectAcks FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Batch of storage objects. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaStorageObjects +{ + GENERATED_BODY() + + /** The batch of storage objects. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Objects; + + static FNakamaStorageObjects FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* List of storage objects. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaStorageObjectList +{ + GENERATED_BODY() + + /** The list of storage objects. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Objects; + + /** The cursor for the next page of results, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Cursor; + + static FNakamaStorageObjectList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A tournament on the server. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaTournament +{ + GENERATED_BODY() + + /** The ID of the tournament. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Id; + + /** The title for the tournament. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Title; + + /** The description of the tournament. May be blank. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Description; + + /** The category of the tournament. e.g. "vip" could be category 1. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 Category = 0; + + /** ASC (0) or DESC (1) sort mode of scores in the tournament. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 SortOrder = 0; + + /** The current number of players in the tournament. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 Size = 0; + + /** The maximum number of players for the tournament. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 MaxSize = 0; + + /** The maximum score updates allowed per player for the current tournament. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 MaxNumScore = 0; + + /** True if the tournament is active and can enter. A computed value. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + bool CanEnter = false; + + /** The UNIX time when the tournament stops being active until next reset. A computed value. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 EndActive = 0; + + /** The UNIX time when the tournament is next playable. A computed value. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 NextReset = 0; + + /** Additional information stored as a JSON object. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Metadata; + + /** The UNIX time (for gRPC clients) or ISO string (for REST clients) when the tournament was created. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime CreateTime; + + /** The UNIX time (for gRPC clients) or ISO string (for REST clients) when the tournament will start. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime StartTime; + + /** The UNIX time (for gRPC clients) or ISO string (for REST clients) when the tournament will be stopped. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime EndTime; + + /** Duration of the tournament in seconds. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 Duration = 0; + + /** The UNIX time when the tournament start being active. A computed value. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 StartActive = 0; + + /** The UNIX time when the tournament was last reset. A computed value. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 PrevReset = 0; + + /** Operator. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + ENakamaOperator Operator = static_cast(0); + + /** Whether the leaderboard was created authoritatively or not. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + bool Authoritative = false; + + /** Whether the user must join the tournament before being able to submit scores. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + bool JoinRequired = false; + + static FNakamaTournament FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A list of tournaments. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaTournamentList +{ + GENERATED_BODY() + + /** The list of tournaments returned. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Tournaments; + + /** A pagination cursor (optional). */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Cursor; + + static FNakamaTournamentList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A set of tournament records which may be part of a tournament records page or a batch of individual records. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaTournamentRecordList +{ + GENERATED_BODY() + + /** A list of tournament records. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Records; + + /** A batched set of tournament records belonging to specified owners. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray OwnerRecords; + + /** The cursor to send when retireving the next page (optional). */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString NextCursor; + + /** The cursor to send when retrieving the previous page (optional). */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString PrevCursor; + + /** The total number of ranks available. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int64 RankCount = 0; + + static FNakamaTournamentRecordList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Update a user's account details. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaUpdateAccountRequest +{ + GENERATED_BODY() + + /** The username of the user's account. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Username; + + /** The display name of the user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString DisplayName; + + /** A URL for an avatar image. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString AvatarUrl; + + /** The language expected to be a tag which follows the BCP-47 spec. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString LangTag; + + /** The location set by the user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Location; + + /** The timezone set by the user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Timezone; + + static FNakamaUpdateAccountRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Update fields in a given group. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaUpdateGroupRequest +{ + GENERATED_BODY() + + /** The ID of the group to update. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString GroupId; + + /** Name. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Name; + + /** Description string. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Description; + + /** Lang tag. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString LangTag; + + /** Avatar URL. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString AvatarUrl; + + /** Open is true if anyone should be allowed to join, or false if joins must be approved by a group admin. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Open; + + static FNakamaUpdateGroupRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A single group-role pair. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaUserGroupListUserGroup +{ + GENERATED_BODY() + + /** Group. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaGroup Group; + + /** The user's relationship to the group. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 State; + + static FNakamaUserGroupListUserGroup FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A list of groups belonging to a user, along with the user's role in each group. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaUserGroupList +{ + GENERATED_BODY() + + /** Group-role pairs for a user. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray UserGroups; + + /** Cursor for the next page of results, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Cursor; + + static FNakamaUserGroupList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A collection of zero or more users. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaUsers +{ + GENERATED_BODY() + + /** The User objects. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Users; + + static FNakamaUsers FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Apple IAP Purchases validation request +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaValidatePurchaseAppleRequest +{ + GENERATED_BODY() + + /** Base64 encoded Apple receipt data payload. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Receipt; + + /** Persist the purchase */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Persist; + + static FNakamaValidatePurchaseAppleRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Apple Subscription validation request +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaValidateSubscriptionAppleRequest +{ + GENERATED_BODY() + + /** Base64 encoded Apple receipt data payload. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Receipt; + + /** Persist the subscription. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Persist; + + static FNakamaValidateSubscriptionAppleRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Google IAP Purchase validation request +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaValidatePurchaseGoogleRequest +{ + GENERATED_BODY() + + /** JSON encoded Google purchase payload. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Purchase; + + /** Persist the purchase */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Persist; + + static FNakamaValidatePurchaseGoogleRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Google Subscription validation request +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaValidateSubscriptionGoogleRequest +{ + GENERATED_BODY() + + /** JSON encoded Google purchase payload. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Receipt; + + /** Persist the subscription. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Persist; + + static FNakamaValidateSubscriptionGoogleRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Huawei IAP Purchase validation request +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaValidatePurchaseHuaweiRequest +{ + GENERATED_BODY() + + /** JSON encoded Huawei InAppPurchaseData. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Purchase; + + /** InAppPurchaseData signature. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Signature; + + /** Persist the purchase */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Persist; + + static FNakamaValidatePurchaseHuaweiRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Facebook Instant IAP Purchase validation request +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaValidatePurchaseFacebookInstantRequest +{ + GENERATED_BODY() + + /** Base64 encoded Facebook Instant signedRequest receipt data payload. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString SignedRequest; + + /** Persist the purchase */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Persist; + + static FNakamaValidatePurchaseFacebookInstantRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Validated Purchase stored by Nakama. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaValidatedPurchase +{ + GENERATED_BODY() + + /** Purchase User ID. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString UserId; + + /** Purchase Product ID. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString ProductId; + + /** Purchase Transaction ID. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString TransactionId; + + /** Store identifier */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + ENakamaStoreProvider Store = static_cast(0); + + /** Timestamp when the purchase was done. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime PurchaseTime; + + /** Timestamp when the receipt validation was stored in DB. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime CreateTime; + + /** Timestamp when the receipt validation was updated in DB. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime UpdateTime; + + /** Timestamp when the purchase was refunded. Set to UNIX */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime RefundTime; + + /** Raw provider validation response. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString ProviderResponse; + + /** Whether the purchase was done in production or sandbox environment. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + ENakamaStoreEnvironment Environment = static_cast(0); + + /** Whether the purchase had already been validated by Nakama before. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + bool SeenBefore = false; + + static FNakamaValidatedPurchase FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Validate IAP response. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaValidatePurchaseResponse +{ + GENERATED_BODY() + + /** Newly seen validated purchases. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray ValidatedPurchases; + + static FNakamaValidatePurchaseResponse FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaValidatedSubscription +{ + GENERATED_BODY() + + /** Subscription User ID. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString UserId; + + /** Purchase Product ID. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString ProductId; + + /** Purchase Original transaction ID (we only keep track of the original subscription, not subsequent renewals). */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString OriginalTransactionId; + + /** Store identifier */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + ENakamaStoreProvider Store = static_cast(0); + + /** UNIX Timestamp when the purchase was done. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime PurchaseTime; + + /** UNIX Timestamp when the receipt validation was stored in DB. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime CreateTime; + + /** UNIX Timestamp when the receipt validation was updated in DB. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime UpdateTime; + + /** Whether the purchase was done in production or sandbox environment. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + ENakamaStoreEnvironment Environment = static_cast(0); + + /** Subscription expiration time. The subscription can still be auto-renewed to extend the expiration time further. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime ExpiryTime; + + /** Subscription refund time. If this time is set, the subscription was refunded. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FDateTime RefundTime; + + /** Raw provider validation response body. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString ProviderResponse; + + /** Raw provider notification body. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString ProviderNotification; + + /** Whether the subscription is currently active or not. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + bool Active = false; + + static FNakamaValidatedSubscription FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Validate Subscription response. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaValidateSubscriptionResponse +{ + GENERATED_BODY() + + /** */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaValidatedSubscription ValidatedSubscription; + + static FNakamaValidateSubscriptionResponse FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A list of validated purchases stored by Nakama. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaPurchaseList +{ + GENERATED_BODY() + + /** Stored validated purchases. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray ValidatedPurchases; + + /** The cursor to send when retrieving the next page, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Cursor; + + /** The cursor to send when retrieving the previous page, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString PrevCursor; + + static FNakamaPurchaseList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A list of validated subscriptions stored by Nakama. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaSubscriptionList +{ + GENERATED_BODY() + + /** Stored validated subscriptions. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray ValidatedSubscriptions; + + /** The cursor to send when retrieving the next page, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Cursor; + + /** The cursor to send when retrieving the previous page, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString PrevCursor; + + static FNakamaSubscriptionList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Record values to write. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaWriteLeaderboardRecordRequestLeaderboardRecordWrite +{ + GENERATED_BODY() + + /** The score value to submit. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int64 Score = 0; + + /** An optional secondary value. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int64 Subscore = 0; + + /** Optional record metadata. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Metadata; + + /** Operator override. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + ENakamaOperator Operator = static_cast(0); + + static FNakamaWriteLeaderboardRecordRequestLeaderboardRecordWrite FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A request to submit a score to a leaderboard. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaWriteLeaderboardRecordRequest +{ + GENERATED_BODY() + + /** The ID of the leaderboard to write to. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString LeaderboardId; + + /** Record input. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaWriteLeaderboardRecordRequestLeaderboardRecordWrite Record; + + static FNakamaWriteLeaderboardRecordRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* The object to store. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaWriteStorageObject +{ + GENERATED_BODY() + + /** The collection to store the object. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Collection; + + /** The key for the object within the collection. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Key; + + /** The value of the object. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Value; + + /** The version hash of the object to check. Possible values are: ["", "*", "#hash#"]. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Version; + + /** The read access permissions for the object. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 PermissionRead; + + /** The write access permissions for the object. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 PermissionWrite; + + static FNakamaWriteStorageObject FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Write objects to the storage engine. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaWriteStorageObjectsRequest +{ + GENERATED_BODY() + + /** The objects to store on the server. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Objects; + + static FNakamaWriteStorageObjectsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Record values to write. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaWriteTournamentRecordRequestTournamentRecordWrite +{ + GENERATED_BODY() + + /** The score value to submit. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int64 Score = 0; + + /** An optional secondary value. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int64 Subscore = 0; + + /** A JSON object of additional properties (optional). */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Metadata; + + /** Operator override. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + ENakamaOperator Operator = static_cast(0); + + static FNakamaWriteTournamentRecordRequestTournamentRecordWrite FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A request to submit a score to a tournament. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaWriteTournamentRecordRequest +{ + GENERATED_BODY() + + /** The tournament ID to write the record for. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString TournamentId; + + /** Record input. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaWriteTournamentRecordRequestTournamentRecordWrite Record; + + static FNakamaWriteTournamentRecordRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A request to list parties. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaListPartiesRequest +{ + GENERATED_BODY() + + /** Limit the number of returned parties. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalInt32 Limit; + + /** Optionally filter by open/closed parties. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FNakamaOptionalBool Open; + + /** Arbitrary label query. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Query; + + /** Cursor for the next page of results, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Cursor; + + static FNakamaListPartiesRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Incoming information about a party. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaParty +{ + GENERATED_BODY() + + /** Unique party identifier. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString PartyId; + + /** Open flag. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + bool Open = false; + + /** Hidden flag. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + bool Hidden = false; + + /** Maximum number of party members. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + int32 MaxSize = 0; + + /** The party label, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Label; + + static FNakamaParty FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A list of realtime matches. +*/ +USTRUCT(BlueprintType) +struct NAKAMAAPI_API FNakamaPartyList +{ + GENERATED_BODY() + + /** A number of parties corresponding to a list operation. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + TArray Parties; + + /** A cursor to send when retrieving the next page, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Nakama") + FString Cursor; + + static FNakamaPartyList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + diff --git a/Nakama/Source/NakamaBlueprints/NakamaBlueprints.Build.cs b/Nakama/Source/NakamaBlueprints/NakamaBlueprints.Build.cs index 01ce700c2..1c8c199a6 100644 --- a/Nakama/Source/NakamaBlueprints/NakamaBlueprints.Build.cs +++ b/Nakama/Source/NakamaBlueprints/NakamaBlueprints.Build.cs @@ -40,7 +40,8 @@ public NakamaBlueprints(ReadOnlyTargetRules Target) : base(Target) new string[] { "Core", - "NakamaUnreal" + "Nakama", + "NakamaApi", // ... add other public dependencies that you statically link with here ... } ); diff --git a/Nakama/Source/NakamaBlueprints/NakamaClientBlueprintLibrary.cpp b/Nakama/Source/NakamaBlueprints/NakamaClientBlueprintLibrary.cpp new file mode 100644 index 000000000..65f71e5fb --- /dev/null +++ b/Nakama/Source/NakamaBlueprints/NakamaClientBlueprintLibrary.cpp @@ -0,0 +1,4273 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + +#include "NakamaClientBlueprintLibrary.h" + +UNakamaClientAddFriends* UNakamaClientAddFriends::AddFriends( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const TArray& Ids + , const TArray& Usernames + , const FString& Metadata +) +{ + UNakamaClientAddFriends* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredIds = Ids; + Action->StoredUsernames = Usernames; + Action->StoredMetadata = Metadata; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientAddFriends::Activate() +{ + static const TCHAR* TraceScope_AddFriends = TEXT("NakamaBP_AddFriends"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_AddFriends); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::AddFriends( + StoredClientConfig, + StoredSession, + StoredIds, + StoredUsernames, + StoredMetadata, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientAddGroupUsers* UNakamaClientAddGroupUsers::AddGroupUsers( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& GroupId + , const TArray& UserIds +) +{ + UNakamaClientAddGroupUsers* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredGroupId = GroupId; + Action->StoredUserIds = UserIds; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientAddGroupUsers::Activate() +{ + static const TCHAR* TraceScope_AddGroupUsers = TEXT("NakamaBP_AddGroupUsers"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_AddGroupUsers); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::AddGroupUsers( + StoredClientConfig, + StoredSession, + StoredGroupId, + StoredUserIds, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientSessionRefresh* UNakamaClientSessionRefresh::SessionRefresh( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FString& Token + , const TMap& Vars +) +{ + UNakamaClientSessionRefresh* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredToken = Token; + Action->StoredVars = Vars; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientSessionRefresh::Activate() +{ + static const TCHAR* TraceScope_SessionRefresh = TEXT("NakamaBP_SessionRefresh"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_SessionRefresh); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::SessionRefresh( + StoredClientConfig, + StoredToken, + StoredVars, + [WeakThis](const FNakamaSession& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientSessionLogout* UNakamaClientSessionLogout::SessionLogout( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Token + , const FString& RefreshToken +) +{ + UNakamaClientSessionLogout* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredToken = Token; + Action->StoredRefreshToken = RefreshToken; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientSessionLogout::Activate() +{ + static const TCHAR* TraceScope_SessionLogout = TEXT("NakamaBP_SessionLogout"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_SessionLogout); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::SessionLogout( + StoredClientConfig, + StoredSession, + StoredToken, + StoredRefreshToken, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientAuthenticateApple* UNakamaClientAuthenticateApple::AuthenticateApple( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaAccountApple& Account + , FNakamaOptionalBool Create + , const FString& Username +) +{ + UNakamaClientAuthenticateApple* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredAccount = Account; + Action->StoredCreate = Create; + Action->StoredUsername = Username; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientAuthenticateApple::Activate() +{ + static const TCHAR* TraceScope_AuthenticateApple = TEXT("NakamaBP_AuthenticateApple"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_AuthenticateApple); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::AuthenticateApple( + StoredClientConfig, + StoredAccount, + StoredCreate, + StoredUsername, + [WeakThis](const FNakamaSession& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientAuthenticateCustom* UNakamaClientAuthenticateCustom::AuthenticateCustom( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaAccountCustom& Account + , FNakamaOptionalBool Create + , const FString& Username +) +{ + UNakamaClientAuthenticateCustom* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredAccount = Account; + Action->StoredCreate = Create; + Action->StoredUsername = Username; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientAuthenticateCustom::Activate() +{ + static const TCHAR* TraceScope_AuthenticateCustom = TEXT("NakamaBP_AuthenticateCustom"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_AuthenticateCustom); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::AuthenticateCustom( + StoredClientConfig, + StoredAccount, + StoredCreate, + StoredUsername, + [WeakThis](const FNakamaSession& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientAuthenticateDevice* UNakamaClientAuthenticateDevice::AuthenticateDevice( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaAccountDevice& Account + , FNakamaOptionalBool Create + , const FString& Username +) +{ + UNakamaClientAuthenticateDevice* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredAccount = Account; + Action->StoredCreate = Create; + Action->StoredUsername = Username; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientAuthenticateDevice::Activate() +{ + static const TCHAR* TraceScope_AuthenticateDevice = TEXT("NakamaBP_AuthenticateDevice"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_AuthenticateDevice); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::AuthenticateDevice( + StoredClientConfig, + StoredAccount, + StoredCreate, + StoredUsername, + [WeakThis](const FNakamaSession& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientAuthenticateEmail* UNakamaClientAuthenticateEmail::AuthenticateEmail( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaAccountEmail& Account + , FNakamaOptionalBool Create + , const FString& Username +) +{ + UNakamaClientAuthenticateEmail* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredAccount = Account; + Action->StoredCreate = Create; + Action->StoredUsername = Username; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientAuthenticateEmail::Activate() +{ + static const TCHAR* TraceScope_AuthenticateEmail = TEXT("NakamaBP_AuthenticateEmail"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_AuthenticateEmail); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::AuthenticateEmail( + StoredClientConfig, + StoredAccount, + StoredCreate, + StoredUsername, + [WeakThis](const FNakamaSession& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientAuthenticateFacebook* UNakamaClientAuthenticateFacebook::AuthenticateFacebook( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaAccountFacebook& Account + , FNakamaOptionalBool Create + , const FString& Username + , FNakamaOptionalBool Sync +) +{ + UNakamaClientAuthenticateFacebook* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredAccount = Account; + Action->StoredCreate = Create; + Action->StoredUsername = Username; + Action->StoredSync = Sync; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientAuthenticateFacebook::Activate() +{ + static const TCHAR* TraceScope_AuthenticateFacebook = TEXT("NakamaBP_AuthenticateFacebook"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_AuthenticateFacebook); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::AuthenticateFacebook( + StoredClientConfig, + StoredAccount, + StoredCreate, + StoredUsername, + StoredSync, + [WeakThis](const FNakamaSession& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientAuthenticateFacebookInstantGame* UNakamaClientAuthenticateFacebookInstantGame::AuthenticateFacebookInstantGame( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaAccountFacebookInstantGame& Account + , FNakamaOptionalBool Create + , const FString& Username +) +{ + UNakamaClientAuthenticateFacebookInstantGame* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredAccount = Account; + Action->StoredCreate = Create; + Action->StoredUsername = Username; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientAuthenticateFacebookInstantGame::Activate() +{ + static const TCHAR* TraceScope_AuthenticateFacebookInstantGame = TEXT("NakamaBP_AuthenticateFacebookInstantGame"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_AuthenticateFacebookInstantGame); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::AuthenticateFacebookInstantGame( + StoredClientConfig, + StoredAccount, + StoredCreate, + StoredUsername, + [WeakThis](const FNakamaSession& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientAuthenticateGameCenter* UNakamaClientAuthenticateGameCenter::AuthenticateGameCenter( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaAccountGameCenter& Account + , FNakamaOptionalBool Create + , const FString& Username +) +{ + UNakamaClientAuthenticateGameCenter* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredAccount = Account; + Action->StoredCreate = Create; + Action->StoredUsername = Username; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientAuthenticateGameCenter::Activate() +{ + static const TCHAR* TraceScope_AuthenticateGameCenter = TEXT("NakamaBP_AuthenticateGameCenter"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_AuthenticateGameCenter); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::AuthenticateGameCenter( + StoredClientConfig, + StoredAccount, + StoredCreate, + StoredUsername, + [WeakThis](const FNakamaSession& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientAuthenticateGoogle* UNakamaClientAuthenticateGoogle::AuthenticateGoogle( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaAccountGoogle& Account + , FNakamaOptionalBool Create + , const FString& Username +) +{ + UNakamaClientAuthenticateGoogle* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredAccount = Account; + Action->StoredCreate = Create; + Action->StoredUsername = Username; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientAuthenticateGoogle::Activate() +{ + static const TCHAR* TraceScope_AuthenticateGoogle = TEXT("NakamaBP_AuthenticateGoogle"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_AuthenticateGoogle); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::AuthenticateGoogle( + StoredClientConfig, + StoredAccount, + StoredCreate, + StoredUsername, + [WeakThis](const FNakamaSession& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientAuthenticateSteam* UNakamaClientAuthenticateSteam::AuthenticateSteam( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaAccountSteam& Account + , FNakamaOptionalBool Create + , const FString& Username + , FNakamaOptionalBool Sync +) +{ + UNakamaClientAuthenticateSteam* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredAccount = Account; + Action->StoredCreate = Create; + Action->StoredUsername = Username; + Action->StoredSync = Sync; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientAuthenticateSteam::Activate() +{ + static const TCHAR* TraceScope_AuthenticateSteam = TEXT("NakamaBP_AuthenticateSteam"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_AuthenticateSteam); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::AuthenticateSteam( + StoredClientConfig, + StoredAccount, + StoredCreate, + StoredUsername, + StoredSync, + [WeakThis](const FNakamaSession& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientBanGroupUsers* UNakamaClientBanGroupUsers::BanGroupUsers( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& GroupId + , const TArray& UserIds +) +{ + UNakamaClientBanGroupUsers* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredGroupId = GroupId; + Action->StoredUserIds = UserIds; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientBanGroupUsers::Activate() +{ + static const TCHAR* TraceScope_BanGroupUsers = TEXT("NakamaBP_BanGroupUsers"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_BanGroupUsers); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::BanGroupUsers( + StoredClientConfig, + StoredSession, + StoredGroupId, + StoredUserIds, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientBlockFriends* UNakamaClientBlockFriends::BlockFriends( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const TArray& Ids + , const TArray& Usernames +) +{ + UNakamaClientBlockFriends* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredIds = Ids; + Action->StoredUsernames = Usernames; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientBlockFriends::Activate() +{ + static const TCHAR* TraceScope_BlockFriends = TEXT("NakamaBP_BlockFriends"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_BlockFriends); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::BlockFriends( + StoredClientConfig, + StoredSession, + StoredIds, + StoredUsernames, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientCreateGroup* UNakamaClientCreateGroup::CreateGroup( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Name + , const FString& Description + , const FString& LangTag + , const FString& AvatarUrl + , bool Open + , int32 MaxCount +) +{ + UNakamaClientCreateGroup* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredName = Name; + Action->StoredDescription = Description; + Action->StoredLangTag = LangTag; + Action->StoredAvatarUrl = AvatarUrl; + Action->StoredOpen = Open; + Action->StoredMaxCount = MaxCount; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientCreateGroup::Activate() +{ + static const TCHAR* TraceScope_CreateGroup = TEXT("NakamaBP_CreateGroup"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_CreateGroup); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::CreateGroup( + StoredClientConfig, + StoredSession, + StoredName, + StoredDescription, + StoredLangTag, + StoredAvatarUrl, + StoredOpen, + StoredMaxCount, + [WeakThis](const FNakamaGroup& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientDeleteAccount* UNakamaClientDeleteAccount::DeleteAccount( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session +) +{ + UNakamaClientDeleteAccount* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientDeleteAccount::Activate() +{ + static const TCHAR* TraceScope_DeleteAccount = TEXT("NakamaBP_DeleteAccount"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_DeleteAccount); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::DeleteAccount( + StoredClientConfig, + StoredSession, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientDeleteFriends* UNakamaClientDeleteFriends::DeleteFriends( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const TArray& Ids + , const TArray& Usernames +) +{ + UNakamaClientDeleteFriends* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredIds = Ids; + Action->StoredUsernames = Usernames; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientDeleteFriends::Activate() +{ + static const TCHAR* TraceScope_DeleteFriends = TEXT("NakamaBP_DeleteFriends"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_DeleteFriends); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::DeleteFriends( + StoredClientConfig, + StoredSession, + StoredIds, + StoredUsernames, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientDeleteGroup* UNakamaClientDeleteGroup::DeleteGroup( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& GroupId +) +{ + UNakamaClientDeleteGroup* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredGroupId = GroupId; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientDeleteGroup::Activate() +{ + static const TCHAR* TraceScope_DeleteGroup = TEXT("NakamaBP_DeleteGroup"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_DeleteGroup); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::DeleteGroup( + StoredClientConfig, + StoredSession, + StoredGroupId, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientDeleteLeaderboardRecord* UNakamaClientDeleteLeaderboardRecord::DeleteLeaderboardRecord( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& LeaderboardId +) +{ + UNakamaClientDeleteLeaderboardRecord* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredLeaderboardId = LeaderboardId; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientDeleteLeaderboardRecord::Activate() +{ + static const TCHAR* TraceScope_DeleteLeaderboardRecord = TEXT("NakamaBP_DeleteLeaderboardRecord"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_DeleteLeaderboardRecord); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::DeleteLeaderboardRecord( + StoredClientConfig, + StoredSession, + StoredLeaderboardId, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientDeleteNotifications* UNakamaClientDeleteNotifications::DeleteNotifications( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const TArray& Ids +) +{ + UNakamaClientDeleteNotifications* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredIds = Ids; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientDeleteNotifications::Activate() +{ + static const TCHAR* TraceScope_DeleteNotifications = TEXT("NakamaBP_DeleteNotifications"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_DeleteNotifications); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::DeleteNotifications( + StoredClientConfig, + StoredSession, + StoredIds, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientDeleteTournamentRecord* UNakamaClientDeleteTournamentRecord::DeleteTournamentRecord( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& TournamentId +) +{ + UNakamaClientDeleteTournamentRecord* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredTournamentId = TournamentId; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientDeleteTournamentRecord::Activate() +{ + static const TCHAR* TraceScope_DeleteTournamentRecord = TEXT("NakamaBP_DeleteTournamentRecord"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_DeleteTournamentRecord); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::DeleteTournamentRecord( + StoredClientConfig, + StoredSession, + StoredTournamentId, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientDeleteStorageObjects* UNakamaClientDeleteStorageObjects::DeleteStorageObjects( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const TArray& ObjectIds +) +{ + UNakamaClientDeleteStorageObjects* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredObjectIds = ObjectIds; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientDeleteStorageObjects::Activate() +{ + static const TCHAR* TraceScope_DeleteStorageObjects = TEXT("NakamaBP_DeleteStorageObjects"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_DeleteStorageObjects); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::DeleteStorageObjects( + StoredClientConfig, + StoredSession, + StoredObjectIds, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientEvent* UNakamaClientEvent::Event( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Name + , const FDateTime& Timestamp + , bool External + , const TMap& Properties +) +{ + UNakamaClientEvent* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredName = Name; + Action->StoredTimestamp = Timestamp; + Action->StoredExternal = External; + Action->StoredProperties = Properties; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientEvent::Activate() +{ + static const TCHAR* TraceScope_Event = TEXT("NakamaBP_Event"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_Event); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::Event( + StoredClientConfig, + StoredSession, + StoredName, + StoredTimestamp, + StoredExternal, + StoredProperties, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientGetAccount* UNakamaClientGetAccount::GetAccount( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session +) +{ + UNakamaClientGetAccount* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientGetAccount::Activate() +{ + static const TCHAR* TraceScope_GetAccount = TEXT("NakamaBP_GetAccount"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_GetAccount); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::GetAccount( + StoredClientConfig, + StoredSession, + [WeakThis](const FNakamaAccount& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientGetUsers* UNakamaClientGetUsers::GetUsers( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const TArray& Ids + , const TArray& Usernames + , const TArray& FacebookIds +) +{ + UNakamaClientGetUsers* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredIds = Ids; + Action->StoredUsernames = Usernames; + Action->StoredFacebookIds = FacebookIds; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientGetUsers::Activate() +{ + static const TCHAR* TraceScope_GetUsers = TEXT("NakamaBP_GetUsers"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_GetUsers); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::GetUsers( + StoredClientConfig, + StoredSession, + StoredIds, + StoredUsernames, + StoredFacebookIds, + [WeakThis](const FNakamaUsers& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientGetSubscription* UNakamaClientGetSubscription::GetSubscription( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& ProductId +) +{ + UNakamaClientGetSubscription* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredProductId = ProductId; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientGetSubscription::Activate() +{ + static const TCHAR* TraceScope_GetSubscription = TEXT("NakamaBP_GetSubscription"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_GetSubscription); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::GetSubscription( + StoredClientConfig, + StoredSession, + StoredProductId, + [WeakThis](const FNakamaValidatedSubscription& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientGetMatchmakerStats* UNakamaClientGetMatchmakerStats::GetMatchmakerStats( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session +) +{ + UNakamaClientGetMatchmakerStats* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientGetMatchmakerStats::Activate() +{ + static const TCHAR* TraceScope_GetMatchmakerStats = TEXT("NakamaBP_GetMatchmakerStats"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_GetMatchmakerStats); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::GetMatchmakerStats( + StoredClientConfig, + StoredSession, + [WeakThis](const FNakamaMatchmakerStats& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientHealthcheck* UNakamaClientHealthcheck::Healthcheck( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session +) +{ + UNakamaClientHealthcheck* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientHealthcheck::Activate() +{ + static const TCHAR* TraceScope_Healthcheck = TEXT("NakamaBP_Healthcheck"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_Healthcheck); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::Healthcheck( + StoredClientConfig, + StoredSession, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientImportFacebookFriends* UNakamaClientImportFacebookFriends::ImportFacebookFriends( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FNakamaAccountFacebook& Account + , FNakamaOptionalBool Reset +) +{ + UNakamaClientImportFacebookFriends* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredAccount = Account; + Action->StoredReset = Reset; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientImportFacebookFriends::Activate() +{ + static const TCHAR* TraceScope_ImportFacebookFriends = TEXT("NakamaBP_ImportFacebookFriends"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ImportFacebookFriends); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::ImportFacebookFriends( + StoredClientConfig, + StoredSession, + StoredAccount, + StoredReset, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientImportSteamFriends* UNakamaClientImportSteamFriends::ImportSteamFriends( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FNakamaAccountSteam& Account + , FNakamaOptionalBool Reset +) +{ + UNakamaClientImportSteamFriends* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredAccount = Account; + Action->StoredReset = Reset; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientImportSteamFriends::Activate() +{ + static const TCHAR* TraceScope_ImportSteamFriends = TEXT("NakamaBP_ImportSteamFriends"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ImportSteamFriends); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::ImportSteamFriends( + StoredClientConfig, + StoredSession, + StoredAccount, + StoredReset, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientJoinGroup* UNakamaClientJoinGroup::JoinGroup( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& GroupId +) +{ + UNakamaClientJoinGroup* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredGroupId = GroupId; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientJoinGroup::Activate() +{ + static const TCHAR* TraceScope_JoinGroup = TEXT("NakamaBP_JoinGroup"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_JoinGroup); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::JoinGroup( + StoredClientConfig, + StoredSession, + StoredGroupId, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientJoinTournament* UNakamaClientJoinTournament::JoinTournament( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& TournamentId +) +{ + UNakamaClientJoinTournament* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredTournamentId = TournamentId; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientJoinTournament::Activate() +{ + static const TCHAR* TraceScope_JoinTournament = TEXT("NakamaBP_JoinTournament"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_JoinTournament); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::JoinTournament( + StoredClientConfig, + StoredSession, + StoredTournamentId, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientKickGroupUsers* UNakamaClientKickGroupUsers::KickGroupUsers( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& GroupId + , const TArray& UserIds +) +{ + UNakamaClientKickGroupUsers* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredGroupId = GroupId; + Action->StoredUserIds = UserIds; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientKickGroupUsers::Activate() +{ + static const TCHAR* TraceScope_KickGroupUsers = TEXT("NakamaBP_KickGroupUsers"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_KickGroupUsers); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::KickGroupUsers( + StoredClientConfig, + StoredSession, + StoredGroupId, + StoredUserIds, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientLeaveGroup* UNakamaClientLeaveGroup::LeaveGroup( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& GroupId +) +{ + UNakamaClientLeaveGroup* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredGroupId = GroupId; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientLeaveGroup::Activate() +{ + static const TCHAR* TraceScope_LeaveGroup = TEXT("NakamaBP_LeaveGroup"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_LeaveGroup); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::LeaveGroup( + StoredClientConfig, + StoredSession, + StoredGroupId, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientLinkApple* UNakamaClientLinkApple::LinkApple( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Token + , const TMap& Vars +) +{ + UNakamaClientLinkApple* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredToken = Token; + Action->StoredVars = Vars; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientLinkApple::Activate() +{ + static const TCHAR* TraceScope_LinkApple = TEXT("NakamaBP_LinkApple"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_LinkApple); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::LinkApple( + StoredClientConfig, + StoredSession, + StoredToken, + StoredVars, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientLinkCustom* UNakamaClientLinkCustom::LinkCustom( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Id + , const TMap& Vars +) +{ + UNakamaClientLinkCustom* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredId = Id; + Action->StoredVars = Vars; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientLinkCustom::Activate() +{ + static const TCHAR* TraceScope_LinkCustom = TEXT("NakamaBP_LinkCustom"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_LinkCustom); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::LinkCustom( + StoredClientConfig, + StoredSession, + StoredId, + StoredVars, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientLinkDevice* UNakamaClientLinkDevice::LinkDevice( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Id + , const TMap& Vars +) +{ + UNakamaClientLinkDevice* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredId = Id; + Action->StoredVars = Vars; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientLinkDevice::Activate() +{ + static const TCHAR* TraceScope_LinkDevice = TEXT("NakamaBP_LinkDevice"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_LinkDevice); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::LinkDevice( + StoredClientConfig, + StoredSession, + StoredId, + StoredVars, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientLinkEmail* UNakamaClientLinkEmail::LinkEmail( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Email + , const FString& Password + , const TMap& Vars +) +{ + UNakamaClientLinkEmail* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredEmail = Email; + Action->StoredPassword = Password; + Action->StoredVars = Vars; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientLinkEmail::Activate() +{ + static const TCHAR* TraceScope_LinkEmail = TEXT("NakamaBP_LinkEmail"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_LinkEmail); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::LinkEmail( + StoredClientConfig, + StoredSession, + StoredEmail, + StoredPassword, + StoredVars, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientLinkFacebook* UNakamaClientLinkFacebook::LinkFacebook( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FNakamaAccountFacebook& Account + , FNakamaOptionalBool Sync +) +{ + UNakamaClientLinkFacebook* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredAccount = Account; + Action->StoredSync = Sync; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientLinkFacebook::Activate() +{ + static const TCHAR* TraceScope_LinkFacebook = TEXT("NakamaBP_LinkFacebook"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_LinkFacebook); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::LinkFacebook( + StoredClientConfig, + StoredSession, + StoredAccount, + StoredSync, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientLinkFacebookInstantGame* UNakamaClientLinkFacebookInstantGame::LinkFacebookInstantGame( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& SignedPlayerInfo + , const TMap& Vars +) +{ + UNakamaClientLinkFacebookInstantGame* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredSignedPlayerInfo = SignedPlayerInfo; + Action->StoredVars = Vars; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientLinkFacebookInstantGame::Activate() +{ + static const TCHAR* TraceScope_LinkFacebookInstantGame = TEXT("NakamaBP_LinkFacebookInstantGame"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_LinkFacebookInstantGame); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::LinkFacebookInstantGame( + StoredClientConfig, + StoredSession, + StoredSignedPlayerInfo, + StoredVars, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientLinkGameCenter* UNakamaClientLinkGameCenter::LinkGameCenter( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& PlayerId + , const FString& BundleId + , int64 TimestampSeconds + , const FString& Salt + , const FString& Signature + , const FString& PublicKeyUrl + , const TMap& Vars +) +{ + UNakamaClientLinkGameCenter* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredPlayerId = PlayerId; + Action->StoredBundleId = BundleId; + Action->StoredTimestampSeconds = TimestampSeconds; + Action->StoredSalt = Salt; + Action->StoredSignature = Signature; + Action->StoredPublicKeyUrl = PublicKeyUrl; + Action->StoredVars = Vars; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientLinkGameCenter::Activate() +{ + static const TCHAR* TraceScope_LinkGameCenter = TEXT("NakamaBP_LinkGameCenter"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_LinkGameCenter); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::LinkGameCenter( + StoredClientConfig, + StoredSession, + StoredPlayerId, + StoredBundleId, + StoredTimestampSeconds, + StoredSalt, + StoredSignature, + StoredPublicKeyUrl, + StoredVars, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientLinkGoogle* UNakamaClientLinkGoogle::LinkGoogle( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Token + , const TMap& Vars +) +{ + UNakamaClientLinkGoogle* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredToken = Token; + Action->StoredVars = Vars; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientLinkGoogle::Activate() +{ + static const TCHAR* TraceScope_LinkGoogle = TEXT("NakamaBP_LinkGoogle"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_LinkGoogle); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::LinkGoogle( + StoredClientConfig, + StoredSession, + StoredToken, + StoredVars, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientLinkSteam* UNakamaClientLinkSteam::LinkSteam( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FNakamaAccountSteam& Account + , FNakamaOptionalBool Sync +) +{ + UNakamaClientLinkSteam* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredAccount = Account; + Action->StoredSync = Sync; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientLinkSteam::Activate() +{ + static const TCHAR* TraceScope_LinkSteam = TEXT("NakamaBP_LinkSteam"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_LinkSteam); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::LinkSteam( + StoredClientConfig, + StoredSession, + StoredAccount, + StoredSync, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientListChannelMessages* UNakamaClientListChannelMessages::ListChannelMessages( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& ChannelId + , FNakamaOptionalInt32 Limit + , FNakamaOptionalBool Forward + , const FString& Cursor +) +{ + UNakamaClientListChannelMessages* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredChannelId = ChannelId; + Action->StoredLimit = Limit; + Action->StoredForward = Forward; + Action->StoredCursor = Cursor; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientListChannelMessages::Activate() +{ + static const TCHAR* TraceScope_ListChannelMessages = TEXT("NakamaBP_ListChannelMessages"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ListChannelMessages); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::ListChannelMessages( + StoredClientConfig, + StoredSession, + StoredChannelId, + StoredLimit, + StoredForward, + StoredCursor, + [WeakThis](const FNakamaChannelMessageList& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientListFriends* UNakamaClientListFriends::ListFriends( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , FNakamaOptionalInt32 Limit + , FNakamaOptionalInt32 State + , const FString& Cursor +) +{ + UNakamaClientListFriends* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredLimit = Limit; + Action->StoredState = State; + Action->StoredCursor = Cursor; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientListFriends::Activate() +{ + static const TCHAR* TraceScope_ListFriends = TEXT("NakamaBP_ListFriends"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ListFriends); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::ListFriends( + StoredClientConfig, + StoredSession, + StoredLimit, + StoredState, + StoredCursor, + [WeakThis](const FNakamaFriendList& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientListFriendsOfFriends* UNakamaClientListFriendsOfFriends::ListFriendsOfFriends( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , FNakamaOptionalInt32 Limit + , const FString& Cursor +) +{ + UNakamaClientListFriendsOfFriends* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredLimit = Limit; + Action->StoredCursor = Cursor; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientListFriendsOfFriends::Activate() +{ + static const TCHAR* TraceScope_ListFriendsOfFriends = TEXT("NakamaBP_ListFriendsOfFriends"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ListFriendsOfFriends); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::ListFriendsOfFriends( + StoredClientConfig, + StoredSession, + StoredLimit, + StoredCursor, + [WeakThis](const FNakamaFriendsOfFriendsList& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientListGroups* UNakamaClientListGroups::ListGroups( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Name + , const FString& Cursor + , FNakamaOptionalInt32 Limit + , const FString& LangTag + , FNakamaOptionalInt32 Members + , FNakamaOptionalBool Open +) +{ + UNakamaClientListGroups* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredName = Name; + Action->StoredCursor = Cursor; + Action->StoredLimit = Limit; + Action->StoredLangTag = LangTag; + Action->StoredMembers = Members; + Action->StoredOpen = Open; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientListGroups::Activate() +{ + static const TCHAR* TraceScope_ListGroups = TEXT("NakamaBP_ListGroups"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ListGroups); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::ListGroups( + StoredClientConfig, + StoredSession, + StoredName, + StoredCursor, + StoredLimit, + StoredLangTag, + StoredMembers, + StoredOpen, + [WeakThis](const FNakamaGroupList& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientListGroupUsers* UNakamaClientListGroupUsers::ListGroupUsers( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& GroupId + , FNakamaOptionalInt32 Limit + , FNakamaOptionalInt32 State + , const FString& Cursor +) +{ + UNakamaClientListGroupUsers* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredGroupId = GroupId; + Action->StoredLimit = Limit; + Action->StoredState = State; + Action->StoredCursor = Cursor; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientListGroupUsers::Activate() +{ + static const TCHAR* TraceScope_ListGroupUsers = TEXT("NakamaBP_ListGroupUsers"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ListGroupUsers); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::ListGroupUsers( + StoredClientConfig, + StoredSession, + StoredGroupId, + StoredLimit, + StoredState, + StoredCursor, + [WeakThis](const FNakamaGroupUserList& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientListLeaderboardRecords* UNakamaClientListLeaderboardRecords::ListLeaderboardRecords( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& LeaderboardId + , const TArray& OwnerIds + , FNakamaOptionalInt32 Limit + , const FString& Cursor + , FNakamaOptionalInt64 Expiry +) +{ + UNakamaClientListLeaderboardRecords* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredLeaderboardId = LeaderboardId; + Action->StoredOwnerIds = OwnerIds; + Action->StoredLimit = Limit; + Action->StoredCursor = Cursor; + Action->StoredExpiry = Expiry; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientListLeaderboardRecords::Activate() +{ + static const TCHAR* TraceScope_ListLeaderboardRecords = TEXT("NakamaBP_ListLeaderboardRecords"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ListLeaderboardRecords); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::ListLeaderboardRecords( + StoredClientConfig, + StoredSession, + StoredLeaderboardId, + StoredOwnerIds, + StoredLimit, + StoredCursor, + StoredExpiry, + [WeakThis](const FNakamaLeaderboardRecordList& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientListLeaderboardRecordsAroundOwner* UNakamaClientListLeaderboardRecordsAroundOwner::ListLeaderboardRecordsAroundOwner( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& LeaderboardId + , FNakamaOptionalInt32 Limit + , const FString& OwnerId + , FNakamaOptionalInt64 Expiry + , const FString& Cursor +) +{ + UNakamaClientListLeaderboardRecordsAroundOwner* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredLeaderboardId = LeaderboardId; + Action->StoredLimit = Limit; + Action->StoredOwnerId = OwnerId; + Action->StoredExpiry = Expiry; + Action->StoredCursor = Cursor; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientListLeaderboardRecordsAroundOwner::Activate() +{ + static const TCHAR* TraceScope_ListLeaderboardRecordsAroundOwner = TEXT("NakamaBP_ListLeaderboardRecordsAroundOwner"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ListLeaderboardRecordsAroundOwner); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::ListLeaderboardRecordsAroundOwner( + StoredClientConfig, + StoredSession, + StoredLeaderboardId, + StoredLimit, + StoredOwnerId, + StoredExpiry, + StoredCursor, + [WeakThis](const FNakamaLeaderboardRecordList& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientListMatches* UNakamaClientListMatches::ListMatches( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , FNakamaOptionalInt32 Limit + , FNakamaOptionalBool Authoritative + , const FString& Label + , FNakamaOptionalInt32 MinSize + , FNakamaOptionalInt32 MaxSize + , const FString& Query +) +{ + UNakamaClientListMatches* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredLimit = Limit; + Action->StoredAuthoritative = Authoritative; + Action->StoredLabel = Label; + Action->StoredMinSize = MinSize; + Action->StoredMaxSize = MaxSize; + Action->StoredQuery = Query; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientListMatches::Activate() +{ + static const TCHAR* TraceScope_ListMatches = TEXT("NakamaBP_ListMatches"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ListMatches); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::ListMatches( + StoredClientConfig, + StoredSession, + StoredLimit, + StoredAuthoritative, + StoredLabel, + StoredMinSize, + StoredMaxSize, + StoredQuery, + [WeakThis](const FNakamaMatchList& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientListParties* UNakamaClientListParties::ListParties( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , FNakamaOptionalInt32 Limit + , FNakamaOptionalBool Open + , const FString& Query + , const FString& Cursor +) +{ + UNakamaClientListParties* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredLimit = Limit; + Action->StoredOpen = Open; + Action->StoredQuery = Query; + Action->StoredCursor = Cursor; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientListParties::Activate() +{ + static const TCHAR* TraceScope_ListParties = TEXT("NakamaBP_ListParties"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ListParties); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::ListParties( + StoredClientConfig, + StoredSession, + StoredLimit, + StoredOpen, + StoredQuery, + StoredCursor, + [WeakThis](const FNakamaPartyList& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientListNotifications* UNakamaClientListNotifications::ListNotifications( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , FNakamaOptionalInt32 Limit + , const FString& CacheableCursor +) +{ + UNakamaClientListNotifications* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredLimit = Limit; + Action->StoredCacheableCursor = CacheableCursor; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientListNotifications::Activate() +{ + static const TCHAR* TraceScope_ListNotifications = TEXT("NakamaBP_ListNotifications"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ListNotifications); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::ListNotifications( + StoredClientConfig, + StoredSession, + StoredLimit, + StoredCacheableCursor, + [WeakThis](const FNakamaNotificationList& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientListStorageObjects* UNakamaClientListStorageObjects::ListStorageObjects( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& UserId + , const FString& Collection + , FNakamaOptionalInt32 Limit + , const FString& Cursor +) +{ + UNakamaClientListStorageObjects* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredUserId = UserId; + Action->StoredCollection = Collection; + Action->StoredLimit = Limit; + Action->StoredCursor = Cursor; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientListStorageObjects::Activate() +{ + static const TCHAR* TraceScope_ListStorageObjects = TEXT("NakamaBP_ListStorageObjects"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ListStorageObjects); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::ListStorageObjects( + StoredClientConfig, + StoredSession, + StoredUserId, + StoredCollection, + StoredLimit, + StoredCursor, + [WeakThis](const FNakamaStorageObjectList& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientListSubscriptions* UNakamaClientListSubscriptions::ListSubscriptions( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , FNakamaOptionalInt32 Limit + , const FString& Cursor +) +{ + UNakamaClientListSubscriptions* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredLimit = Limit; + Action->StoredCursor = Cursor; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientListSubscriptions::Activate() +{ + static const TCHAR* TraceScope_ListSubscriptions = TEXT("NakamaBP_ListSubscriptions"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ListSubscriptions); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::ListSubscriptions( + StoredClientConfig, + StoredSession, + StoredLimit, + StoredCursor, + [WeakThis](const FNakamaSubscriptionList& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientListTournaments* UNakamaClientListTournaments::ListTournaments( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , FNakamaOptionalInt32 CategoryStart + , FNakamaOptionalInt32 CategoryEnd + , FNakamaOptionalInt32 StartTime + , FNakamaOptionalInt32 EndTime + , FNakamaOptionalInt32 Limit + , const FString& Cursor +) +{ + UNakamaClientListTournaments* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredCategoryStart = CategoryStart; + Action->StoredCategoryEnd = CategoryEnd; + Action->StoredStartTime = StartTime; + Action->StoredEndTime = EndTime; + Action->StoredLimit = Limit; + Action->StoredCursor = Cursor; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientListTournaments::Activate() +{ + static const TCHAR* TraceScope_ListTournaments = TEXT("NakamaBP_ListTournaments"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ListTournaments); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::ListTournaments( + StoredClientConfig, + StoredSession, + StoredCategoryStart, + StoredCategoryEnd, + StoredStartTime, + StoredEndTime, + StoredLimit, + StoredCursor, + [WeakThis](const FNakamaTournamentList& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientListTournamentRecords* UNakamaClientListTournamentRecords::ListTournamentRecords( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& TournamentId + , const TArray& OwnerIds + , FNakamaOptionalInt32 Limit + , const FString& Cursor + , FNakamaOptionalInt64 Expiry +) +{ + UNakamaClientListTournamentRecords* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredTournamentId = TournamentId; + Action->StoredOwnerIds = OwnerIds; + Action->StoredLimit = Limit; + Action->StoredCursor = Cursor; + Action->StoredExpiry = Expiry; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientListTournamentRecords::Activate() +{ + static const TCHAR* TraceScope_ListTournamentRecords = TEXT("NakamaBP_ListTournamentRecords"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ListTournamentRecords); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::ListTournamentRecords( + StoredClientConfig, + StoredSession, + StoredTournamentId, + StoredOwnerIds, + StoredLimit, + StoredCursor, + StoredExpiry, + [WeakThis](const FNakamaTournamentRecordList& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientListTournamentRecordsAroundOwner* UNakamaClientListTournamentRecordsAroundOwner::ListTournamentRecordsAroundOwner( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& TournamentId + , FNakamaOptionalInt32 Limit + , const FString& OwnerId + , FNakamaOptionalInt64 Expiry + , const FString& Cursor +) +{ + UNakamaClientListTournamentRecordsAroundOwner* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredTournamentId = TournamentId; + Action->StoredLimit = Limit; + Action->StoredOwnerId = OwnerId; + Action->StoredExpiry = Expiry; + Action->StoredCursor = Cursor; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientListTournamentRecordsAroundOwner::Activate() +{ + static const TCHAR* TraceScope_ListTournamentRecordsAroundOwner = TEXT("NakamaBP_ListTournamentRecordsAroundOwner"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ListTournamentRecordsAroundOwner); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::ListTournamentRecordsAroundOwner( + StoredClientConfig, + StoredSession, + StoredTournamentId, + StoredLimit, + StoredOwnerId, + StoredExpiry, + StoredCursor, + [WeakThis](const FNakamaTournamentRecordList& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientListUserGroups* UNakamaClientListUserGroups::ListUserGroups( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& UserId + , FNakamaOptionalInt32 Limit + , FNakamaOptionalInt32 State + , const FString& Cursor +) +{ + UNakamaClientListUserGroups* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredUserId = UserId; + Action->StoredLimit = Limit; + Action->StoredState = State; + Action->StoredCursor = Cursor; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientListUserGroups::Activate() +{ + static const TCHAR* TraceScope_ListUserGroups = TEXT("NakamaBP_ListUserGroups"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ListUserGroups); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::ListUserGroups( + StoredClientConfig, + StoredSession, + StoredUserId, + StoredLimit, + StoredState, + StoredCursor, + [WeakThis](const FNakamaUserGroupList& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientPromoteGroupUsers* UNakamaClientPromoteGroupUsers::PromoteGroupUsers( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& GroupId + , const TArray& UserIds +) +{ + UNakamaClientPromoteGroupUsers* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredGroupId = GroupId; + Action->StoredUserIds = UserIds; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientPromoteGroupUsers::Activate() +{ + static const TCHAR* TraceScope_PromoteGroupUsers = TEXT("NakamaBP_PromoteGroupUsers"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_PromoteGroupUsers); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::PromoteGroupUsers( + StoredClientConfig, + StoredSession, + StoredGroupId, + StoredUserIds, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientDemoteGroupUsers* UNakamaClientDemoteGroupUsers::DemoteGroupUsers( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& GroupId + , const TArray& UserIds +) +{ + UNakamaClientDemoteGroupUsers* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredGroupId = GroupId; + Action->StoredUserIds = UserIds; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientDemoteGroupUsers::Activate() +{ + static const TCHAR* TraceScope_DemoteGroupUsers = TEXT("NakamaBP_DemoteGroupUsers"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_DemoteGroupUsers); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::DemoteGroupUsers( + StoredClientConfig, + StoredSession, + StoredGroupId, + StoredUserIds, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientReadStorageObjects* UNakamaClientReadStorageObjects::ReadStorageObjects( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const TArray& ObjectIds +) +{ + UNakamaClientReadStorageObjects* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredObjectIds = ObjectIds; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientReadStorageObjects::Activate() +{ + static const TCHAR* TraceScope_ReadStorageObjects = TEXT("NakamaBP_ReadStorageObjects"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ReadStorageObjects); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::ReadStorageObjects( + StoredClientConfig, + StoredSession, + StoredObjectIds, + [WeakThis](const FNakamaStorageObjects& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientRpcFunc* UNakamaClientRpcFunc::RpcFunc( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Id + , const FString& Payload +) +{ + UNakamaClientRpcFunc* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredId = Id; + Action->StoredPayload = Payload; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientRpcFunc::Activate() +{ + static const TCHAR* TraceScope_RpcFunc = TEXT("NakamaBP_RpcFunc"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_RpcFunc); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::RpcFunc( + StoredClientConfig, + StoredSession, + StoredId, + StoredPayload, + [WeakThis](const FNakamaRpc& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientUnlinkApple* UNakamaClientUnlinkApple::UnlinkApple( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Token + , const TMap& Vars +) +{ + UNakamaClientUnlinkApple* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredToken = Token; + Action->StoredVars = Vars; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientUnlinkApple::Activate() +{ + static const TCHAR* TraceScope_UnlinkApple = TEXT("NakamaBP_UnlinkApple"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_UnlinkApple); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::UnlinkApple( + StoredClientConfig, + StoredSession, + StoredToken, + StoredVars, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientUnlinkCustom* UNakamaClientUnlinkCustom::UnlinkCustom( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Id + , const TMap& Vars +) +{ + UNakamaClientUnlinkCustom* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredId = Id; + Action->StoredVars = Vars; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientUnlinkCustom::Activate() +{ + static const TCHAR* TraceScope_UnlinkCustom = TEXT("NakamaBP_UnlinkCustom"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_UnlinkCustom); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::UnlinkCustom( + StoredClientConfig, + StoredSession, + StoredId, + StoredVars, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientUnlinkDevice* UNakamaClientUnlinkDevice::UnlinkDevice( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Id + , const TMap& Vars +) +{ + UNakamaClientUnlinkDevice* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredId = Id; + Action->StoredVars = Vars; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientUnlinkDevice::Activate() +{ + static const TCHAR* TraceScope_UnlinkDevice = TEXT("NakamaBP_UnlinkDevice"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_UnlinkDevice); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::UnlinkDevice( + StoredClientConfig, + StoredSession, + StoredId, + StoredVars, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientUnlinkEmail* UNakamaClientUnlinkEmail::UnlinkEmail( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Email + , const FString& Password + , const TMap& Vars +) +{ + UNakamaClientUnlinkEmail* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredEmail = Email; + Action->StoredPassword = Password; + Action->StoredVars = Vars; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientUnlinkEmail::Activate() +{ + static const TCHAR* TraceScope_UnlinkEmail = TEXT("NakamaBP_UnlinkEmail"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_UnlinkEmail); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::UnlinkEmail( + StoredClientConfig, + StoredSession, + StoredEmail, + StoredPassword, + StoredVars, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientUnlinkFacebook* UNakamaClientUnlinkFacebook::UnlinkFacebook( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Token + , const TMap& Vars +) +{ + UNakamaClientUnlinkFacebook* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredToken = Token; + Action->StoredVars = Vars; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientUnlinkFacebook::Activate() +{ + static const TCHAR* TraceScope_UnlinkFacebook = TEXT("NakamaBP_UnlinkFacebook"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_UnlinkFacebook); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::UnlinkFacebook( + StoredClientConfig, + StoredSession, + StoredToken, + StoredVars, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientUnlinkFacebookInstantGame* UNakamaClientUnlinkFacebookInstantGame::UnlinkFacebookInstantGame( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& SignedPlayerInfo + , const TMap& Vars +) +{ + UNakamaClientUnlinkFacebookInstantGame* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredSignedPlayerInfo = SignedPlayerInfo; + Action->StoredVars = Vars; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientUnlinkFacebookInstantGame::Activate() +{ + static const TCHAR* TraceScope_UnlinkFacebookInstantGame = TEXT("NakamaBP_UnlinkFacebookInstantGame"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_UnlinkFacebookInstantGame); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::UnlinkFacebookInstantGame( + StoredClientConfig, + StoredSession, + StoredSignedPlayerInfo, + StoredVars, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientUnlinkGameCenter* UNakamaClientUnlinkGameCenter::UnlinkGameCenter( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& PlayerId + , const FString& BundleId + , int64 TimestampSeconds + , const FString& Salt + , const FString& Signature + , const FString& PublicKeyUrl + , const TMap& Vars +) +{ + UNakamaClientUnlinkGameCenter* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredPlayerId = PlayerId; + Action->StoredBundleId = BundleId; + Action->StoredTimestampSeconds = TimestampSeconds; + Action->StoredSalt = Salt; + Action->StoredSignature = Signature; + Action->StoredPublicKeyUrl = PublicKeyUrl; + Action->StoredVars = Vars; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientUnlinkGameCenter::Activate() +{ + static const TCHAR* TraceScope_UnlinkGameCenter = TEXT("NakamaBP_UnlinkGameCenter"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_UnlinkGameCenter); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::UnlinkGameCenter( + StoredClientConfig, + StoredSession, + StoredPlayerId, + StoredBundleId, + StoredTimestampSeconds, + StoredSalt, + StoredSignature, + StoredPublicKeyUrl, + StoredVars, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientUnlinkGoogle* UNakamaClientUnlinkGoogle::UnlinkGoogle( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Token + , const TMap& Vars +) +{ + UNakamaClientUnlinkGoogle* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredToken = Token; + Action->StoredVars = Vars; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientUnlinkGoogle::Activate() +{ + static const TCHAR* TraceScope_UnlinkGoogle = TEXT("NakamaBP_UnlinkGoogle"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_UnlinkGoogle); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::UnlinkGoogle( + StoredClientConfig, + StoredSession, + StoredToken, + StoredVars, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientUnlinkSteam* UNakamaClientUnlinkSteam::UnlinkSteam( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Token + , const TMap& Vars +) +{ + UNakamaClientUnlinkSteam* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredToken = Token; + Action->StoredVars = Vars; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientUnlinkSteam::Activate() +{ + static const TCHAR* TraceScope_UnlinkSteam = TEXT("NakamaBP_UnlinkSteam"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_UnlinkSteam); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::UnlinkSteam( + StoredClientConfig, + StoredSession, + StoredToken, + StoredVars, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientUpdateAccount* UNakamaClientUpdateAccount::UpdateAccount( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Username + , const FString& DisplayName + , const FString& AvatarUrl + , const FString& LangTag + , const FString& Location + , const FString& Timezone +) +{ + UNakamaClientUpdateAccount* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredUsername = Username; + Action->StoredDisplayName = DisplayName; + Action->StoredAvatarUrl = AvatarUrl; + Action->StoredLangTag = LangTag; + Action->StoredLocation = Location; + Action->StoredTimezone = Timezone; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientUpdateAccount::Activate() +{ + static const TCHAR* TraceScope_UpdateAccount = TEXT("NakamaBP_UpdateAccount"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_UpdateAccount); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::UpdateAccount( + StoredClientConfig, + StoredSession, + StoredUsername, + StoredDisplayName, + StoredAvatarUrl, + StoredLangTag, + StoredLocation, + StoredTimezone, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientUpdateGroup* UNakamaClientUpdateGroup::UpdateGroup( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& GroupId + , const FString& Name + , const FString& Description + , const FString& LangTag + , const FString& AvatarUrl + , FNakamaOptionalBool Open +) +{ + UNakamaClientUpdateGroup* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredGroupId = GroupId; + Action->StoredName = Name; + Action->StoredDescription = Description; + Action->StoredLangTag = LangTag; + Action->StoredAvatarUrl = AvatarUrl; + Action->StoredOpen = Open; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientUpdateGroup::Activate() +{ + static const TCHAR* TraceScope_UpdateGroup = TEXT("NakamaBP_UpdateGroup"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_UpdateGroup); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::UpdateGroup( + StoredClientConfig, + StoredSession, + StoredGroupId, + StoredName, + StoredDescription, + StoredLangTag, + StoredAvatarUrl, + StoredOpen, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientValidatePurchaseApple* UNakamaClientValidatePurchaseApple::ValidatePurchaseApple( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Receipt + , FNakamaOptionalBool Persist +) +{ + UNakamaClientValidatePurchaseApple* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredReceipt = Receipt; + Action->StoredPersist = Persist; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientValidatePurchaseApple::Activate() +{ + static const TCHAR* TraceScope_ValidatePurchaseApple = TEXT("NakamaBP_ValidatePurchaseApple"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ValidatePurchaseApple); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::ValidatePurchaseApple( + StoredClientConfig, + StoredSession, + StoredReceipt, + StoredPersist, + [WeakThis](const FNakamaValidatePurchaseResponse& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientValidateSubscriptionApple* UNakamaClientValidateSubscriptionApple::ValidateSubscriptionApple( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Receipt + , FNakamaOptionalBool Persist +) +{ + UNakamaClientValidateSubscriptionApple* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredReceipt = Receipt; + Action->StoredPersist = Persist; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientValidateSubscriptionApple::Activate() +{ + static const TCHAR* TraceScope_ValidateSubscriptionApple = TEXT("NakamaBP_ValidateSubscriptionApple"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ValidateSubscriptionApple); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::ValidateSubscriptionApple( + StoredClientConfig, + StoredSession, + StoredReceipt, + StoredPersist, + [WeakThis](const FNakamaValidateSubscriptionResponse& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientValidatePurchaseGoogle* UNakamaClientValidatePurchaseGoogle::ValidatePurchaseGoogle( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Purchase + , FNakamaOptionalBool Persist +) +{ + UNakamaClientValidatePurchaseGoogle* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredPurchase = Purchase; + Action->StoredPersist = Persist; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientValidatePurchaseGoogle::Activate() +{ + static const TCHAR* TraceScope_ValidatePurchaseGoogle = TEXT("NakamaBP_ValidatePurchaseGoogle"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ValidatePurchaseGoogle); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::ValidatePurchaseGoogle( + StoredClientConfig, + StoredSession, + StoredPurchase, + StoredPersist, + [WeakThis](const FNakamaValidatePurchaseResponse& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientValidateSubscriptionGoogle* UNakamaClientValidateSubscriptionGoogle::ValidateSubscriptionGoogle( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Receipt + , FNakamaOptionalBool Persist +) +{ + UNakamaClientValidateSubscriptionGoogle* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredReceipt = Receipt; + Action->StoredPersist = Persist; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientValidateSubscriptionGoogle::Activate() +{ + static const TCHAR* TraceScope_ValidateSubscriptionGoogle = TEXT("NakamaBP_ValidateSubscriptionGoogle"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ValidateSubscriptionGoogle); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::ValidateSubscriptionGoogle( + StoredClientConfig, + StoredSession, + StoredReceipt, + StoredPersist, + [WeakThis](const FNakamaValidateSubscriptionResponse& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientValidatePurchaseHuawei* UNakamaClientValidatePurchaseHuawei::ValidatePurchaseHuawei( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Purchase + , const FString& Signature + , FNakamaOptionalBool Persist +) +{ + UNakamaClientValidatePurchaseHuawei* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredPurchase = Purchase; + Action->StoredSignature = Signature; + Action->StoredPersist = Persist; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientValidatePurchaseHuawei::Activate() +{ + static const TCHAR* TraceScope_ValidatePurchaseHuawei = TEXT("NakamaBP_ValidatePurchaseHuawei"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ValidatePurchaseHuawei); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::ValidatePurchaseHuawei( + StoredClientConfig, + StoredSession, + StoredPurchase, + StoredSignature, + StoredPersist, + [WeakThis](const FNakamaValidatePurchaseResponse& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientValidatePurchaseFacebookInstant* UNakamaClientValidatePurchaseFacebookInstant::ValidatePurchaseFacebookInstant( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& SignedRequest + , FNakamaOptionalBool Persist +) +{ + UNakamaClientValidatePurchaseFacebookInstant* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredSignedRequest = SignedRequest; + Action->StoredPersist = Persist; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientValidatePurchaseFacebookInstant::Activate() +{ + static const TCHAR* TraceScope_ValidatePurchaseFacebookInstant = TEXT("NakamaBP_ValidatePurchaseFacebookInstant"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ValidatePurchaseFacebookInstant); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::ValidatePurchaseFacebookInstant( + StoredClientConfig, + StoredSession, + StoredSignedRequest, + StoredPersist, + [WeakThis](const FNakamaValidatePurchaseResponse& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientWriteLeaderboardRecord* UNakamaClientWriteLeaderboardRecord::WriteLeaderboardRecord( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& LeaderboardId + , const FNakamaWriteLeaderboardRecordRequestLeaderboardRecordWrite& Record +) +{ + UNakamaClientWriteLeaderboardRecord* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredLeaderboardId = LeaderboardId; + Action->StoredRecord = Record; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientWriteLeaderboardRecord::Activate() +{ + static const TCHAR* TraceScope_WriteLeaderboardRecord = TEXT("NakamaBP_WriteLeaderboardRecord"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_WriteLeaderboardRecord); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::WriteLeaderboardRecord( + StoredClientConfig, + StoredSession, + StoredLeaderboardId, + StoredRecord, + [WeakThis](const FNakamaLeaderboardRecord& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientWriteStorageObjects* UNakamaClientWriteStorageObjects::WriteStorageObjects( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const TArray& Objects +) +{ + UNakamaClientWriteStorageObjects* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredObjects = Objects; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientWriteStorageObjects::Activate() +{ + static const TCHAR* TraceScope_WriteStorageObjects = TEXT("NakamaBP_WriteStorageObjects"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_WriteStorageObjects); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::WriteStorageObjects( + StoredClientConfig, + StoredSession, + StoredObjects, + [WeakThis](const FNakamaStorageObjectAcks& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +UNakamaClientWriteTournamentRecord* UNakamaClientWriteTournamentRecord::WriteTournamentRecord( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& TournamentId + , const FNakamaWriteTournamentRecordRequestTournamentRecordWrite& Record +) +{ + UNakamaClientWriteTournamentRecord* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredTournamentId = TournamentId; + Action->StoredRecord = Record; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaClientWriteTournamentRecord::Activate() +{ + static const TCHAR* TraceScope_WriteTournamentRecord = TEXT("NakamaBP_WriteTournamentRecord"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_WriteTournamentRecord); + + TWeakObjectPtr WeakThis(this); + + NakamaApi::WriteTournamentRecord( + StoredClientConfig, + StoredSession, + StoredTournamentId, + StoredRecord, + [WeakThis](const FNakamaLeaderboardRecord& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FNakamaError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} diff --git a/Nakama/Source/NakamaBlueprints/NakamaClientBlueprintLibrary.h b/Nakama/Source/NakamaBlueprints/NakamaClientBlueprintLibrary.h new file mode 100644 index 000000000..fd622ab5a --- /dev/null +++ b/Nakama/Source/NakamaBlueprints/NakamaClientBlueprintLibrary.h @@ -0,0 +1,3614 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + +#pragma once + +#include "CoreMinimal.h" +#include "Kismet/BlueprintAsyncActionBase.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "NakamaTypes.h" +#include "NakamaApi.h" +#include "NakamaClientBlueprintLibrary.generated.h" + +// ============================================================================ +// Dynamic delegates for async actions +// ============================================================================ + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnNakamaEmptyResponse, const FNakamaError&, Error); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaSessionRefresh, const FNakamaError&, Error, const FNakamaSession&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaAuthenticateApple, const FNakamaError&, Error, const FNakamaSession&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaAuthenticateCustom, const FNakamaError&, Error, const FNakamaSession&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaAuthenticateDevice, const FNakamaError&, Error, const FNakamaSession&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaAuthenticateEmail, const FNakamaError&, Error, const FNakamaSession&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaAuthenticateFacebook, const FNakamaError&, Error, const FNakamaSession&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaAuthenticateFacebookInstantGame, const FNakamaError&, Error, const FNakamaSession&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaAuthenticateGameCenter, const FNakamaError&, Error, const FNakamaSession&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaAuthenticateGoogle, const FNakamaError&, Error, const FNakamaSession&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaAuthenticateSteam, const FNakamaError&, Error, const FNakamaSession&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaCreateGroup, const FNakamaError&, Error, const FNakamaGroup&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaGetAccount, const FNakamaError&, Error, const FNakamaAccount&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaGetUsers, const FNakamaError&, Error, const FNakamaUsers&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaGetSubscription, const FNakamaError&, Error, const FNakamaValidatedSubscription&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaGetMatchmakerStats, const FNakamaError&, Error, const FNakamaMatchmakerStats&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaListChannelMessages, const FNakamaError&, Error, const FNakamaChannelMessageList&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaListFriends, const FNakamaError&, Error, const FNakamaFriendList&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaListFriendsOfFriends, const FNakamaError&, Error, const FNakamaFriendsOfFriendsList&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaListGroups, const FNakamaError&, Error, const FNakamaGroupList&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaListGroupUsers, const FNakamaError&, Error, const FNakamaGroupUserList&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaListLeaderboardRecords, const FNakamaError&, Error, const FNakamaLeaderboardRecordList&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaListLeaderboardRecordsAroundOwner, const FNakamaError&, Error, const FNakamaLeaderboardRecordList&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaListMatches, const FNakamaError&, Error, const FNakamaMatchList&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaListParties, const FNakamaError&, Error, const FNakamaPartyList&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaListNotifications, const FNakamaError&, Error, const FNakamaNotificationList&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaListStorageObjects, const FNakamaError&, Error, const FNakamaStorageObjectList&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaListSubscriptions, const FNakamaError&, Error, const FNakamaSubscriptionList&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaListTournaments, const FNakamaError&, Error, const FNakamaTournamentList&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaListTournamentRecords, const FNakamaError&, Error, const FNakamaTournamentRecordList&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaListTournamentRecordsAroundOwner, const FNakamaError&, Error, const FNakamaTournamentRecordList&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaListUserGroups, const FNakamaError&, Error, const FNakamaUserGroupList&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaReadStorageObjects, const FNakamaError&, Error, const FNakamaStorageObjects&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaRpcFunc, const FNakamaError&, Error, const FNakamaRpc&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaValidatePurchaseApple, const FNakamaError&, Error, const FNakamaValidatePurchaseResponse&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaValidateSubscriptionApple, const FNakamaError&, Error, const FNakamaValidateSubscriptionResponse&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaValidatePurchaseGoogle, const FNakamaError&, Error, const FNakamaValidatePurchaseResponse&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaValidateSubscriptionGoogle, const FNakamaError&, Error, const FNakamaValidateSubscriptionResponse&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaValidatePurchaseHuawei, const FNakamaError&, Error, const FNakamaValidatePurchaseResponse&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaValidatePurchaseFacebookInstant, const FNakamaError&, Error, const FNakamaValidatePurchaseResponse&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaWriteLeaderboardRecord, const FNakamaError&, Error, const FNakamaLeaderboardRecord&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaWriteStorageObjects, const FNakamaError&, Error, const FNakamaStorageObjectAcks&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaWriteTournamentRecord, const FNakamaError&, Error, const FNakamaLeaderboardRecord&, Result); + +// ============================================================================ +// Async Action Classes (one per RPC) +// ============================================================================ + +/** +* Add friends by ID or username to a user's account. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientAddFriends : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "AddFriends") + , Category = "Nakama|Client" + ) + static UNakamaClientAddFriends* AddFriends( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const TArray& Ids + , const TArray& Usernames + , const FString& Metadata + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + TArray StoredIds; + TArray StoredUsernames; + FString StoredMetadata; +}; + +/** +* Add users to a group. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientAddGroupUsers : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "AddGroupUsers") + , Category = "Nakama|Client" + ) + static UNakamaClientAddGroupUsers* AddGroupUsers( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& GroupId + , const TArray& UserIds + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredGroupId; + TArray StoredUserIds; +}; + +/** +* Refresh a user's session using a refresh token retrieved from a previous authentication request. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientSessionRefresh : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaSessionRefresh OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaSessionRefresh OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "SessionRefresh", AutoCreateRefTerm = "Vars") + , Category = "Nakama|Client" + ) + static UNakamaClientSessionRefresh* SessionRefresh( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FString& Token + , const TMap& Vars + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FString StoredToken; + TMap StoredVars; +}; + +/** +* Log out a session, invalidate a refresh token, or log out all sessions/refresh tokens for a user. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientSessionLogout : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "SessionLogout") + , Category = "Nakama|Client" + ) + static UNakamaClientSessionLogout* SessionLogout( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Token + , const FString& RefreshToken + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredToken; + FString StoredRefreshToken; +}; + +/** +* Authenticate a user with an Apple ID against the server. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientAuthenticateApple : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaAuthenticateApple OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaAuthenticateApple OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "AuthenticateApple") + , Category = "Nakama|Client" + ) + static UNakamaClientAuthenticateApple* AuthenticateApple( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaAccountApple& Account + , FNakamaOptionalBool Create + , const FString& Username + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaAccountApple StoredAccount; + FNakamaOptionalBool StoredCreate; + FString StoredUsername; +}; + +/** +* Authenticate a user with a custom id against the server. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientAuthenticateCustom : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaAuthenticateCustom OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaAuthenticateCustom OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "AuthenticateCustom") + , Category = "Nakama|Client" + ) + static UNakamaClientAuthenticateCustom* AuthenticateCustom( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaAccountCustom& Account + , FNakamaOptionalBool Create + , const FString& Username + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaAccountCustom StoredAccount; + FNakamaOptionalBool StoredCreate; + FString StoredUsername; +}; + +/** +* Authenticate a user with a device id against the server. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientAuthenticateDevice : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaAuthenticateDevice OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaAuthenticateDevice OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "AuthenticateDevice") + , Category = "Nakama|Client" + ) + static UNakamaClientAuthenticateDevice* AuthenticateDevice( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaAccountDevice& Account + , FNakamaOptionalBool Create + , const FString& Username + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaAccountDevice StoredAccount; + FNakamaOptionalBool StoredCreate; + FString StoredUsername; +}; + +/** +* Authenticate a user with an email+password against the server. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientAuthenticateEmail : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaAuthenticateEmail OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaAuthenticateEmail OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "AuthenticateEmail") + , Category = "Nakama|Client" + ) + static UNakamaClientAuthenticateEmail* AuthenticateEmail( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaAccountEmail& Account + , FNakamaOptionalBool Create + , const FString& Username + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaAccountEmail StoredAccount; + FNakamaOptionalBool StoredCreate; + FString StoredUsername; +}; + +/** +* Authenticate a user with a Facebook OAuth token against the server. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientAuthenticateFacebook : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaAuthenticateFacebook OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaAuthenticateFacebook OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "AuthenticateFacebook") + , Category = "Nakama|Client" + ) + static UNakamaClientAuthenticateFacebook* AuthenticateFacebook( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaAccountFacebook& Account + , FNakamaOptionalBool Create + , const FString& Username + , FNakamaOptionalBool Sync + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaAccountFacebook StoredAccount; + FNakamaOptionalBool StoredCreate; + FString StoredUsername; + FNakamaOptionalBool StoredSync; +}; + +/** +* Authenticate a user with a Facebook Instant Game token against the server. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientAuthenticateFacebookInstantGame : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaAuthenticateFacebookInstantGame OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaAuthenticateFacebookInstantGame OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "AuthenticateFacebookInstantGame") + , Category = "Nakama|Client" + ) + static UNakamaClientAuthenticateFacebookInstantGame* AuthenticateFacebookInstantGame( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaAccountFacebookInstantGame& Account + , FNakamaOptionalBool Create + , const FString& Username + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaAccountFacebookInstantGame StoredAccount; + FNakamaOptionalBool StoredCreate; + FString StoredUsername; +}; + +/** +* Authenticate a user with Apple's GameCenter against the server. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientAuthenticateGameCenter : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaAuthenticateGameCenter OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaAuthenticateGameCenter OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "AuthenticateGameCenter") + , Category = "Nakama|Client" + ) + static UNakamaClientAuthenticateGameCenter* AuthenticateGameCenter( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaAccountGameCenter& Account + , FNakamaOptionalBool Create + , const FString& Username + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaAccountGameCenter StoredAccount; + FNakamaOptionalBool StoredCreate; + FString StoredUsername; +}; + +/** +* Authenticate a user with Google against the server. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientAuthenticateGoogle : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaAuthenticateGoogle OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaAuthenticateGoogle OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "AuthenticateGoogle") + , Category = "Nakama|Client" + ) + static UNakamaClientAuthenticateGoogle* AuthenticateGoogle( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaAccountGoogle& Account + , FNakamaOptionalBool Create + , const FString& Username + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaAccountGoogle StoredAccount; + FNakamaOptionalBool StoredCreate; + FString StoredUsername; +}; + +/** +* Authenticate a user with Steam against the server. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientAuthenticateSteam : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaAuthenticateSteam OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaAuthenticateSteam OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "AuthenticateSteam") + , Category = "Nakama|Client" + ) + static UNakamaClientAuthenticateSteam* AuthenticateSteam( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaAccountSteam& Account + , FNakamaOptionalBool Create + , const FString& Username + , FNakamaOptionalBool Sync + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaAccountSteam StoredAccount; + FNakamaOptionalBool StoredCreate; + FString StoredUsername; + FNakamaOptionalBool StoredSync; +}; + +/** +* Ban a set of users from a group. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientBanGroupUsers : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "BanGroupUsers") + , Category = "Nakama|Client" + ) + static UNakamaClientBanGroupUsers* BanGroupUsers( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& GroupId + , const TArray& UserIds + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredGroupId; + TArray StoredUserIds; +}; + +/** +* Block one or more users by ID or username. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientBlockFriends : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "BlockFriends") + , Category = "Nakama|Client" + ) + static UNakamaClientBlockFriends* BlockFriends( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const TArray& Ids + , const TArray& Usernames + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + TArray StoredIds; + TArray StoredUsernames; +}; + +/** +* Create a new group with the current user as the owner. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientCreateGroup : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaCreateGroup OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaCreateGroup OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "CreateGroup") + , Category = "Nakama|Client" + ) + static UNakamaClientCreateGroup* CreateGroup( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Name + , const FString& Description + , const FString& LangTag + , const FString& AvatarUrl + , bool Open + , int32 MaxCount + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredName; + FString StoredDescription; + FString StoredLangTag; + FString StoredAvatarUrl; + bool StoredOpen; + int32 StoredMaxCount; +}; + +/** +* Delete the current user's account. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientDeleteAccount : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "DeleteAccount") + , Category = "Nakama|Client" + ) + static UNakamaClientDeleteAccount* DeleteAccount( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; +}; + +/** +* Delete one or more users by ID or username. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientDeleteFriends : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "DeleteFriends") + , Category = "Nakama|Client" + ) + static UNakamaClientDeleteFriends* DeleteFriends( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const TArray& Ids + , const TArray& Usernames + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + TArray StoredIds; + TArray StoredUsernames; +}; + +/** +* Delete a group by ID. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientDeleteGroup : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "DeleteGroup") + , Category = "Nakama|Client" + ) + static UNakamaClientDeleteGroup* DeleteGroup( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& GroupId + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredGroupId; +}; + +/** +* Delete a leaderboard record. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientDeleteLeaderboardRecord : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "DeleteLeaderboardRecord") + , Category = "Nakama|Client" + ) + static UNakamaClientDeleteLeaderboardRecord* DeleteLeaderboardRecord( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& LeaderboardId + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredLeaderboardId; +}; + +/** +* Delete one or more notifications for the current user. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientDeleteNotifications : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "DeleteNotifications") + , Category = "Nakama|Client" + ) + static UNakamaClientDeleteNotifications* DeleteNotifications( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const TArray& Ids + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + TArray StoredIds; +}; + +/** +* Delete a tournament record. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientDeleteTournamentRecord : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "DeleteTournamentRecord") + , Category = "Nakama|Client" + ) + static UNakamaClientDeleteTournamentRecord* DeleteTournamentRecord( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& TournamentId + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredTournamentId; +}; + +/** +* Delete one or more objects by ID or username. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientDeleteStorageObjects : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "DeleteStorageObjects") + , Category = "Nakama|Client" + ) + static UNakamaClientDeleteStorageObjects* DeleteStorageObjects( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const TArray& ObjectIds + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + TArray StoredObjectIds; +}; + +/** +* Submit an event for processing in the server's registered runtime custom events handler. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientEvent : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "Event", AutoCreateRefTerm = "Properties") + , Category = "Nakama|Client" + ) + static UNakamaClientEvent* Event( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Name + , const FDateTime& Timestamp + , bool External + , const TMap& Properties + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredName; + FDateTime StoredTimestamp; + bool StoredExternal; + TMap StoredProperties; +}; + +/** +* Fetch the current user's account. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientGetAccount : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaGetAccount OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaGetAccount OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "GetAccount") + , Category = "Nakama|Client" + ) + static UNakamaClientGetAccount* GetAccount( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; +}; + +/** +* Fetch zero or more users by ID and/or username. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientGetUsers : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaGetUsers OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaGetUsers OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "GetUsers") + , Category = "Nakama|Client" + ) + static UNakamaClientGetUsers* GetUsers( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const TArray& Ids + , const TArray& Usernames + , const TArray& FacebookIds + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + TArray StoredIds; + TArray StoredUsernames; + TArray StoredFacebookIds; +}; + +/** +* Get subscription by product id. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientGetSubscription : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaGetSubscription OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaGetSubscription OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "GetSubscription") + , Category = "Nakama|Client" + ) + static UNakamaClientGetSubscription* GetSubscription( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& ProductId + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredProductId; +}; + +/** +* Get matchmaker stats. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientGetMatchmakerStats : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaGetMatchmakerStats OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaGetMatchmakerStats OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "GetMatchmakerStats") + , Category = "Nakama|Client" + ) + static UNakamaClientGetMatchmakerStats* GetMatchmakerStats( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; +}; + +/** +* A healthcheck which load balancers can use to check the service. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientHealthcheck : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "Healthcheck") + , Category = "Nakama|Client" + ) + static UNakamaClientHealthcheck* Healthcheck( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; +}; + +/** +* Import Facebook friends and add them to a user's account. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientImportFacebookFriends : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ImportFacebookFriends") + , Category = "Nakama|Client" + ) + static UNakamaClientImportFacebookFriends* ImportFacebookFriends( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FNakamaAccountFacebook& Account + , FNakamaOptionalBool Reset + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FNakamaAccountFacebook StoredAccount; + FNakamaOptionalBool StoredReset; +}; + +/** +* Import Steam friends and add them to a user's account. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientImportSteamFriends : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ImportSteamFriends") + , Category = "Nakama|Client" + ) + static UNakamaClientImportSteamFriends* ImportSteamFriends( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FNakamaAccountSteam& Account + , FNakamaOptionalBool Reset + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FNakamaAccountSteam StoredAccount; + FNakamaOptionalBool StoredReset; +}; + +/** +* Immediately join an open group, or request to join a closed one. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientJoinGroup : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "JoinGroup") + , Category = "Nakama|Client" + ) + static UNakamaClientJoinGroup* JoinGroup( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& GroupId + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredGroupId; +}; + +/** +* Attempt to join an open and running tournament. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientJoinTournament : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "JoinTournament") + , Category = "Nakama|Client" + ) + static UNakamaClientJoinTournament* JoinTournament( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& TournamentId + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredTournamentId; +}; + +/** +* Kick a set of users from a group. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientKickGroupUsers : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "KickGroupUsers") + , Category = "Nakama|Client" + ) + static UNakamaClientKickGroupUsers* KickGroupUsers( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& GroupId + , const TArray& UserIds + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredGroupId; + TArray StoredUserIds; +}; + +/** +* Leave a group the user is a member of. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientLeaveGroup : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "LeaveGroup") + , Category = "Nakama|Client" + ) + static UNakamaClientLeaveGroup* LeaveGroup( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& GroupId + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredGroupId; +}; + +/** +* Add an Apple ID to the social profiles on the current user's account. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientLinkApple : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "LinkApple", AutoCreateRefTerm = "Vars") + , Category = "Nakama|Client" + ) + static UNakamaClientLinkApple* LinkApple( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Token + , const TMap& Vars + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredToken; + TMap StoredVars; +}; + +/** +* Add a custom ID to the social profiles on the current user's account. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientLinkCustom : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "LinkCustom", AutoCreateRefTerm = "Vars") + , Category = "Nakama|Client" + ) + static UNakamaClientLinkCustom* LinkCustom( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Id + , const TMap& Vars + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredId; + TMap StoredVars; +}; + +/** +* Add a device ID to the social profiles on the current user's account. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientLinkDevice : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "LinkDevice", AutoCreateRefTerm = "Vars") + , Category = "Nakama|Client" + ) + static UNakamaClientLinkDevice* LinkDevice( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Id + , const TMap& Vars + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredId; + TMap StoredVars; +}; + +/** +* Add an email+password to the social profiles on the current user's account. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientLinkEmail : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "LinkEmail", AutoCreateRefTerm = "Vars") + , Category = "Nakama|Client" + ) + static UNakamaClientLinkEmail* LinkEmail( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Email + , const FString& Password + , const TMap& Vars + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredEmail; + FString StoredPassword; + TMap StoredVars; +}; + +/** +* Add Facebook to the social profiles on the current user's account. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientLinkFacebook : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "LinkFacebook") + , Category = "Nakama|Client" + ) + static UNakamaClientLinkFacebook* LinkFacebook( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FNakamaAccountFacebook& Account + , FNakamaOptionalBool Sync + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FNakamaAccountFacebook StoredAccount; + FNakamaOptionalBool StoredSync; +}; + +/** +* Add Facebook Instant Game to the social profiles on the current user's account. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientLinkFacebookInstantGame : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "LinkFacebookInstantGame", AutoCreateRefTerm = "Vars") + , Category = "Nakama|Client" + ) + static UNakamaClientLinkFacebookInstantGame* LinkFacebookInstantGame( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& SignedPlayerInfo + , const TMap& Vars + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredSignedPlayerInfo; + TMap StoredVars; +}; + +/** +* Add Apple's GameCenter to the social profiles on the current user's account. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientLinkGameCenter : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "LinkGameCenter", AutoCreateRefTerm = "Vars") + , Category = "Nakama|Client" + ) + static UNakamaClientLinkGameCenter* LinkGameCenter( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& PlayerId + , const FString& BundleId + , int64 TimestampSeconds + , const FString& Salt + , const FString& Signature + , const FString& PublicKeyUrl + , const TMap& Vars + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredPlayerId; + FString StoredBundleId; + int64 StoredTimestampSeconds; + FString StoredSalt; + FString StoredSignature; + FString StoredPublicKeyUrl; + TMap StoredVars; +}; + +/** +* Add Google to the social profiles on the current user's account. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientLinkGoogle : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "LinkGoogle", AutoCreateRefTerm = "Vars") + , Category = "Nakama|Client" + ) + static UNakamaClientLinkGoogle* LinkGoogle( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Token + , const TMap& Vars + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredToken; + TMap StoredVars; +}; + +/** +* Add Steam to the social profiles on the current user's account. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientLinkSteam : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "LinkSteam") + , Category = "Nakama|Client" + ) + static UNakamaClientLinkSteam* LinkSteam( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FNakamaAccountSteam& Account + , FNakamaOptionalBool Sync + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FNakamaAccountSteam StoredAccount; + FNakamaOptionalBool StoredSync; +}; + +/** +* List a channel's message history. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientListChannelMessages : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaListChannelMessages OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaListChannelMessages OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ListChannelMessages") + , Category = "Nakama|Client" + ) + static UNakamaClientListChannelMessages* ListChannelMessages( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& ChannelId + , FNakamaOptionalInt32 Limit + , FNakamaOptionalBool Forward + , const FString& Cursor + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredChannelId; + FNakamaOptionalInt32 StoredLimit; + FNakamaOptionalBool StoredForward; + FString StoredCursor; +}; + +/** +* List all friends for the current user. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientListFriends : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaListFriends OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaListFriends OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ListFriends") + , Category = "Nakama|Client" + ) + static UNakamaClientListFriends* ListFriends( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , FNakamaOptionalInt32 Limit + , FNakamaOptionalInt32 State + , const FString& Cursor + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FNakamaOptionalInt32 StoredLimit; + FNakamaOptionalInt32 StoredState; + FString StoredCursor; +}; + +/** +* List friends of friends for the current user. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientListFriendsOfFriends : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaListFriendsOfFriends OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaListFriendsOfFriends OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ListFriendsOfFriends") + , Category = "Nakama|Client" + ) + static UNakamaClientListFriendsOfFriends* ListFriendsOfFriends( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , FNakamaOptionalInt32 Limit + , const FString& Cursor + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FNakamaOptionalInt32 StoredLimit; + FString StoredCursor; +}; + +/** +* List groups based on given filters. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientListGroups : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaListGroups OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaListGroups OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ListGroups") + , Category = "Nakama|Client" + ) + static UNakamaClientListGroups* ListGroups( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Name + , const FString& Cursor + , FNakamaOptionalInt32 Limit + , const FString& LangTag + , FNakamaOptionalInt32 Members + , FNakamaOptionalBool Open + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredName; + FString StoredCursor; + FNakamaOptionalInt32 StoredLimit; + FString StoredLangTag; + FNakamaOptionalInt32 StoredMembers; + FNakamaOptionalBool StoredOpen; +}; + +/** +* List all users that are part of a group. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientListGroupUsers : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaListGroupUsers OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaListGroupUsers OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ListGroupUsers") + , Category = "Nakama|Client" + ) + static UNakamaClientListGroupUsers* ListGroupUsers( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& GroupId + , FNakamaOptionalInt32 Limit + , FNakamaOptionalInt32 State + , const FString& Cursor + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredGroupId; + FNakamaOptionalInt32 StoredLimit; + FNakamaOptionalInt32 StoredState; + FString StoredCursor; +}; + +/** +* List leaderboard records. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientListLeaderboardRecords : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaListLeaderboardRecords OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaListLeaderboardRecords OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ListLeaderboardRecords") + , Category = "Nakama|Client" + ) + static UNakamaClientListLeaderboardRecords* ListLeaderboardRecords( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& LeaderboardId + , const TArray& OwnerIds + , FNakamaOptionalInt32 Limit + , const FString& Cursor + , FNakamaOptionalInt64 Expiry + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredLeaderboardId; + TArray StoredOwnerIds; + FNakamaOptionalInt32 StoredLimit; + FString StoredCursor; + FNakamaOptionalInt64 StoredExpiry; +}; + +/** +* List leaderboard records around the target ownerId. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientListLeaderboardRecordsAroundOwner : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaListLeaderboardRecordsAroundOwner OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaListLeaderboardRecordsAroundOwner OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ListLeaderboardRecordsAroundOwner") + , Category = "Nakama|Client" + ) + static UNakamaClientListLeaderboardRecordsAroundOwner* ListLeaderboardRecordsAroundOwner( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& LeaderboardId + , FNakamaOptionalInt32 Limit + , const FString& OwnerId + , FNakamaOptionalInt64 Expiry + , const FString& Cursor + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredLeaderboardId; + FNakamaOptionalInt32 StoredLimit; + FString StoredOwnerId; + FNakamaOptionalInt64 StoredExpiry; + FString StoredCursor; +}; + +/** +* List running matches and optionally filter by matching criteria. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientListMatches : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaListMatches OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaListMatches OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ListMatches") + , Category = "Nakama|Client" + ) + static UNakamaClientListMatches* ListMatches( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , FNakamaOptionalInt32 Limit + , FNakamaOptionalBool Authoritative + , const FString& Label + , FNakamaOptionalInt32 MinSize + , FNakamaOptionalInt32 MaxSize + , const FString& Query + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FNakamaOptionalInt32 StoredLimit; + FNakamaOptionalBool StoredAuthoritative; + FString StoredLabel; + FNakamaOptionalInt32 StoredMinSize; + FNakamaOptionalInt32 StoredMaxSize; + FString StoredQuery; +}; + +/** +* List parties and optionally filter by matching criteria. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientListParties : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaListParties OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaListParties OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ListParties") + , Category = "Nakama|Client" + ) + static UNakamaClientListParties* ListParties( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , FNakamaOptionalInt32 Limit + , FNakamaOptionalBool Open + , const FString& Query + , const FString& Cursor + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FNakamaOptionalInt32 StoredLimit; + FNakamaOptionalBool StoredOpen; + FString StoredQuery; + FString StoredCursor; +}; + +/** +* Fetch list of notifications. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientListNotifications : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaListNotifications OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaListNotifications OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ListNotifications") + , Category = "Nakama|Client" + ) + static UNakamaClientListNotifications* ListNotifications( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , FNakamaOptionalInt32 Limit + , const FString& CacheableCursor + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FNakamaOptionalInt32 StoredLimit; + FString StoredCacheableCursor; +}; + +/** +* List publicly readable storage objects in a given collection. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientListStorageObjects : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaListStorageObjects OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaListStorageObjects OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ListStorageObjects") + , Category = "Nakama|Client" + ) + static UNakamaClientListStorageObjects* ListStorageObjects( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& UserId + , const FString& Collection + , FNakamaOptionalInt32 Limit + , const FString& Cursor + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredUserId; + FString StoredCollection; + FNakamaOptionalInt32 StoredLimit; + FString StoredCursor; +}; + +/** +* List user's subscriptions. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientListSubscriptions : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaListSubscriptions OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaListSubscriptions OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ListSubscriptions") + , Category = "Nakama|Client" + ) + static UNakamaClientListSubscriptions* ListSubscriptions( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , FNakamaOptionalInt32 Limit + , const FString& Cursor + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FNakamaOptionalInt32 StoredLimit; + FString StoredCursor; +}; + +/** +* List current or upcoming tournaments. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientListTournaments : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaListTournaments OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaListTournaments OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ListTournaments") + , Category = "Nakama|Client" + ) + static UNakamaClientListTournaments* ListTournaments( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , FNakamaOptionalInt32 CategoryStart + , FNakamaOptionalInt32 CategoryEnd + , FNakamaOptionalInt32 StartTime + , FNakamaOptionalInt32 EndTime + , FNakamaOptionalInt32 Limit + , const FString& Cursor + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FNakamaOptionalInt32 StoredCategoryStart; + FNakamaOptionalInt32 StoredCategoryEnd; + FNakamaOptionalInt32 StoredStartTime; + FNakamaOptionalInt32 StoredEndTime; + FNakamaOptionalInt32 StoredLimit; + FString StoredCursor; +}; + +/** +* List tournament records. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientListTournamentRecords : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaListTournamentRecords OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaListTournamentRecords OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ListTournamentRecords") + , Category = "Nakama|Client" + ) + static UNakamaClientListTournamentRecords* ListTournamentRecords( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& TournamentId + , const TArray& OwnerIds + , FNakamaOptionalInt32 Limit + , const FString& Cursor + , FNakamaOptionalInt64 Expiry + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredTournamentId; + TArray StoredOwnerIds; + FNakamaOptionalInt32 StoredLimit; + FString StoredCursor; + FNakamaOptionalInt64 StoredExpiry; +}; + +/** +* List tournament records for a given owner. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientListTournamentRecordsAroundOwner : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaListTournamentRecordsAroundOwner OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaListTournamentRecordsAroundOwner OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ListTournamentRecordsAroundOwner") + , Category = "Nakama|Client" + ) + static UNakamaClientListTournamentRecordsAroundOwner* ListTournamentRecordsAroundOwner( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& TournamentId + , FNakamaOptionalInt32 Limit + , const FString& OwnerId + , FNakamaOptionalInt64 Expiry + , const FString& Cursor + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredTournamentId; + FNakamaOptionalInt32 StoredLimit; + FString StoredOwnerId; + FNakamaOptionalInt64 StoredExpiry; + FString StoredCursor; +}; + +/** +* List groups the current user belongs to. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientListUserGroups : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaListUserGroups OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaListUserGroups OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ListUserGroups") + , Category = "Nakama|Client" + ) + static UNakamaClientListUserGroups* ListUserGroups( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& UserId + , FNakamaOptionalInt32 Limit + , FNakamaOptionalInt32 State + , const FString& Cursor + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredUserId; + FNakamaOptionalInt32 StoredLimit; + FNakamaOptionalInt32 StoredState; + FString StoredCursor; +}; + +/** +* Promote a set of users in a group to the next role up. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientPromoteGroupUsers : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "PromoteGroupUsers") + , Category = "Nakama|Client" + ) + static UNakamaClientPromoteGroupUsers* PromoteGroupUsers( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& GroupId + , const TArray& UserIds + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredGroupId; + TArray StoredUserIds; +}; + +/** +* Demote a set of users in a group to the next role down. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientDemoteGroupUsers : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "DemoteGroupUsers") + , Category = "Nakama|Client" + ) + static UNakamaClientDemoteGroupUsers* DemoteGroupUsers( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& GroupId + , const TArray& UserIds + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredGroupId; + TArray StoredUserIds; +}; + +/** +* Get storage objects. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientReadStorageObjects : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaReadStorageObjects OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaReadStorageObjects OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ReadStorageObjects") + , Category = "Nakama|Client" + ) + static UNakamaClientReadStorageObjects* ReadStorageObjects( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const TArray& ObjectIds + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + TArray StoredObjectIds; +}; + +/** +* Execute a Lua function on the server. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientRpcFunc : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaRpcFunc OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaRpcFunc OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "RpcFunc") + , Category = "Nakama|Client" + ) + static UNakamaClientRpcFunc* RpcFunc( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Id + , const FString& Payload + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredId; + FString StoredPayload; +}; + +/** +* Remove the Apple ID from the social profiles on the current user's account. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientUnlinkApple : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "UnlinkApple", AutoCreateRefTerm = "Vars") + , Category = "Nakama|Client" + ) + static UNakamaClientUnlinkApple* UnlinkApple( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Token + , const TMap& Vars + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredToken; + TMap StoredVars; +}; + +/** +* Remove the custom ID from the social profiles on the current user's account. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientUnlinkCustom : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "UnlinkCustom", AutoCreateRefTerm = "Vars") + , Category = "Nakama|Client" + ) + static UNakamaClientUnlinkCustom* UnlinkCustom( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Id + , const TMap& Vars + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredId; + TMap StoredVars; +}; + +/** +* Remove the device ID from the social profiles on the current user's account. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientUnlinkDevice : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "UnlinkDevice", AutoCreateRefTerm = "Vars") + , Category = "Nakama|Client" + ) + static UNakamaClientUnlinkDevice* UnlinkDevice( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Id + , const TMap& Vars + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredId; + TMap StoredVars; +}; + +/** +* Remove the email+password from the social profiles on the current user's account. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientUnlinkEmail : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "UnlinkEmail", AutoCreateRefTerm = "Vars") + , Category = "Nakama|Client" + ) + static UNakamaClientUnlinkEmail* UnlinkEmail( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Email + , const FString& Password + , const TMap& Vars + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredEmail; + FString StoredPassword; + TMap StoredVars; +}; + +/** +* Remove Facebook from the social profiles on the current user's account. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientUnlinkFacebook : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "UnlinkFacebook", AutoCreateRefTerm = "Vars") + , Category = "Nakama|Client" + ) + static UNakamaClientUnlinkFacebook* UnlinkFacebook( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Token + , const TMap& Vars + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredToken; + TMap StoredVars; +}; + +/** +* Remove Facebook Instant Game profile from the social profiles on the current user's account. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientUnlinkFacebookInstantGame : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "UnlinkFacebookInstantGame", AutoCreateRefTerm = "Vars") + , Category = "Nakama|Client" + ) + static UNakamaClientUnlinkFacebookInstantGame* UnlinkFacebookInstantGame( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& SignedPlayerInfo + , const TMap& Vars + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredSignedPlayerInfo; + TMap StoredVars; +}; + +/** +* Remove Apple's GameCenter from the social profiles on the current user's account. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientUnlinkGameCenter : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "UnlinkGameCenter", AutoCreateRefTerm = "Vars") + , Category = "Nakama|Client" + ) + static UNakamaClientUnlinkGameCenter* UnlinkGameCenter( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& PlayerId + , const FString& BundleId + , int64 TimestampSeconds + , const FString& Salt + , const FString& Signature + , const FString& PublicKeyUrl + , const TMap& Vars + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredPlayerId; + FString StoredBundleId; + int64 StoredTimestampSeconds; + FString StoredSalt; + FString StoredSignature; + FString StoredPublicKeyUrl; + TMap StoredVars; +}; + +/** +* Remove Google from the social profiles on the current user's account. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientUnlinkGoogle : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "UnlinkGoogle", AutoCreateRefTerm = "Vars") + , Category = "Nakama|Client" + ) + static UNakamaClientUnlinkGoogle* UnlinkGoogle( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Token + , const TMap& Vars + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredToken; + TMap StoredVars; +}; + +/** +* Remove Steam from the social profiles on the current user's account. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientUnlinkSteam : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "UnlinkSteam", AutoCreateRefTerm = "Vars") + , Category = "Nakama|Client" + ) + static UNakamaClientUnlinkSteam* UnlinkSteam( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Token + , const TMap& Vars + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredToken; + TMap StoredVars; +}; + +/** +* Update fields in the current user's account. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientUpdateAccount : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "UpdateAccount") + , Category = "Nakama|Client" + ) + static UNakamaClientUpdateAccount* UpdateAccount( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Username + , const FString& DisplayName + , const FString& AvatarUrl + , const FString& LangTag + , const FString& Location + , const FString& Timezone + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredUsername; + FString StoredDisplayName; + FString StoredAvatarUrl; + FString StoredLangTag; + FString StoredLocation; + FString StoredTimezone; +}; + +/** +* Update fields in a given group. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientUpdateGroup : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "UpdateGroup") + , Category = "Nakama|Client" + ) + static UNakamaClientUpdateGroup* UpdateGroup( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& GroupId + , const FString& Name + , const FString& Description + , const FString& LangTag + , const FString& AvatarUrl + , FNakamaOptionalBool Open + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredGroupId; + FString StoredName; + FString StoredDescription; + FString StoredLangTag; + FString StoredAvatarUrl; + FNakamaOptionalBool StoredOpen; +}; + +/** +* Validate Apple IAP Receipt +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientValidatePurchaseApple : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaValidatePurchaseApple OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaValidatePurchaseApple OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ValidatePurchaseApple") + , Category = "Nakama|Client" + ) + static UNakamaClientValidatePurchaseApple* ValidatePurchaseApple( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Receipt + , FNakamaOptionalBool Persist + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredReceipt; + FNakamaOptionalBool StoredPersist; +}; + +/** +* Validate Apple Subscription Receipt +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientValidateSubscriptionApple : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaValidateSubscriptionApple OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaValidateSubscriptionApple OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ValidateSubscriptionApple") + , Category = "Nakama|Client" + ) + static UNakamaClientValidateSubscriptionApple* ValidateSubscriptionApple( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Receipt + , FNakamaOptionalBool Persist + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredReceipt; + FNakamaOptionalBool StoredPersist; +}; + +/** +* Validate Google IAP Receipt +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientValidatePurchaseGoogle : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaValidatePurchaseGoogle OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaValidatePurchaseGoogle OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ValidatePurchaseGoogle") + , Category = "Nakama|Client" + ) + static UNakamaClientValidatePurchaseGoogle* ValidatePurchaseGoogle( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Purchase + , FNakamaOptionalBool Persist + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredPurchase; + FNakamaOptionalBool StoredPersist; +}; + +/** +* Validate Google Subscription Receipt +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientValidateSubscriptionGoogle : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaValidateSubscriptionGoogle OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaValidateSubscriptionGoogle OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ValidateSubscriptionGoogle") + , Category = "Nakama|Client" + ) + static UNakamaClientValidateSubscriptionGoogle* ValidateSubscriptionGoogle( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Receipt + , FNakamaOptionalBool Persist + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredReceipt; + FNakamaOptionalBool StoredPersist; +}; + +/** +* Validate Huawei IAP Receipt +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientValidatePurchaseHuawei : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaValidatePurchaseHuawei OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaValidatePurchaseHuawei OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ValidatePurchaseHuawei") + , Category = "Nakama|Client" + ) + static UNakamaClientValidatePurchaseHuawei* ValidatePurchaseHuawei( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& Purchase + , const FString& Signature + , FNakamaOptionalBool Persist + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredPurchase; + FString StoredSignature; + FNakamaOptionalBool StoredPersist; +}; + +/** +* Validate FB Instant IAP Receipt +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientValidatePurchaseFacebookInstant : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaValidatePurchaseFacebookInstant OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaValidatePurchaseFacebookInstant OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ValidatePurchaseFacebookInstant") + , Category = "Nakama|Client" + ) + static UNakamaClientValidatePurchaseFacebookInstant* ValidatePurchaseFacebookInstant( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& SignedRequest + , FNakamaOptionalBool Persist + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredSignedRequest; + FNakamaOptionalBool StoredPersist; +}; + +/** +* Write a record to a leaderboard. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientWriteLeaderboardRecord : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaWriteLeaderboardRecord OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaWriteLeaderboardRecord OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "WriteLeaderboardRecord") + , Category = "Nakama|Client" + ) + static UNakamaClientWriteLeaderboardRecord* WriteLeaderboardRecord( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& LeaderboardId + , const FNakamaWriteLeaderboardRecordRequestLeaderboardRecordWrite& Record + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredLeaderboardId; + FNakamaWriteLeaderboardRecordRequestLeaderboardRecordWrite StoredRecord; +}; + +/** +* Write objects into the storage engine. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientWriteStorageObjects : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaWriteStorageObjects OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaWriteStorageObjects OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "WriteStorageObjects") + , Category = "Nakama|Client" + ) + static UNakamaClientWriteStorageObjects* WriteStorageObjects( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const TArray& Objects + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + TArray StoredObjects; +}; + +/** +* Write a record to a tournament. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaClientWriteTournamentRecord : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnNakamaWriteTournamentRecord OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaWriteTournamentRecord OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "WriteTournamentRecord") + , Category = "Nakama|Client" + ) + static UNakamaClientWriteTournamentRecord* WriteTournamentRecord( + UObject* WorldContextObject + , FNakamaClientConfig ClientConfig + , const FNakamaSession& Session + , const FString& TournamentId + , const FNakamaWriteTournamentRecordRequestTournamentRecordWrite& Record + ); + + virtual void Activate() override; + +private: + FNakamaClientConfig StoredClientConfig; + FNakamaSession StoredSession; + FString StoredTournamentId; + FNakamaWriteTournamentRecordRequestTournamentRecordWrite StoredRecord; +}; diff --git a/Nakama/Source/NakamaBlueprints/NakamaRtClientBlueprintLibrary.cpp b/Nakama/Source/NakamaBlueprints/NakamaRtClientBlueprintLibrary.cpp new file mode 100644 index 000000000..9b40f560a --- /dev/null +++ b/Nakama/Source/NakamaBlueprints/NakamaRtClientBlueprintLibrary.cpp @@ -0,0 +1,1618 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + +#include "NakamaRtClientBlueprintLibrary.h" +#include "NakamaRt.h" + +UNakamaRealtimeClientChannelJoin* UNakamaRealtimeClientChannelJoin::ChannelJoin( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& Target + , int32 Type + , FNakamaRtOptionalBool Persistence + , FNakamaRtOptionalBool Hidden +) +{ + UNakamaRealtimeClientChannelJoin* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredTarget = Target; + Action->StoredType = Type; + Action->StoredPersistence = Persistence; + Action->StoredHidden = Hidden; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientChannelJoin::Activate() +{ + static const TCHAR* TraceScope_ChannelJoin = TEXT("NakamaRTBP_ChannelJoin"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ChannelJoin); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err, {}); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::ChannelJoin( + StoredConnection + , StoredTarget + , StoredType + , StoredPersistence + , StoredHidden + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}, Resp.Data.GetValue()); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue(), {}); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientChannelLeave* UNakamaRealtimeClientChannelLeave::ChannelLeave( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& ChannelId +) +{ + UNakamaRealtimeClientChannelLeave* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredChannelId = ChannelId; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientChannelLeave::Activate() +{ + static const TCHAR* TraceScope_ChannelLeave = TEXT("NakamaRTBP_ChannelLeave"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ChannelLeave); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::ChannelLeave( + StoredConnection + , StoredChannelId + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue()); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientChannelMessageSend* UNakamaRealtimeClientChannelMessageSend::ChannelMessageSend( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& ChannelId + , const FString& Content +) +{ + UNakamaRealtimeClientChannelMessageSend* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredChannelId = ChannelId; + Action->StoredContent = Content; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientChannelMessageSend::Activate() +{ + static const TCHAR* TraceScope_ChannelMessageSend = TEXT("NakamaRTBP_ChannelMessageSend"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ChannelMessageSend); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err, {}); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::ChannelMessageSend( + StoredConnection + , StoredChannelId + , StoredContent + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}, Resp.Data.GetValue()); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue(), {}); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientChannelMessageUpdate* UNakamaRealtimeClientChannelMessageUpdate::ChannelMessageUpdate( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& ChannelId + , const FString& MessageId + , const FString& Content +) +{ + UNakamaRealtimeClientChannelMessageUpdate* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredChannelId = ChannelId; + Action->StoredMessageId = MessageId; + Action->StoredContent = Content; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientChannelMessageUpdate::Activate() +{ + static const TCHAR* TraceScope_ChannelMessageUpdate = TEXT("NakamaRTBP_ChannelMessageUpdate"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ChannelMessageUpdate); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err, {}); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::ChannelMessageUpdate( + StoredConnection + , StoredChannelId + , StoredMessageId + , StoredContent + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}, Resp.Data.GetValue()); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue(), {}); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientChannelMessageRemove* UNakamaRealtimeClientChannelMessageRemove::ChannelMessageRemove( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& ChannelId + , const FString& MessageId +) +{ + UNakamaRealtimeClientChannelMessageRemove* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredChannelId = ChannelId; + Action->StoredMessageId = MessageId; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientChannelMessageRemove::Activate() +{ + static const TCHAR* TraceScope_ChannelMessageRemove = TEXT("NakamaRTBP_ChannelMessageRemove"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ChannelMessageRemove); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err, {}); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::ChannelMessageRemove( + StoredConnection + , StoredChannelId + , StoredMessageId + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}, Resp.Data.GetValue()); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue(), {}); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientMatchCreate* UNakamaRealtimeClientMatchCreate::MatchCreate( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& Name +) +{ + UNakamaRealtimeClientMatchCreate* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredName = Name; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientMatchCreate::Activate() +{ + static const TCHAR* TraceScope_MatchCreate = TEXT("NakamaRTBP_MatchCreate"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_MatchCreate); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err, {}); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::MatchCreate( + StoredConnection + , StoredName + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}, Resp.Data.GetValue()); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue(), {}); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientMatchDataSend* UNakamaRealtimeClientMatchDataSend::MatchDataSend( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& MatchId + , int64 OpCode + , const TArray& Data + , const TArray& Presences + , bool Reliable +) +{ + UNakamaRealtimeClientMatchDataSend* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredMatchId = MatchId; + Action->StoredOpCode = OpCode; + Action->StoredData = Data; + Action->StoredPresences = Presences; + Action->StoredReliable = Reliable; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientMatchDataSend::Activate() +{ + static const TCHAR* TraceScope_MatchDataSend = TEXT("NakamaRTBP_MatchDataSend"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_MatchDataSend); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::MatchDataSend( + StoredConnection + , StoredMatchId + , StoredOpCode + , StoredData + , StoredPresences + , StoredReliable + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue()); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientMatchJoin* UNakamaRealtimeClientMatchJoin::MatchJoin( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& MatchId + , const FString& Token + , const TMap& Metadata +) +{ + UNakamaRealtimeClientMatchJoin* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredMatchId = MatchId; + Action->StoredToken = Token; + Action->StoredMetadata = Metadata; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientMatchJoin::Activate() +{ + static const TCHAR* TraceScope_MatchJoin = TEXT("NakamaRTBP_MatchJoin"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_MatchJoin); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err, {}); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::MatchJoin( + StoredConnection + , StoredMatchId + , StoredToken + , StoredMetadata + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}, Resp.Data.GetValue()); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue(), {}); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientMatchLeave* UNakamaRealtimeClientMatchLeave::MatchLeave( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& MatchId +) +{ + UNakamaRealtimeClientMatchLeave* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredMatchId = MatchId; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientMatchLeave::Activate() +{ + static const TCHAR* TraceScope_MatchLeave = TEXT("NakamaRTBP_MatchLeave"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_MatchLeave); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::MatchLeave( + StoredConnection + , StoredMatchId + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue()); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientMatchmakerAdd* UNakamaRealtimeClientMatchmakerAdd::MatchmakerAdd( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , int32 MinCount + , int32 MaxCount + , const FString& Query + , FNakamaRtOptionalInt32 CountMultiple + , const TMap& StringProperties + , const TMap& NumericProperties +) +{ + UNakamaRealtimeClientMatchmakerAdd* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredMinCount = MinCount; + Action->StoredMaxCount = MaxCount; + Action->StoredQuery = Query; + Action->StoredCountMultiple = CountMultiple; + Action->StoredStringProperties = StringProperties; + Action->StoredNumericProperties = NumericProperties; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientMatchmakerAdd::Activate() +{ + static const TCHAR* TraceScope_MatchmakerAdd = TEXT("NakamaRTBP_MatchmakerAdd"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_MatchmakerAdd); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err, {}); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::MatchmakerAdd( + StoredConnection + , StoredMinCount + , StoredMaxCount + , StoredQuery + , StoredCountMultiple + , StoredStringProperties + , StoredNumericProperties + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}, Resp.Data.GetValue()); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue(), {}); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientMatchmakerRemove* UNakamaRealtimeClientMatchmakerRemove::MatchmakerRemove( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& Ticket +) +{ + UNakamaRealtimeClientMatchmakerRemove* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredTicket = Ticket; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientMatchmakerRemove::Activate() +{ + static const TCHAR* TraceScope_MatchmakerRemove = TEXT("NakamaRTBP_MatchmakerRemove"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_MatchmakerRemove); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::MatchmakerRemove( + StoredConnection + , StoredTicket + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue()); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientRpc* UNakamaRealtimeClientRpc::Rpc( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& Id + , const FString& Payload + , const FString& HttpKey +) +{ + UNakamaRealtimeClientRpc* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredId = Id; + Action->StoredPayload = Payload; + Action->StoredHttpKey = HttpKey; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientRpc::Activate() +{ + static const TCHAR* TraceScope_Rpc = TEXT("NakamaRTBP_Rpc"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_Rpc); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err, {}); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::Rpc( + StoredConnection + , StoredId + , StoredPayload + , StoredHttpKey + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}, Resp.Data.GetValue()); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue(), {}); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientStatusFollow* UNakamaRealtimeClientStatusFollow::StatusFollow( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const TArray& UserIds + , const TArray& Usernames +) +{ + UNakamaRealtimeClientStatusFollow* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredUserIds = UserIds; + Action->StoredUsernames = Usernames; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientStatusFollow::Activate() +{ + static const TCHAR* TraceScope_StatusFollow = TEXT("NakamaRTBP_StatusFollow"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_StatusFollow); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err, {}); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::StatusFollow( + StoredConnection + , StoredUserIds + , StoredUsernames + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}, Resp.Data.GetValue()); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue(), {}); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientStatusUnfollow* UNakamaRealtimeClientStatusUnfollow::StatusUnfollow( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const TArray& UserIds +) +{ + UNakamaRealtimeClientStatusUnfollow* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredUserIds = UserIds; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientStatusUnfollow::Activate() +{ + static const TCHAR* TraceScope_StatusUnfollow = TEXT("NakamaRTBP_StatusUnfollow"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_StatusUnfollow); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::StatusUnfollow( + StoredConnection + , StoredUserIds + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue()); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientStatusUpdate* UNakamaRealtimeClientStatusUpdate::StatusUpdate( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& Status +) +{ + UNakamaRealtimeClientStatusUpdate* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredStatus = Status; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientStatusUpdate::Activate() +{ + static const TCHAR* TraceScope_StatusUpdate = TEXT("NakamaRTBP_StatusUpdate"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_StatusUpdate); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::StatusUpdate( + StoredConnection + , StoredStatus + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue()); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientPing* UNakamaRealtimeClientPing::Ping( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle +) +{ + UNakamaRealtimeClientPing* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientPing::Activate() +{ + static const TCHAR* TraceScope_Ping = TEXT("NakamaRTBP_Ping"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_Ping); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err, {}); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::Ping( + StoredConnection + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}, Resp.Data.GetValue()); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue(), {}); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientPartyCreate* UNakamaRealtimeClientPartyCreate::PartyCreate( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , bool Open + , int32 MaxSize + , const FString& Label + , bool Hidden +) +{ + UNakamaRealtimeClientPartyCreate* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredOpen = Open; + Action->StoredMaxSize = MaxSize; + Action->StoredLabel = Label; + Action->StoredHidden = Hidden; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientPartyCreate::Activate() +{ + static const TCHAR* TraceScope_PartyCreate = TEXT("NakamaRTBP_PartyCreate"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_PartyCreate); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err, {}); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::PartyCreate( + StoredConnection + , StoredOpen + , StoredMaxSize + , StoredLabel + , StoredHidden + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}, Resp.Data.GetValue()); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue(), {}); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientPartyJoin* UNakamaRealtimeClientPartyJoin::PartyJoin( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& PartyId +) +{ + UNakamaRealtimeClientPartyJoin* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredPartyId = PartyId; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientPartyJoin::Activate() +{ + static const TCHAR* TraceScope_PartyJoin = TEXT("NakamaRTBP_PartyJoin"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_PartyJoin); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::PartyJoin( + StoredConnection + , StoredPartyId + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue()); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientPartyLeave* UNakamaRealtimeClientPartyLeave::PartyLeave( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& PartyId +) +{ + UNakamaRealtimeClientPartyLeave* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredPartyId = PartyId; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientPartyLeave::Activate() +{ + static const TCHAR* TraceScope_PartyLeave = TEXT("NakamaRTBP_PartyLeave"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_PartyLeave); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::PartyLeave( + StoredConnection + , StoredPartyId + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue()); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientPartyPromote* UNakamaRealtimeClientPartyPromote::PartyPromote( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& PartyId + , const FNakamaRtUserPresence& Presence +) +{ + UNakamaRealtimeClientPartyPromote* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredPartyId = PartyId; + Action->StoredPresence = Presence; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientPartyPromote::Activate() +{ + static const TCHAR* TraceScope_PartyPromote = TEXT("NakamaRTBP_PartyPromote"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_PartyPromote); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::PartyPromote( + StoredConnection + , StoredPartyId + , StoredPresence + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue()); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientPartyAccept* UNakamaRealtimeClientPartyAccept::PartyAccept( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& PartyId + , const FNakamaRtUserPresence& Presence +) +{ + UNakamaRealtimeClientPartyAccept* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredPartyId = PartyId; + Action->StoredPresence = Presence; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientPartyAccept::Activate() +{ + static const TCHAR* TraceScope_PartyAccept = TEXT("NakamaRTBP_PartyAccept"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_PartyAccept); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::PartyAccept( + StoredConnection + , StoredPartyId + , StoredPresence + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue()); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientPartyRemove* UNakamaRealtimeClientPartyRemove::PartyRemove( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& PartyId + , const FNakamaRtUserPresence& Presence +) +{ + UNakamaRealtimeClientPartyRemove* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredPartyId = PartyId; + Action->StoredPresence = Presence; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientPartyRemove::Activate() +{ + static const TCHAR* TraceScope_PartyRemove = TEXT("NakamaRTBP_PartyRemove"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_PartyRemove); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::PartyRemove( + StoredConnection + , StoredPartyId + , StoredPresence + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue()); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientPartyClose* UNakamaRealtimeClientPartyClose::PartyClose( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& PartyId +) +{ + UNakamaRealtimeClientPartyClose* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredPartyId = PartyId; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientPartyClose::Activate() +{ + static const TCHAR* TraceScope_PartyClose = TEXT("NakamaRTBP_PartyClose"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_PartyClose); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::PartyClose( + StoredConnection + , StoredPartyId + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue()); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientPartyJoinRequestList* UNakamaRealtimeClientPartyJoinRequestList::PartyJoinRequestList( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& PartyId +) +{ + UNakamaRealtimeClientPartyJoinRequestList* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredPartyId = PartyId; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientPartyJoinRequestList::Activate() +{ + static const TCHAR* TraceScope_PartyJoinRequestList = TEXT("NakamaRTBP_PartyJoinRequestList"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_PartyJoinRequestList); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err, {}); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::PartyJoinRequestList( + StoredConnection + , StoredPartyId + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}, Resp.Data.GetValue()); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue(), {}); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientPartyMatchmakerAdd* UNakamaRealtimeClientPartyMatchmakerAdd::PartyMatchmakerAdd( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& PartyId + , int32 MinCount + , int32 MaxCount + , const FString& Query + , FNakamaRtOptionalInt32 CountMultiple + , const TMap& StringProperties + , const TMap& NumericProperties +) +{ + UNakamaRealtimeClientPartyMatchmakerAdd* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredPartyId = PartyId; + Action->StoredMinCount = MinCount; + Action->StoredMaxCount = MaxCount; + Action->StoredQuery = Query; + Action->StoredCountMultiple = CountMultiple; + Action->StoredStringProperties = StringProperties; + Action->StoredNumericProperties = NumericProperties; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientPartyMatchmakerAdd::Activate() +{ + static const TCHAR* TraceScope_PartyMatchmakerAdd = TEXT("NakamaRTBP_PartyMatchmakerAdd"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_PartyMatchmakerAdd); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err, {}); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::PartyMatchmakerAdd( + StoredConnection + , StoredPartyId + , StoredMinCount + , StoredMaxCount + , StoredQuery + , StoredCountMultiple + , StoredStringProperties + , StoredNumericProperties + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}, Resp.Data.GetValue()); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue(), {}); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientPartyMatchmakerRemove* UNakamaRealtimeClientPartyMatchmakerRemove::PartyMatchmakerRemove( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& PartyId + , const FString& Ticket +) +{ + UNakamaRealtimeClientPartyMatchmakerRemove* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredPartyId = PartyId; + Action->StoredTicket = Ticket; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientPartyMatchmakerRemove::Activate() +{ + static const TCHAR* TraceScope_PartyMatchmakerRemove = TEXT("NakamaRTBP_PartyMatchmakerRemove"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_PartyMatchmakerRemove); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::PartyMatchmakerRemove( + StoredConnection + , StoredPartyId + , StoredTicket + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue()); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientPartyDataSend* UNakamaRealtimeClientPartyDataSend::PartyDataSend( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& PartyId + , int64 OpCode + , const TArray& Data +) +{ + UNakamaRealtimeClientPartyDataSend* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredPartyId = PartyId; + Action->StoredOpCode = OpCode; + Action->StoredData = Data; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientPartyDataSend::Activate() +{ + static const TCHAR* TraceScope_PartyDataSend = TEXT("NakamaRTBP_PartyDataSend"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_PartyDataSend); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::PartyDataSend( + StoredConnection + , StoredPartyId + , StoredOpCode + , StoredData + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue()); + } + Self->SetReadyToDestroy(); + }); +} + +UNakamaRealtimeClientPartyUpdate* UNakamaRealtimeClientPartyUpdate::PartyUpdate( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& PartyId + , const FString& Label + , bool Open + , bool Hidden +) +{ + UNakamaRealtimeClientPartyUpdate* Action = NewObject(GetTransientPackage()); + Action->StoredConnection = ConnectionHandle->Connection; + Action->StoredPartyId = PartyId; + Action->StoredLabel = Label; + Action->StoredOpen = Open; + Action->StoredHidden = Hidden; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void UNakamaRealtimeClientPartyUpdate::Activate() +{ + static const TCHAR* TraceScope_PartyUpdate = TEXT("NakamaRTBP_PartyUpdate"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_PartyUpdate); + + if (!StoredConnection.IsValid()) + { + FNakamaRtError Err; + Err.Message = TEXT("Realtime Connection is invalid"); + OnError.Broadcast(Err); + SetReadyToDestroy(); + return; + } + + TWeakObjectPtr WeakThis(this); + NakamaRt::PartyUpdate( + StoredConnection + , StoredPartyId + , StoredLabel + , StoredOpen + , StoredHidden + ) + .Next([WeakThis](NakamaRt::FNakamaRtResult Resp) + { + auto* Self = WeakThis.Get(); + if (!Self) + { + return; + } + + if (Resp.bIsSuccess) + { + Self->OnSuccess.Broadcast({}); + } + else + { + Self->OnError.Broadcast(Resp.Error.GetValue()); + } + Self->SetReadyToDestroy(); + }); +} diff --git a/Nakama/Source/NakamaBlueprints/NakamaRtClientBlueprintLibrary.h b/Nakama/Source/NakamaBlueprints/NakamaRtClientBlueprintLibrary.h new file mode 100644 index 000000000..dc10b5f81 --- /dev/null +++ b/Nakama/Source/NakamaBlueprints/NakamaRtClientBlueprintLibrary.h @@ -0,0 +1,1106 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + +#pragma once + +#include "CoreMinimal.h" +#include "Kismet/BlueprintAsyncActionBase.h" +#include "NakamaRtTypes.h" +#include "NakamaRtConnection.h" +#include "NakamaRtHandle.h" + +#include "NakamaRtClientBlueprintLibrary.generated.h" + +// ============================================================================ +// Dynamic delegates for async actions +// ============================================================================ + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnNakamaRtEmptyResponse, const FNakamaRtError&, Error); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaChannelJoinResponse, const FNakamaRtError&, Error, const FNakamaRtChannel&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaChannelMessageSendResponse, const FNakamaRtError&, Error, const FNakamaRtChannelMessageAck&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaChannelMessageUpdateResponse, const FNakamaRtError&, Error, const FNakamaRtChannelMessageAck&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaChannelMessageRemoveResponse, const FNakamaRtError&, Error, const FNakamaRtChannelMessageAck&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaMatchCreateResponse, const FNakamaRtError&, Error, const FNakamaRtMatch&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaMatchJoinResponse, const FNakamaRtError&, Error, const FNakamaRtMatch&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaMatchmakerAddResponse, const FNakamaRtError&, Error, const FNakamaRtMatchmakerTicket&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaRpcResponse, const FNakamaRtError&, Error, const FNakamaRtRpc&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaStatusFollowResponse, const FNakamaRtError&, Error, const FNakamaRtStatus&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaPingResponse, const FNakamaRtError&, Error, const FNakamaRtPong&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaPartyCreateResponse, const FNakamaRtError&, Error, const FNakamaRtParty&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaPartyJoinRequestListResponse, const FNakamaRtError&, Error, const FNakamaRtPartyJoinRequest&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnNakamaPartyMatchmakerAddResponse, const FNakamaRtError&, Error, const FNakamaRtPartyMatchmakerTicket&, Result); + +// ============================================================================ +// Async Action Classes (one per RPC) +// ============================================================================ +/** +* Join a realtime chat channel. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientChannelJoin : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaChannelJoinResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaChannelJoinResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ChannelJoin") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientChannelJoin* ChannelJoin( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& Target + , int32 Type + , FNakamaRtOptionalBool Persistence + , FNakamaRtOptionalBool Hidden + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + FString StoredTarget; + int32 StoredType; + FNakamaRtOptionalBool StoredPersistence; + FNakamaRtOptionalBool StoredHidden; +}; +/** +* Leave a realtime chat channel. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientChannelLeave : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ChannelLeave") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientChannelLeave* ChannelLeave( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& ChannelId + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + FString StoredChannelId; +}; +/** +* Send a message to a realtime chat channel. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientChannelMessageSend : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaChannelMessageSendResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaChannelMessageSendResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ChannelMessageSend") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientChannelMessageSend* ChannelMessageSend( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& ChannelId + , const FString& Content + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + FString StoredChannelId; + FString StoredContent; +}; +/** +* Update a message previously sent to a realtime chat channel. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientChannelMessageUpdate : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaChannelMessageUpdateResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaChannelMessageUpdateResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ChannelMessageUpdate") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientChannelMessageUpdate* ChannelMessageUpdate( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& ChannelId + , const FString& MessageId + , const FString& Content + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + FString StoredChannelId; + FString StoredMessageId; + FString StoredContent; +}; +/** +* Remove a message previously sent to a realtime chat channel. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientChannelMessageRemove : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaChannelMessageRemoveResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaChannelMessageRemoveResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ChannelMessageRemove") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientChannelMessageRemove* ChannelMessageRemove( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& ChannelId + , const FString& MessageId + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + FString StoredChannelId; + FString StoredMessageId; +}; +/** +* A client to server request to create a realtime match. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientMatchCreate : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaMatchCreateResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaMatchCreateResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "MatchCreate") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientMatchCreate* MatchCreate( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& Name + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + FString StoredName; +}; +/** +* A client to server request to send data to a realtime match. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientMatchDataSend : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "MatchDataSend") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientMatchDataSend* MatchDataSend( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& MatchId + , int64 OpCode + , const TArray& Data + , const TArray& Presences + , bool Reliable + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + FString StoredMatchId; + int64 StoredOpCode; + TArray StoredData; + TArray StoredPresences; + bool StoredReliable; +}; +/** +* A client to server request to join a realtime match. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientMatchJoin : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaMatchJoinResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaMatchJoinResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "MatchJoin", AutoCreateRefTerm = "Metadata") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientMatchJoin* MatchJoin( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& MatchId + , const FString& Token + , const TMap& Metadata + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + FString StoredMatchId; + FString StoredToken; + TMap StoredMetadata; +}; +/** +* A client to server request to leave a realtime match. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientMatchLeave : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "MatchLeave") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientMatchLeave* MatchLeave( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& MatchId + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + FString StoredMatchId; +}; +/** +* Submit a new matchmaking process request. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientMatchmakerAdd : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaMatchmakerAddResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaMatchmakerAddResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "MatchmakerAdd", AutoCreateRefTerm = "StringProperties,NumericProperties") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientMatchmakerAdd* MatchmakerAdd( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , int32 MinCount + , int32 MaxCount + , const FString& Query + , FNakamaRtOptionalInt32 CountMultiple + , const TMap& StringProperties + , const TMap& NumericProperties + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + int32 StoredMinCount; + int32 StoredMaxCount; + FString StoredQuery; + FNakamaRtOptionalInt32 StoredCountMultiple; + TMap StoredStringProperties; + TMap StoredNumericProperties; +}; +/** +* Cancel a matchmaking process using a ticket. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientMatchmakerRemove : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "MatchmakerRemove") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientMatchmakerRemove* MatchmakerRemove( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& Ticket + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + FString StoredTicket; +}; +/** +* RPC call or response. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientRpc : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaRpcResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaRpcResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "Rpc") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientRpc* Rpc( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& Id + , const FString& Payload + , const FString& HttpKey + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + FString StoredId; + FString StoredPayload; + FString StoredHttpKey; +}; +/** +* Start following some set of users to receive their status updates. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientStatusFollow : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaStatusFollowResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaStatusFollowResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "StatusFollow") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientStatusFollow* StatusFollow( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const TArray& UserIds + , const TArray& Usernames + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + TArray StoredUserIds; + TArray StoredUsernames; +}; +/** +* Stop following some set of users to no longer receive their status updates. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientStatusUnfollow : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "StatusUnfollow") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientStatusUnfollow* StatusUnfollow( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const TArray& UserIds + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + TArray StoredUserIds; +}; +/** +* Set the user's own status. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientStatusUpdate : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "StatusUpdate") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientStatusUpdate* StatusUpdate( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& Status + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + FString StoredStatus; +}; +/** +* Application-level heartbeat and connection check. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientPing : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaPingResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaPingResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "Ping") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientPing* Ping( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; +}; +/** +* Create a party. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientPartyCreate : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaPartyCreateResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaPartyCreateResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "PartyCreate") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientPartyCreate* PartyCreate( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , bool Open + , int32 MaxSize + , const FString& Label + , bool Hidden + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + bool StoredOpen; + int32 StoredMaxSize; + FString StoredLabel; + bool StoredHidden; +}; +/** +* Join a party, or request to join if the party is not open. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientPartyJoin : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "PartyJoin") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientPartyJoin* PartyJoin( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& PartyId + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + FString StoredPartyId; +}; +/** +* Leave a party. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientPartyLeave : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "PartyLeave") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientPartyLeave* PartyLeave( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& PartyId + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + FString StoredPartyId; +}; +/** +* Promote a new party leader. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientPartyPromote : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "PartyPromote") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientPartyPromote* PartyPromote( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& PartyId + , const FNakamaRtUserPresence& Presence + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + FString StoredPartyId; + FNakamaRtUserPresence StoredPresence; +}; +/** +* Accept a request to join. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientPartyAccept : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "PartyAccept") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientPartyAccept* PartyAccept( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& PartyId + , const FNakamaRtUserPresence& Presence + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + FString StoredPartyId; + FNakamaRtUserPresence StoredPresence; +}; +/** +* Kick a party member, or decline a request to join. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientPartyRemove : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "PartyRemove") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientPartyRemove* PartyRemove( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& PartyId + , const FNakamaRtUserPresence& Presence + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + FString StoredPartyId; + FNakamaRtUserPresence StoredPresence; +}; +/** +* End a party, kicking all party members and closing it. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientPartyClose : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "PartyClose") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientPartyClose* PartyClose( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& PartyId + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + FString StoredPartyId; +}; +/** +* Request a list of pending join requests for a party. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientPartyJoinRequestList : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaPartyJoinRequestListResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaPartyJoinRequestListResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "PartyJoinRequestList") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientPartyJoinRequestList* PartyJoinRequestList( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& PartyId + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + FString StoredPartyId; +}; +/** +* Begin matchmaking as a party. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientPartyMatchmakerAdd : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaPartyMatchmakerAddResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaPartyMatchmakerAddResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "PartyMatchmakerAdd", AutoCreateRefTerm = "StringProperties,NumericProperties") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientPartyMatchmakerAdd* PartyMatchmakerAdd( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& PartyId + , int32 MinCount + , int32 MaxCount + , const FString& Query + , FNakamaRtOptionalInt32 CountMultiple + , const TMap& StringProperties + , const TMap& NumericProperties + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + FString StoredPartyId; + int32 StoredMinCount; + int32 StoredMaxCount; + FString StoredQuery; + FNakamaRtOptionalInt32 StoredCountMultiple; + TMap StoredStringProperties; + TMap StoredNumericProperties; +}; +/** +* Cancel a party matchmaking process using a ticket. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientPartyMatchmakerRemove : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "PartyMatchmakerRemove") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientPartyMatchmakerRemove* PartyMatchmakerRemove( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& PartyId + , const FString& Ticket + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + FString StoredPartyId; + FString StoredTicket; +}; +/** +* A client to server request to send data to a party. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientPartyDataSend : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "PartyDataSend") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientPartyDataSend* PartyDataSend( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& PartyId + , int64 OpCode + , const TArray& Data + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + FString StoredPartyId; + int64 StoredOpCode; + TArray StoredData; +}; +/** +* Update Party label and whether it's open or closed. +*/ +UCLASS(Transient) +class NAKAMABLUEPRINTS_API UNakamaRealtimeClientPartyUpdate : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnNakamaRtEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "PartyUpdate") + , Category = "Nakama|RealtimeClient" + ) + static UNakamaRealtimeClientPartyUpdate* PartyUpdate( + UObject* WorldContextObject + , UNakamaRtHandle* ConnectionHandle + , const FString& PartyId + , const FString& Label + , bool Open + , bool Hidden + ); + + virtual void Activate() override; + +private: + TSharedPtr StoredConnection; + FString StoredPartyId; + FString StoredLabel; + bool StoredOpen; + bool StoredHidden; +}; diff --git a/Nakama/Source/NakamaBlueprints/NakamaRtHandle.cpp b/Nakama/Source/NakamaBlueprints/NakamaRtHandle.cpp new file mode 100644 index 000000000..04b78e607 --- /dev/null +++ b/Nakama/Source/NakamaBlueprints/NakamaRtHandle.cpp @@ -0,0 +1,264 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + +#include "NakamaRtHandle.h" + +UNakamaRtHandle* UNakamaRtHandle::CreateNakamaRtConnection() +{ + UNakamaRtHandle* Handle = NewObject(); + + Handle->Connection = MakeShared(); + Handle->SetupRtEventHandlers(); + + return Handle; +} + +void UNakamaRtHandle::Connect(const FNakamaWebSocketConnectionParams& Params) +{ + if (!Connection.IsValid()) + { + ConnectCompleted.Broadcast(FNakamaWebSocketConnectionResult { .ErrorCode = ENakamaWebSocketError::ConnectionFailed }); + return; + } + + TWeakObjectPtr WeakHandle(this); + + Connection->Connect(Params) + .Next([WeakHandle](const FNakamaWebSocketConnectionResult& Result) + { + if (TStrongObjectPtr StrongHandle = WeakHandle.Pin()) + { + StrongHandle->ConnectCompleted.Broadcast(Result); + } + }); +} + +void UNakamaRtHandle::SetupRtEventHandlers() +{ + TWeakObjectPtr WeakHandle(this); + + Connection->Closed.AddLambda([WeakHandle](int32 StatusCode, const FString& Reason, bool bWasClean) + { + AsyncTask(ENamedThreads::GameThread, [WeakHandle, StatusCode, Reason, bWasClean] + { + if (TStrongObjectPtr StrongHandle = WeakHandle.Pin()) + { + if (StrongHandle->Closed.IsBound()) + { + StrongHandle->Closed.Broadcast(StatusCode, Reason, bWasClean); + } + } + }); + }); + Connection->MessageError.AddLambda([WeakHandle](EWebSocketMessageError ErrorType, const FString& Message) + { + AsyncTask(ENamedThreads::GameThread, [WeakHandle, ErrorType, Message] + { + if (TStrongObjectPtr StrongHandle = WeakHandle.Pin()) + { + if (StrongHandle->MessageError.IsBound()) + { + StrongHandle->MessageError.Broadcast(ErrorType, Message); + } + } + }); + }); + + Connection->ChannelMessage.AddLambda([WeakHandle](const FNakamaRtChannelMessage& Data) + { + AsyncTask(ENamedThreads::GameThread, [WeakHandle, Data] + { + if (TStrongObjectPtr StrongHandle = WeakHandle.Pin()) + { + if (StrongHandle->ChannelMessage.IsBound()) + { + StrongHandle->ChannelMessage.Broadcast(Data); + } + } + }); + }); + + Connection->ChannelPresenceEvent.AddLambda([WeakHandle](const FNakamaRtChannelPresenceEvent& Data) + { + AsyncTask(ENamedThreads::GameThread, [WeakHandle, Data] + { + if (TStrongObjectPtr StrongHandle = WeakHandle.Pin()) + { + if (StrongHandle->ChannelPresenceEvent.IsBound()) + { + StrongHandle->ChannelPresenceEvent.Broadcast(Data); + } + } + }); + }); + + Connection->MatchData.AddLambda([WeakHandle](const FNakamaRtMatchData& Data) + { + AsyncTask(ENamedThreads::GameThread, [WeakHandle, Data] + { + if (TStrongObjectPtr StrongHandle = WeakHandle.Pin()) + { + if (StrongHandle->MatchData.IsBound()) + { + StrongHandle->MatchData.Broadcast(Data); + } + } + }); + }); + + Connection->MatchPresenceEvent.AddLambda([WeakHandle](const FNakamaRtMatchPresenceEvent& Data) + { + AsyncTask(ENamedThreads::GameThread, [WeakHandle, Data] + { + if (TStrongObjectPtr StrongHandle = WeakHandle.Pin()) + { + if (StrongHandle->MatchPresenceEvent.IsBound()) + { + StrongHandle->MatchPresenceEvent.Broadcast(Data); + } + } + }); + }); + + Connection->MatchmakerMatched.AddLambda([WeakHandle](const FNakamaRtMatchmakerMatched& Data) + { + AsyncTask(ENamedThreads::GameThread, [WeakHandle, Data] + { + if (TStrongObjectPtr StrongHandle = WeakHandle.Pin()) + { + if (StrongHandle->MatchmakerMatched.IsBound()) + { + StrongHandle->MatchmakerMatched.Broadcast(Data); + } + } + }); + }); + + Connection->Notifications.AddLambda([WeakHandle](const FNakamaRtNotifications& Data) + { + AsyncTask(ENamedThreads::GameThread, [WeakHandle, Data] + { + if (TStrongObjectPtr StrongHandle = WeakHandle.Pin()) + { + if (StrongHandle->Notifications.IsBound()) + { + StrongHandle->Notifications.Broadcast(Data); + } + } + }); + }); + + Connection->PartyLeader.AddLambda([WeakHandle](const FNakamaRtPartyLeader& Data) + { + AsyncTask(ENamedThreads::GameThread, [WeakHandle, Data] + { + if (TStrongObjectPtr StrongHandle = WeakHandle.Pin()) + { + if (StrongHandle->PartyLeader.IsBound()) + { + StrongHandle->PartyLeader.Broadcast(Data); + } + } + }); + }); + + Connection->PartyJoinRequest.AddLambda([WeakHandle](const FNakamaRtPartyJoinRequest& Data) + { + AsyncTask(ENamedThreads::GameThread, [WeakHandle, Data] + { + if (TStrongObjectPtr StrongHandle = WeakHandle.Pin()) + { + if (StrongHandle->PartyJoinRequest.IsBound()) + { + StrongHandle->PartyJoinRequest.Broadcast(Data); + } + } + }); + }); + + Connection->PartyData.AddLambda([WeakHandle](const FNakamaRtPartyData& Data) + { + AsyncTask(ENamedThreads::GameThread, [WeakHandle, Data] + { + if (TStrongObjectPtr StrongHandle = WeakHandle.Pin()) + { + if (StrongHandle->PartyData.IsBound()) + { + StrongHandle->PartyData.Broadcast(Data); + } + } + }); + }); + + Connection->PartyPresenceEvent.AddLambda([WeakHandle](const FNakamaRtPartyPresenceEvent& Data) + { + AsyncTask(ENamedThreads::GameThread, [WeakHandle, Data] + { + if (TStrongObjectPtr StrongHandle = WeakHandle.Pin()) + { + if (StrongHandle->PartyPresenceEvent.IsBound()) + { + StrongHandle->PartyPresenceEvent.Broadcast(Data); + } + } + }); + }); + + Connection->StatusPresenceEvent.AddLambda([WeakHandle](const FNakamaRtStatusPresenceEvent& Data) + { + AsyncTask(ENamedThreads::GameThread, [WeakHandle, Data] + { + if (TStrongObjectPtr StrongHandle = WeakHandle.Pin()) + { + if (StrongHandle->StatusPresenceEvent.IsBound()) + { + StrongHandle->StatusPresenceEvent.Broadcast(Data); + } + } + }); + }); + + Connection->StreamData.AddLambda([WeakHandle](const FNakamaRtStreamData& Data) + { + AsyncTask(ENamedThreads::GameThread, [WeakHandle, Data] + { + if (TStrongObjectPtr StrongHandle = WeakHandle.Pin()) + { + if (StrongHandle->StreamData.IsBound()) + { + StrongHandle->StreamData.Broadcast(Data); + } + } + }); + }); + + Connection->StreamPresenceEvent.AddLambda([WeakHandle](const FNakamaRtStreamPresenceEvent& Data) + { + AsyncTask(ENamedThreads::GameThread, [WeakHandle, Data] + { + if (TStrongObjectPtr StrongHandle = WeakHandle.Pin()) + { + if (StrongHandle->StreamPresenceEvent.IsBound()) + { + StrongHandle->StreamPresenceEvent.Broadcast(Data); + } + } + }); + }); +} + diff --git a/Nakama/Source/NakamaBlueprints/NakamaRtHandle.h b/Nakama/Source/NakamaBlueprints/NakamaRtHandle.h new file mode 100644 index 000000000..ac7d0e539 --- /dev/null +++ b/Nakama/Source/NakamaBlueprints/NakamaRtHandle.h @@ -0,0 +1,120 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + +#pragma once + +#include "CoreMinimal.h" +#include "NakamaRt.h" +#include "NakamaRtHandle.generated.h" + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDelegateOnWebSocketConnectCompleted, const FNakamaWebSocketConnectionResult&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FDelegateOnWebSocketClosed, int32, StatusCode, const FString&, Reason, bool, WasClean); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FDelegateOnMessageError, EWebSocketMessageError, ErrorType, const FString&, Message); + +// +// Delegates for event callbacks (dynamic = BlueprintAssignable; native = C++ AddLambda) +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDelegateOnChannelMessage, const FNakamaRtChannelMessage&, Data); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDelegateOnChannelPresenceEvent, const FNakamaRtChannelPresenceEvent&, Data); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDelegateOnMatchData, const FNakamaRtMatchData&, Data); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDelegateOnMatchPresenceEvent, const FNakamaRtMatchPresenceEvent&, Data); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDelegateOnMatchmakerMatched, const FNakamaRtMatchmakerMatched&, Data); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDelegateOnNotifications, const FNakamaRtNotifications&, Data); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDelegateOnPartyLeader, const FNakamaRtPartyLeader&, Data); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDelegateOnPartyJoinRequest, const FNakamaRtPartyJoinRequest&, Data); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDelegateOnPartyData, const FNakamaRtPartyData&, Data); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDelegateOnPartyPresenceEvent, const FNakamaRtPartyPresenceEvent&, Data); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDelegateOnStatusPresenceEvent, const FNakamaRtStatusPresenceEvent&, Data); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDelegateOnStreamData, const FNakamaRtStreamData&, Data); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDelegateOnStreamPresenceEvent, const FNakamaRtStreamPresenceEvent&, Data); + +UCLASS(BlueprintType) +class NAKAMABLUEPRINTS_API UNakamaRtHandle : public UObject +{ + GENERATED_BODY() + +public: + TSharedPtr Connection; + + UPROPERTY(BlueprintAssignable, Category = "Nakama|Realtime") + FDelegateOnWebSocketConnectCompleted ConnectCompleted; + + UPROPERTY(BlueprintAssignable, Category = "Nakama|Realtime") + FDelegateOnWebSocketClosed Closed; + + UPROPERTY(BlueprintAssignable, Category = "Nakama|Realtime") + FDelegateOnMessageError MessageError; + + UPROPERTY(BlueprintAssignable, Category = "Nakama|Realtime") + FDelegateOnChannelMessage ChannelMessage; + + UPROPERTY(BlueprintAssignable, Category = "Nakama|Realtime") + FDelegateOnChannelPresenceEvent ChannelPresenceEvent; + + UPROPERTY(BlueprintAssignable, Category = "Nakama|Realtime") + FDelegateOnMatchData MatchData; + + UPROPERTY(BlueprintAssignable, Category = "Nakama|Realtime") + FDelegateOnMatchPresenceEvent MatchPresenceEvent; + + UPROPERTY(BlueprintAssignable, Category = "Nakama|Realtime") + FDelegateOnMatchmakerMatched MatchmakerMatched; + + UPROPERTY(BlueprintAssignable, Category = "Nakama|Realtime") + FDelegateOnNotifications Notifications; + + UPROPERTY(BlueprintAssignable, Category = "Nakama|Realtime") + FDelegateOnPartyLeader PartyLeader; + + UPROPERTY(BlueprintAssignable, Category = "Nakama|Realtime") + FDelegateOnPartyJoinRequest PartyJoinRequest; + + UPROPERTY(BlueprintAssignable, Category = "Nakama|Realtime") + FDelegateOnPartyData PartyData; + + UPROPERTY(BlueprintAssignable, Category = "Nakama|Realtime") + FDelegateOnPartyPresenceEvent PartyPresenceEvent; + + UPROPERTY(BlueprintAssignable, Category = "Nakama|Realtime") + FDelegateOnStatusPresenceEvent StatusPresenceEvent; + + UPROPERTY(BlueprintAssignable, Category = "Nakama|Realtime") + FDelegateOnStreamData StreamData; + + UPROPERTY(BlueprintAssignable, Category = "Nakama|Realtime") + FDelegateOnStreamPresenceEvent StreamPresenceEvent; + + UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime") + static UNakamaRtHandle* CreateNakamaRtConnection(); + + UFUNCTION(BlueprintCallable) + void Connect(const FNakamaWebSocketConnectionParams& Params); + + UFUNCTION(BlueprintCallable) + int32 GetPendingRequestCount() + { + if (!Connection.IsValid()) + { + return 0; + } + return Connection->GetPendingRequestCount(); + } + +private: + void SetupRtEventHandlers(); +}; + diff --git a/Nakama/Source/NakamaBlueprints/Private/NakamaBlueprints.cpp b/Nakama/Source/NakamaBlueprints/Private/NakamaBlueprints.cpp deleted file mode 100644 index 30fc75bc5..000000000 --- a/Nakama/Source/NakamaBlueprints/Private/NakamaBlueprints.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaBlueprints.h" -#include "Modules/ModuleManager.h" - -#define LOCTEXT_NAMESPACE "FNakamaBlueprintsModule" - -DEFINE_LOG_CATEGORY(LogNakamaBlueprints); - - -void FNakamaBlueprintsModule::StartupModule() -{ - // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module - - //NLogger::init(std::make_shared(), NLogLevel::Debug); - -} - -void FNakamaBlueprintsModule::ShutdownModule() -{ - // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, - // we call this function before unloading the module. -} - -#undef LOCTEXT_NAMESPACE - -IMPLEMENT_MODULE(FNakamaBlueprintsModule, NakamaBlueprints) diff --git a/Nakama/Source/NakamaBlueprints/Private/NakamaBlueprintsModule.cpp b/Nakama/Source/NakamaBlueprints/Private/NakamaBlueprintsModule.cpp new file mode 100644 index 000000000..fb82a18bd --- /dev/null +++ b/Nakama/Source/NakamaBlueprints/Private/NakamaBlueprintsModule.cpp @@ -0,0 +1,3 @@ +#include "NakamaBlueprintsModule.h" + +IMPLEMENT_MODULE(FNakamaBlueprintsModule, NakamaBlueprints) diff --git a/Nakama/Source/NakamaBlueprints/Private/NakamaClientRequests.cpp b/Nakama/Source/NakamaBlueprints/Private/NakamaClientRequests.cpp deleted file mode 100644 index d6d76f2f2..000000000 --- a/Nakama/Source/NakamaBlueprints/Private/NakamaClientRequests.cpp +++ /dev/null @@ -1,4566 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaClientRequests.h" -#include "NakamaStorageObject.h" -#include "NakamaUtils.h" -#include "NakamaError.h" -#include "NakamaGroup.h" -#include "NakamaClient.h" -#include "NakamaMatch.h" -#include "NakamaFriend.h" -#include "NakamaNotification.h" -#include "NakamaRPC.h" -#include "NakamaChannelTypes.h" -#include "NakamaLeaderboard.h" -#include "NakamaTournament.h" - -UNakamaClientAuthenticateCustom* UNakamaClientAuthenticateCustom::AuthenticateCustom(UNakamaClient* Client, - FString UserID, FString Username, bool CreateAccount, TMap Vars) -{ - UNakamaClientAuthenticateCustom* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserID = UserID; - Node->Username = Username; - Node->bCreateAccount = CreateAccount; - Node->Vars = Vars; - - return Node; -} - -void UNakamaClientAuthenticateCustom::Activate() -{ - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](UNakamaSession* session) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, session); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - NakamaClient->AuthenticateCustom(UserID, Username, bCreateAccount, Vars, successCallback, errorCallback); -} - -UNakamaClientAuthenticateEmail* UNakamaClientAuthenticateEmail::AuthenticateEmail(UNakamaClient* Client, FString Email, FString Password, - FString Username, bool CreateAccount, TMap Vars) -{ - UNakamaClientAuthenticateEmail* Node = NewObject(); - Node->NakamaClient = Client; - Node->Email = Email; - Node->Password = Password; - Node->Username = Username; - Node->bCreateAccount = CreateAccount; - Node->Vars = Vars; - - return Node; -} - -void UNakamaClientAuthenticateEmail::Activate() -{ - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](UNakamaSession* session) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, session); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - NakamaClient->AuthenticateEmail(Email, Password, Username, bCreateAccount, Vars, successCallback, errorCallback); -} - -UNakamaClientAuthenticateDevice* UNakamaClientAuthenticateDevice::AuthenticateDevice(UNakamaClient* Client, - FString DeviceID, FString Username, bool CreateAccount, TMap Vars) -{ - UNakamaClientAuthenticateDevice* Node = NewObject(); - Node->NakamaClient = Client; - Node->DeviceID = DeviceID; - Node->Username = Username; - Node->bCreateAccount = CreateAccount; - Node->Vars = Vars; - - return Node; -} - -void UNakamaClientAuthenticateDevice::Activate() -{ - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](UNakamaSession* session) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, session); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - const auto OptUsername = FNakamaUtils::CreateOptional(Username, FString()); - - NakamaClient->AuthenticateDevice(DeviceID, bCreateAccount, OptUsername, Vars, successCallback, errorCallback); -} - -UNakamaClientAuthenticateSteam* UNakamaClientAuthenticateSteam::AuthenticateSteam(UNakamaClient* Client, - FString SteamToken, FString Username, bool CreateAccount, bool ImportFriends, TMap Vars) -{ - UNakamaClientAuthenticateSteam* Node = NewObject(); - Node->NakamaClient = Client; - Node->SteamToken = SteamToken; - Node->Username = Username; - Node->bCreateAccount = CreateAccount; - Node->bImportFriends = ImportFriends; - Node->Vars = Vars; - - return Node; -} - -void UNakamaClientAuthenticateSteam::Activate() -{ - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](UNakamaSession* session) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, session); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - NakamaClient->AuthenticateSteam(SteamToken, Username, bCreateAccount, bImportFriends, Vars, successCallback, errorCallback); -} - -UNakamaClientAuthenticateGoogle* UNakamaClientAuthenticateGoogle::AuthenticateGoogle(UNakamaClient* Client, - FString AccessToken, FString Username, bool CreateAccount, TMap Vars) -{ - UNakamaClientAuthenticateGoogle* Node = NewObject(); - Node->NakamaClient = Client; - Node->AccessToken = AccessToken; - Node->Username = Username; - Node->bCreateAccount = CreateAccount; - Node->Vars = Vars; - - return Node; -} - -void UNakamaClientAuthenticateGoogle::Activate() -{ - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](UNakamaSession* session) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, session); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - NakamaClient->AuthenticateGoogle(AccessToken, Username, bCreateAccount, Vars, successCallback, errorCallback); -} - -UNakamaClientAuthenticateGameCenter* UNakamaClientAuthenticateGameCenter::AuthenticateGameCenter(UNakamaClient* Client, - FString PlayerId, FString BundleId, int64 TimeStampSeconds, FString Salt, FString Signature, FString PublicKeyUrl, - FString Username, bool CreateAccount, TMap Vars) -{ - UNakamaClientAuthenticateGameCenter* Node = NewObject(); - Node->NakamaClient = Client; - Node->PlayerId = PlayerId; - Node->BundleId = BundleId; - Node->TimeStampSeconds = TimeStampSeconds; - Node->Salt = Salt; - Node->Signature = Signature; - Node->PublicKeyUrl = PublicKeyUrl; - Node->Username = Username; - Node->bCreateAccount = CreateAccount; - Node->Vars = Vars; - - return Node; -} - -void UNakamaClientAuthenticateGameCenter::Activate() -{ - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](UNakamaSession* session) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, session); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - NakamaClient->AuthenticateGameCenter( - PlayerId, - BundleId, - TimeStampSeconds, - Salt, - Signature, - PublicKeyUrl, - Username, - bCreateAccount, - Vars, - successCallback, - errorCallback); -} - -UNakamaClientAuthenticateFacebook* UNakamaClientAuthenticateFacebook::AuthenticateFacebook(UNakamaClient* Client, - FString AccessToken, FString Username, bool CreateAccount, bool ImportFriends, TMap Vars) -{ - UNakamaClientAuthenticateFacebook* Node = NewObject(); - Node->NakamaClient = Client; - Node->AccessToken = AccessToken; - Node->Username = Username; - Node->ImportFriends = ImportFriends; - Node->bCreateAccount = CreateAccount; - Node->Vars = Vars; - - return Node; -} - -void UNakamaClientAuthenticateFacebook::Activate() -{ - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](UNakamaSession* session) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, session); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - NakamaClient->AuthenticateFacebook(AccessToken, Username, bCreateAccount, ImportFriends, Vars, successCallback, errorCallback); -} - -UNakamaClientAuthenticateApple* UNakamaClientAuthenticateApple::AuthenticateApple(UNakamaClient* Client, FString Token, - FString Username, bool CreateAccount, TMap Vars) -{ - UNakamaClientAuthenticateApple* Node = NewObject(); - Node->NakamaClient = Client; - Node->Token = Token; - Node->Username = Username; - Node->bCreateAccount = CreateAccount; - Node->Vars = Vars; - - return Node; -} - -void UNakamaClientAuthenticateApple::Activate() -{ - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](UNakamaSession* session) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, session); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - NakamaClient->AuthenticateApple(Token, Username, bCreateAccount, Vars, successCallback, errorCallback); -} - -UNakamaClientAuthenticateRefresh* UNakamaClientAuthenticateRefresh::AuthenticateRefresh(UNakamaClient* Client, UNakamaSession* Session) -{ - UNakamaClientAuthenticateRefresh* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - - return Node; -} - -void UNakamaClientAuthenticateRefresh::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](UNakamaSession* session) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, session); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - NakamaClient->AuthenticateRefresh(UserSession, successCallback, errorCallback); -} - -UNakamaClientLinkCustom* UNakamaClientLinkCustom::LinkCustom(UNakamaClient* Client, UNakamaSession* Session, - FString CustomId) -{ - UNakamaClientLinkCustom* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->CustomId = CustomId; - - return Node; -} - -void UNakamaClientLinkCustom::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->LinkCustom(UserSession, CustomId, successCallback, errorCallback); -} - -UNakamaClientUnLinkCustom* UNakamaClientUnLinkCustom::UnLinkCustom(UNakamaClient* Client, UNakamaSession* Session, - FString CustomId) -{ - UNakamaClientUnLinkCustom* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->CustomId = CustomId; - - return Node; -} - -void UNakamaClientUnLinkCustom::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->UnLinkCustom(UserSession, CustomId, successCallback, errorCallback); -} - -UNakamaClientUnLinkDevice* UNakamaClientUnLinkDevice::UnLinkDevice(UNakamaClient* Client, UNakamaSession* Session, - FString DeviceId) -{ - UNakamaClientUnLinkDevice* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->DeviceId = DeviceId; - - return Node; -} - -void UNakamaClientUnLinkDevice::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->UnLinkDevice(UserSession, DeviceId, successCallback, errorCallback); -} - -UNakamaClientUnLinkEmail* UNakamaClientUnLinkEmail::UnLinkEmail(UNakamaClient* Client, UNakamaSession* Session, - FString Email, FString Password) -{ - UNakamaClientUnLinkEmail* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->Email = Email; - Node->Password = Password; - - return Node; -} - -void UNakamaClientUnLinkEmail::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->UnLinkEmail(UserSession, Email, Password, successCallback, errorCallback); -} - -UNakamaClientUnLinkFacebook* UNakamaClientUnLinkFacebook::UnLinkFacebook(UNakamaClient* Client, UNakamaSession* Session, - FString AccessToken) -{ - UNakamaClientUnLinkFacebook* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->AccessToken = AccessToken; - - return Node; -} - -void UNakamaClientUnLinkFacebook::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->UnLinkFacebook(UserSession, AccessToken, successCallback, errorCallback); -} - -UNakamaClientUnLinkGameCenter* UNakamaClientUnLinkGameCenter::UnLinkGameCenter(UNakamaClient* Client, - UNakamaSession* Session, FString PlayerId, FString BundleId, int64 TimeStampSeconds, FString Salt, - FString Signature, FString PublicKeyUrl) -{ - UNakamaClientUnLinkGameCenter* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->PlayerId = PlayerId; - Node->BundleId = BundleId; - Node->TimeStampSeconds = TimeStampSeconds; - Node->Salt = Salt; - Node->Signature = Signature; - Node->PublicKeyUrl = PublicKeyUrl; - - return Node; -} - -void UNakamaClientUnLinkGameCenter::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->UnLinkGameCenter( - UserSession, - PlayerId, - BundleId, - TimeStampSeconds, - Salt, - Signature, - PublicKeyUrl, - successCallback, - errorCallback - ); - -} - -UNakamaClientUnLinkGoogle* UNakamaClientUnLinkGoogle::UnLinkGoogle(UNakamaClient* Client, UNakamaSession* Session, - FString AccessToken) -{ - UNakamaClientUnLinkGoogle* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->AccessToken = AccessToken; - - return Node; -} - -void UNakamaClientUnLinkGoogle::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->UnLinkGoogle(UserSession, AccessToken, successCallback, errorCallback); -} - -UNakamaClientUnLinkSteam* UNakamaClientUnLinkSteam::UnLinkSteam(UNakamaClient* Client, UNakamaSession* Session, - FString SteamToken) -{ - UNakamaClientUnLinkSteam* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->SteamToken = SteamToken; - - return Node; -} - -void UNakamaClientUnLinkSteam::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->UnLinkSteam(UserSession, SteamToken, successCallback, errorCallback); -} - -UNakamaClientUnLinkApple* UNakamaClientUnLinkApple::UnLinkApple(UNakamaClient* Client, UNakamaSession* Session, - FString Token) -{ - UNakamaClientUnLinkApple* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->Token = Token; - - return Node; -} - -void UNakamaClientUnLinkApple::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->UnLinkApple(UserSession, Token, successCallback, errorCallback); -} - -UNakamaClientRefreshSession* UNakamaClientRefreshSession::RefreshSession(UNakamaClient* Client, UNakamaSession* Session) -{ - UNakamaClientRefreshSession* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - - return Node; -} - -void UNakamaClientRefreshSession::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](UNakamaSession* session) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, session); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - NakamaClient->AuthenticateRefresh(UserSession, successCallback, errorCallback); -} - -UNakamaClientImportFacebookFriends* UNakamaClientImportFacebookFriends::ImportFacebookFriends(UNakamaClient* Client, - UNakamaSession* Session, FString Token, bool Reset) -{ - UNakamaClientImportFacebookFriends* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->Token = Token; - Node->Reset = Reset; - - return Node; -} - -void UNakamaClientImportFacebookFriends::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->ImportFacebookFriends(UserSession, Token, Reset, successCallback, errorCallback); -} - -UNakamaClientImportSteamFriends* UNakamaClientImportSteamFriends::ImportSteamFriends(UNakamaClient* Client, - UNakamaSession* Session, FString SteamToken, bool Reset) -{ - UNakamaClientImportSteamFriends* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->SteamToken = SteamToken; - Node->Reset = Reset; - - return Node; -} - -void UNakamaClientImportSteamFriends::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->ImportSteamFriends(UserSession, SteamToken, Reset, successCallback, errorCallback); -} - -UNakamaClientGetUserAccount* UNakamaClientGetUserAccount::GetUserAccount(UNakamaClient* Client, UNakamaSession* Session) -{ - UNakamaClientGetUserAccount* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - - return Node; -} - -void UNakamaClientGetUserAccount::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - // Get Account: - auto successCallback = [this](const FNakamaAccount& Account) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, Account); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - NakamaClient->GetAccount(UserSession, successCallback, errorCallback); -} - -UNakamaClientGetUsers* UNakamaClientGetUsers::GetUsers(UNakamaClient* Client, UNakamaSession* Session, - TArray UserIds, TArray Usernames, TArray FacebookIds) -{ - UNakamaClientGetUsers* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->UserIds = UserIds; - Node->Usernames = Usernames; - Node->FacebookIds = FacebookIds; - - return Node; -} - -void UNakamaClientGetUsers::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaUserList& UserList) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, UserList); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - NakamaClient->GetUsers(UserSession, UserIds, Usernames, FacebookIds, successCallback, errorCallback); -} - -UNakamaClientUpdateAccount* UNakamaClientUpdateAccount::UpdateAccount(UNakamaClient* Client, UNakamaSession* Session, - FString Username, FString DisplayName, FString AvatarUrl, FString LanguageTag, FString Location, FString Timezone) -{ - UNakamaClientUpdateAccount* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->Username = Username; - Node->DisplayName = DisplayName; - Node->AvatarUrl = AvatarUrl; - Node->LanguageTag = LanguageTag; - Node->Location = Location; - Node->Timezone = Timezone; - - return Node; -} - -void UNakamaClientUpdateAccount::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->UpdateAccount( - UserSession, - Username, - DisplayName, - AvatarUrl, - LanguageTag, - Location, - Timezone, - successCallback, - errorCallback - ); -} - -UNakamaClientListMatches* UNakamaClientListMatches::ListMatches(UNakamaClient* Client, UNakamaSession* Session, - int32 MinSize, int32 MaxSize, int32 Limit, FString Label, FString Query, bool Authoritative) -{ - UNakamaClientListMatches* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->MinSize = MinSize; - Node->MaxSize = MaxSize; - Node->Limit = Limit; - Node->Label = Label; - Node->Query = Query; - Node->Authoritative = Authoritative; - - return Node; -} - -void UNakamaClientListMatches::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaMatchList& MatchList) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, MatchList); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - const auto OptLabel = FNakamaUtils::CreateOptional(Label, FString()); - const auto OptQuery = FNakamaUtils::CreateOptional(Query, FString()); - const auto OptMinSize = FNakamaUtils::CreateOptional(MinSize, 0); - const auto OptMaxSize = FNakamaUtils::CreateOptional(MaxSize, 0); - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - //const auto OptAuthoritative = FNakamaUtils::CreateOptional(Authoritative, false); - - NakamaClient->ListMatches( - UserSession, - OptMinSize, - OptMaxSize, - OptLimit, - OptLabel, - OptQuery, - Authoritative, - successCallback, - errorCallback - ); -} - -UNakamaClientGetFriends* UNakamaClientGetFriends::GetFriends(UNakamaClient* Client, UNakamaSession* Session, - int32 Limit, ENakamaFriendState State, FString Cursor) -{ - UNakamaClientGetFriends* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->Limit = Limit; - Node->State = State; - Node->Cursor = Cursor; - - return Node; -} - -void UNakamaClientGetFriends::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaFriendList& Friends) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, Friends); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - - // If "All" Friend States are requested, we return an empty Enum Object - if(State == ENakamaFriendState::ALL) - { - NakamaClient->ListFriends(UserSession, OptLimit, {}, Cursor, successCallback, errorCallback); - } - else - { - NakamaClient->ListFriends(UserSession, OptLimit, State, Cursor, successCallback, errorCallback); - } -} - -UNakamaClientAddFriends* UNakamaClientAddFriends::AddFriends(UNakamaClient* Client, UNakamaSession* Session, - TArray Ids, TArray Usernames) -{ - UNakamaClientAddFriends* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->Ids = Ids; - Node->Usernames = Usernames; - - return Node; -} - -void UNakamaClientAddFriends::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->AddFriends(UserSession, Ids, Usernames, successCallback, errorCallback); -} - - -UNakamaClientRemoveFriends* UNakamaClientRemoveFriends::RemoveFriends(UNakamaClient* Client, UNakamaSession* Session, - TArray Ids, TArray Usernames) -{ - UNakamaClientRemoveFriends* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->Ids = Ids; - Node->Usernames = Usernames; - - return Node; -} - -void UNakamaClientRemoveFriends::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->DeleteFriends(UserSession, Ids, Usernames, successCallback, errorCallback); -} - -UNakamaClientBlockFriends* UNakamaClientBlockFriends::BlockFriends(UNakamaClient* Client, UNakamaSession* Session, - TArray Ids, TArray Usernames) -{ - UNakamaClientBlockFriends* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->Ids = Ids; - Node->Usernames = Usernames; - - return Node; -} - -void UNakamaClientBlockFriends::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->BlockFriends(UserSession, Ids, Usernames, successCallback, errorCallback); -} - -UNakamaClientCreateGroup* UNakamaClientCreateGroup::CreateGroup(UNakamaClient* Client, UNakamaSession* Session, - FString GroupName, FString Description, FString AvatarUrl, FString LanguageTag, bool Open, int32 MaxMembers) -{ - UNakamaClientCreateGroup* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->GroupName = GroupName; - Node->Description = Description; - Node->AvatarUrl = AvatarUrl; - Node->LanguageTag = LanguageTag; - Node->Open = Open; - Node->MaxMembers = MaxMembers; - - return Node; -} - -void UNakamaClientCreateGroup::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaGroup& Group) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, Group); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - const auto OptMaxCount = FNakamaUtils::CreateOptional(MaxMembers, 0); - - NakamaClient->CreateGroup( - UserSession, - GroupName, - Description, - AvatarUrl, - LanguageTag, - Open, - OptMaxCount, - successCallback, - errorCallback - ); -} - -UNakamaClientListGroups* UNakamaClientListGroups::ListGroups(UNakamaClient* Client, UNakamaSession* Session, - FString GroupNameFilter, int32 Limit, FString Cursor) -{ - UNakamaClientListGroups* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->GroupNameFilter = GroupNameFilter; - Node->Limit = Limit; - Node->Cursor = Cursor; - - return Node; -} - -void UNakamaClientListGroups::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaGroupList& Groups) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, Groups); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - NakamaClient->ListGroups(UserSession, GroupNameFilter, Limit, Cursor, successCallback, errorCallback); -} - -UNakamaClientJoinGroup* UNakamaClientJoinGroup::JoinGroup(UNakamaClient* Client, UNakamaSession* Session, - FString GroupId) -{ - UNakamaClientJoinGroup* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->GroupId = GroupId; - - return Node; -} - -void UNakamaClientJoinGroup::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->JoinGroup(UserSession, GroupId, successCallback, errorCallback); -} - -UNakamaClientListUserGroups* UNakamaClientListUserGroups::ListUserGroups(UNakamaClient* Client, UNakamaSession* Session, - FString UserId, int32 Limit, ENakamaGroupState State, FString Cursor) -{ - UNakamaClientListUserGroups* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->UserId = UserId; - Node->Limit = Limit; - Node->State = State; - Node->Cursor = Cursor; - - return Node; -} - -// Note: Does not get members! -void UNakamaClientListUserGroups::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaUserGroupList& UserGroupList) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, UserGroupList); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - - // If "All" States are requested, we return an empty Enum Object - if(State == ENakamaGroupState::ALL) - { - NakamaClient->ListUserGroups(UserSession, OptLimit, {}, Cursor, successCallback, errorCallback); - } - else - { - NakamaClient->ListUserGroups(UserSession, OptLimit, State, Cursor, successCallback, errorCallback); - } - -} - -UNakamaClientListListGroupUsers* UNakamaClientListListGroupUsers::ListGroupUsers(UNakamaClient* Client, - UNakamaSession* Session, FString GroupId, int32 Limit, ENakamaGroupState State, FString Cursor) -{ - UNakamaClientListListGroupUsers* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->GroupId = GroupId; - Node->Limit = Limit; - Node->State = State; - Node->Cursor = Cursor; - - return Node; -} - -void UNakamaClientListListGroupUsers::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaGroupUsersList& GroupUsersList) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, GroupUsersList); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - - // If "All" States are requested, we return an empty Enum Object - if(State == ENakamaGroupState::ALL) - { - NakamaClient->ListGroupUsers(UserSession, GroupId, OptLimit, {}, Cursor, successCallback, errorCallback); - } - else - { - NakamaClient->ListGroupUsers(UserSession, GroupId, OptLimit, State, Cursor, successCallback, errorCallback); - } -} - -UNakamaClientUpdateGroup* UNakamaClientUpdateGroup::UpdateGroup(UNakamaClient* Client, UNakamaSession* Session, - FString GroupId, FString Name, FString Description, FString AvatarUrl, FString LanguageTag, bool Open) -{ - UNakamaClientUpdateGroup* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->GroupId = GroupId; - Node->Name = Name; - Node->Description = Description; - Node->AvatarUrl = AvatarUrl; - Node->LanguageTag = LanguageTag; - Node->Open = Open; - - return Node; -} - -void UNakamaClientUpdateGroup::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - const auto OptName = FNakamaUtils::CreateOptional(Name, FString()); - const auto OptDescription = FNakamaUtils::CreateOptional(Description, FString()); - const auto OptAvatarUrl = FNakamaUtils::CreateOptional(AvatarUrl, FString()); - const auto OptLanguageTag = FNakamaUtils::CreateOptional(LanguageTag, FString()); - - NakamaClient->UpdateGroup( - UserSession, - GroupId, - OptName, - OptDescription, - OptAvatarUrl, - OptLanguageTag, - Open, - successCallback, - errorCallback - ); -} - -UNakamaClientLeaveGroup* UNakamaClientLeaveGroup::LeaveGroup(UNakamaClient* Client, UNakamaSession* Session, - FString GroupId) -{ - UNakamaClientLeaveGroup* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->GroupId = GroupId; - - return Node; -} - -void UNakamaClientLeaveGroup::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->LeaveGroup(UserSession, GroupId, successCallback, errorCallback); -} - -UNakamaClientAddGroupUsers* UNakamaClientAddGroupUsers::AddGroupUsers(UNakamaClient* Client, UNakamaSession* Session, - FString GroupId, TArray UserIds) -{ - UNakamaClientAddGroupUsers* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->GroupId = GroupId; - Node->UserIds = UserIds; - - return Node; -} - -void UNakamaClientAddGroupUsers::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->AddGroupUsers(UserSession, GroupId, UserIds, successCallback, errorCallback); -} - -UNakamaClientPromoteGroupUsers* UNakamaClientPromoteGroupUsers::PromoteGroupUsers(UNakamaClient* Client, - UNakamaSession* Session, FString GroupId, TArray UserIds) -{ - UNakamaClientPromoteGroupUsers* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->GroupId = GroupId; - Node->UserIds = UserIds; - - return Node; -} - -void UNakamaClientPromoteGroupUsers::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->PromoteGroupUsers(UserSession, GroupId, UserIds, successCallback, errorCallback); -} - -UNakamaClientKickGroupUsers* UNakamaClientKickGroupUsers::KickGroupUsers(UNakamaClient* Client, UNakamaSession* Session, - FString GroupId, TArray UserIds) -{ - UNakamaClientKickGroupUsers* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->GroupId = GroupId; - Node->UserIds = UserIds; - - return Node; -} - -void UNakamaClientKickGroupUsers::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->KickGroupUsers(UserSession, GroupId, UserIds, successCallback, errorCallback); -} - -UNakamaClientDemoteGroupUsers* UNakamaClientDemoteGroupUsers::DemoteGroupUsers(UNakamaClient* Client, - UNakamaSession* Session, FString GroupId, TArray UserIds) -{ - UNakamaClientDemoteGroupUsers* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->GroupId = GroupId; - Node->UserIds = UserIds; - - return Node; -} - -void UNakamaClientDemoteGroupUsers::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->DemoteGroupUsers(UserSession, GroupId, UserIds, successCallback, errorCallback); -} - -UNakamaClientDeleteGroup* UNakamaClientDeleteGroup::DeleteGroup(UNakamaClient* Client, UNakamaSession* Session, - FString GroupId) -{ - UNakamaClientDeleteGroup* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->GroupId = GroupId; - - return Node; -} - -void UNakamaClientDeleteGroup::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->DeleteGroup(UserSession, GroupId, successCallback, errorCallback); -} - -UNakamaClientListNotifications* UNakamaClientListNotifications::ListNotifications(UNakamaClient* Client, - UNakamaSession* Session, int32 Limit, FString Cursor) -{ - UNakamaClientListNotifications* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->Limit = Limit; - Node->Cursor = Cursor; - - return Node; -} - -void UNakamaClientListNotifications::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaNotificationList& NotificationList) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, NotificationList); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - const auto OptCacheableCursor = FNakamaUtils::CreateOptional(Cursor, FString()); - - NakamaClient->ListNotifications(UserSession, OptLimit, OptCacheableCursor, successCallback, errorCallback); -} - -UNakamaClientDeleteNotifications* UNakamaClientDeleteNotifications::DeleteNotifications(UNakamaClient* Client, - UNakamaSession* Session, TArray NotificationIds) -{ - UNakamaClientDeleteNotifications* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->NotificationIds = NotificationIds; - - return Node; -} - -void UNakamaClientDeleteNotifications::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->DeleteNotifications(UserSession, NotificationIds, successCallback, errorCallback); -} - -UNakamaClientWriteStorageObjects* UNakamaClientWriteStorageObjects::WriteStorageObjects(UNakamaClient* Client, - UNakamaSession* Session, TArray StorageObjectsData) -{ - UNakamaClientWriteStorageObjects* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->StorageObjectsData = StorageObjectsData; - - return Node; -} - -void UNakamaClientWriteStorageObjects::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaStoreObjectAcks& StorageObjectAcks) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, StorageObjectAcks); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - NakamaClient->WriteStorageObjects(UserSession, StorageObjectsData, successCallback, errorCallback); -} - -UNakamaClientReadStorageObjects* UNakamaClientReadStorageObjects::ReadStorageObjects(UNakamaClient* Client, - UNakamaSession* Session, TArray StorageObjectsData) -{ - UNakamaClientReadStorageObjects* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->StorageObjectsData = StorageObjectsData; - - return Node; -} - -void UNakamaClientReadStorageObjects::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaStorageObjectList& StorageObjectList) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, StorageObjectList); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - NakamaClient->ReadStorageObjects(UserSession, StorageObjectsData, successCallback, errorCallback); -} - -UNakamaClientListtorageObjects* UNakamaClientListtorageObjects::ListStorageObjects(UNakamaClient* Client, - UNakamaSession* Session, FString Collection, FString UserId, int32 Limit, FString Cursor) -{ - UNakamaClientListtorageObjects* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->Collection = Collection; - Node->UserId = UserId; - Node->Limit = Limit; - Node->Cursor = Cursor; - - return Node; -} - -void UNakamaClientListtorageObjects::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaStorageObjectList& StorageObjectList) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, StorageObjectList); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - const auto OptCursor = FNakamaUtils::CreateOptional(Cursor, FString()); - - if(UserId.IsEmpty()) - { - NakamaClient->ListStorageObjects(UserSession, Collection, OptLimit, OptCursor, successCallback, errorCallback); - } - else - { - NakamaClient->ListUsersStorageObjects(UserSession, Collection, UserId, OptLimit, OptCursor, successCallback, errorCallback); - } -} - -UNakamaClientRemoveStorageObjects* UNakamaClientRemoveStorageObjects::RemoveStorageObjects(UNakamaClient* Client, - UNakamaSession* Session, TArray StorageObjectsData) -{ - UNakamaClientRemoveStorageObjects* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->StorageObjectsData = StorageObjectsData; - - return Node; -} - -void UNakamaClientRemoveStorageObjects::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->DeleteStorageObjects(UserSession, StorageObjectsData, successCallback, errorCallback); -} - -UNakamaClientRPC* UNakamaClientRPC::RPC(UNakamaClient* Client, UNakamaSession* Session, FString FunctionId, - FString Payload) -{ - UNakamaClientRPC* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->FunctionId = FunctionId; - Node->Payload = Payload; - - return Node; -} - -void UNakamaClientRPC::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](FNakamaRPC&& Rpc) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, MoveTemp(Rpc)); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - NakamaClient->RPCm(UserSession, FunctionId, Payload, successCallback, errorCallback); -} - -// RPC HttpKey - -UNakamaClientRPCHttpKey* UNakamaClientRPCHttpKey::RPCHttpKey(UNakamaClient* Client, FString HttpKey, FString FunctionId, FString Payload) -{ - UNakamaClientRPCHttpKey* Node = NewObject(); - Node->NakamaClient = Client; - Node->HttpKey = HttpKey; - Node->FunctionId = FunctionId; - Node->Payload = Payload; - - return Node; -} - -void UNakamaClientRPCHttpKey::Activate() -{ - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](FNakamaRPC&& Rpc) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, MoveTemp(Rpc)); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - NakamaClient->RPCm(HttpKey, FunctionId, MoveTemp(Payload), successCallback, errorCallback); -} - -UNakamaClientListChannelMessages* UNakamaClientListChannelMessages::ListChannelMessages(UNakamaClient* Client, - UNakamaSession* Session, FString ChannelId, int32 Limit, FString Cursor, bool Forward) -{ - UNakamaClientListChannelMessages* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->ChannelId = ChannelId; - Node->Limit = Limit; - Node->Cursor = Cursor; - Node->Forward = Forward; - - return Node; -} - -// List Channel Messages - -void UNakamaClientListChannelMessages::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaChannelMessageList& ChannelMessageList) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, ChannelMessageList); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - const auto OptCursor = FNakamaUtils::CreateOptional(Cursor, FString()); - - NakamaClient->ListChannelMessages(UserSession, ChannelId, OptLimit, OptCursor, Forward, successCallback, errorCallback); -} - -UNakamaClientWriteLeaderboardRecord* UNakamaClientWriteLeaderboardRecord::WriteLeaderboardRecord(UNakamaClient* Client, - UNakamaSession* Session, FString LeaderboardId, int64 Score, int64 SubScore, FString Metadata) -{ - UNakamaClientWriteLeaderboardRecord* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->LeaderboardId = LeaderboardId; - Node->Score = Score; - Node->SubScore = SubScore; - Node->Metadata = Metadata; - - return Node; -} - -void UNakamaClientWriteLeaderboardRecord::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaLeaderboardRecord& LeaderboardRecord) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, LeaderboardRecord); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - const auto OptSubScore = FNakamaUtils::CreateOptional(SubScore, 0); - const auto OptMetadata = FNakamaUtils::CreateOptional(Metadata, FString()); - - NakamaClient->WriteLeaderboardRecord( - UserSession, - LeaderboardId, - Score, - OptSubScore, - OptMetadata, - successCallback, - errorCallback - ); -} - -UNakamaClientListLeaderboardRecords* UNakamaClientListLeaderboardRecords::ListLeaderboardRecords(UNakamaClient* Client, - UNakamaSession* Session, FString LeaderboardId, TArray OwnerIds, int32 Limit, FString Cursor, - ENakamaLeaderboardListBy ListBy) -{ - UNakamaClientListLeaderboardRecords* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->LeaderboardId = LeaderboardId; - Node->OwnerIds = OwnerIds; - Node->Limit = Limit; - Node->Cursor = Cursor; - Node->ListBy = ListBy; - - return Node; -} - -void UNakamaClientListLeaderboardRecords::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaLeaderboardRecordList& LeaderboardRecords) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, LeaderboardRecords); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - const auto OptCursor = FNakamaUtils::CreateOptional(Cursor, FString()); - - if (ListBy == ENakamaLeaderboardListBy::BY_SCORE) - { - NakamaClient->ListLeaderboardRecords( - UserSession, - LeaderboardId, - {}, // None because of listing by score - OptLimit, - OptCursor, - successCallback, - errorCallback - ); - } - else if (ListBy == ENakamaLeaderboardListBy::BY_FRIENDS) - { - NakamaClient->ListLeaderboardRecords( - UserSession, - LeaderboardId, - OwnerIds, // OwnerIds, Can be empty - OptLimit, - OptCursor, - successCallback, - errorCallback - ); - } -} - -UNakamaClientListLeaderboardRecordsAroundOwner* UNakamaClientListLeaderboardRecordsAroundOwner:: -ListLeaderboardRecordsAroundOwner(UNakamaClient* Client, UNakamaSession* Session, FString LeaderboardId, - FString OwnerId, int32 Limit) -{ - UNakamaClientListLeaderboardRecordsAroundOwner* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->LeaderboardId = LeaderboardId; - Node->OwnerId = OwnerId; - Node->Limit = Limit; - - return Node; -} - -void UNakamaClientListLeaderboardRecordsAroundOwner::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaLeaderboardRecordList& LeaderboardRecords) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, LeaderboardRecords); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - - NakamaClient->ListLeaderboardRecordsAroundOwner(UserSession, LeaderboardId, OwnerId, OptLimit, successCallback, errorCallback); -} - -UNakamaClientDeleteLeaderboardRecord* UNakamaClientDeleteLeaderboardRecord::DeleteLeaderboardRecord( - UNakamaClient* Client, UNakamaSession* Session, FString LeaderboardId) -{ - UNakamaClientDeleteLeaderboardRecord* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->LeaderboardId = LeaderboardId; - - return Node; -} - -void UNakamaClientDeleteLeaderboardRecord::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->DeleteLeaderboardRecord(UserSession, LeaderboardId, successCallback, errorCallback); -} - -UNakamaClientWriteTournamentRecord* UNakamaClientWriteTournamentRecord::WriteTournamentRecord(UNakamaClient* Client, - UNakamaSession* Session, FString TournamentId, int64 Score, int64 SubScore, FString Metadata) -{ - UNakamaClientWriteTournamentRecord* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->TournamentId = TournamentId; - Node->Score = Score; - Node->SubScore = SubScore; - Node->Metadata = Metadata; - - return Node; -} - -void UNakamaClientWriteTournamentRecord::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaLeaderboardRecord& LeaderboardRecord) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, LeaderboardRecord); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - const auto OptSubScore = FNakamaUtils::CreateOptional(SubScore, 0); - const auto OptMetadata = FNakamaUtils::CreateOptional(Metadata, FString()); - - NakamaClient->WriteTournamentRecord(UserSession, TournamentId, Score, OptSubScore, OptMetadata, successCallback, errorCallback); -} - -UNakamaClientListTournamentRecords* UNakamaClientListTournamentRecords::ListTournamentRecords(UNakamaClient* Client, - UNakamaSession* Session, FString TournamentId, TArray OwnerIds, int32 Limit, FString Cursor, - ENakamaLeaderboardListBy ListBy) -{ - UNakamaClientListTournamentRecords* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->TournamentId = TournamentId; - Node->OwnerIds = OwnerIds; - Node->Limit = Limit; - Node->Cursor = Cursor; - Node->ListBy = ListBy; - - return Node; -} - -void UNakamaClientListTournamentRecords::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaTournamentRecordList& TournamentRecordList) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, TournamentRecordList); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - const auto OptCursor = FNakamaUtils::CreateOptional(Cursor, FString()); - - NakamaClient->ListTournamentRecords( - UserSession, - TournamentId, - OptLimit, - OptCursor, - OwnerIds, - successCallback, - errorCallback - ); -} - -UNakamaClientListTournamentRecordsAroundOwner* UNakamaClientListTournamentRecordsAroundOwner:: -ListTournamentRecordsAroundOwner(UNakamaClient* Client, UNakamaSession* Session, FString TournamentId, FString OwnerId, - int32 Limit) -{ - UNakamaClientListTournamentRecordsAroundOwner* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->TournamentId = TournamentId; - Node->OwnerId = OwnerId; - Node->Limit = Limit; - - return Node; -} - -void UNakamaClientListTournamentRecordsAroundOwner::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaTournamentRecordList& TournamentRecordList) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, TournamentRecordList); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - - NakamaClient->ListTournamentRecordsAroundOwner( - UserSession, - TournamentId, - OwnerId, - OptLimit, - successCallback, - errorCallback - ); -} - -UNakamaClientJoinTournament* UNakamaClientJoinTournament::JoinTournament(UNakamaClient* Client, UNakamaSession* Session, - FString TournamentId) -{ - UNakamaClientJoinTournament* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->TournamentId = TournamentId; - - return Node; -} - -void UNakamaClientJoinTournament::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({},TournamentId); // Deviates from normal client - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - NakamaClient->JoinTournament(UserSession, TournamentId, successCallback, errorCallback); -} - -UNakamaClientListTournaments* UNakamaClientListTournaments::ListTournaments(UNakamaClient* Client, - UNakamaSession* Session, int32 CategoryStart, int32 CategoryEnd, int32 StartTime, int32 EndTime, int32 Limit, - FString Cursor) -{ - UNakamaClientListTournaments* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->CategoryStart = CategoryStart; - Node->CategoryEnd = CategoryEnd; - Node->StartTime = StartTime; - Node->EndTime = EndTime; - Node->Limit = Limit; - Node->Cursor = Cursor; - - return Node; -} - -void UNakamaClientListTournaments::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaTournamentList& TournamentList) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({},TournamentList); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - const auto OptCategoryStart = FNakamaUtils::CreateOptional(CategoryStart, 0); - const auto OptCategoryEnd = FNakamaUtils::CreateOptional(CategoryEnd, 0); - const auto OptStartTime = FNakamaUtils::CreateOptional(StartTime, 0); - const auto OptEndTime = FNakamaUtils::CreateOptional(EndTime, 0); - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - const auto OptCursor = FNakamaUtils::CreateOptional(Cursor, FString()); - - NakamaClient->ListTournaments( - UserSession, - OptCategoryStart, - OptCategoryEnd, - OptStartTime, - OptEndTime, - OptLimit, - OptCursor, - successCallback, errorCallback - ); -} - -UNakamaClientListParties* UNakamaClientListParties::ListParties(UNakamaClient* Client, UNakamaSession* Session, - int32 Limit, bool Open, FString Query, FString Cursor) -{ - UNakamaClientListParties* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->Limit = Limit; - Node->Open = Open; - Node->Query = Query; - Node->Cursor = Cursor; - - return Node; -} - -void UNakamaClientListParties::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaPartyList& PartyList) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({},PartyList); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error, {}); - SetReadyToDestroy(); - }; - - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - const auto OptOpen = FNakamaUtils::CreateOptional(Open, false); - const auto OptQuery = FNakamaUtils::CreateOptional(Query, FString()); - const auto OptCursor = FNakamaUtils::CreateOptional(Cursor, FString()); - - NakamaClient->ListParties( - UserSession, - OptLimit, - OptOpen, - OptQuery, - OptCursor, - successCallback, errorCallback - ); -} - -UNakamaClientLinkDevice* UNakamaClientLinkDevice::LinkDevice(UNakamaClient* Client, UNakamaSession* Session, - FString DeviceId) -{ - UNakamaClientLinkDevice* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->DeviceId = DeviceId; - - return Node; -} - -void UNakamaClientLinkDevice::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->LinkDevice(UserSession, DeviceId, successCallback, errorCallback); -} - -UNakamaClientLinkEmail* UNakamaClientLinkEmail::LinkEmail(UNakamaClient* Client, UNakamaSession* Session, FString Email, - FString Password) -{ - UNakamaClientLinkEmail* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->Email = Email; - Node->Password = Password; - - return Node; -} - -void UNakamaClientLinkEmail::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->LinkEmail(UserSession, Email, Password, successCallback, errorCallback); -} - -UNakamaClientLinkFacebook* UNakamaClientLinkFacebook::LinkFacebook(UNakamaClient* Client, UNakamaSession* Session, - FString AccessToken, bool ImportFriends) -{ - UNakamaClientLinkFacebook* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->AccessToken = AccessToken; - Node->ImportFriends = ImportFriends; - - return Node; -} - -void UNakamaClientLinkFacebook::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->LinkFacebook(UserSession, AccessToken, ImportFriends, successCallback, errorCallback); -} - -UNakamaClientLinkGameCenter* UNakamaClientLinkGameCenter::LinkGameCenter(UNakamaClient* Client, UNakamaSession* Session, - FString PlayerId, FString BundleId, int64 TimeStampSeconds, FString Salt, FString Signature, FString PublicKeyUrl) -{ - UNakamaClientLinkGameCenter* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->PlayerId = PlayerId; - Node->BundleId = BundleId; - Node->TimeStampSeconds = TimeStampSeconds; - Node->Salt = Salt; - Node->Signature = Signature; - Node->PublicKeyUrl = PublicKeyUrl; - - - return Node; -} - -void UNakamaClientLinkGameCenter::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->LinkGameCenter(UserSession, PlayerId, BundleId, TimeStampSeconds, Salt, Signature, PublicKeyUrl, successCallback, errorCallback); -} - -UNakamaClientLinkGoogle* UNakamaClientLinkGoogle::LinkGoogle(UNakamaClient* Client, UNakamaSession* Session, - FString AccessToken) -{ - UNakamaClientLinkGoogle* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->AccessToken = AccessToken; - - return Node; -} - -void UNakamaClientLinkGoogle::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->LinkGoogle(UserSession, AccessToken, successCallback, errorCallback); -} - -UNakamaClientLinkSteam* UNakamaClientLinkSteam::LinkSteam(UNakamaClient* Client, UNakamaSession* Session, - FString SteamToken) -{ - UNakamaClientLinkSteam* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->SteamToken = SteamToken; - - return Node; -} - -void UNakamaClientLinkSteam::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->LinkSteam(UserSession, SteamToken, successCallback, errorCallback); -} - -UNakamaClientLinkApple* UNakamaClientLinkApple::LinkApple(UNakamaClient* Client, UNakamaSession* Session, FString Token) -{ - UNakamaClientLinkApple* Node = NewObject(); - Node->NakamaClient = Client; - Node->UserSession = Session; - Node->Token = Token; - - return Node; -} - -void UNakamaClientLinkApple::Activate() -{ - // Check validity of client and session - if (!NakamaClient && !UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!NakamaClient) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FNakamaError Error = FNakamaUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(NakamaClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - NakamaClient->LinkApple(UserSession, Token, successCallback, errorCallback); -} diff --git a/Nakama/Source/NakamaBlueprints/Private/NakamaLibrary.cpp b/Nakama/Source/NakamaBlueprints/Private/NakamaLibrary.cpp deleted file mode 100644 index 2782651b7..000000000 --- a/Nakama/Source/NakamaBlueprints/Private/NakamaLibrary.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaLibrary.h" - -#include "JsonObjectConverter.h" - -FNakamaChatMessage UNakamaLibrary::ChatMessageJsonToStruct(FString JsonMessage) -{ - FNakamaChatMessage parsedStruct; - - FJsonObjectConverter::JsonObjectStringToUStruct(JsonMessage, &parsedStruct, 0, 0); - - return parsedStruct; -} - -FString UNakamaLibrary::ChatMessageStructToJson(FNakamaChatMessage MessageStruct) -{ - FString Message; - FJsonObjectConverter::UStructToJsonObjectString(MessageStruct, Message, 0, 0, 0, 0, true); - return Message; -} - diff --git a/Nakama/Source/NakamaBlueprints/Private/NakamaRealtimeClientRequests.cpp b/Nakama/Source/NakamaBlueprints/Private/NakamaRealtimeClientRequests.cpp deleted file mode 100644 index 9d952541c..000000000 --- a/Nakama/Source/NakamaBlueprints/Private/NakamaRealtimeClientRequests.cpp +++ /dev/null @@ -1,1434 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaRealtimeClientRequests.h" -#include "NakamaUtils.h" -#include "NakamaChannelTypes.h" -#include "NakamaMatch.h" -#include "NakamaMatchTypes.h" -#include "NakamaParty.h" -#include "NakamaPresence.h" -#include "NakamaStatus.h" -#include "NakamaChat.h" - -UNakamaRealtimeClientConnect* UNakamaRealtimeClientConnect::Connect(UNakamaRealtimeClient* RealtimeClient, UNakamaSession *Session, bool bInShowAsOnline) -{ - UNakamaRealtimeClientConnect* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->UserSession = Session; - Node->bShowAsOnline = bInShowAsOnline; - return Node; -} - -void UNakamaRealtimeClientConnect::Activate() -{ - // Check validity of client and session - if (!RealtimeClient && !UserSession) - { - FNakamaRtError Error; - Error.Message = FString(TEXT("Client and Session Missing")); - Error.Code = ENakamaRtErrorCode::UNKNOWN; - - NAKAMA_LOG_ERROR(Error.Message); - - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - FNakamaRtError Error; - Error.Message = FString(TEXT("Session Missing")); - Error.Code = ENakamaRtErrorCode::UNKNOWN; - - NAKAMA_LOG_ERROR(Error.Message); - - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - // Connect Callback - auto connectSuccessCallback = [this]() - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - // Connection error Callback - auto connectErrorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error); - SetReadyToDestroy(); - }; - - // NOTE: Uses lambdas here - RealtimeClient->Connect(UserSession, bShowAsOnline,connectSuccessCallback, connectErrorCallback); -} - -UNakamaRealtimeClientSendMessage* UNakamaRealtimeClientSendMessage::SendMessage(UNakamaRealtimeClient* RealtimeClient, - FString ChannelId, FString Content) -{ - UNakamaRealtimeClientSendMessage* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->ChannelId = ChannelId; - Node->Content = Content; - - return Node; -} - -void UNakamaRealtimeClientSendMessage::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaChannelMessageAck& ChannelMessageAck) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, ChannelMessageAck); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - }; - - RealtimeClient->WriteChatMessage(ChannelId, Content, successCallback, errorCallback); -} - -UNakamaRealtimeClientSendDirectMessage* UNakamaRealtimeClientSendDirectMessage::SendDirectMessage( - UNakamaRealtimeClient* RealtimeClient, FString UserID, FString Content) -{ - UNakamaRealtimeClientSendDirectMessage* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->UserID = UserID; - Node->Content = Content; - - return Node; -} - -void UNakamaRealtimeClientSendDirectMessage::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaChannelMessageAck& ChannelMessageAck) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, ChannelMessageAck); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - }; - - RealtimeClient->WriteChatMessage(UserID, Content, successCallback, errorCallback); -} - -UNakamaRealtimeClientUpdateChatMessage* UNakamaRealtimeClientUpdateChatMessage::UpdateChatMessage( - UNakamaRealtimeClient* RealtimeClient, FString ChannelId, FString MessageId, FString Content) -{ - UNakamaRealtimeClientUpdateChatMessage* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->ChannelId = ChannelId; - Node->MessageId = MessageId; - Node->Content = Content; - - return Node; -} - -void UNakamaRealtimeClientUpdateChatMessage::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaChannelMessageAck& ChannelMessageAck) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, ChannelMessageAck); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if(!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - }; - - RealtimeClient->UpdateChatMessage(ChannelId, MessageId, Content, successCallback, errorCallback); -} - -UNakamaRealtimeClientRemoveChatMessage* UNakamaRealtimeClientRemoveChatMessage::RemoveChatMessage(UNakamaRealtimeClient* RealtimeClient, FString ChannelId, FString MessageId) -{ - UNakamaRealtimeClientRemoveChatMessage* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->ChannelId = ChannelId; - Node->MessageId = MessageId; - - return Node; -} - -void UNakamaRealtimeClientRemoveChatMessage::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaChannelMessageAck& ChannelMessageAck) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, ChannelMessageAck); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - }; - - RealtimeClient->RemoveChatMessage(ChannelId, MessageId, successCallback, errorCallback); -} - - -UNakamaRealtimeClientJoinChat* UNakamaRealtimeClientJoinChat::JoinChat(UNakamaRealtimeClient* RealtimeClient, - FString ChatId, ENakamaChannelType ChannelType, bool Persistence, bool Hidden) -{ - UNakamaRealtimeClientJoinChat* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->ChatId = ChatId; - Node->ChannelType = ChannelType; - Node->Persistence = Persistence; - Node->Hidden = Hidden; - - return Node; -} - -void UNakamaRealtimeClientJoinChat::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaChannel& Channel) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, Channel); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - }; - - RealtimeClient->JoinChat(ChatId, ChannelType, Persistence, Hidden, successCallback, errorCallback); -} - -UNakamaRealtimeClientLeaveChat* UNakamaRealtimeClientLeaveChat::LeaveChat(UNakamaRealtimeClient* RealtimeClient, - FString ChannelId) -{ - UNakamaRealtimeClientLeaveChat* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->ChannelId = ChannelId; - - return Node; -} - -void UNakamaRealtimeClientLeaveChat::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, ChannelId); // Channel Parameter Deviates from the other C++ Client - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - }; - - RealtimeClient->LeaveChat(ChannelId, successCallback, errorCallback); -} - -UNakamaRealtimeClientAddMatchmaker* UNakamaRealtimeClientAddMatchmaker::AddMatchmaker( - UNakamaRealtimeClient* RealtimeClient, int32 MinCount, int32 MaxCount, FString Query, - TMap StringProperties, TMap NumericProperties, int32 CountMultiple, - bool IgnoreCountMultiple) -{ - UNakamaRealtimeClientAddMatchmaker* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->MinCount = MinCount; - Node->MaxCount = MaxCount; - Node->Query = Query; - Node->StringProperties = StringProperties; - Node->NumericProperties = NumericProperties; - Node->CountMultiple = CountMultiple; - Node->IgnoreCountMultiple = IgnoreCountMultiple; - - return Node; -} - -void UNakamaRealtimeClientAddMatchmaker::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaMatchmakerTicket& MatchmakerTicket) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, MatchmakerTicket); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - }; - - auto OptMinCount = FNakamaUtils::CreateOptional(MinCount, 0); - auto OptMaxCount = FNakamaUtils::CreateOptional(MaxCount, 0); - const auto OptQuery = FNakamaUtils::CreateOptional(Query, FString()); - const auto OptCountMultiple = FNakamaUtils::CreateOptional(CountMultiple, 0); - - // NOTE: Unreal 4.27 Blueprints does not support TMap with Double so we use float for Blueprints instead - const TMap NumericPropertiesDouble = FNakamaUtils::ConvertFloatMapToDouble(NumericProperties); - - RealtimeClient->AddMatchmaker( - OptMinCount, - OptMaxCount, - OptQuery, - StringProperties, - NumericPropertiesDouble, - OptCountMultiple, - successCallback, - errorCallback - ); -} - -UNakamaRealtimeClientLeaveMatchmaker* UNakamaRealtimeClientLeaveMatchmaker::LeaveMatchmaker( - UNakamaRealtimeClient* RealtimeClient, FString Ticket) -{ - UNakamaRealtimeClientLeaveMatchmaker* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->Ticket = Ticket; - - return Node; -} - -void UNakamaRealtimeClientLeaveMatchmaker::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, Ticket); // Deviates from the other C++ Client by returning Ticket - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - }; - - RealtimeClient->RemoveMatchmaker(Ticket, successCallback, errorCallback); -} - -UNakamaRealtimeClientUpdateStatus* UNakamaRealtimeClientUpdateStatus::UpdateStatus( - UNakamaRealtimeClient* RealtimeClient, FString StatusMessage) -{ - UNakamaRealtimeClientUpdateStatus* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->StatusMessage = StatusMessage; - - return Node; -} - -void UNakamaRealtimeClientUpdateStatus::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error); - SetReadyToDestroy(); - }; - - RealtimeClient->UpdateStatus(StatusMessage, successCallback, errorCallback); -} - -UNakamaRealtimeClientSetAppearOffline* UNakamaRealtimeClientSetAppearOffline::SetAppearOffline( - UNakamaRealtimeClient* RealtimeClient) -{ - UNakamaRealtimeClientSetAppearOffline* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - - return Node; -} - -void UNakamaRealtimeClientSetAppearOffline::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error); - SetReadyToDestroy(); - }; - - RealtimeClient->UpdateStatus("", successCallback, errorCallback); // "Invisible" Status -} - -UNakamaRealtimeClientFollowUsers* UNakamaRealtimeClientFollowUsers::FollowUsers(UNakamaRealtimeClient* RealtimeClient, - TArray UserIds) -{ - UNakamaRealtimeClientFollowUsers* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->UserIds = UserIds; - - return Node; -} - -void UNakamaRealtimeClientFollowUsers::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaStatus& Status) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, Status); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - }; - - RealtimeClient->FollowUsers(UserIds, successCallback, errorCallback); -} - -UNakamaRealtimeClientUnFollowUsers* UNakamaRealtimeClientUnFollowUsers::UnFollowUsers( - UNakamaRealtimeClient* RealtimeClient, TArray UserIds) -{ - UNakamaRealtimeClientUnFollowUsers* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->UserIds = UserIds; - - return Node; -} - -void UNakamaRealtimeClientUnFollowUsers::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error); - SetReadyToDestroy(); - }; - - RealtimeClient->UnfollowUsers(UserIds, successCallback, errorCallback); -} - -UNakamaRealtimeClientCreateMatch* UNakamaRealtimeClientCreateMatch::CreateMatch(UNakamaRealtimeClient* RealtimeClient) -{ - UNakamaRealtimeClientCreateMatch* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - - return Node; -} - -void UNakamaRealtimeClientCreateMatch::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaMatch& Match) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({},Match); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - }; - - RealtimeClient->CreateMatch(successCallback, errorCallback); -} - -UNakamaRealtimeClientJoinMatch* UNakamaRealtimeClientJoinMatch::JoinMatch(UNakamaRealtimeClient* RealtimeClient, - FString MatchId, TMap MetaData) -{ - UNakamaRealtimeClientJoinMatch* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->MatchId = MatchId; - Node->MetaData = MetaData; - - return Node; -} - -void UNakamaRealtimeClientJoinMatch::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaMatch& Match) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({},Match); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - }; - - RealtimeClient->JoinMatch(MatchId, MetaData, successCallback, errorCallback); -} - -UNakamaRealtimeClientJoinMatchByToken* UNakamaRealtimeClientJoinMatchByToken::JoinMatchByToken( - UNakamaRealtimeClient* RealtimeClient, FString Token) -{ - UNakamaRealtimeClientJoinMatchByToken* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->Token = Token; - - return Node; -} - -void UNakamaRealtimeClientJoinMatchByToken::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaMatch& Match) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({},Match); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - }; - - RealtimeClient->JoinMatchByToken(Token, successCallback, errorCallback); -} - - - -UNakamaRealtimeClientLeaveMatch* UNakamaRealtimeClientLeaveMatch::LeaveMatch(UNakamaRealtimeClient* RealtimeClient, - FString MatchId) -{ - UNakamaRealtimeClientLeaveMatch* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->MatchId = MatchId; - - return Node; -} - -void UNakamaRealtimeClientLeaveMatch::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, MatchId); // Deviation from C++ SDK by returning the MatchId - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - }; - - RealtimeClient->LeaveMatch(MatchId, successCallback, errorCallback); -} - -UNakamaRealtimeClientCreateParty* UNakamaRealtimeClientCreateParty::CreateParty(UNakamaRealtimeClient* RealtimeClient, bool Open, - int32 MaxSize) -{ - UNakamaRealtimeClientCreateParty* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->Open = Open; - Node->MaxSize = MaxSize; - - return Node; -} - -void UNakamaRealtimeClientCreateParty::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaParty& Party) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, Party); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - }; - - RealtimeClient->CreateParty(Open, MaxSize, successCallback, errorCallback); -} - -UNakamaRealtimeClientJoinParty* UNakamaRealtimeClientJoinParty::JoinParty(UNakamaRealtimeClient* RealtimeClient, FString PartyId) -{ - UNakamaRealtimeClientJoinParty* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->PartyId = PartyId; - - return Node; -} - -void UNakamaRealtimeClientJoinParty::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, PartyId); // Deviates from C++ SDK by returning the PartyId - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - }; - - RealtimeClient->JoinParty(PartyId, successCallback, errorCallback); -} - -UNakamaRealtimeClientLeaveParty* UNakamaRealtimeClientLeaveParty::LeaveParty(UNakamaRealtimeClient* RealtimeClient, FString PartyId) -{ - UNakamaRealtimeClientLeaveParty* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->PartyId = PartyId; - - return Node; -} - -void UNakamaRealtimeClientLeaveParty::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, PartyId); // Deviates from C++ SDK by returning the PartyId - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - }; - - RealtimeClient->LeaveParty(PartyId, successCallback, errorCallback); -} - -UNakamaRealtimeClientListPartyJoinRequests* UNakamaRealtimeClientListPartyJoinRequests::ListPartyJoinRequests( - UNakamaRealtimeClient* RealtimeClient, FString PartyId) -{ - UNakamaRealtimeClientListPartyJoinRequests* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->PartyId = PartyId; - - return Node; -} - -void UNakamaRealtimeClientListPartyJoinRequests::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaPartyJoinRequest& PartyJoinRequest) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, PartyJoinRequest); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - }; - - RealtimeClient->ListPartyJoinRequests(PartyId, successCallback, errorCallback); -} - -UNakamaRealtimeClientPromotePartyMember* UNakamaRealtimeClientPromotePartyMember::PromotePartyMember( - UNakamaRealtimeClient* RealtimeClient, FString PartyId, FNakamaUserPresence PartyMember) -{ - UNakamaRealtimeClientPromotePartyMember* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->PartyId = PartyId; - Node->PartyMember = PartyMember; - - return Node; -} - -void UNakamaRealtimeClientPromotePartyMember::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error); - SetReadyToDestroy(); - }; - - RealtimeClient->PromotePartyMember(PartyId, PartyMember, successCallback, errorCallback); -} - -UNakamaRealtimeClientRemoveMatchmakerParty* UNakamaRealtimeClientRemoveMatchmakerParty::RemoveMatchmakerParty( - UNakamaRealtimeClient* RealtimeClient, FString PartyId, FString Ticket) -{ - UNakamaRealtimeClientRemoveMatchmakerParty* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->PartyId = PartyId; - Node->Ticket = Ticket; - - return Node; -} - -void UNakamaRealtimeClientRemoveMatchmakerParty::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, Ticket); // Deviates from C++ SDK by returning the Ticket - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - }; - - RealtimeClient->RemoveMatchmakerParty(PartyId, Ticket, successCallback, errorCallback); -} - -UNakamaRealtimeClientRemovePartyMember* UNakamaRealtimeClientRemovePartyMember::RemovePartyMember( - UNakamaRealtimeClient* RealtimeClient, FString PartyId, FNakamaUserPresence Presence) -{ - UNakamaRealtimeClientRemovePartyMember* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->PartyId = PartyId; - Node->Presence = Presence; - - return Node; -} - -void UNakamaRealtimeClientRemovePartyMember::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error); - SetReadyToDestroy(); - }; - - RealtimeClient->RemovePartyMember(PartyId, Presence, successCallback, errorCallback); -} - - -UNakamaRealtimeClientAcceptPartyMember* UNakamaRealtimeClientAcceptPartyMember::AcceptPartyMember( - UNakamaRealtimeClient* RealtimeClient, FString PartyId, FNakamaUserPresence Presence) -{ - UNakamaRealtimeClientAcceptPartyMember* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->PartyId = PartyId; - Node->Presence = Presence; - - return Node; -} - -void UNakamaRealtimeClientAcceptPartyMember::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error); - SetReadyToDestroy(); - }; - - RealtimeClient->AcceptPartyMember(PartyId, Presence, successCallback, errorCallback); -} - -UNakamaRealtimeClientAddMatchmakerParty* UNakamaRealtimeClientAddMatchmakerParty::AddMatchmakerParty( - UNakamaRealtimeClient* RealtimeClient, FString PartyId, int32 MinCount, int32 MaxCount, FString Query, - TMap StringProperties, TMap NumericProperties, int32 CountMultiple, - bool IgnoreCountMultiple) -{ - UNakamaRealtimeClientAddMatchmakerParty* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->PartyId = PartyId; - Node->MinCount = MinCount; - Node->MaxCount = MaxCount; - Node->Query = Query; - Node->StringProperties = StringProperties; - Node->NumericProperties = NumericProperties; - Node->CountMultiple = CountMultiple; - Node->IgnoreCountMultiple = IgnoreCountMultiple; - - return Node; -} - -void UNakamaRealtimeClientAddMatchmakerParty::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaPartyMatchmakerTicket& PartyMatchmakerTicket) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, PartyMatchmakerTicket); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - }; - - auto OptMinCount = FNakamaUtils::CreateOptional(MinCount, 0); - auto OptMaxCount = FNakamaUtils::CreateOptional(MaxCount, 0); - const auto OptQuery = FNakamaUtils::CreateOptional(Query, FString()); - const auto OptCountMultiple = FNakamaUtils::CreateOptional(CountMultiple, 0); - - // NOTE: Unreal 4.27 Blueprints does not support TMap with Double so we use float for Blueprints instead - const TMap NumericPropertiesDouble = FNakamaUtils::ConvertFloatMapToDouble(NumericProperties); - - RealtimeClient->AddMatchmakerParty( - PartyId, - OptMinCount, - OptMaxCount, - OptQuery, - StringProperties, - NumericPropertiesDouble, - OptCountMultiple, - successCallback, - errorCallback - ); -} - -UNakamaRealtimeClientCloseParty* UNakamaRealtimeClientCloseParty::CloseParty(UNakamaRealtimeClient* RealtimeClient, - FString PartyId) -{ - UNakamaRealtimeClientCloseParty* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->PartyId = PartyId; - - return Node; -} - -void UNakamaRealtimeClientCloseParty::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error); - SetReadyToDestroy(); - }; - - RealtimeClient->CloseParty(PartyId, successCallback, errorCallback); -} - -UNakamaRealtimeClientRPC* UNakamaRealtimeClientRPC::RPC(UNakamaRealtimeClient* RealtimeClient, const FString& FunctionId, const FString& Payload) -{ - UNakamaRealtimeClientRPC* Node = NewObject(); - Node->RealtimeClient = RealtimeClient; - Node->FunctionId = FunctionId; - Node->Payload = Payload; - - return Node; -} - -void UNakamaRealtimeClientRPC::Activate() -{ - if (!RealtimeClient) - { - const FNakamaRtError Error = FNakamaUtils::HandleInvalidRealtimeClient(); - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FNakamaRPC& rpc) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}, rpc); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - if (!FNakamaUtils::IsRealtimeClientActive(RealtimeClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(Error, {}); - SetReadyToDestroy(); - }; - - RealtimeClient->RPC(FunctionId, Payload, successCallback, errorCallback); -} diff --git a/Satori/Source/SatoriUnreal/Private/SatoriUserSession.cpp b/Nakama/Source/NakamaBlueprints/Public/NakamaBlueprintsModule.h similarity index 79% rename from Satori/Source/SatoriUnreal/Private/SatoriUserSession.cpp rename to Nakama/Source/NakamaBlueprints/Public/NakamaBlueprintsModule.h index 20336ce79..28c290e9f 100644 --- a/Satori/Source/SatoriUnreal/Private/SatoriUserSession.cpp +++ b/Nakama/Source/NakamaBlueprints/Public/NakamaBlueprintsModule.h @@ -1,5 +1,5 @@ /* -* Copyright 2025 The Nakama Authors + * Copyright 2026 The Nakama Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,9 +14,10 @@ * limitations under the License. */ -#include "SatoriUserSession.h" +#pragma once -FSatoriUserSession::FSatoriUserSession() +#include "Modules/ModuleManager.h" + +class FNakamaBlueprintsModule : public IModuleInterface { - -} \ No newline at end of file +}; diff --git a/Nakama/Source/NakamaBlueprints/Public/NakamaClientRequests.h b/Nakama/Source/NakamaBlueprints/Public/NakamaClientRequests.h deleted file mode 100644 index 0e2637bf6..000000000 --- a/Nakama/Source/NakamaBlueprints/Public/NakamaClientRequests.h +++ /dev/null @@ -1,3120 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "Kismet/BlueprintAsyncActionBase.h" -#include "Delegates/DelegateCombinations.h" -#include "NakamaClient.h" -#include "NakamaStorageObject.h" -#include "NakamaError.h" -#include "NakamaGroup.h" -#include "NakamaMatch.h" -#include "NakamaFriend.h" -#include "NakamaNotification.h" -#include "NakamaRPC.h" -#include "NakamaChannelTypes.h" -#include "NakamaLeaderboard.h" -#include "NakamaTournament.h" - -#include "NakamaClientRequests.generated.h" - -// Global Delegates for empty success / error -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAnyError, FNakamaError, Error); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnSuccessful); - -// --- Authentication --- // - -/** - * Authenticate Custom - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnAuthenticateCustom, FNakamaError, Error, UNakamaSession*, Session); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientAuthenticateCustom : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY(BlueprintAssignable) - FOnAuthenticateCustom OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnAuthenticateCustom OnError; - - /** - * Authenticate a user with a custom id. - * @param Client The Client to use. - * @param UserID A custom identifier usually obtained from an external authentication service. - * @param Username A username used to create the user. - * @param CreateAccount True if the user should be created when authenticated. - * @param Vars Extra information that will be bundled in the session token. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication", meta = (BlueprintInternalUseOnly = "true", AutoCreateRefTerm = "Vars")) - static UNakamaClientAuthenticateCustom* AuthenticateCustom(UNakamaClient *Client, FString UserID, FString Username, bool CreateAccount, TMap Vars); - - virtual void Activate() override; - -private: - - FString UserID; - FString Username; - bool bCreateAccount; - TMap Vars; - -}; - - -/** - * Authenticate Email - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnAuthenticateEmail, FNakamaError, Error, UNakamaSession*, Session); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientAuthenticateEmail : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY(BlueprintAssignable) - FOnAuthenticateEmail OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnAuthenticateEmail OnError; - - - /** - * Authenticate a user with an email and password. - * - * @param Client The Client to use. - * @param Email The email address of the user. - * @param Password The password for the user. - * @param Username A username used to create the user. - * @param CreateAccount True if the user should be created when authenticated. - * @param Vars Extra information that will be bundled in the session token. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication", meta = (BlueprintInternalUseOnly = "true", AutoCreateRefTerm = "Vars")) - static UNakamaClientAuthenticateEmail* AuthenticateEmail(UNakamaClient *Client, FString Email, FString Password, FString Username, bool CreateAccount, TMap Vars); - - virtual void Activate() override; - -private: - - FString Email; - FString Password; - FString Username; - bool bCreateAccount; - TMap Vars; - -}; - -/** - * Authenticate Device - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnAuthenticateDevice, FNakamaError, Error, UNakamaSession*, Session); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientAuthenticateDevice : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY(BlueprintAssignable) - FOnAuthenticateDevice OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnAuthenticateDevice OnError; - - /** - * Authenticate a user with a device id. - * - * @param Client The Client to use. - * @param DeviceID A device identifier usually obtained from a platform API. - * @param Username A username used to create the user. Defaults to empty string. - * @param CreateAccount True if the user should be created when authenticated. Defaults to false. - * @param Vars Extra information that will be bundled in the session token. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication", meta = (BlueprintInternalUseOnly = "true", AutoCreateRefTerm = "Vars")) - static UNakamaClientAuthenticateDevice* AuthenticateDevice(UNakamaClient *Client, FString DeviceID, FString Username, bool CreateAccount, TMap Vars); - - virtual void Activate() override; - -private: - - FString DeviceID; - FString Username; - bool bCreateAccount; - TMap Vars; - -}; - - -/** - * Authenticate Steam - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnAuthenticateSteam, FNakamaError, Error, UNakamaSession*, Session); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientAuthenticateSteam : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY(BlueprintAssignable) - FOnAuthenticateSteam OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnAuthenticateSteam OnError; - - /** - * Authenticate a user with a Steam auth token. - * - * @param Client The Client to use. - * @param SteamToken An authentication token from the Steam network. - * @param Username A username used to create the user. - * @param CreateAccount True if the user should be created when authenticated. - * @param ImportFriends True if the Steam friends should be imported. - * @param Vars Extra information that will be bundled in the session token. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication", meta = (BlueprintInternalUseOnly = "true", AutoCreateRefTerm = "Vars")) - static UNakamaClientAuthenticateSteam* AuthenticateSteam(UNakamaClient *Client, FString SteamToken, FString Username, bool CreateAccount, bool ImportFriends, TMap Vars); - - virtual void Activate() override; - -private: - - FString SteamToken; - FString Username; - bool bCreateAccount; - bool bImportFriends; - TMap Vars; - -}; - - -/** - * Authenticate Google - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnAuthenticateGoogle, FNakamaError, Error, UNakamaSession*, Session); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientAuthenticateGoogle : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY(BlueprintAssignable) - FOnAuthenticateGoogle OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnAuthenticateGoogle OnError; - - /** - * Authenticate a user with a Google auth token. - * - * @param Client The Client to use. - * @param AccessToken An OAuth access token from the Google SDK. - * @param Username A username used to create the user. - * @param CreateAccount True if the user should be created when authenticated. - * @param Vars Extra information that will be bundled in the session token. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication", meta = (BlueprintInternalUseOnly = "true", AutoCreateRefTerm = "Vars")) - static UNakamaClientAuthenticateGoogle* AuthenticateGoogle(UNakamaClient *Client, FString AccessToken, FString Username, bool CreateAccount, TMap Vars); - - virtual void Activate() override; - -private: - - FString AccessToken; - FString Username; - bool bCreateAccount; - TMap Vars; - -}; - - -/** - * Authenticate Game Center - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnAuthenticateGameCenter, FNakamaError, Error, UNakamaSession*, Session); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientAuthenticateGameCenter: public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY(BlueprintAssignable) - FOnAuthenticateGameCenter OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnAuthenticateGameCenter OnError; - - /** - * Authenticate a user with Apple Game Center. - * - * @param Client The Client to use. - * @param PlayerId The player id of the user in Game Center. - * @param BundleId The bundle id of the Game Center application. - * @param TimeStampSeconds The date and time that the signature was created. - * @param Salt A random NSString used to compute the hash and keep it randomized. - * @param Signature The verification signature data generated. - * @param PublicKeyUrl The URL for the public encryption key. - * @param Username A username used to create the user. - * @param CreateAccount True if the user should be created when authenticated. - * @param Vars Extra information that will be bundled in the session token. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication", meta = (BlueprintInternalUseOnly = "true", AutoCreateRefTerm = "Vars")) - static UNakamaClientAuthenticateGameCenter* AuthenticateGameCenter(UNakamaClient *Client, FString PlayerId, FString BundleId, int64 TimeStampSeconds, FString Salt, FString Signature, FString PublicKeyUrl, FString Username, bool CreateAccount, TMap Vars); - - virtual void Activate() override; - -private: - - FString PlayerId; - FString BundleId; - int64 TimeStampSeconds; - FString Salt; - FString Signature; - FString PublicKeyUrl; - FString Username; - bool bCreateAccount; - TMap Vars; - -}; - - -/** - * Authenticate Facebook - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnAuthenticateFacebook, FNakamaError, Error, UNakamaSession*, Session); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientAuthenticateFacebook: public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY(BlueprintAssignable) - FOnAuthenticateFacebook OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnAuthenticateFacebook OnError; - - /** - * Authenticate a user with a Facebook auth token. - * - * @param Client The Client to use. - * @param AccessToken An OAuth access token from the Facebook SDK. - * @param Username A username used to create the user. - * @param CreateAccount True if the user should be created when authenticated. - * @param ImportFriends True if the Facebook friends should be imported. - * @param Vars Extra information that will be bundled in the session token. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication", meta = (BlueprintInternalUseOnly = "true", AutoCreateRefTerm = "Vars")) - static UNakamaClientAuthenticateFacebook* AuthenticateFacebook(UNakamaClient *Client, FString AccessToken, FString Username, bool CreateAccount, bool ImportFriends, TMap Vars); - - virtual void Activate() override; - -private: - - FString AccessToken; - FString Username; - bool ImportFriends; - bool bCreateAccount; - TMap Vars; - -}; - - -/** - * Authenticate Apple - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnAuthenticateApple, FNakamaError, Error, UNakamaSession*, Session); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientAuthenticateApple: public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY(BlueprintAssignable) - FOnAuthenticateApple OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnAuthenticateApple OnError; - - /** - * Authenticate a user with Apple Sign In. - * - * @param Client The Client to use. - * @param Token The ID token received from Apple to validate. - * @param Username A username used to create the user. - * @param CreateAccount True if the user should be created when authenticated. - * @param Vars Extra information that will be bundled in the session token. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication", meta = (BlueprintInternalUseOnly = "true", AutoCreateRefTerm = "Vars")) - static UNakamaClientAuthenticateApple* AuthenticateApple(UNakamaClient *Client, FString Token, FString Username, bool CreateAccount, TMap Vars); - - virtual void Activate() override; - -private: - - FString Token; - FString Username; - bool ImportFriends; - bool bCreateAccount; - TMap Vars; - -}; - -/** - * Authenticate Refresh (using session refresh token) - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnAuthenticateRefresh, FNakamaError, Error, UNakamaSession*, Session); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientAuthenticateRefresh: public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnAuthenticateRefresh OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnAuthenticateRefresh OnError; - - /** - * Refresh a user's session using a refresh token retrieved from a previous authentication request. - * @param Client The Client to use. - * @param Session The session of the user. - **/ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientAuthenticateRefresh* AuthenticateRefresh(UNakamaClient *Client, UNakamaSession* Session); - - virtual void Activate() override; - -}; - - -// --- Linking --- // - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnLinkError, FNakamaError, Error); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnLinkSuccessful); // Renamed - -/** - * Link Custom - */ - - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientLinkCustom: public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnLinkError OnError; - - UPROPERTY(BlueprintAssignable) - FOnLinkSuccessful OnSuccess; - - /** - * - * Link a custom id to the user account owned by the session. - * - * @param Client The Client to use. - * @param CustomId A custom identifier usually obtained from an external authentication service. - * @param Session The session of the user. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication|Link", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientLinkCustom* LinkCustom(UNakamaClient *Client, UNakamaSession *Session, FString CustomId); - - virtual void Activate() override; - -private: - - FString CustomId; - -}; - -/** - * Link Device - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientLinkDevice: public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnLinkError OnError; - - UPROPERTY(BlueprintAssignable) - FOnLinkSuccessful OnSuccess; - - /** - * Link a device id to the user account owned by the session. - * - * @param Client The Client to use. - * @param DeviceId A device identifier usually obtained from a platform API. - * @param Session The session of the user. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication|Link", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientLinkDevice* LinkDevice(UNakamaClient *Client, UNakamaSession *Session, FString DeviceId); - - virtual void Activate() override; - -private: - - FString DeviceId; - -}; - -/** - * Link Email - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientLinkEmail: public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnLinkError OnError; - - UPROPERTY(BlueprintAssignable) - FOnLinkSuccessful OnSuccess; - - /** - * Link an email with password to the user account owned by the session. - * - * @param Client The Client to use. - * @param Email The email address of the user. - * @param Password The password for the user. - * @param Session The session of the user. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication|Link", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientLinkEmail* LinkEmail(UNakamaClient *Client, UNakamaSession *Session, FString Email, FString Password); - - virtual void Activate() override; - -private: - - FString Email; - FString Password; - -}; - - -/** - * Link Facebook - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientLinkFacebook: public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnLinkError OnError; - - UPROPERTY(BlueprintAssignable) - FOnLinkSuccessful OnSuccess; - - /** - * Link a Facebook profile to a user account. - * - * @param Client The Client to use. - * @param AccessToken An OAuth access token from the Facebook SDK. - * @param ImportFriends True if the Facebook friends should be imported. - * @param Session The session of the user. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication|Link", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientLinkFacebook* LinkFacebook(UNakamaClient *Client, UNakamaSession *Session, FString AccessToken, bool ImportFriends); - - virtual void Activate() override; - -private: - - FString AccessToken; - bool ImportFriends; - -}; - - -/** - * Link GameCenter - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientLinkGameCenter : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnLinkError OnError; - - UPROPERTY(BlueprintAssignable) - FOnLinkSuccessful OnSuccess; - - /** - * Link a Game Center profile to a user account. - * - * @param Client The Client to use. - * @param PlayerId The player id of the user in Game Center. - * @param BundleId The bundle id of the Game Center application. - * @param TimeStampSeconds The date and time that the signature was created. - * @param Salt A random NSString used to compute the hash and keep it randomized. - * @param Signature The verification signature data generated. - * @param PublicKeyUrl The URL for the public encryption key. - * @param Session The session of the user. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication|Link", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientLinkGameCenter* LinkGameCenter(UNakamaClient *Client, UNakamaSession *Session, FString PlayerId, FString BundleId, int64 TimeStampSeconds, FString Salt, FString Signature, FString PublicKeyUrl); - - virtual void Activate() override; - -private: - - FString PlayerId; - FString BundleId; - int64 TimeStampSeconds; - FString Salt; - FString Signature; - FString PublicKeyUrl; - -}; - - -/** - * Link Google - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientLinkGoogle: public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnLinkError OnError; - - UPROPERTY(BlueprintAssignable) - FOnLinkSuccessful OnSuccess; - - /** - * Link a Google profile to a user account. - * - * @param Client The Client to use. - * @param AccessToken An OAuth access token from the Google SDK. - * @param Session The session of the user. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication|Link", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientLinkGoogle* LinkGoogle(UNakamaClient *Client, UNakamaSession *Session, FString AccessToken); - - virtual void Activate() override; - -private: - - FString AccessToken; - -}; - - -/** - * Link Steam - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientLinkSteam: public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnLinkError OnError; - - UPROPERTY(BlueprintAssignable) - FOnLinkSuccessful OnSuccess; - - /** - * Link a Steam profile to a user account. - * - * @param Client The Client to use. - * @param SteamToken An authentication token from the Steam network. - * @param Session The session of the user. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication|Link", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientLinkSteam* LinkSteam(UNakamaClient *Client, UNakamaSession *Session, FString SteamToken); - - virtual void Activate() override; - -private: - - FString SteamToken; - -}; - - -/** - * Link Apple - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientLinkApple: public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnLinkError OnError; - - UPROPERTY(BlueprintAssignable) - FOnLinkSuccessful OnSuccess; - - /** - * Link an Apple ID to the social profiles on the current user's account. - * - * @param Client The Client to use. - * @param Token The ID token received from Apple. - * @param Session The session of the user. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication|Link", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientLinkApple* LinkApple(UNakamaClient *Client, UNakamaSession *Session, FString Token); - - virtual void Activate() override; - -private: - - FString Token; - -}; - - -// --- Unlinking --- // - - -/** - * UnLink Custom - */ - - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientUnLinkCustom: public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnLinkError OnError; - - UPROPERTY(BlueprintAssignable) - FOnLinkSuccessful OnSuccess; - - /** - * Unlink a custom id from the user account owned by the session. - * - * @param Client The Client to use. - * @param CustomId A custom identifier usually obtained from an external authentication service. - * @param Session The session of the user. - */ - - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication|UnLink", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientUnLinkCustom* UnLinkCustom(UNakamaClient *Client, UNakamaSession *Session, FString CustomId); - - virtual void Activate() override; - -private: - - FString CustomId; - -}; - -/** - * UnLink Device - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientUnLinkDevice: public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnLinkError OnError; - - UPROPERTY(BlueprintAssignable) - FOnLinkSuccessful OnSuccess; - - /** - * Unlink a device id from the user account owned by the session. - * - * @param Client The Client to use. - * @param DeviceId A device identifier usually obtained from a platform API. - * @param Session The session of the user. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication|UnLink", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientUnLinkDevice* UnLinkDevice(UNakamaClient *Client, UNakamaSession *Session, FString DeviceId); - - virtual void Activate() override; - -private: - - FString DeviceId; - -}; - -/** - * UnLink Email - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientUnLinkEmail: public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnLinkError OnError; - - UPROPERTY(BlueprintAssignable) - FOnLinkSuccessful OnSuccess; - - /** - * Unlink an email with password from the user account owned by the session. - * - * @param Client The Client to use. - * @param Email The email address of the user. - * @param Password The password for the user. - * @param Session The session of the user. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication|UnLink", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientUnLinkEmail* UnLinkEmail(UNakamaClient *Client, UNakamaSession *Session, FString Email, FString Password); - - virtual void Activate() override; - -private: - - FString Email; - FString Password; - -}; - - -/** - * UnLink Facebook - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientUnLinkFacebook: public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnLinkError OnError; - - UPROPERTY(BlueprintAssignable) - FOnLinkSuccessful OnSuccess; - - /** - * Unlink a Facebook profile from the user account owned by the session. - * - * @param Client The Client to use. - * @param AccessToken An OAuth access token from the Facebook SDK. - * @param Session The session of the user. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication|UnLink", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientUnLinkFacebook* UnLinkFacebook(UNakamaClient *Client, UNakamaSession *Session, FString AccessToken); - - virtual void Activate() override; - -private: - - FString AccessToken; - -}; - - -/** - * UnLink GameCenter - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientUnLinkGameCenter : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnLinkError OnError; - - UPROPERTY(BlueprintAssignable) - FOnLinkSuccessful OnSuccess; - - /** - * Unlink a Game Center profile from the user account owned by the session. - * - * @param Client The Client to use. - * @param PlayerId The player id of the user in Game Center. - * @param BundleId The bundle id of the Game Center application. - * @param TimeStampSeconds The date and time that the signature was created. - * @param Salt A random NSString used to compute the hash and keep it randomized. - * @param Signature The verification signature data generated. - * @param PublicKeyUrl The URL for the public encryption key. - * @param Session The session of the user. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication|UnLink", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientUnLinkGameCenter* UnLinkGameCenter(UNakamaClient *Client, UNakamaSession *Session, FString PlayerId, FString BundleId, int64 TimeStampSeconds, FString Salt, FString Signature, FString PublicKeyUrl); - - virtual void Activate() override; - -private: - - FString PlayerId; - FString BundleId; - int64 TimeStampSeconds; - FString Salt; - FString Signature; - FString PublicKeyUrl; - -}; - - -/** - * UnLink Google - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientUnLinkGoogle: public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnLinkError OnError; - - UPROPERTY(BlueprintAssignable) - FOnLinkSuccessful OnSuccess; - - /** - * Unlink a Google profile from the user account owned by the session. - * - * @param Client The Client to use. - * @param AccessToken An OAuth access token from the Google SDK. - * @param Session The session of the user. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication|UnLink", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientUnLinkGoogle* UnLinkGoogle(UNakamaClient *Client, UNakamaSession *Session, FString AccessToken); - - virtual void Activate() override; - -private: - - FString AccessToken; - -}; - - -/** - * UnLink Steam - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientUnLinkSteam: public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnLinkError OnError; - - UPROPERTY(BlueprintAssignable) - FOnLinkSuccessful OnSuccess; - - /** - * Unlink a Steam profile from the user account owned by the session. - * - * @param Client The Client to use. - * @param SteamToken An authentication token from the Steam network. - * @param Session The session of the user. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication|UnLink", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientUnLinkSteam* UnLinkSteam(UNakamaClient *Client, UNakamaSession *Session, FString SteamToken); - - virtual void Activate() override; - -private: - - FString SteamToken; - -}; - - -/** - * UnLink Apple - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientUnLinkApple: public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnLinkError OnError; - - UPROPERTY(BlueprintAssignable) - FOnLinkSuccessful OnSuccess; - - /** - * Unlink a Apple profile from the user account owned by the session. - * - * @param Client The Client to use. - * @param Token An Apple authentication token. - * @param Session The session of the user. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication|UnLink", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientUnLinkApple* UnLinkApple(UNakamaClient *Client, UNakamaSession *Session, FString Token); - - virtual void Activate() override; - -private: - - FString Token; - -}; - -// --- Functions --- // - -/** - * Refresh Session - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnRefreshSession, FNakamaError, Error, UNakamaSession*, Session); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientRefreshSession : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnRefreshSession OnError; - - UPROPERTY(BlueprintAssignable) - FOnRefreshSession OnSuccess; - - /** - * Refresh a user's session using a refresh token retrieved from a previous authentication request. - * @param Session The session of the user. - * @param Client The Client to use. - **/ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication|Refresh", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientRefreshSession* RefreshSession(UNakamaClient *Client, UNakamaSession *Session); - - virtual void Activate() override; - -private: - - FString Token; - -}; - -/** - * Import Facebook Friends - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientImportFacebookFriends : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnAnyError OnError; - - UPROPERTY(BlueprintAssignable) - FOnSuccessful OnSuccess; - - /** - * Import Facebook friends and add them to the user's account. - * - * The server will import friends when the user authenticates with Facebook. This function can be used to be - * explicit with the import operation. - * - * @param Client The Client to use. - * @param Session The session of the user. - * @param Token An OAuth access token from the Facebook SDK. - * @param Reset True if the Facebook friend import for the user should be reset. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Friends", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientImportFacebookFriends* ImportFacebookFriends(UNakamaClient *Client, UNakamaSession *Session, FString Token, bool Reset); - - virtual void Activate() override; - -private: - - FString Token; - bool Reset; - -}; - -/** - * Import Steam Friends - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientImportSteamFriends : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnAnyError OnError; - - UPROPERTY(BlueprintAssignable) - FOnSuccessful OnSuccess; - - /** - * Import Steam friends and add them to the user's account. - * - * The server will import friends when the user authenticates with Steam. This function can be used to be - * explicit with the import operation. - * - * @param Client The Client to use. - * @param Session The session of the user. - * @param SteamToken The Steam token to use. - * @param Reset True if the Steam friend import for the user should be reset. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Friends", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientImportSteamFriends* ImportSteamFriends(UNakamaClient *Client, UNakamaSession *Session, FString SteamToken, bool Reset); - - virtual void Activate() override; - -private: - - FString SteamToken; - bool Reset; - -}; - - -// --- Account and User Info --- // - -/** - * Get User Account - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnGetUserAccount, FNakamaError, Error, FNakamaAccount, Account); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientGetUserAccount : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnGetUserAccount OnError; - - UPROPERTY(BlueprintAssignable) - FOnGetUserAccount OnSuccess; - - /** - * Fetch the user account owned by the session. - * - * @param Client The Client to use. - * @param Session The session of the user. - */ - - UFUNCTION(BlueprintCallable, Category = "Nakama|Users", meta = (BlueprintInternalUseOnly = "true", DisplayName = "Get Account")) - static UNakamaClientGetUserAccount* GetUserAccount(UNakamaClient *Client, UNakamaSession *Session); - - virtual void Activate() override; -}; - - -/** - * Get Users - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FGetUsers, FNakamaError, Error, FNakamaUserList, Users); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientGetUsers : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FGetUsers OnSuccess; - - UPROPERTY(BlueprintAssignable) - FGetUsers OnError; - - - /** - * Fetch one or more users by id, usernames, and Facebook ids. - * - * @param Client The Client to use. - * @param UserIds List of user IDs.f - * @param Usernames List of usernames. - * @param FacebookIds List of Facebook IDs. - * @param Session The session of the user. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Users", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientGetUsers* GetUsers(UNakamaClient *Client, UNakamaSession *Session, TArray UserIds, TArray Usernames, TArray FacebookIds); - - virtual void Activate() override; - -private: - - TArray UserIds; - TArray Usernames; - TArray FacebookIds; -}; - - - -/** - * Update Account - */ - - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientUpdateAccount : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnAnyError OnError; - - UPROPERTY(BlueprintAssignable) - FOnSuccessful OnSuccess; - - /** - * Update the current user's account on the server. - * - * @param Username The new username for the user. - * @param DisplayName A new display name for the user. - * @param AvatarUrl A new avatar url for the user. - * @param LanguageTag A new language tag in BCP-47 format for the user. - * @param Location A new location for the user. - * @param Timezone New timezone information for the user. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Users", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientUpdateAccount* UpdateAccount(UNakamaClient *Client, UNakamaSession *Session, FString Username, FString DisplayName, FString AvatarUrl, FString LanguageTag, FString Location, FString Timezone); - - virtual void Activate() override; - -private: - - FString Username; - FString DisplayName; - FString AvatarUrl; - FString LanguageTag; - FString Location; - FString Timezone; -}; - -/** - * List Matches - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnListMatches, FNakamaError, Error, FNakamaMatchList, MatchList); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientListMatches : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnListMatches OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnListMatches OnError; - - /** - * Fetch a list of matches active on the server. - * - * @param Client The Client to use. - * @param Session The session of the user. - * @param MinSize The minimum number of match participants. - * @param MaxSize The maximum number of match participants. - * @param Limit The number of matches to list. - * @param Label The label to filter the match list on. - * @param Query The query to the match listing. - * @param Authoritative True to include authoritative matches. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientListMatches* ListMatches(UNakamaClient* Client, UNakamaSession *Session, int32 MinSize, int32 MaxSize, int32 Limit, FString Label, FString Query, bool Authoritative); - - virtual void Activate() override; - -private: - - int32 MinSize; - int32 MaxSize; - int32 Limit; - FString Label; - FString Query; - bool Authoritative; - -}; - -// --- Friends --- // - -/** - * Get Friends - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnListFriends, FNakamaError, Error, FNakamaFriendList, FriendsList); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientGetFriends : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnListFriends OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnListFriends OnError; - - /** - * List of friends of the current user. - * - * @param Limit The max number of records to return. Between 1 and 100. - * @param State The friend state to list. - * @param Cursor An optional next page cursor. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Friends", meta = (BlueprintInternalUseOnly = "true", DisplayName = "List Friends")) - static UNakamaClientGetFriends* GetFriends(UNakamaClient* Client, UNakamaSession *Session, int32 Limit, ENakamaFriendState State, FString Cursor); - - virtual void Activate() override; - -private: - int32 Limit; - ENakamaFriendState State; - FString Cursor; - -}; - - -/** - * Add Friends - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientAddFriends : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnAnyError OnError; - - UPROPERTY(BlueprintAssignable) - FOnSuccessful OnSuccess; - - /** - * Add one or more friends by id. - * - * @param Ids The ids of the users to add or invite as friends. - * @param Usernames The usernames of the users to add as friends. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Friends", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientAddFriends* AddFriends(UNakamaClient* Client, UNakamaSession *Session, TArray Ids, TArray Usernames); - - virtual void Activate() override; - -private: - TArray Ids; - TArray Usernames; -}; - - - -/** - * Remove Friends - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientRemoveFriends : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnAnyError OnError; - - UPROPERTY(BlueprintAssignable) - FOnSuccessful OnSuccess; - - - /** - * Delete one more or users by id or username from friends. - * - * @param Ids the user ids to remove as friends. - * @param Usernames The usernames to remove as friends. - * @param Session The session of the user. - * @param Client The Client to use. - */ - - UFUNCTION(BlueprintCallable, Category = "Nakama|Friends", meta = (BlueprintInternalUseOnly = "true", DisplayName = "Delete Friends")) - static UNakamaClientRemoveFriends* RemoveFriends(UNakamaClient* Client, UNakamaSession *Session, TArray Ids, TArray Usernames); - - virtual void Activate() override; - -private: - TArray Ids; - TArray Usernames; -}; - -/** - * Block Friends - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientBlockFriends : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnAnyError OnError; - - UPROPERTY(BlueprintAssignable) - FOnSuccessful OnSuccess; - - - /** - * Block one or more friends by id. - * - * @param Ids The ids of the users to block. - * @param Usernames The usernames of the users to block. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Friends", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientBlockFriends* BlockFriends(UNakamaClient* Client, UNakamaSession *Session, TArray Ids, TArray Usernames); - - virtual void Activate() override; - -private: - TArray Ids; - TArray Usernames; -}; - - -// --- Groups --- // - -/** - * Create Group - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FCreateGroup, FNakamaError, Error, FNakamaGroup, Group); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientCreateGroup : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FCreateGroup OnSuccess; - - UPROPERTY(BlueprintAssignable) - FCreateGroup OnError; - - /** - * Create a group. - * - * @param GroupName The name for the group. - * @param Description A description for the group. - * @param AvatarUrl An avatar url for the group. - * @param LanguageTag A language tag in BCP-47 format for the group. - * @param Open True if the group should have open membership. - * @param MaxMembers Maximum number of group members. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Groups", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientCreateGroup* CreateGroup(UNakamaClient* Client, UNakamaSession *Session, FString GroupName, FString Description, FString AvatarUrl, FString LanguageTag, bool Open, int32 MaxMembers); - - virtual void Activate() override; - -private: - FString GroupName; - FString Description; - FString AvatarUrl; - FString LanguageTag; - bool Open; - int32 MaxMembers; -}; - - -/** - * List Groups - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnListGroups, FNakamaError, Error, FNakamaGroupList, GroupList); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientListGroups : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnListGroups OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnListGroups OnError; - - /** - * List groups on the server. - * - * @param GroupNameFilter The name filter to apply to the group list. - * @param Limit The number of groups to list. - * @param Cursor A cursor for the current position in the groups to list. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Groups", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientListGroups* ListGroups(UNakamaClient* Client, UNakamaSession *Session, FString GroupNameFilter, int32 Limit, FString Cursor); - - virtual void Activate() override; - -private: - FString GroupNameFilter; - int32 Limit; - FString Cursor; - -}; - - -/** - * Join Group - */ - -//DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnListGroups, FNakamaError, Error, FNakamaGroupList, GroupList); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientJoinGroup : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnAnyError OnError; - - UPROPERTY(BlueprintAssignable) - FOnSuccessful OnSuccess; - - - - /** - * Join a group if it has open membership or request to join it. - * - * @param GroupId The id of the group to join. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Groups", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientJoinGroup* JoinGroup(UNakamaClient* Client, UNakamaSession *Session, FString GroupId); - - virtual void Activate() override; - -private: - FString GroupId; - -}; - - -/** - * List Groups - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnListUserGroups, FNakamaError, Error, FNakamaUserGroupList, UserGroupList); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientListUserGroups : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnListUserGroups OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnListUserGroups OnError; - - /** - * List of groups the current user is a member of. - * @param UserId The id of the user whose groups to list. - * @param Limit The max number of records to return. Between 1 and 100. - * @param State The friend state to list. - * @param Cursor An optional next page cursor. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Groups", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientListUserGroups* ListUserGroups(UNakamaClient* Client, UNakamaSession *Session, FString UserId, int32 Limit, ENakamaGroupState State, FString Cursor); - - virtual void Activate() override; - -private: - FString UserId; - int32 Limit; - ENakamaGroupState State; - FString Cursor; - -}; - -/** - * List Group Users - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnListListGroupUsers, FNakamaError, Error, FNakamaGroupUsersList, GroupUsersList); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientListListGroupUsers : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnListListGroupUsers OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnListListGroupUsers OnError; - - /** - * List all users part of the group. - * - * @param GroupId The id of the group. - * @param Limit The max number of records to return. Between 1 and 100. - * @param State The friend state to list. - * @param Cursor An optional next page cursor. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Groups", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientListListGroupUsers* ListGroupUsers(UNakamaClient* Client, UNakamaSession *Session, FString GroupId, int32 Limit, ENakamaGroupState State, FString Cursor); - - virtual void Activate() override; - -private: - FString GroupId; - int32 Limit; - ENakamaGroupState State; - FString Cursor; - -}; - -/** - * Update Group - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientUpdateGroup : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnAnyError OnError; - - UPROPERTY(BlueprintAssignable) - FOnSuccessful OnSuccess; - - - - /** - * Update a group. - * - * The user must have the correct access permissions for the group. - * - * @param GroupId The id of the group to update. - * @param Name A new name for the group. - * @param Description A new description for the group. - * @param AvatarUrl A new avatar url for the group. - * @param LanguageTag A new language tag in BCP-47 format for the group. - * @param Open True if the group should have open membership. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Groups", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientUpdateGroup* UpdateGroup(UNakamaClient* Client, UNakamaSession *Session, FString GroupId, FString Name, FString Description, FString AvatarUrl, FString LanguageTag, bool Open); - - virtual void Activate() override; - -private: - FString GroupId; - FString Name; - FString Description; - FString AvatarUrl; - FString LanguageTag; - bool Open; - -}; - - -/** - * Update Group - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientLeaveGroup : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnAnyError OnError; - - UPROPERTY(BlueprintAssignable) - FOnSuccessful OnSuccess; - - - - /** - * Leave a group by id. - * - * @param GroupId The id of the group to leave. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Groups", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientLeaveGroup* LeaveGroup(UNakamaClient* Client, UNakamaSession *Session, FString GroupId); - - virtual void Activate() override; - -private: - FString GroupId; - -}; - -/** - * Add Group Users - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientAddGroupUsers : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnAnyError OnError; - - UPROPERTY(BlueprintAssignable) - FOnSuccessful OnSuccess; - - - - /** - * Add one or more users to the group. - * - * @param GroupId The id of the group to add users into. - * @param UserIds The ids of the users to add or invite to the group. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Groups", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientAddGroupUsers* AddGroupUsers(UNakamaClient* Client, UNakamaSession *Session, FString GroupId, TArray UserIds); - - virtual void Activate() override; - -private: - FString GroupId; - TArray UserIds; - -}; - -/** - * Promote Group Users - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientPromoteGroupUsers : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnAnyError OnError; - - UPROPERTY(BlueprintAssignable) - FOnSuccessful OnSuccess; - - - /** - * Promote a set of users in a group to the next role up. - * - * @param GroupId The group ID to promote in. - * @param UserIds The ids of the users to promote. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Groups", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientPromoteGroupUsers* PromoteGroupUsers(UNakamaClient* Client, UNakamaSession *Session, FString GroupId, TArray UserIds); - - virtual void Activate() override; - -private: - FString GroupId; - TArray UserIds; - -}; - - -/** - * Kick Group Users - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientKickGroupUsers : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnAnyError OnError; - - UPROPERTY(BlueprintAssignable) - FOnSuccessful OnSuccess; - - - - /** - * Kick one or more users from the group. - * - * @param GroupId The id of the group. - * @param UserIds The ids of the users to kick. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Groups", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientKickGroupUsers* KickGroupUsers(UNakamaClient* Client, UNakamaSession *Session, FString GroupId, TArray UserIds); - - virtual void Activate() override; - -private: - FString GroupId; - TArray UserIds; - -}; - -/** - * Kick Group Users - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientDemoteGroupUsers : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnAnyError OnError; - - UPROPERTY(BlueprintAssignable) - FOnSuccessful OnSuccess; - - - - /** - * Demote a set of users in a group to the next role down. - * - * @param GroupId The group ID to demote in. - * @param UserIds The ids of the users to demote. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Groups", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientDemoteGroupUsers* DemoteGroupUsers(UNakamaClient* Client, UNakamaSession *Session, FString GroupId, TArray UserIds); - - virtual void Activate() override; - -private: - FString GroupId; - TArray UserIds; - -}; - -/** - * Delete Group - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientDeleteGroup : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnAnyError OnError; - - UPROPERTY(BlueprintAssignable) - FOnSuccessful OnSuccess; - - /** - * Delete a group by id. - * - * @param GroupId The group id to to remove. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Groups", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientDeleteGroup* DeleteGroup(UNakamaClient* Client, UNakamaSession *Session, FString GroupId); - - virtual void Activate() override; - -private: - FString GroupId; - -}; - - -// --- Notifications --- // - - -/** - * List Notifications - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FListNotifications, FNakamaError, Error, FNakamaNotificationList, NotificationList); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientListNotifications : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FListNotifications OnSuccess; - - UPROPERTY(BlueprintAssignable) - FListNotifications OnError; - - /** - * List notifications for the user with an optional cursor. - * - * @param Limit The number of notifications to list. - * @param Cursor A cursor for the current position in notifications to list. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Notifications", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientListNotifications* ListNotifications(UNakamaClient* Client, UNakamaSession *Session, int32 Limit, FString Cursor); - - virtual void Activate() override; - -private: - int32 Limit; - FString Cursor; - -}; - - -/** - * Delete Notifications - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientDeleteNotifications : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnAnyError OnError; - - UPROPERTY(BlueprintAssignable) - FOnSuccessful OnSuccess; - - - /** - * Delete one or more notifications by id. - * - * @param NotificationIds The notification ids to remove. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Notifications", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientDeleteNotifications* DeleteNotifications(UNakamaClient* Client, UNakamaSession *Session, TArray NotificationIds); - - virtual void Activate() override; - -private: - TArray NotificationIds; - -}; - - -// --- Storage --- // - -/** - * Write Storage Objects - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnWriteStorageObjects, FNakamaError, Error, FNakamaStoreObjectAcks, StorageObjectsAcks); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientWriteStorageObjects : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnWriteStorageObjects OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnWriteStorageObjects OnError; - - /** - * Write objects to the storage engine. - * - * @param StorageObjectsData The objects to write. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Storage", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientWriteStorageObjects* WriteStorageObjects(UNakamaClient* Client, UNakamaSession *Session, TArray StorageObjectsData); - - virtual void Activate() override; - -private: - TArray StorageObjectsData; -}; - -/** - * Read Storage Objects - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnReadStorageObjects, FNakamaError, Error, const FNakamaStorageObjectList&, StorageObjects); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientReadStorageObjects : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnReadStorageObjects OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnReadStorageObjects OnError; - - /** - * Read one or more objects from the storage engine. - * - * @param StorageObjectsData The objects to read. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Storage", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientReadStorageObjects* ReadStorageObjects(UNakamaClient* Client, UNakamaSession *Session, TArray StorageObjectsData); - - virtual void Activate() override; - -private: - TArray StorageObjectsData; -}; - -/** - * List Storage Objects - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnListStorageObjects, FNakamaError, Error, FNakamaStorageObjectList, StorageObjectList); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientListtorageObjects : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnListStorageObjects OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnListStorageObjects OnError; - - /** - * List storage objects in a collection which belong to a specific user and have public read access. - * - * @param Collection The collection to list over. - * @param UserId The user ID of the user to list objects for. - * @param Limit The number of objects to list. - * @param Cursor A cursor to paginate over the collection. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Storage", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientListtorageObjects* ListStorageObjects(UNakamaClient* Client, UNakamaSession *Session, FString Collection, FString UserId, int32 Limit, FString Cursor); - - virtual void Activate() override; - -private: - - FString Collection; - FString UserId; - int32 Limit; - FString Cursor; - -}; - - -/** - * Remove Storage Objects - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientRemoveStorageObjects : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnAnyError OnError; - - UPROPERTY(BlueprintAssignable) - FOnSuccessful OnSuccess; - - - /** - * Delete one or more storage objects. - * - * @param StorageObjectsData The ids of the objects to delete. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Storage", meta = (BlueprintInternalUseOnly = "true", DisplayName = "Delete Storage Objects")) - static UNakamaClientRemoveStorageObjects* RemoveStorageObjects(UNakamaClient* Client, UNakamaSession *Session, TArray StorageObjectsData); - - virtual void Activate() override; - -private: - - TArray StorageObjectsData; - -}; - - -// --- RPC --- // - -/** - * RPC - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnRPCResponse, FNakamaError, Error, FNakamaRPC, RPCResponse); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientRPC : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnRPCResponse OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnRPCResponse OnError; - - /** - * Send an RPC message to the server. - * - * @param FunctionId The ID of the function to execute. - * @param Payload The string content to send to the server. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|RPC", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientRPC* RPC(UNakamaClient* Client, UNakamaSession *Session, FString FunctionId, FString Payload); - - virtual void Activate() override; - -private: - - FString FunctionId; - FString Payload; -}; - -/** - * RPC HttpKey - */ - -//DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnRPCResponse, FNakamaError, Error, FNakamaRPC, RPCResponse); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientRPCHttpKey : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY(BlueprintAssignable) - FOnRPCResponse OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnRPCResponse OnError; - - /** - * Send an RPC message to the server. - * - * @param Client The Client to use. - * @param HttpKey The HTTP key for the server. - * @param FunctionId The ID of the function to execute. - * @param Payload The string content to send to the server. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|RPC", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientRPCHttpKey* RPCHttpKey(UNakamaClient* Client, FString HttpKey, FString FunctionId, FString Payload); - - virtual void Activate() override; - -private: - - FString HttpKey; - FString FunctionId; - FString Payload; -}; - - -// --- Chat --- // - -/** - * List Channel Messages - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnChannelMessagesListed, FNakamaError, Error, FNakamaChannelMessageList, ChannelMessages); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientListChannelMessages : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnChannelMessagesListed OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnChannelMessagesListed OnError; - - /** - * List messages from a chat channel. - * - * @param Client The Client to use. - * @param Session The session of the user. - * @param ChannelId A channel identifier. - * @param Limit The number of chat messages to list. - * @param Cursor A cursor for the current position in the messages history to list. - * @param Forward Fetch messages forward from the current cursor (or the start). - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Chat", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientListChannelMessages* ListChannelMessages(UNakamaClient* Client, UNakamaSession *Session, FString ChannelId, int32 Limit, FString Cursor, bool Forward); - - virtual void Activate() override; - -private: - - FString ChannelId; - int32 Limit; - FString Cursor; - bool Forward; -}; - - -// --- Leaderboards --- // - - -/** - * Write Leaderboard Record - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FWriteLeaderboardRecord, FNakamaError, Error, FNakamaLeaderboardRecord, LeaderboardRecord); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientWriteLeaderboardRecord : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FWriteLeaderboardRecord OnSuccess; - - UPROPERTY(BlueprintAssignable) - FWriteLeaderboardRecord OnError; - - /** - * Write a record to a leaderboard. - * - * @param LeaderboardId The id of the leaderboard to write. - * @param Score The score for the leaderboard record. - * @param SubScore The subscore for the leaderboard record. - * @param Metadata The metadata for the leaderboard record. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Leaderboards", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientWriteLeaderboardRecord* WriteLeaderboardRecord(UNakamaClient* Client, UNakamaSession *Session, FString LeaderboardId, int64 Score, int64 SubScore, FString Metadata); - - virtual void Activate() override; - -private: - - FString LeaderboardId; - int64 Score; - int64 SubScore; - FString Metadata; -}; - -/** - * List Leaderboard Records - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FListLeaderboardRecords, FNakamaError, Error, FNakamaLeaderboardRecordList, LeaderboardRecordList); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientListLeaderboardRecords : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FListLeaderboardRecords OnSuccess; - - UPROPERTY(BlueprintAssignable) - FListLeaderboardRecords OnError; - - /** - * List records from a leaderboard. - * - * @param LeaderboardId The id of the leaderboard to list. - * @param OwnerIds Record owners to fetch with the list of records. - * @param Limit The number of records to list. - * @param Cursor A cursor for the current position in the leaderboard records to list. - * @param ListBy List by either Score or Friends - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Leaderboards", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientListLeaderboardRecords* ListLeaderboardRecords(UNakamaClient* Client, UNakamaSession *Session, FString LeaderboardId, TArray OwnerIds, int32 Limit, FString Cursor, ENakamaLeaderboardListBy ListBy); - - virtual void Activate() override; - -private: - - FString LeaderboardId; - TArray OwnerIds; - int32 Limit; - FString Cursor; - ENakamaLeaderboardListBy ListBy; -}; - - -/** - * List Leaderboard Records Around Owner - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientListLeaderboardRecordsAroundOwner : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FListLeaderboardRecords OnSuccess; - - UPROPERTY(BlueprintAssignable) - FListLeaderboardRecords OnError; - - /** - * List leaderboard records from a given leaderboard around the owner. - * - * @param LeaderboardId The id of the leaderboard to list. - * @param OwnerId The owner to retrieve records around. - * @param Limit Max number of records to return. Between 1 and 100. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Leaderboards", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientListLeaderboardRecordsAroundOwner* ListLeaderboardRecordsAroundOwner(UNakamaClient* Client, UNakamaSession *Session, FString LeaderboardId, FString OwnerId, int32 Limit); - - virtual void Activate() override; - -private: - - FString LeaderboardId; - FString OwnerId; - int32 Limit; -}; - -/** - * Delete Leaderboard Record - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientDeleteLeaderboardRecord : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnAnyError OnError; - - UPROPERTY(BlueprintAssignable) - FOnSuccessful OnSuccess; - - - /** - * Delete a leaderboard record. - * - * @param LeaderboardId The id of the leaderboard with the record to be deleted. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Leaderboards", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientDeleteLeaderboardRecord* DeleteLeaderboardRecord(UNakamaClient* Client, UNakamaSession *Session, FString LeaderboardId); - - virtual void Activate() override; - -private: - - FString LeaderboardId; - -}; - -// --- Tournaments --- // - -/** - * Write Tournament Record - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientWriteTournamentRecord : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FWriteLeaderboardRecord OnSuccess; - - UPROPERTY(BlueprintAssignable) - FWriteLeaderboardRecord OnError; - - /** - * A request to submit a score to a tournament. - * - * @param TournamentId The tournament ID to write the record for. - * @param Score The score value to submit. - * @param SubScore An optional secondary value. - * @param Metadata A JSON object of additional properties. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Tournaments", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientWriteTournamentRecord* WriteTournamentRecord(UNakamaClient* Client, UNakamaSession *Session, FString TournamentId, int64 Score, int64 SubScore, FString Metadata); - - virtual void Activate() override; - -private: - - FString TournamentId; - int64 Score; - int64 SubScore; - FString Metadata; -}; - - -/** - * List Tournaments Records - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FListTournamentRecords, FNakamaError, Error, FNakamaTournamentRecordList, TournamentRecordList); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientListTournamentRecords : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FListTournamentRecords OnSuccess; - - UPROPERTY(BlueprintAssignable) - FListTournamentRecords OnError; - - /** - * List tournament records from a given tournament. - * - * @param TournamentId The ID of the tournament to list for. - * @param Limit Max number of records to return. Between 1 and 100. - * @param Cursor A next or previous page cursor. - * @param OwnerIds One or more owners to retrieve records for. - * @param ListBy List By Score or Friends - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Tournaments", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientListTournamentRecords* ListTournamentRecords(UNakamaClient* Client, UNakamaSession *Session, FString TournamentId, TArray OwnerIds, int32 Limit, FString Cursor, ENakamaLeaderboardListBy ListBy); - - virtual void Activate() override; - -private: - - FString TournamentId; - TArray OwnerIds; - int32 Limit; - FString Cursor; - ENakamaLeaderboardListBy ListBy; -}; - - -/** - * List Tournament Records Around Owner - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientListTournamentRecordsAroundOwner : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FListTournamentRecords OnSuccess; - - UPROPERTY(BlueprintAssignable) - FListTournamentRecords OnError; - - /** - * List tournament records from a given tournament around the owner. - * - * @param TournamentId The ID of the tournament to list for. - * @param OwnerId The owner to retrieve records around. - * @param Limit Max number of records to return. Between 1 and 100. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Tournaments", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientListTournamentRecordsAroundOwner* ListTournamentRecordsAroundOwner(UNakamaClient* Client, UNakamaSession *Session, FString TournamentId, FString OwnerId, int32 Limit); - - virtual void Activate() override; - -private: - - FString TournamentId; - FString OwnerId; - int32 Limit; -}; - -/** - * Join Tournament - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnJoinTournament, FNakamaError, Error, FString, TournamentId); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientJoinTournament : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnJoinTournament OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnJoinTournament OnError; - - /** - * Join a tournament if it has open membership or request to join it. - * - * @param TournamentId The id of the tournament to join. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Tournaments", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientJoinTournament* JoinTournament(UNakamaClient* Client, UNakamaSession *Session, FString TournamentId); - - virtual void Activate() override; - -private: - - FString TournamentId; -}; - - -/** - * List Tournaments - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FListTournaments, FNakamaError, Error, FNakamaTournamentList, TournamentList); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientListTournaments : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FListTournaments OnSuccess; - - UPROPERTY(BlueprintAssignable) - FListTournaments OnError; - - /** - * List active/upcoming tournaments based on given filters. - * - * @param CategoryStart The start of the categories to include. Defaults to 0. - * @param CategoryEnd The end of the categories to include. Defaults to 128. - * @param StartTime The start time for tournaments. Defaults to current Unix time. - * @param EndTime The end time for tournaments. Defaults to +1 year from current Unix time. - * @param Limit Max number of records to return. Between 1 and 100. - * @param Cursor A next page cursor for listings. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Tournaments", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientListTournaments* ListTournaments(UNakamaClient* Client, UNakamaSession *Session, int32 CategoryStart, int32 CategoryEnd, int32 StartTime, int32 EndTime, int32 Limit, FString Cursor); - - virtual void Activate() override; - -private: - int32 CategoryStart; - int32 CategoryEnd; - int32 StartTime; - int32 EndTime; - int32 Limit; - FString Cursor; - -}; - - -/** - * List Parties - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnListParties, FNakamaError, Error, FNakamaPartyList, PartyList); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaClientListParties : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr NakamaClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnListParties OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnListParties OnError; - - /** - * List parties and optionally filter by matching criteria. - * - * @param Limit Limit the number of returned parties. - * @param Open Optionally filter by open/closed parties. - * @param Query Arbitrary label query. - * @param Cursor Cursor for the next page of results, if any. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Parties", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaClientListParties* ListParties( - UNakamaClient* Client, - UNakamaSession *Session, - int32 Limit, - bool Open, - FString Query, - FString Cursor - ); - - virtual void Activate() override; - -private: - - int32 Limit; - bool Open; - FString Query; - FString Cursor; - -}; diff --git a/Nakama/Source/NakamaBlueprints/Public/NakamaLibrary.h b/Nakama/Source/NakamaBlueprints/Public/NakamaLibrary.h deleted file mode 100644 index 01b4da4d8..000000000 --- a/Nakama/Source/NakamaBlueprints/Public/NakamaLibrary.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaClient.h" -#include "Kismet/BlueprintFunctionLibrary.h" -#include "NakamaLibrary.generated.h" - - -/** - * - */ -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaLibrary : public UBlueprintFunctionLibrary -{ - GENERATED_BODY() - -public: - - - UFUNCTION(BlueprintCallable, Category = "Nakama|Utilities") - static FNakamaChatMessage ChatMessageJsonToStruct(FString JsonMessage); - - UFUNCTION(BlueprintCallable, Category = "Nakama|Utilities") - static FString ChatMessageStructToJson(FNakamaChatMessage MessageStruct); - - -}; diff --git a/Nakama/Source/NakamaBlueprints/Public/NakamaRealtimeClientRequests.h b/Nakama/Source/NakamaBlueprints/Public/NakamaRealtimeClientRequests.h deleted file mode 100644 index 8484b3f53..000000000 --- a/Nakama/Source/NakamaBlueprints/Public/NakamaRealtimeClientRequests.h +++ /dev/null @@ -1,1208 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaRealtimeClient.h" -#include "Kismet/BlueprintAsyncActionBase.h" -#include "NakamaStatus.h" -#include "NakamaRtError.h" -#include "NakamaChannelTypes.h" -#include "NakamaMatch.h" -#include "NakamaMatchTypes.h" -#include "NakamaParty.h" -#include "NakamaPresence.h" -#include "NakamaChat.h" -#include "NakamaRealtimeClientRequests.generated.h" - -/** - * - */ -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientRequests : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -}; - - -// --- Messaging --- // - -// Global Delegates for empty success / error -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAnyRtError, FNakamaRtError, Error); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnRealtimeSuccessful); - -/** - * Connect to Server. - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRealtimeClientConnectError, FNakamaRtError, Error); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnRealtimeClientConnect); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientConnect : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FOnRealtimeClientConnectError OnError; - - UPROPERTY(BlueprintAssignable) - FOnRealtimeClientConnect OnSuccess; - - /** - * Connect to the Server. - * - * @param RealtimeClient The Realtime Client (Socket) to use. - * @param Session The Session to use. - * @param bInShowAsOnline Show as online. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Setup", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaRealtimeClientConnect* Connect(UNakamaRealtimeClient* RealtimeClient, UNakamaSession* Session, bool bInShowAsOnline); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - UPROPERTY() - TObjectPtr UserSession; - - bool bShowAsOnline; - -}; - - -/** - * Send Chat Message - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnWriteChannelMessage, FNakamaRtError, Error, FNakamaChannelMessageAck, MessageAck); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientSendMessage : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FOnWriteChannelMessage OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnWriteChannelMessage OnError; - - /** - * Send a chat message to a channel on the server. - * - * @param ChannelId The channel to send on. - * @param Content The content of the chat message. Must be a JSON object. - * @param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Chat|Messaging", meta = (BlueprintInternalUseOnly = "true", DisplayName = "Write Chat Message")) - static UNakamaRealtimeClientSendMessage* SendMessage(UNakamaRealtimeClient* RealtimeClient, FString ChannelId, FString Content); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - FString ChannelId; - FString Content; - -}; - - -/** - * Send Direct Chat Message - */ - - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientSendDirectMessage : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FOnWriteChannelMessage OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnWriteChannelMessage OnError; - - /** - * Send a direct chat message to another user. - * - * @param UserID The user to send to. - * @param Content The content of the chat message. Must be a JSON object. - * @param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Chat|Messaging", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaRealtimeClientSendDirectMessage* SendDirectMessage(UNakamaRealtimeClient* RealtimeClient, FString UserID, FString Content); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - FString UserID; - FString Content; - -}; - -/** - * Update Chat Message - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnUpdateChatMessage, FNakamaRtError, Error, FNakamaChannelMessageAck, MessageAck); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientUpdateChatMessage : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FOnUpdateChatMessage OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnUpdateChatMessage OnError; - - /** - * Update a chat message to a channel on the server. - * - * @param ChannelId The ID of the chat channel with the message. - * @param MessageId The ID of the message to update. - * @param Content The content update for the message. Must be a JSON object. - * @param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Chat|Messaging", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaRealtimeClientUpdateChatMessage* UpdateChatMessage(UNakamaRealtimeClient* RealtimeClient, FString ChannelId, FString MessageId, FString Content); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - FString ChannelId; - FString Content; - FString MessageId; - -}; - -/** - * Remove Chat Message - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnRemoveChatMessage, FNakamaRtError, Error, FNakamaChannelMessageAck, MessageAck); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientRemoveChatMessage : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FOnRemoveChatMessage OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnRemoveChatMessage OnError; - - /** - * Remove a chat message from a channel on the server. - * - * @param ChannelId The ID of the chat channel with the message. - * @param MessageId The ID of the message to remove. - * @param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Chat|Messaging", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaRealtimeClientRemoveChatMessage* RemoveChatMessage(UNakamaRealtimeClient* RealtimeClient, FString ChannelId, FString MessageId); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - FString ChannelId; - FString MessageId; -}; - -// --- Chat --- // - - -/** - * Join Chat - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FJoinChat, FNakamaRtError, Error, FNakamaChannel, Channel); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientJoinChat : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FJoinChat OnSuccess; - - UPROPERTY(BlueprintAssignable) - FJoinChat OnError; - - /** - * Join a chat channel on the server. - * - * @param ChatId The target channel to join. - * @param ChannelType The type of channel to join. - * @param Persistence True if chat messages should be stored. - * @param Hidden True if the user should be hidden on the channel. - * @param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Chat", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaRealtimeClientJoinChat* JoinChat(UNakamaRealtimeClient* RealtimeClient, FString ChatId, ENakamaChannelType ChannelType, bool Persistence, bool Hidden); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - FString ChatId; - ENakamaChannelType ChannelType; - bool Persistence; - bool Hidden; -}; - - - -/** - * Leave Chat - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FLeaveChat, FNakamaRtError, Error, FString, ChannelId); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientLeaveChat : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FLeaveChat OnSuccess; - - UPROPERTY(BlueprintAssignable) - FLeaveChat OnError; - - /** - * Leave a chat channel on the server. - * - * @param ChannelId The channel to leave. - * @param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Chat", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaRealtimeClientLeaveChat* LeaveChat(UNakamaRealtimeClient* RealtimeClient, FString ChannelId); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - FString ChannelId; -}; - - -// --- Matchmaker --- // - -/** - * Join Matchmaker - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FAddMatchmaker, FNakamaRtError, Error, FNakamaMatchmakerTicket, Ticket); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientAddMatchmaker : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FAddMatchmaker OnSuccess; - - UPROPERTY(BlueprintAssignable) - FAddMatchmaker OnError; - - /** - * Join the matchmaker pool and search for opponents on the server. - * - * @param MinCount The minimum number of players to compete against. - * @param MaxCount The maximum number of players to compete against. - * @param Query A matchmaker query to search for opponents. - * @param StringProperties A set of k/v properties to provide in searches. - * @param NumericProperties A set of k/v numeric properties to provide in searches. - * @param CountMultiple An optional multiple of the matched count that must be satisfied. - * @param IgnoreCountMultiple Ignore Countmultiple parameter. - * @param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Matchmaker", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaRealtimeClientAddMatchmaker* AddMatchmaker(UNakamaRealtimeClient* RealtimeClient, int32 MinCount, int32 MaxCount, FString Query, TMap StringProperties, TMap NumericProperties, int32 CountMultiple, bool IgnoreCountMultiple); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - int32 MinCount; - int32 MaxCount; - FString Query; - TMap StringProperties; - TMap NumericProperties; - int32 CountMultiple; - bool IgnoreCountMultiple; - - -}; - - -/** - * Leave Matchmaker - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FLeaveMatchmaker, FNakamaRtError, Error, FString, Ticket); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientLeaveMatchmaker : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FLeaveMatchmaker OnSuccess; - - UPROPERTY(BlueprintAssignable) - FLeaveMatchmaker OnError; - - /** - * Leave the matchmaker pool by ticket. - * - * @param Ticket The ticket returned by the matchmaker on join. See NMatchmakerTicket.ticket. - * @param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Matchmaker", meta = (BlueprintInternalUseOnly = "true", DisplayName = "Remove Matchmaker")) - static UNakamaRealtimeClientLeaveMatchmaker* LeaveMatchmaker(UNakamaRealtimeClient* RealtimeClient, FString Ticket); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - FString Ticket; - -}; - - -// --- Statuses --- // - - -/** - * Update Status - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientUpdateStatus : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - - UPROPERTY(BlueprintAssignable) - FOnAnyRtError OnError; - - UPROPERTY(BlueprintAssignable) - FOnRealtimeSuccessful OnSuccess; - - /** - * Update the user's status online. - * - * @param StatusMessage The new status of the user. - * @param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Status", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaRealtimeClientUpdateStatus* UpdateStatus(UNakamaRealtimeClient* RealtimeClient, FString StatusMessage); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - FString StatusMessage; - -}; - - -/** - * Set Appear Offline - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientSetAppearOffline : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FOnAnyRtError OnError; - - UPROPERTY(BlueprintAssignable) - FOnRealtimeSuccessful OnSuccess; - - /** - * Update the user's status to offline, appearing invisible to others. - * - *@param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Status", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaRealtimeClientSetAppearOffline* SetAppearOffline(UNakamaRealtimeClient* RealtimeClient); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - FString StatusMessage; - -}; - - -/** - * Follow Users - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FFollowUsers, FNakamaRtError, Error, FNakamaStatus, Status); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientFollowUsers : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FFollowUsers OnSuccess; - - UPROPERTY(BlueprintAssignable) - FFollowUsers OnError; - - /** - * Follow one or more users for status updates. - * - * @param UserIds The user Ids to follow. - * @param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Status", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaRealtimeClientFollowUsers* FollowUsers(UNakamaRealtimeClient* RealtimeClient, TArray UserIds); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - TArray UserIds; - -}; - - -/** - * UnFollow Users - */ - -//DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FFollowUsers, FNakamaRtError, Error, FNakamaStatus, Status); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientUnFollowUsers : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FOnAnyRtError OnError; - - UPROPERTY(BlueprintAssignable) - FOnRealtimeSuccessful OnSuccess; - - - /** - * Unfollow status updates for one or more users. - * - * @param UserIds The ids of users to unfollow. - * @param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Status", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaRealtimeClientUnFollowUsers* UnFollowUsers(UNakamaRealtimeClient* RealtimeClient, TArray UserIds); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - TArray UserIds; - -}; - -// --- Realtime and Match (To send RPC, please use normal Client) --- // - - -/** - * Create Match - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FCreateMatch, FNakamaRtError, Error, FNakamaMatch, Match); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientCreateMatch : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FCreateMatch OnSuccess; - - UPROPERTY(BlueprintAssignable) - FCreateMatch OnError; - - /** - * Create a multiplayer match on the server. - * @param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Match", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaRealtimeClientCreateMatch* CreateMatch(UNakamaRealtimeClient* RealtimeClient); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - -}; - - -/** - * Join Match - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FJoinMatch, FNakamaRtError, Error, FNakamaMatch, Match); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientJoinMatch : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FJoinMatch OnSuccess; - - UPROPERTY(BlueprintAssignable) - FJoinMatch OnError; - - /** - * Join a multiplayer match by ID. - * - * @param MatchId A match ID. - * @param MetaData Metadata. - * @param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Match", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaRealtimeClientJoinMatch* JoinMatch(UNakamaRealtimeClient* RealtimeClient, FString MatchId, TMap MetaData); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - FString MatchId; - TMap MetaData; -}; - - -/** - * Join Match by Token - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientJoinMatchByToken : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FJoinMatch OnSuccess; - - UPROPERTY(BlueprintAssignable) - FJoinMatch OnError; - - /** - * Join a multiplayer match with a matchmaker. - * - * @param Token A matchmaker ticket result object. - * @param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Match", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaRealtimeClientJoinMatchByToken* JoinMatchByToken(UNakamaRealtimeClient* RealtimeClient, FString Token); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - FString Token; -}; - - -/** - * Leave Match - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FLeaveMatch, FNakamaRtError, Error, FString, MatchId); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientLeaveMatch : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FLeaveMatch OnSuccess; - - UPROPERTY(BlueprintAssignable) - FLeaveMatch OnError; - - /** - * Leave a match on the server. - * - * @param MatchId The match to leave. - * @param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Match", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaRealtimeClientLeaveMatch* LeaveMatch(UNakamaRealtimeClient* RealtimeClient, FString MatchId); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - FString MatchId; -}; - - -// --- Parties --- // - -/** - * Create Party - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FCreateParty, FNakamaRtError, Error, FNakamaParty, Party); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientCreateParty : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FCreateParty OnSuccess; - - UPROPERTY(BlueprintAssignable) - FCreateParty OnError; - - /** - * Create a party. - * - * @param Open Whether or not the party will require join requests to be approved by the party leader. - * @param MaxSize Maximum number of party members. - * @param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Parties", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaRealtimeClientCreateParty* CreateParty(UNakamaRealtimeClient* RealtimeClient, bool Open, int32 MaxSize); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - bool Open; - int32 MaxSize; -}; - - -/** - * Join Party - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FJoinParty, FNakamaRtError, Error, FString, PartyId); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientJoinParty : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FJoinParty OnSuccess; - - UPROPERTY(BlueprintAssignable) - FJoinParty OnError; - - /** - * Join a party. - * - * @param PartyId Party ID. - * @param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Parties", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaRealtimeClientJoinParty* JoinParty(UNakamaRealtimeClient* RealtimeClient, FString PartyId); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - FString PartyId; - -}; - - -/** - * Leave Party - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FLeaveParty, FNakamaRtError, Error, FString, PartyId); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientLeaveParty : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FLeaveParty OnSuccess; - - UPROPERTY(BlueprintAssignable) - FLeaveParty OnError; - - /** - * Leave the party. - * - * @param PartyId Party ID. - * @param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Parties", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaRealtimeClientLeaveParty* LeaveParty(UNakamaRealtimeClient* RealtimeClient, FString PartyId); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - FString PartyId; - -}; - - - -/** - * List Party Join Requests - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FListPartyJoinRequests, FNakamaRtError, Error, FNakamaPartyJoinRequest, PartyId); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientListPartyJoinRequests : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FListPartyJoinRequests OnSuccess; - - UPROPERTY(BlueprintAssignable) - FListPartyJoinRequests OnError; - - /** - * Request a list of pending join requests for a party. - * - * @param PartyId Party ID. - * @param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Parties", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaRealtimeClientListPartyJoinRequests* ListPartyJoinRequests(UNakamaRealtimeClient* RealtimeClient, FString PartyId); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - FString PartyId; - -}; - -/** - * Promote Party Member - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientPromotePartyMember : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FOnAnyRtError OnError; - - UPROPERTY(BlueprintAssignable) - FOnRealtimeSuccessful OnSuccess; - - /** - * Promote a new party leader. - * @param PartyId Party ID. - * @param PartyMember The presence of an existing party member to promote as the new leader. - * @param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Parties", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaRealtimeClientPromotePartyMember* PromotePartyMember(UNakamaRealtimeClient* RealtimeClient, FString PartyId, FNakamaUserPresence PartyMember); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - FString PartyId; - FNakamaUserPresence PartyMember; - -}; - - -/** - * Remove Matchmaker Party - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FRemoveMatchmakerParty, FNakamaRtError, Error, FString, Ticket); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientRemoveMatchmakerParty : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FRemoveMatchmakerParty OnSuccess; - - UPROPERTY(BlueprintAssignable) - FRemoveMatchmakerParty OnError; - - /** - * Cancel a party matchmaking process using a ticket. - * @param PartyId Party ID. - * @param Ticket The ticket to cancel. - * @param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Parties", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaRealtimeClientRemoveMatchmakerParty* RemoveMatchmakerParty(UNakamaRealtimeClient* RealtimeClient, FString PartyId, FString Ticket); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - FString PartyId; - FString Ticket; - -}; - -/** - * Remove Party Member - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientRemovePartyMember : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FOnAnyRtError OnError; - - UPROPERTY(BlueprintAssignable) - FOnRealtimeSuccessful OnSuccess; - - - /** - * Kick a party member, or decline a request to join. - * @param PartyId Party ID to remove/reject from. - * @param Presence The presence to remove or reject. - * @param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Parties", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaRealtimeClientRemovePartyMember* RemovePartyMember(UNakamaRealtimeClient* RealtimeClient, FString PartyId, FNakamaUserPresence Presence); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - FString PartyId; - FNakamaUserPresence Presence; - -}; - - -/** - * Accept Party Member - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientAcceptPartyMember : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FOnAnyRtError OnError; - - UPROPERTY(BlueprintAssignable) - FOnRealtimeSuccessful OnSuccess; - - - /** - * Accept a party member's request to join the party. - * - * @param PartyId The party ID to accept the join request for. - * @param Presence The presence to accept as a party member. - * @param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Parties", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaRealtimeClientAcceptPartyMember* AcceptPartyMember(UNakamaRealtimeClient* RealtimeClient, FString PartyId, FNakamaUserPresence Presence); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - FString PartyId; - FNakamaUserPresence Presence; - -}; - - -/** - * Add Matchmaker Party - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FAddMatchmakerParty, FNakamaRtError, Error, FNakamaPartyMatchmakerTicket, Ticket); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientAddMatchmakerParty : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FAddMatchmakerParty OnSuccess; - - UPROPERTY(BlueprintAssignable) - FAddMatchmakerParty OnError; - - /** - * Begin matchmaking as a party. - * - * @param PartyId Party ID. - * @param Query Filter query used to identify suitable users. - * @param MinCount Minimum total user count to match together. - * @param MaxCount Maximum total user count to match together. - * @param StringProperties String properties. - * @param NumericProperties Numeric properties. - * @param CountMultiple An optional multiple of the matched count that must be satisfied. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Matchmaker", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaRealtimeClientAddMatchmakerParty* AddMatchmakerParty(UNakamaRealtimeClient* RealtimeClient, FString PartyId, int32 MinCount, int32 MaxCount, FString Query, TMap StringProperties, TMap NumericProperties, int32 CountMultiple, bool IgnoreCountMultiple); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - FString PartyId; - int32 MinCount; - int32 MaxCount; - FString Query; - TMap StringProperties; - TMap NumericProperties; - int32 CountMultiple; - bool IgnoreCountMultiple; - - -}; - -/** - * Close Party - */ - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientCloseParty : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY(BlueprintAssignable) - FOnAnyRtError OnError; - - UPROPERTY(BlueprintAssignable) - FOnRealtimeSuccessful OnSuccess; - - /** - * End a party, kicking all party members and closing it. - * @param PartyId The ID of the party. - * @param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Parties", meta = (BlueprintInternalUseOnly = "true")) - static UNakamaRealtimeClientCloseParty* CloseParty(UNakamaRealtimeClient* RealtimeClient, FString PartyId); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - FString PartyId; - -}; - - -/** - * RPC - */ - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnRtRPCResponse, FNakamaRtError, Error, FNakamaRPC, RPCResponse); - -UCLASS() -class NAKAMABLUEPRINTS_API UNakamaRealtimeClientRPC : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - - UPROPERTY(BlueprintAssignable) - FOnRtRPCResponse OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnRtRPCResponse OnError; - - /** - * Send an RPC message to the server. - * - * @param FunctionId The ID of the function to execute. - * @param Payload The string content to send to the server. - * @param RealtimeClient The Realtime Client (Socket) to use. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|RPC", meta = (BlueprintInternalUseOnly = "true", DisplayName="RPC Realtime")) - static UNakamaRealtimeClientRPC* RPC(UNakamaRealtimeClient* RealtimeClient, const FString& FunctionId, const FString& Payload); - - virtual void Activate() override; - -private: - - UPROPERTY() - TObjectPtr RealtimeClient; - - FString FunctionId; - FString Payload; -}; \ No newline at end of file diff --git a/Nakama/Source/NakamaTests/Private/NakamaTests.cpp b/Nakama/Source/NakamaTests/Private/NakamaTests.cpp deleted file mode 100644 index 555cabc8d..000000000 --- a/Nakama/Source/NakamaTests/Private/NakamaTests.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaTests.h" -#include "Modules/ModuleManager.h" - -#define LOCTEXT_NAMESPACE "FNakamaTestsModule" - -DEFINE_LOG_CATEGORY(LogNakamaTests); - - -void FNakamaTestsModule::StartupModule() -{ - // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module -} - -void FNakamaTestsModule::ShutdownModule() -{ - // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, - // we call this function before unloading the module. -} - -#undef LOCTEXT_NAMESPACE - -IMPLEMENT_MODULE(FNakamaTestsModule, NakamaTests) diff --git a/Nakama/Source/NakamaTests/Private/Tests/Realtime/Test_AuthoritativeMatch.cpp b/Nakama/Source/NakamaTests/Private/Tests/Realtime/Test_AuthoritativeMatch.cpp deleted file mode 100644 index 33bcb7300..000000000 --- a/Nakama/Source/NakamaTests/Private/Tests/Realtime/Test_AuthoritativeMatch.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Tests/Realtime/Test_AuthoritativeMatch.h" - -#include "NakamaLoggingMacros.h" - -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(AuthoritativeMatch, FNakamaAuthoritativeMatchTestBase, "Nakama.Base.Realtime.Matches.AuthoritativeMatch", NAKAMA_MODULE_TEST_MASK) -inline bool AuthoritativeMatch::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [this](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - // Setup socket: - Socket = Client->SetupRealtimeClient(); - - Socket->SetConnectCallback([this]() - { - UE_LOG(LogTemp, Warning, TEXT("Test Socket 1 Connected")); - - // Setup Client 2 and connect - - Client2 = CreateClient(); - - auto Client2AuthenticateSuccess = [this] (UNakamaSession* Client2Session) - { - Session2 = Client2Session; - - Socket2 = Client2->SetupRealtimeClient(); - - Socket2->SetConnectCallback([this]() - { - UE_LOG(LogTemp, Warning, TEXT("Test Socket 2 Connected")); - - auto RpcSuccessCallback = [this](const FNakamaRPC& Rpc) - { - FString MatchId = GetMatchIdFromJsonString(Rpc.Payload); - if(!MatchId.IsEmpty()) - { - auto JoinMatchSuccessCallback = [this](const FNakamaMatch& Match) - { - UE_LOG(LogTemp, Display, TEXT("Joined Match. MatchId: %s"), *Match.MatchId); - TestTrue("Authoritative Match Test Passed", !Match.MatchId.IsEmpty()); - StopTest(); - }; - - auto JoinMatchErrorCallback = [this](const FNakamaRtError& Error) - { - UE_LOG(LogTemp, Warning, TEXT("Join Match Error. Message: %s"), *Error.Message); - TestFalse("Authoritative Match Test Failed - Join Match Error", true); - StopTest(); - }; - - Socket2->JoinMatch(MatchId, {}, JoinMatchSuccessCallback, JoinMatchErrorCallback); - } - else - { - UE_LOG(LogTemp, Warning, TEXT("MatchId is Empty")); - TestFalse("MatchId is Empty", true); - StopTest(); - } - }; - - auto RpcErrorCallback = [this](const FNakamaRtError& Error) - { - UE_LOG(LogTemp, Warning, TEXT("RPC Error. Message: %s"), *Error.Message); - TestFalse("Authoritative Match Test Failed - RPC Error", true); - StopTest(); - }; - - const FString JsonPayload = "{\"debug\": true, \"label\": \"TestAuthoritativeMatch\"}"; - Socket->RPC("clientrpc.create_authoritative_match", JsonPayload, RpcSuccessCallback, RpcErrorCallback); - }); - - Socket2->Connect(Session, true); - }; - - auto Client2AuthenticateError = [this](const FNakamaError& Error) - { - UE_LOG(LogTemp, Warning, TEXT("Client 2 Authentication Error. Message: %s"), *Error.Message); - TestFalse("Authoritative Match Test Failed - Client 2 Authentication Error", true); - StopTest(); - }; - - Client2->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, Client2AuthenticateSuccess, Client2AuthenticateError); - - }); - - Socket->Connect(Session, true); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Authoritative Match Auth Test Failed", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} - -FString FNakamaAuthoritativeMatchTestBase::GetMatchIdFromJsonString(const FString& JsonString) const -{ - TSharedPtr JsonObject; - TSharedRef> JsonReader = TJsonReaderFactory::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - FString MatchId; - if (JsonObject->TryGetStringField(TEXT("match_id"), MatchId)) - { - return MatchId; - } - } - - // Return an empty string if the parsing fails or if the "match_id" field is not present - return FString(); -} diff --git a/Nakama/Source/NakamaTests/Private/Tests/Realtime/Test_Chat.cpp b/Nakama/Source/NakamaTests/Private/Tests/Realtime/Test_Chat.cpp deleted file mode 100644 index 185817ec1..000000000 --- a/Nakama/Source/NakamaTests/Private/Tests/Realtime/Test_Chat.cpp +++ /dev/null @@ -1,261 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaTestBase.h" -#include "Misc/AutomationTest.h" - -// Join Chat -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(JoinChat, FNakamaTestBase, "Nakama.Base.Realtime.Chat.JoinChat", NAKAMA_MODULE_TEST_MASK) -inline bool JoinChat::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [this](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - // Setup socket: - Socket = Client->SetupRealtimeClient(); - - Socket->SetConnectCallback([this]() - { - UE_LOG(LogTemp, Warning, TEXT("Test Socket connected")); - - // Join Chat - auto successCallback = [&](const FNakamaChannel& Channel) - { - UE_LOG(LogTemp, Display, TEXT("Joined Chat ID: %s"), *Channel.Id); - UE_LOG(LogTemp, Display, TEXT("Joined Chat Room: %s"), *Channel.RoomName); - TestTrue("Join Chat Test Passed", !Channel.Id.IsEmpty()); - StopTest(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - UE_LOG(LogTemp, Warning, TEXT("Join Chat error. ErrorMessage: %s"), *Error.Message); - TestFalse("Join Chat Test error.", true); - StopTest(); - }; - - Socket->JoinChat(TEXT("General"), ENakamaChannelType::ROOM, true, false, successCallback, errorCallback); - }); - - Socket->SetDisconnectCallback( [](const FNakamaDisconnectInfo& DisconnectInfo) - { - UE_LOG(LogTemp, Warning, TEXT("Socket disconnected!")); - UE_LOG(LogTemp, Warning, TEXT("Socket disconnected: %s"), *DisconnectInfo.Reason); - }); - - Socket->Connect(Session, true); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Authentication Test Failed", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} - - -// Join Chat and Wirte Chat Message -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(JoinChatWriteMessage, FNakamaTestBase, "Nakama.Base.Realtime.Chat.JoinChatWriteMessage", NAKAMA_MODULE_TEST_MASK) -inline bool JoinChatWriteMessage::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [this](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - // Setup socket: - Socket = Client->SetupRealtimeClient(); - - Socket->SetConnectCallback([this]() - { - UE_LOG(LogTemp, Warning, TEXT("Test Socket connected")); - - // Join Chat - auto successCallback = [&](const FNakamaChannel& Channel) - { - auto writeChatMessageSuccessCallback = [&](const FNakamaChannelMessageAck& ChannelMessageAck) - { - UE_LOG(LogTemp, Display, TEXT("Joined Chat ID: %s"), *ChannelMessageAck.ChannelId); - UE_LOG(LogTemp, Display, TEXT("Joined Chat Room: %s"), *ChannelMessageAck.RoomName); - TestTrue("Join Chat Test Passed", !ChannelMessageAck.ChannelId.IsEmpty()); - StopTest(); - }; - - auto writechatMessageErrorCallback = [this](const FNakamaRtError& Error) - { - UE_LOG(LogTemp, Warning, TEXT("Write Chat Message error. ErrorMessage: %s"), *Error.Message); - TestFalse("Write Chat Message Test error.", true); - StopTest(); - }; - - Socket->WriteChatMessage( Channel.Id, TEXT("{ \"Hello\" : \"World\" }"), writeChatMessageSuccessCallback, writechatMessageErrorCallback ); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - UE_LOG(LogTemp, Warning, TEXT("Join Chat error. ErrorMessage: %s"), *Error.Message); - TestFalse("Join Chat Test error.", true); - StopTest(); - }; - - Socket->JoinChat(TEXT("General"), ENakamaChannelType::ROOM, true, false, successCallback, errorCallback); - }); - - Socket->SetDisconnectCallback( [](const FNakamaDisconnectInfo& DisconnectInfo) - { - UE_LOG(LogTemp, Warning, TEXT("Socket disconnected!")); - UE_LOG(LogTemp, Warning, TEXT("Socket disconnected: %s"), *DisconnectInfo.Reason); - }); - - Socket->SetChannelMessageCallback( [](const FNakamaChannelMessage& ChannelMessage) - { - UE_LOG(LogTemp, Warning, TEXT("Channel Message: %s"), *ChannelMessage.Content); - }); - - Socket->SetNotificationsCallback( [](const FNakamaNotificationList& NotificationList) - { - UE_LOG(LogTemp, Warning, TEXT("Notifications: %d"), NotificationList.Notifications.Num()); - for (auto& Notification : NotificationList.Notifications) - { - UE_LOG(LogTemp, Warning, TEXT("Notification: %s"), *Notification.Content); - } - }); - - Socket->SetStatusPresenceCallback( [](const FNakamaStatusPresenceEvent& StatusPresenceEvent) - { - UE_LOG(LogTemp, Warning, TEXT("Status Presences: %d"), StatusPresenceEvent.Joins.Num()); - for (auto& Presence : StatusPresenceEvent.Joins) - { - UE_LOG(LogTemp, Warning, TEXT("Joined Presence: %s"), *Presence.UserID); - } - }); - - Socket->Connect(Session, true); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Authentication Test Failed", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} - -// Join Chat -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(JoinGroupChat, FNakamaTestBase, "Nakama.Base.Realtime.Chat.JoinGroupChat", NAKAMA_MODULE_TEST_MASK) -inline bool JoinGroupChat::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [this](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - // Setup socket:S - Socket = Client->SetupRealtimeClient(); - - Socket->SetConnectCallback([this]() - { - UE_LOG(LogTemp, Warning, TEXT("Test Socket connected")); - - auto CreateGroupSuccessCallback = [&](const FNakamaGroup& Group) - { - UE_LOG(LogTemp, Warning, TEXT("Group created: %s"), *Group.Id); - - // Join the group chat - - auto JoinGroupSuccessCallback = [&](const FNakamaChannel& Channel) - { - UE_LOG(LogTemp, Warning, TEXT("Joined Group Chat ID: %s"), *Channel.Id); - TestTrue("Join Group Chat Test Passed", !Channel.Id.IsEmpty()); - StopTest(); - }; - - auto JoinGroupErrorCallback = [this](const FNakamaRtError& Error) - { - UE_LOG(LogTemp, Warning, TEXT("Join Group Chat Error - Join Group. ErrorMessage: %s"), *Error.Message); - TestFalse("Join Group Chat Error - Join Group.", true); - StopTest(); - }; - - Socket->JoinChat(Group.Id, ENakamaChannelType::GROUP, {}, {}, JoinGroupSuccessCallback, JoinGroupErrorCallback); - }; - - auto CreateGroupErrorCallback = [this](const FNakamaError& Error) - { - UE_LOG(LogTemp, Warning, TEXT("Join Group Chat Error - Create Group. ErrorMessage: %s"), *Error.Message); - TestFalse("Join Group Chat Error - Create Group.", true); - StopTest(); - }; - - FString GroupName = FString::Printf(TEXT("Group chat %s"), *Session->GetAuthToken()); - Client->CreateGroup(Session, GroupName, TEXT("A group for chatting"), {}, {}, false, {}, CreateGroupSuccessCallback, CreateGroupErrorCallback); - }); - - Socket->Connect(Session, true); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Authentication Test Failed", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} diff --git a/Nakama/Source/NakamaTests/Private/Tests/Realtime/Test_FollowUsers.cpp b/Nakama/Source/NakamaTests/Private/Tests/Realtime/Test_FollowUsers.cpp deleted file mode 100644 index 750c6ee8e..000000000 --- a/Nakama/Source/NakamaTests/Private/Tests/Realtime/Test_FollowUsers.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Tests/Realtime/Test_FollowUsers.h" -#include "NakamaTestBase.h" -#include "Misc/AutomationTest.h" - -// Add Matchmaker, wait for Match Matched and Join Match by Token -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(FollowUsers, FNakamaFollowUsersTestBase, "Nakama.Base.Realtime." - ".FollowUsers", NAKAMA_MODULE_TEST_MASK) -inline bool FollowUsers::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - Client2 = CreateClient(); - - // Define success callback - auto successCallback = [&](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - // Setup socket: - Socket = Client->SetupRealtimeClient(); - - Socket->SetConnectCallback([&]() - { - UE_LOG(LogTemp, Warning, TEXT("Socket connected")); - - // Client 2 - auto Client2AuthSuccessCallback = [&](UNakamaSession* session) - { - // Set the session for later use - Session2 = session; - - // Setup socket: - Socket2 = Client2->SetupRealtimeClient(); - - Socket2->SetConnectCallback([&]() - { - auto successCallback = [&](const FNakamaStatus& Status) - { - TestTrue("FollowUsers test success", Status.Presences.Num() == 1); - StopTest(); - }; - - auto errorCallback = [&](const FNakamaRtError& Error) - { - UE_LOG(LogTemp, Error, TEXT("FollowUsers. ErrorMessage: %s"), *Error.Message); - TestFalse("FollowUsers Test error.", true); - StopTest(); - }; - - Socket->FollowUsers({Session2->GetUserId()}, successCallback, errorCallback); - }); - - // Connect with Socket - Socket2->Connect(Session2, true); // NOTE: This must use Session2 - }; - - auto Client2AuthErrorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Authentication Test Failed", true); - StopTest(); - }; - - Client2->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, Client2AuthSuccessCallback, Client2AuthErrorCallback); - }); - - // Connect with Socket - Socket->Connect(Session, true); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Authentication Test Failed", true); - StopTest(); - }; - - // Call the Authenticate function - Client->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} \ No newline at end of file diff --git a/Nakama/Source/NakamaTests/Private/Tests/Realtime/Test_Match.cpp b/Nakama/Source/NakamaTests/Private/Tests/Realtime/Test_Match.cpp deleted file mode 100644 index ef6b7bb36..000000000 --- a/Nakama/Source/NakamaTests/Private/Tests/Realtime/Test_Match.cpp +++ /dev/null @@ -1,308 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Tests/Realtime/Test_Match.h" -#include "NakamaTestBase.h" -#include "Misc/AutomationTest.h" - -// Create Match -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(CreateMatch, FNakamaTestBase, "Nakama.Base.Realtime.Matches.Match", NAKAMA_MODULE_TEST_MASK) -inline bool CreateMatch::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [this](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - // Setup socket: - Socket = Client->SetupRealtimeClient(); - - Socket->SetConnectCallback([this]() - { - UE_LOG(LogTemp, Warning, TEXT("Test Socket connected")); - - // Join Chat - auto successCallback = [&](const FNakamaMatch& Match) - { - UE_LOG(LogTemp, Display, TEXT("Created Match: %s"), *Match.MatchId); - UE_LOG(LogTemp, Display, TEXT("Created Match Self Id: %s"), *Match.Me.UserID); - TestTrue("Create Match Test Passed", !Match.MatchId.IsEmpty() && !Match.Me.UserID.IsEmpty()); - StopTest(); - }; - - auto errorCallback = [this](const FNakamaRtError& Error) - { - UE_LOG(LogTemp, Error, TEXT("Create Match error. ErrorMessage: %s"), *Error.Message); - TestFalse("Create Match Test error.", true); - StopTest(); - }; - - Socket->CreateMatch(successCallback, errorCallback); - }); - - Socket->Connect(Session, true); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Authentication Test Failed", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} - -// Add Matchmaker, must return a TicketId -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(AddMatchmaker, FNakamaTestBase, "Nakama.Base.Realtime.Matches" - ".AddMatchmaker", NAKAMA_MODULE_TEST_MASK) -inline bool AddMatchmaker::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [&](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - // Setup socket: - Socket = Client->SetupRealtimeClient(); - - Socket->SetConnectCallback([&]() - { - UE_LOG(LogTemp, Warning, TEXT("Socket connected")); - - // Add Matchmaker Callbacks - auto successCallback = [&](const FNakamaMatchmakerTicket& Ticket) - { - UE_LOG(LogTemp, Display, TEXT("Added Matchmaker with Ticket: %s"), *Ticket.TicketId); - TestTrue("Add Matchmaker Test Passed", !Ticket.TicketId.IsEmpty()); - StopTest(); - }; - - auto errorCallback = [&](const FNakamaRtError& Error) - { - UE_LOG(LogTemp, Error, TEXT("Add Matchmaker. ErrorMessage: %s"), *Error.Message); - TestFalse("Add Matchmaker Test error.", true); - StopTest(); - }; - - // Matchmaker Params - // Note: Query: player_level >= 10 does not work. See: https://heroiclabs.com/docs/nakama/concepts/multiplayer/query-syntax/ - // Doing * instead - TOptional MinCount = 2; - TOptional MaxCount = 4; - TOptional Query = FString("*"); - TMap StringProperties; - StringProperties.Add("region", "us"); - TMap NumericProperties; - NumericProperties.Add("game_mode", 1); - NumericProperties.Add("game_difficulty", 2); - TOptional CountMultiple = 2; - - Socket->AddMatchmaker(MinCount, MaxCount, Query, StringProperties, NumericProperties, CountMultiple, successCallback, errorCallback); - }); - - Socket->SetDisconnectCallback( [&](const FNakamaDisconnectInfo& DisconnectInfo) - { - UE_LOG(LogTemp, Warning, TEXT("Socket disconnected: %s"), *DisconnectInfo.Reason); - TestFalse("Add Matchmaker Test error, socket disconnected", true); - StopTest(); - }); - - // Connect to Socket - Socket->Connect(Session, true); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Authentication Test Failed", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} - -// Add Matchmaker, wait for Match Matched and Join Match by Token -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(MatchmakerJoinMatch, FNakamaMatchTestBase, "Nakama.Base.Realtime.Matches" - ".MatchmakerJoinMatch", NAKAMA_MODULE_TEST_MASK) -inline bool MatchmakerJoinMatch::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - Client2 = CreateClient(); - - // Define success callback - auto successCallback = [&](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - // Setup socket: - Socket = Client->SetupRealtimeClient(); - - Socket->SetConnectCallback([this]() - { - UE_LOG(LogTemp, Warning, TEXT("Socket connected")); - - // Add Matchmaker Callbacks - auto successCallback = [&](const FNakamaMatchmakerTicket& Ticket) - { - UE_LOG(LogTemp, Display, TEXT("Added Matchmaker with Ticket: %s"), *Ticket.TicketId); - }; - - auto errorCallback = [&](const FNakamaRtError& Error) - { - UE_LOG(LogTemp, Error, TEXT("Add Matchmaker. ErrorMessage: %s"), *Error.Message); - TestFalse("Add Matchmaker Test error.", true); - StopTest(); - }; - - Socket->AddMatchmaker(MinCount, MaxCount, Query, StringProperties, NumericProperties, CountMultiple, successCallback, errorCallback); - }); - - Socket->SetMatchmakerMatchedCallback( [&](const FNakamaMatchmakerMatched& MatchmakerMatched) - { - // Join Match by Token - UE_LOG( LogTemp, Warning, TEXT( "Socket Matchmaker Matched" ) ); - auto JoinMatchSuccessCallback = [&](const FNakamaMatch& Match) - { - UE_LOG(LogTemp, Display, TEXT("Joined Match: %s"), *Match.MatchId); - UE_LOG(LogTemp, Display, TEXT("Joined Match Self Id: %s"), *Match.Me.UserID); - TestTrue("Join Match by Token Test Passed", !Match.MatchId.IsEmpty() && !Match.Me.UserID.IsEmpty()); - StopTest(); - }; - - auto JoinMatchErrorCallback = [&](const FNakamaRtError& Error) - { - UE_LOG(LogTemp, Error, TEXT("Join Match by Token error. ErrorMessage: %s"), *Error.Message); - TestFalse("Join Match by Token Test error.", true); - StopTest(); - }; - - Socket->JoinMatchByToken(MatchmakerMatched.Token, JoinMatchSuccessCallback, JoinMatchErrorCallback); - }); - - // Connect with Socket - Socket->Connect(Session, true); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Authentication Test Failed", true); - StopTest(); - }; - - // Call the Authenticate function - Client->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, successCallback, errorCallback); - - - // Client 2 - auto Client2AuthSuccessCallback = [&](UNakamaSession* session) - { - // Set the session for later use - Session2 = session; - - // Setup socket: - Socket2 = Client2->SetupRealtimeClient(); - - Socket2->SetConnectCallback([this]() - { - UE_LOG(LogTemp, Warning, TEXT("Socket 2 connected")); - - // Add Matchmaker Callbacks - auto successCallback = [&](const FNakamaMatchmakerTicket& Ticket) - { - UE_LOG(LogTemp, Display, TEXT("Added Matchmaker with Ticket: %s"), *Ticket.TicketId); - }; - - auto errorCallback = [&](const FNakamaRtError& Error) - { - UE_LOG(LogTemp, Error, TEXT("Add Matchmaker. ErrorMessage: %s"), *Error.Message); - TestFalse("Add Matchmaker Test error.", true); - StopTest(); - }; - - // Matchmaker Params - - Socket2->AddMatchmaker(MinCount, MaxCount, Query, StringProperties, NumericProperties, CountMultiple, successCallback, errorCallback); - }); - - Socket2->SetMatchmakerMatchedCallback( [&](const FNakamaMatchmakerMatched& MatchmakerMatched) - { - UE_LOG( LogTemp, Warning, TEXT( "Socket 2 Matchmaker Matched" ) ); - // Join Match by Token - auto JoinMatchSuccessCallback = [&](const FNakamaMatch& Match) - { - UE_LOG(LogTemp, Display, TEXT("Joined Match: %s"), *Match.MatchId); - UE_LOG(LogTemp, Display, TEXT("Joined Match Self Id: %s"), *Match.Me.UserID); - TestTrue("Join Match by Token Test Passed", !Match.MatchId.IsEmpty() && !Match.Me.UserID.IsEmpty()); - StopTest(); - }; - - auto JoinMatchErrorCallback = [&](const FNakamaRtError& Error) - { - UE_LOG(LogTemp, Error, TEXT("Join Match by Token error. ErrorMessage: %s"), *Error.Message); - TestFalse("Join Match by Token Test error.", true); - StopTest(); - }; - - Socket2->JoinMatchByToken(MatchmakerMatched.Token, JoinMatchSuccessCallback, JoinMatchErrorCallback); - }); - - // Connect with Socket - Socket2->Connect(Session2, true); // NOTE: This must use Session2 - }; - - auto Client2AuthErrorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Authentication Test Failed", true); - StopTest(); - }; - Client2->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, Client2AuthSuccessCallback, Client2AuthErrorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} \ No newline at end of file diff --git a/Nakama/Source/NakamaTests/Private/Tests/Realtime/Test_Notifications.cpp b/Nakama/Source/NakamaTests/Private/Tests/Realtime/Test_Notifications.cpp deleted file mode 100644 index 8d0731399..000000000 --- a/Nakama/Source/NakamaTests/Private/Tests/Realtime/Test_Notifications.cpp +++ /dev/null @@ -1,203 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaTestBase.h" - -// Send Notification to self from RPC and receive it -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(NotificationsCreateReceive, FNakamaTestBase, "Nakama.Base.Realtime.Notifications.CreateReceive", NAKAMA_MODULE_TEST_MASK) -inline bool NotificationsCreateReceive::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [this](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - // Setup socket: - Socket = Client->SetupRealtimeClient(); - - Socket->SetConnectCallback([this]() - { - UE_LOG(LogTemp, Warning, TEXT("Socket connected")); - - // Notification RPC - auto RPCSuccessCallback = [&](const FNakamaRPC& RPC) - { - UE_LOG(LogTemp, Display, TEXT("Sent RPC: %s"), *RPC.Id); - }; - - auto RPCErrorCallback = [&](const FNakamaRtError& Error) - { - UE_LOG(LogTemp, Error, TEXT("Notification Test. ErrorMessage: %s"), *Error.Message); - TestFalse("RPC error.", true); - StopTest(); - }; - - FString Payload = FString::Printf(TEXT("{\"user_id\":\"%s\"}"), *Session->GetUserId()); - - Socket->RPC("clientrpc.send_notification", Payload, RPCSuccessCallback, RPCErrorCallback); - }); - - Socket->SetNotificationsCallback( [&](const FNakamaNotificationList& NotificationList) - { - UE_LOG (LogTemp, Warning, TEXT("Notification Received: %d"), NotificationList.Notifications.Num()); - - for (auto Notification : NotificationList.Notifications) - { - UE_LOG (LogTemp, Warning, TEXT("Notification ID: %s"), *Notification.Id); - UE_LOG (LogTemp, Warning, TEXT("Notification Subject: %s"), *Notification.Subject); - UE_LOG (LogTemp, Warning, TEXT("Notification Content: %s"), *Notification.Content); - UE_LOG (LogTemp, Warning, TEXT("Notification Code: %d"), Notification.Code); - UE_LOG (LogTemp, Warning, TEXT("Notification SenderId: %s"), *Notification.SenderId); - UE_LOG (LogTemp, Warning, TEXT("Notification CreateTime: %s"), *Notification.CreateTime.ToString()); - UE_LOG (LogTemp, Warning, TEXT("Notification Persistent: %d"), Notification.Persistent); - } - - TestTrue("Notifications Test Passed", NotificationList.Notifications.Num() > 0); - StopTest(); - }); - - // Connect with Socket - Socket->Connect(Session, true); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Authentication Test Failed", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} - - -// Send Notification to self from RPC and receive it -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(NotificationsCreateListDelete, FNakamaTestBase, "Nakama.Base.Realtime.Notifications.CreateListDelete", NAKAMA_MODULE_TEST_MASK) -inline bool NotificationsCreateListDelete::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [this](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - // Setup socket: - Socket = Client->SetupRealtimeClient(); - - Socket->SetConnectCallback([this]() - { - UE_LOG(LogTemp, Warning, TEXT("Socket connected")); - - // Notification RPC - auto RPCSuccessCallback = [&](const FNakamaRPC& RPC) - { - UE_LOG(LogTemp, Display, TEXT("Sent RPC: %s"), *RPC.Id); - - // List Notifications - auto ListNotificationsSuccessCallback = [this](const FNakamaNotificationList& NotificationList) - { - if(NotificationList.Notifications.Num() > 0) - { - for (auto Notification : NotificationList.Notifications) - { - UE_LOG (LogTemp, Display, TEXT("Notification ID: %s"), *Notification.Id); - UE_LOG (LogTemp, Display, TEXT("Notification Code: %d"), Notification.Code); - UE_LOG (LogTemp, Display, TEXT("Notification Content: %s"), *Notification.Content); - - auto DeleteNotificationsSuccessCallback = [this, Notification]() - { - UE_LOG(LogTemp, Display, TEXT("Deleted Notification ID: %s"), *Notification.Id); - TestTrue("Notifications Test Passed", true); - StopTest(); - }; - - auto DeleteNotificationsErrorCallback = [this](const FNakamaError& Error) - { - UE_LOG(LogTemp, Display, TEXT("Delete Notifications Error. ErrorMessage: %s"), *Error.Message); - TestFalse("Delete Notifications Error.", true); - StopTest(); - }; - - // NOTE: Could also add Id's to an array and delete them all at once - Client->DeleteNotifications(Session, {Notification.Id}, DeleteNotificationsSuccessCallback, DeleteNotificationsErrorCallback); - } - } - else - { - UE_LOG(LogTemp, Display, TEXT("No Notifications Received")); - TestFalse("No Notifications Received", true); - StopTest(); - } - }; - - auto ListNotificationsErrorCallback = [this](const FNakamaError& Error) - { - UE_LOG(LogTemp, Display, TEXT("List Notifications Error. ErrorMessage: %s"), *Error.Message); - TestFalse("List Notifications Error.", true); - StopTest(); - }; - - Client->ListNotifications(Session, {}, {}, ListNotificationsSuccessCallback, ListNotificationsErrorCallback); - }; - - auto RPCErrorCallback = [&](const FNakamaRtError& Error) - { - UE_LOG(LogTemp, Error, TEXT("Notification Test. ErrorMessage: %s"), *Error.Message); - TestFalse("RPC error.", true); - StopTest(); - }; - - FString Payload = FString::Printf(TEXT("{\"user_id\":\"%s\"}"), *Session->GetUserId()); - - Socket->RPC("clientrpc.send_notification", Payload, RPCSuccessCallback, RPCErrorCallback); - }); - - // Connect with Socket - Socket->Connect(Session, true); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Authentication Test Failed", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} \ No newline at end of file diff --git a/Nakama/Source/NakamaTests/Private/Tests/Realtime/Test_Parties.cpp b/Nakama/Source/NakamaTests/Private/Tests/Realtime/Test_Parties.cpp deleted file mode 100644 index 38507f3e4..000000000 --- a/Nakama/Source/NakamaTests/Private/Tests/Realtime/Test_Parties.cpp +++ /dev/null @@ -1,463 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Tests/Realtime/Test_Parties.h" - -// Create Party and Join Party Test Case -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(CreateParty, FNakamaPartiesTestBase, "Nakama.Base.Realtime.Parties.CreateParty", NAKAMA_MODULE_TEST_MASK) -inline bool CreateParty::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [&](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - // Setup socket: - Socket = Client->SetupRealtimeClient(); - - Socket->SetConnectCallback([this]() - { - UE_LOG(LogTemp, Warning, TEXT("Socket 1 connected")); - - auto CreatePartySuccessCallback = [&](const FNakamaParty& CreateParty) - { - Party = CreateParty; - SetupClient2AndJoinParty(); - }; - - auto CreatePartyErrorCallback = [&](const FNakamaRtError& Error) - { - UE_LOG(LogTemp, Error, TEXT("Create Party. ErrorMessage: %s"), *Error.Message); - TestFalse("Create Party Test error.", true); - StopTest(); - }; - - Socket->CreateParty(true, 2, CreatePartySuccessCallback, CreatePartyErrorCallback); - }); - - Socket->SetPartyDataCallback( [&](const FNakamaPartyData& PartyData) - { - UE_LOG(LogTemp, Warning, TEXT("Party Data Callback. Data: %s"), *PartyData.Data); - }); - - // Connect with Socket - Socket->Connect(Session, true); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Authentication Test Failed", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} - -void FNakamaPartiesTestBase::SetupClient2AndJoinParty() -{ - Client2 = CreateClient(); - - auto Client2AuthSuccessCallback = [&](UNakamaSession* session) - { - // Set the session for later use - Session2 = session; - - // Setup socket: - Socket2 = Client2->SetupRealtimeClient(); - - Socket2->SetConnectCallback([this]() - { - UE_LOG(LogTemp, Warning, TEXT("Socket 2 connected")); - - // Join Party Callbacks - auto JoinPartySuccessCallback = [&]() - { - UE_LOG(LogTemp, Display, TEXT("Joined Party with Id: %s"), *Party.PartyId); - - Socket2->SendPartyData(Party.PartyId, 100, "Testing if it works"); - TestTrue("Create Party Test Passed", true); - StopTest(); - }; - - auto JoinPartyErrorCallback = [&](const FNakamaRtError& Error) - { - UE_LOG(LogTemp, Error, TEXT("Join Party. ErrorMessage: %s"), *Error.Message); - TestFalse("Join Party Test error.", true); - StopTest(); - }; - - Socket2->JoinParty(Party.PartyId, JoinPartySuccessCallback, JoinPartyErrorCallback); - }); - - Socket2->SetPartyCallback( [&](const FNakamaParty& MyParty) - { - UE_LOG(LogTemp, Warning, TEXT("Party Callback. PartyId: %s"), *MyParty.PartyId); - for (auto& Presence : MyParty.Presences) - { - UE_LOG(LogTemp, Display, TEXT("Event Presence: %s"), *Presence.UserID); - } - }); - - // Connect with Socket - Socket2->Connect(Session2, true); - }; - - auto Client2AuthErrorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Authentication Test Failed", true); - StopTest(); - }; - - Client2->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, Client2AuthSuccessCallback, Client2AuthErrorCallback); -} - -void FNakamaPartiesTestBase::SetupClient2AndRequestJoinParty() -{ - Client2 = CreateClient(); - - auto Client2AuthSuccessCallback = [&](UNakamaSession* session) - { - // Set the session for later use - Session2 = session; - - // Setup socket: - Socket2 = Client2->SetupRealtimeClient(); - - Socket2->SetConnectCallback([&]() - { - UE_LOG(LogTemp, Warning, TEXT("Socket 2 connected")); - - // Join Party Callbacks - auto JoinPartySuccessCallback = [&]() - { - auto ListPartyJoinRequestsSuccessCallback = [&](const FNakamaPartyJoinRequest& JoinRequests) - { - TestTrue("List Party Join Requests Test Passed", JoinRequests.Presences.Num() == 1); - StopTest(); - }; - - auto ListPartyJoinRequestsErrorCallback = [&](const FNakamaRtError& Error) - { - UE_LOG(LogTemp, Error, TEXT("List Party Join Requests. ErrorMessage: %s"), *Error.Message); - TestFalse("List Party Join Requests Test error.", true); - StopTest(); - }; - - this->Socket->ListPartyJoinRequests(Party.PartyId, ListPartyJoinRequestsSuccessCallback, ListPartyJoinRequestsErrorCallback); - }; - - auto JoinPartyErrorCallback = [&](const FNakamaRtError& Error) - { - UE_LOG(LogTemp, Error, TEXT("Join Party. ErrorMessage: %s"), *Error.Message); - TestFalse("List Party Join Requests Test error.", true); - StopTest(); - }; - - Socket2->JoinParty(Party.PartyId, JoinPartySuccessCallback, JoinPartyErrorCallback); - }); - - Socket2->SetPartyCallback([&](const FNakamaParty& MyParty) - { - UE_LOG(LogTemp, Warning, TEXT("Party Callback. PartyId: %s"), *MyParty.PartyId); - for (auto& Presence : MyParty.Presences) - { - UE_LOG(LogTemp, Display, TEXT("Event Presence: %s"), *Presence.UserID); - } - }); - - // Connect with Socket - Socket2->Connect(Session2, true); - }; - - auto Client2AuthErrorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Authentication Test Failed", true); - StopTest(); - }; - - Client2->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, Client2AuthSuccessCallback, Client2AuthErrorCallback); -} - -void FNakamaPartiesTestBase::SetupClient2AndReceiveRequestJoinParty() -{ - Client2 = CreateClient(); - - auto Client2AuthSuccessCallback = [&](UNakamaSession* session) - { - // Set the session for later use - Session2 = session; - - // Setup socket: - Socket2 = Client2->SetupRealtimeClient(); - - Socket2->SetConnectCallback([&]() - { - UE_LOG(LogTemp, Warning, TEXT("Socket 2 connected")); - - // Join Party Callbacks - auto JoinPartySuccessCallback = [&]() {}; - - auto JoinPartyErrorCallback = [&](const FNakamaRtError& Error) - { - UE_LOG(LogTemp, Error, TEXT("Join Party. ErrorMessage: %s"), *Error.Message); - TestFalse("List Party Join Requests Test error.", true); - StopTest(); - }; - - Socket2->JoinParty(Party.PartyId, JoinPartySuccessCallback, JoinPartyErrorCallback); - }); - - Socket2->SetPartyCallback([&](const FNakamaParty& MyParty) - { - UE_LOG(LogTemp, Warning, TEXT("Party Callback. PartyId: %s"), *MyParty.PartyId); - for (auto& Presence : MyParty.Presences) - { - UE_LOG(LogTemp, Display, TEXT("Event Presence: %s"), *Presence.UserID); - } - }); - - // Connect with Socket - Socket2->Connect(Session2, true); - }; - - auto Client2AuthErrorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Authentication Test Failed", true); - StopTest(); - }; - - Client2->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, Client2AuthSuccessCallback, Client2AuthErrorCallback); -} - -// Create Party and Join Party Test Case -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(PartyMatchmaker, FNakamaPartiesTestBase, "Nakama.Base.Realtime.Parties.PartyMatchmaker", NAKAMA_MODULE_TEST_MASK) -inline bool PartyMatchmaker::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [&](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - // Setup socket: - Socket = Client->SetupRealtimeClient(); - - Socket->SetConnectCallback([this]() - { - UE_LOG(LogTemp, Warning, TEXT("Socket 1 connected")); - - auto CreatePartySuccessCallback = [&](const FNakamaParty& CreateParty) - { - Party = CreateParty; - //SetupClient2AndJoinParty(); - - auto AddMatchmakerPartySuccessCallback = [&](const FNakamaPartyMatchmakerTicket& Ticket) - { - UE_LOG(LogTemp, Warning, TEXT("Add Matchmaker Party Success Callback. Ticket: %s"), *Ticket.Ticket); - TestTrue( "Add Matchmaker Party Success", !Ticket.Ticket.IsEmpty()); - StopTest(); - }; - - auto AddMatchmakerPartyErrorCallback = [&](const FNakamaRtError& Error) - { - UE_LOG(LogTemp, Error, TEXT("Add Matchmaker Party Error Callback. ErrorMessage: %s"), *Error.Message); - TestFalse("Add Matchmaker Party Error", true); - }; - - TOptional MinCount = 2; - TOptional MaxCount = 2; - TOptional Query(TEXT("*")); - - Socket->AddMatchmakerParty(Party.PartyId, MinCount, MaxCount, Query, {}, {} , {},AddMatchmakerPartySuccessCallback, AddMatchmakerPartyErrorCallback); - }; - - auto CreatePartyErrorCallback = [&](const FNakamaRtError& Error) - { - UE_LOG(LogTemp, Error, TEXT("Create Party. ErrorMessage: %s"), *Error.Message); - TestFalse("Create Party Test error.", true); - StopTest(); - }; - - Socket->CreateParty(false, 1, CreatePartySuccessCallback, CreatePartyErrorCallback); - }); - - Socket->SetPartyDataCallback( [&](const FNakamaPartyData& PartyData) - { - UE_LOG(LogTemp, Warning, TEXT("Party Data Callback. Data: %s"), *PartyData.Data); - }); - - // Connect with Socket - Socket->Connect(Session, true); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Authentication Test Failed", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} - -// Create Party and Join Party Test Case -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(ListPartyJoinRequests, FNakamaPartiesTestBase, "Nakama.Base.Realtime.Parties.ListPartyJoinRequests", NAKAMA_MODULE_TEST_MASK) -inline bool ListPartyJoinRequests::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [&](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - // Setup socket: - Socket = Client->SetupRealtimeClient(); - - Socket->SetConnectCallback([this]() - { - UE_LOG(LogTemp, Warning, TEXT("Socket 1 connected")); - - auto CreatePartySuccessCallback = [this](const FNakamaParty& CreateParty) - { - Party = CreateParty; - SetupClient2AndRequestJoinParty(); - }; - - auto CreatePartyErrorCallback = [&](const FNakamaRtError& Error) - { - UE_LOG(LogTemp, Error, TEXT("Create Party. ErrorMessage: %s"), *Error.Message); - TestFalse("Create Party Test error.", true); - StopTest(); - }; - - Socket->CreateParty(false, 2, CreatePartySuccessCallback, CreatePartyErrorCallback); - }); - - // Connect with Socket - Socket->Connect(Session, true); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Authentication Test Failed", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} - -// Received Party Join Requests Test Case -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(ReceivedPartyJoinRequests, FNakamaPartiesTestBase, "Nakama.Base.Realtime.Parties.ReceivedPartyJoinRequests", NAKAMA_MODULE_TEST_MASK) -inline bool ReceivedPartyJoinRequests::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [&](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - // Setup socket: - Socket = Client->SetupRealtimeClient(); - - Socket->SetPartyJoinRequestCallback([this](const FNakamaPartyJoinRequest& JoinRequests) - { - TestTrue("List Party Join Requests Test Passed", JoinRequests.Presences.Num() == 1); - StopTest(); - }); - - Socket->SetConnectCallback([this]() - { - UE_LOG(LogTemp, Warning, TEXT("Socket 1 connected")); - - auto CreatePartySuccessCallback = [this](const FNakamaParty& CreateParty) - { - Party = CreateParty; - SetupClient2AndReceiveRequestJoinParty(); - }; - - auto CreatePartyErrorCallback = [&](const FNakamaRtError& Error) - { - UE_LOG(LogTemp, Error, TEXT("Create Party. ErrorMessage: %s"), *Error.Message); - TestFalse("Create Party Test error.", true); - StopTest(); - }; - - Socket->CreateParty(false, 2, CreatePartySuccessCallback, CreatePartyErrorCallback); - }); - - // Connect with Socket - Socket->Connect(Session, true); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Authentication Test Failed", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} \ No newline at end of file diff --git a/Nakama/Source/NakamaTests/Private/Tests/Realtime/Test_RPC.cpp b/Nakama/Source/NakamaTests/Private/Tests/Realtime/Test_RPC.cpp deleted file mode 100644 index adfad2dab..000000000 --- a/Nakama/Source/NakamaTests/Private/Tests/Realtime/Test_RPC.cpp +++ /dev/null @@ -1,431 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaTestBase.h" - -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(RPCWithHttpKey, FNakamaTestBase, "Nakama.Base.Realtime.RPC.RPCWithHttpKey", NAKAMA_MODULE_TEST_MASK) -inline bool RPCWithHttpKey::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [this](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - UE_LOG(LogTemp, Warning, TEXT("Session Token: %s"), *Session->GetAuthToken()); - - // Setup socket: - Socket = Client->SetupRealtimeClient(); - - Socket->SetConnectCallback([this]() - { - UE_LOG(LogTemp, Warning, TEXT("Socket connected")); - - // Notification RPC - auto RPCSuccessCallback = [&](const FNakamaRPC& RPC) - { - UE_LOG(LogTemp, Display, TEXT("Sent RPC with Payload: %s"), *RPC.Payload); - - if(!RPC.Payload.IsEmpty()) - { - UE_LOG(LogTemp, Display, TEXT("RPCWithHttpKey Test Passed - Payload: %s"), *RPC.Payload); - TestTrue("RPCWithHttpKey Test Passed", true); - StopTest(); - } - else - { - UE_LOG(LogTemp, Error, TEXT("RPCWithHttpKey Test Failed")); - TestFalse("RPCWithHttpKey Test Failed", true); - StopTest(); - } - }; - - auto RPCErrorCallback = [&](const FNakamaError& Error) - { - UE_LOG(LogTemp, Error, TEXT("RPCWithHttpKey Test. ErrorMessage: %s"), *Error.Message); - TestFalse("RPCWithHttpKey error.", true); - StopTest(); - }; - - const FString FunctionId = "clientrpc.rpc"; - const FString Payload = "{\"v\":\"test\"}"; - - // Test 1: With Payload - Client->RPC(ServerHttpKey, FunctionId, Payload, RPCSuccessCallback, RPCErrorCallback); - - // Test 2: Without Payload - //Client->RPC(ServerHttpKey, FunctionId, {}, RPCSuccessCallback, RPCErrorCallback); - }); - - // Connect with Socket - Socket->Connect(Session, true); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("RPCWithHttpKey Test Failed", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} - -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(RPCWithHttpKey2, FNakamaTestBase, "Nakama.Base.Realtime.RPC.RPCWithHttpKey2", NAKAMA_MODULE_TEST_MASK) -inline bool RPCWithHttpKey2::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [this](UNakamaSession* session) - { - // Set the session for later use - Session = session; - UE_LOG(LogTemp, Warning, TEXT("Session Token: %s"), *Session->GetAuthToken()); - - // Setup socket: - Socket = Client->SetupRealtimeClient(); - - Socket->SetConnectCallback([this]() - { - UE_LOG(LogTemp, Warning, TEXT("Socket connected")); - - // Notification RPC - auto RPCSuccessCallback = [&](const FNakamaRPC& RPC) - { - UE_LOG(LogTemp, Display, TEXT("Sent RPC: %s"), *RPC.Id); - - // This test is flipped because it does not send payload - if(RPC.Payload.IsEmpty()) - { - UE_LOG(LogTemp, Display, TEXT("RPCWithHttpKey Test Passed - Payload: %s"), *RPC.Payload); - TestTrue("RPCWithHttpKey Test Passed", true); - StopTest(); - } - else - { - UE_LOG(LogTemp, Error, TEXT("RPCWithHttpKey Test Failed")); - TestFalse("RPCWithHttpKey Test Failed", true); - StopTest(); - } - }; - - auto RPCErrorCallback = [&](const FNakamaError& Error) - { - UE_LOG(LogTemp, Error, TEXT("RPCWithHttpKey Test. ErrorMessage: %s"), *Error.Message); - TestFalse("RPCWithHttpKey error.", true); - StopTest(); - }; - - const FString FunctionId = "clientrpc.rpc"; - const FString Payload = "{\"v\":\"test\"}"; - - // Test 1: With Payload - //Client->RPC(ServerHttpKey, FunctionId, Payload, RPCSuccessCallback, RPCErrorCallback); - - // Test 2: Without Payload - Client->RPC(ServerHttpKey, FunctionId, {}, RPCSuccessCallback, RPCErrorCallback); - }); - - // Connect with Socket - Socket->Connect(Session, true); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("RPCWithHttpKey Test Failed", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} - -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(RPCWithAuth1, FNakamaTestBase, "Nakama.Base.Realtime.RPC.RPCWithAuth1", NAKAMA_MODULE_TEST_MASK) -inline bool RPCWithAuth1::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [this](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - UE_LOG(LogTemp, Warning, TEXT("Session Token: %s"), *Session->GetAuthToken()); - - // RPCs - auto RPCErrorCallback = [&](const FNakamaError& Error) - { - UE_LOG(LogTemp, Error, TEXT("RPCWithAuth Test. ErrorMessage: %s"), *Error.Message); - TestFalse("RPCWithAuth error.", true); - StopTest(); - }; - - // Test 1 - auto RPC1SuccessCallback = [&](const FNakamaRPC& RPC) - { - UE_LOG(LogTemp, Display, TEXT("Sent RPC: %s"), *RPC.Payload ); - - // We expect empty payload - if(RPC.Payload.IsEmpty()) - { - UE_LOG(LogTemp, Display, TEXT("RPCWithAuth Test Passed")); - TestTrue("RPCWithAuth Test Passed", true); - StopTest(); - } - else - { - UE_LOG(LogTemp, Error, TEXT("RPCWithAuth Test Failed")); - TestFalse("RPCWithAuth Test Failed", true); - StopTest(); - } - }; - - Client->RPC(Session, "clientrpc.rpc", {}, RPC1SuccessCallback, RPCErrorCallback); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("RPC with Auth Test Failed", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} - -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(RPCWithAuth2, FNakamaTestBase, "Nakama.Base.Realtime.RPC.RPCWithAuth2", NAKAMA_MODULE_TEST_MASK) -inline bool RPCWithAuth2::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [this](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - UE_LOG(LogTemp, Warning, TEXT("Session Token: %s"), *Session->GetAuthToken()); - - // RPCs - auto RPCErrorCallback = [&](const FNakamaError& Error) - { - UE_LOG(LogTemp, Error, TEXT("RPCWithAuth Test. ErrorMessage: %s"), *Error.Message); - TestFalse("RPCWithAuth error.", true); - StopTest(); - }; - - // Test 2 - FString Json = FString::Printf(TEXT("{\"user_id\":\"%s\"}"), *Session->GetUserId()); - auto RPC2SuccessCallback = [&,Json](const FNakamaRPC& RPC) - { - UE_LOG(LogTemp, Display, TEXT("Payload: %s"), *RPC.Payload); - UE_LOG(LogTemp, Display, TEXT("Json: %s"), *Json); - - // We expect User ID in payload - if(RPC.Payload == Json) - { - UE_LOG(LogTemp, Display, TEXT("RPCWithAuth Test Passed")); - TestTrue("RPCWithAuth Test Passed", true); - StopTest(); - } - else - { - UE_LOG(LogTemp, Error, TEXT("RPCWithAuth Test Failed")); - TestFalse("RPCWithAuth Test Failed", true); - StopTest(); - } - }; - - Client->RPC(Session, "clientrpc.rpc", Json, RPC2SuccessCallback, RPCErrorCallback); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("RPC with Auth Test Failed", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} - -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(RPCWithAuth4, FNakamaTestBase, "Nakama.Base.Realtime.RPC.RPCWithAuth4", NAKAMA_MODULE_TEST_MASK) -inline bool RPCWithAuth4::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [this](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - UE_LOG(LogTemp, Warning, TEXT("Session Token: %s"), *Session->GetAuthToken()); - - // RPCs - auto RPCErrorCallback = [&](const FNakamaError& Error) - { - UE_LOG(LogTemp, Error, TEXT("RPCWithAuth Test. ErrorMessage: %s"), *Error.Message); - TestFalse("RPCWithAuth error.", true); - StopTest(); - }; - - // Test 4 - auto RPC4SuccessCallback = [&](const FNakamaRPC& RPC) - { - UE_LOG(LogTemp, Display, TEXT("Payload: %s"), *RPC.Payload); - - // We expect non-empty payload - if(!RPC.Payload.IsEmpty()) - { - UE_LOG(LogTemp, Display, TEXT("RPCWithAuth Test Passed")); - TestTrue("RPCWithAuth Test Passed", true); - StopTest(); - } - else - { - UE_LOG(LogTemp, Error, TEXT("RPCWithAuth Test Failed")); - TestFalse("RPCWithAuth Test Failed", true); - StopTest(); - } - }; - - const FString Payload = "{}"; - Client->RPC(Session, "clientrpc.rpc", Payload, RPC4SuccessCallback, RPCErrorCallback); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("RPC with Auth Test Failed", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} - -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(RPCWithAuth5, FNakamaTestBase, "Nakama.Base.Realtime.RPC.RPCWithAuth5", NAKAMA_MODULE_TEST_MASK) -inline bool RPCWithAuth5::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [this](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - UE_LOG(LogTemp, Warning, TEXT("Session Token: %s"), *Session->GetAuthToken()); - - // RPCs - auto RPCErrorCallback = [&](const FNakamaError& Error) - { - UE_LOG(LogTemp, Error, TEXT("RPCWithAuth Test. ErrorMessage: %s"), *Error.Message); - TestFalse("RPCWithAuth error.", true); - StopTest(); - }; - - // Test 5 - FString JsonRPC5 = FString::Printf(TEXT("{\"user_id\":\"%s\"}"), *Session->GetUserId()); - auto RPC5SuccessCallback = [&](const FNakamaRPC& RPC) - { - UE_LOG(LogTemp, Display, TEXT("Payload: %s"), *RPC.Payload); - - // We expect non-empty payload - if(!RPC.Payload.IsEmpty()) - { - UE_LOG(LogTemp, Display, TEXT("RPCWithAuth Test Passed")); - TestTrue("RPCWithAuth Test Passed", true); - StopTest(); - } - else - { - UE_LOG(LogTemp, Error, TEXT("RPCWithAuth Test Failed")); - TestFalse("RPCWithAuth Test Failed", true); - StopTest(); - } - }; - - Client->RPC(Session, "clientrpc.rpc", JsonRPC5, RPC5SuccessCallback, RPCErrorCallback); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("RPC with Auth Test Failed", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} \ No newline at end of file diff --git a/Nakama/Source/NakamaTests/Private/Tests/Realtime/Test_Tournament.cpp b/Nakama/Source/NakamaTests/Private/Tests/Realtime/Test_Tournament.cpp deleted file mode 100644 index bc0f59b30..000000000 --- a/Nakama/Source/NakamaTests/Private/Tests/Realtime/Test_Tournament.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Tests/Realtime/Test_Tournament.h" - -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(Tournament, FNakamaTournamentTestBase, "Nakama.Base.Realtime.Tournament", NAKAMA_MODULE_TEST_MASK) -inline bool Tournament::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [this](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - // Setup socket: - Socket = Client->SetupRealtimeClient(); - - Socket->SetConnectCallback([this]() - { - UE_LOG(LogTemp, Warning, TEXT("Test Socket connected")); - - // Create a JSON object and add properties to it - TSharedPtr JsonObject = MakeShareable(new FJsonObject); - - JsonObject->SetBoolField("authoritative", true); - JsonObject->SetStringField("sort_order", "desc"); - JsonObject->SetStringField("operator", Operator); - JsonObject->SetNumberField("duration", Duration); - JsonObject->SetStringField("reset_schedule", ResetSchedule); - JsonObject->SetStringField("title", "Daily Dash"); - JsonObject->SetStringField("description", "Dash past your opponents for high scores and big rewards!"); - JsonObject->SetNumberField("category", 1); - JsonObject->SetNumberField("start_time", StartTime); - JsonObject->SetNumberField("end_time", EndTime); - JsonObject->SetNumberField("max_size", MaxSize); - JsonObject->SetNumberField("max_num_score", MaxNumScore); - JsonObject->SetBoolField("join_required", JoinRequired); - - // Convert the JSON object to a string - FString JsonString; - TSharedRef> JsonWriter = TJsonWriterFactory::Create(&JsonString); - FJsonSerializer::Serialize(JsonObject.ToSharedRef(), JsonWriter); - - auto CreateTournamentSuccessCallback = [&](const FNakamaRPC& Rpc) - { - UE_LOG(LogTemp, Warning, TEXT("Tournament created: %s"), *Rpc.Payload ); - - FString TournamentId = GetTournamentIdFromJsonString(Rpc.Payload); - if(!TournamentId.IsEmpty()) - { - // Join the tournament - auto JoinTournamentSuccessCallback = [&, TournamentId]() - { - UE_LOG(LogTemp, Warning, TEXT("Joined Tournament ID: %s"), *TournamentId); - TestTrue("Tournament Test Passed",true); - StopTest(); - }; - - auto JoinTournamentErrorCallback = [this](const FNakamaError& Error) - { - UE_LOG(LogTemp, Warning, TEXT("Join Tournament Error - Join Tournament. ErrorMessage: %s"), *Error.Message); - TestFalse("Join Tournament Error - Join Tournament.", true); - StopTest(); - }; - - Client->JoinTournament(Session, TournamentId, JoinTournamentSuccessCallback, JoinTournamentErrorCallback); - } - else - { - UE_LOG(LogTemp, Warning, TEXT("TournamentId is Empty")); - TestFalse("TournamentId is Empty", true); - StopTest(); - } - }; - - auto CreateTournamentErrorCallback = [this](const FNakamaRtError& Error) - { - UE_LOG(LogTemp, Warning, TEXT("Join Tournament Error - Create Tournament. ErrorMessage: %s"), *Error.Message); - TestFalse("Join Tournament Error - Create Tournament.", true); - StopTest(); - }; - - Socket->RPC("clientrpc.create_tournament", JsonString, CreateTournamentSuccessCallback, CreateTournamentErrorCallback); - }); - - // In this test we use a custom external listener, instead of the one provided with the Realtime Client - Socket->Connect(Session, true); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Tournament Test Failed to Authenticate", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} - -FString FNakamaTournamentTestBase::GetTournamentIdFromJsonString(const FString& JsonString) const -{ - TSharedPtr JsonObject; - TSharedRef> JsonReader = TJsonReaderFactory::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - FString TournamentId; - if (JsonObject->TryGetStringField(TEXT("tournament_id"), TournamentId)) - { - return TournamentId; - } - } - - // Return an empty string if the parsing fails or if the "match_id" field is not present - return FString(); -} diff --git a/Nakama/Source/NakamaTests/Private/Tests/Test_Authentication.cpp b/Nakama/Source/NakamaTests/Private/Tests/Test_Authentication.cpp deleted file mode 100644 index 24092a626..000000000 --- a/Nakama/Source/NakamaTests/Private/Tests/Test_Authentication.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaTestBase.h" -#include "NakamaUtils.h" -#include "Misc/AutomationTest.h" - -// Authenticate Email -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(AuthenticateEmail, FNakamaTestBase, "Nakama.Base.Authenticate.Email", NAKAMA_MODULE_TEST_MASK) -inline bool AuthenticateEmail::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Setup test variables - TMap InVars; - InVars.Add(TEXT("test1"), TEXT("testValue1")); - InVars.Add(TEXT("test2"), TEXT("testValue2")); - - // Define success callback - auto successCallback = [&](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - // Test passes if the authentication succeeds - TestTrue("Authentication Test Passed", !Session->GetAuthToken().IsEmpty()); - StopTest(); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Authentication Test Failed", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateEmail("test@mail.com", "12345678", "", true, InVars, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} - -// Authenticate Email 2 -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(AuthenticateEmail2, FNakamaTestBase, "Nakama.Base.Authenticate.Email2", NAKAMA_MODULE_TEST_MASK) -inline bool AuthenticateEmail2::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Setup test variables - TMap InVars; - InVars.Add(TEXT("test1"), TEXT("testValue1")); - InVars.Add(TEXT("test2"), TEXT("testValue2")); - - // Define success callback - auto successCallback = [&](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - UE_LOG( LogTemp, Warning, TEXT("Username: %s"), *Session->GetUsername()); - - // Test passes if the authentication succeeds - TestTrue("Authentication Test Passed", !Session->GetAuthToken().IsEmpty() && TEXT("βσκαταη1234") == Session->GetUsername()); - StopTest(); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Authentication Test Failed", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateEmail("test1234@mail.com", "12345678", TEXT("βσκαταη1234"), true, {}, successCallback, errorCallback); // has issues - //Client->AuthenticateEmail("test@mail.com", "12345678", "test3", true, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} - -// Authenticate Device -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(AuthenticateDevice, FNakamaTestBase, "Nakama.Base.Authenticate.Device", NAKAMA_MODULE_TEST_MASK) -inline bool AuthenticateDevice::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [&](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - // Test passes if the authentication succeeds - TestTrue("Device Authentication Test Passed", !Session->GetAuthToken().IsEmpty()); - StopTest(); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Device Authentication Test Failed", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - TOptional bCreate = true; - Client->AuthenticateDevice("mytestdevice0000", {}, {}, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} - -// Authenticate Device2 -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(AuthenticateDevice2, FNakamaTestBase, "Nakama.Base.Authenticate.Device2", NAKAMA_MODULE_TEST_MASK) -inline bool AuthenticateDevice2::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Setup test variables - TMap InVars; - InVars.Add(TEXT("param1"), TEXT("test value")); - - // Define success callback - auto successCallback = [&](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - // Test passes if the authentication succeeds - TestTrue("Device Authentication Test Passed", !Session->GetAuthToken().IsEmpty() && Session->GetVariable("param1") == "test value"); - StopTest(); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Device Authentication Test Failed", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateDevice("mytestdevice0000", true, {}, InVars, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} \ No newline at end of file diff --git a/Nakama/Source/NakamaTests/Private/Tests/Test_Errors.cpp b/Nakama/Source/NakamaTests/Private/Tests/Test_Errors.cpp deleted file mode 100644 index e4cdd1ee4..000000000 --- a/Nakama/Source/NakamaTests/Private/Tests/Test_Errors.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaTestBase.h" - -// Error Not Found -// TODO: Disabled because this Test Fails, returns PermissionDenied from server, expects NotFound -/*IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(ErrorNotFound, FNakamaTestBase, "Nakama.Base.Errors.NotFound", NAKAMA_MODULE_TEST_MASK) -inline bool ErrorNotFound::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [&](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - UE_LOG(LogTemp, Warning, TEXT("Session Token: %s"), *Session->GetAuthToken()); - - // We do not want test to pass if authenticated - TestFalse("Not Found Test Failed", true); - StopTest(); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - UE_LOG( LogTemp, Warning, TEXT("Error Code: %d"), Error.Code ); - TestTrue("Not Found Test Passed", Error.Code == ENakamaErrorCode::NotFound); // Test fails as it returns PermissionDenied - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateDevice("_not_existing_device_id_", false, {}, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -}*/ - -// Error Invalid Argument -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(ErrorInvalidArgument, FNakamaTestBase, "Nakama.Base.Errors.InvalidArgument", NAKAMA_MODULE_TEST_MASK) -inline bool ErrorInvalidArgument::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [&](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - UE_LOG(LogTemp, Warning, TEXT("Session Token: %s"), *Session->GetAuthToken()); - - // We do not want test to pass if authenticated - TestFalse("Invalid Argument Test Failed", true); - StopTest(); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - UE_LOG( LogTemp, Warning, TEXT("Error Code: %d"), Error.Code ); - TestTrue("Invalid Argument Test Passed", Error.Code == ENakamaErrorCode::InvalidArgument); - StopTest(); - }; - - Client->AuthenticateDevice("", false, {}, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} - -// Error Invalid Argument -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(ErrorInvalidArgument2, FNakamaTestBase, "Nakama.Base.Errors.InvalidArgument2", NAKAMA_MODULE_TEST_MASK) -inline bool ErrorInvalidArgument2::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [&](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - UE_LOG(LogTemp, Warning, TEXT("Session Token: %s"), *Session->GetAuthToken()); - - // We do not want test to pass if authenticated - TestFalse("Invalid Argument 2 Test Failed", true); - StopTest(); - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - UE_LOG( LogTemp, Warning, TEXT("Error Code: %d"), Error.Code ); - TestTrue("Invalid Argument 2 Test Passed", Error.Code == ENakamaErrorCode::InvalidArgument); - StopTest(); - }; - - Client->AuthenticateDevice("1", false, {}, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} - -// Error Unauthenticated -// TODO: Disabled because this Test Fails, returns Error Code 16 from server, expects Unauthenticated (4) -/*IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(ErrorUnauthenticated, FNakamaTestBase, "Nakama.Base.Errors.Unauthenticated", NAKAMA_MODULE_TEST_MASK) -inline bool ErrorUnauthenticated::RunTest(const FString& Parameters) -{ - // initiates the test - InitiateTest(); - - UNakamaSession* RestoredSession = UNakamaSession::RestoreSession("dfgdfgdfg.dfgdfgdfg.dfgdfgdfg", "dfgdfgdfg.dfgdfgdfg.dfgdfgdfg"); - - auto errorCallback = [this, RestoredSession](const FNakamaError& Error) - { - UE_LOG( LogTemp, Warning, TEXT("Error Code: %d"), Error.Code ); - UE_LOG( LogTemp, Warning, TEXT("Error Message: %s"), *Error.Message); - UE_LOG(LogTemp, Warning, TEXT("Session Token: %s"), *RestoredSession->GetAuthToken()); - - TestTrue("Unauthenticated Test Passed", Error.Code == ENakamaErrorCode::Unauthenticated); // Test Fails, returns Error Code 16 from server, expects Unauthenticated (4) - StopTest(); - }; - - Client->GetAccount(RestoredSession, {}, errorCallback); - - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -}*/ \ No newline at end of file diff --git a/Nakama/Source/NakamaTests/Private/Tests/Test_Friends.cpp b/Nakama/Source/NakamaTests/Private/Tests/Test_Friends.cpp deleted file mode 100644 index 2d936420f..000000000 --- a/Nakama/Source/NakamaTests/Private/Tests/Test_Friends.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Tests/Test_Friends.h" - -#include "NakamaLogger.h" -#include "NakamaLoggingMacros.h" -#include "NakamaTestBase.h" - -// List friends and testing cursor -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(ListFriends, FNakamaFriendsTestBase, "Nakama.Base.Friends.ListFriends", NAKAMA_MODULE_TEST_MASK) -inline bool ListFriends::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - auto AuthenticateSuccess = [&](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - UE_LOG( LogTemp, Display, TEXT("Session: %s"), *Session->GetAuthToken() ); - - for (int i = 0; i < FriendsToAdd; i++) - { - auto FriendAuthenticateSuccess = [&](UNakamaSession* friendSession) - { - FriendIds.Add(friendSession->GetUserId()); - - // Done with authentication of friends - if (FriendIds.Num() == FriendsToAdd) - { - // Add Friends - auto FriendsAddedSuccess = [&]() - { - auto ListFriendsSuccess = [&] (const FNakamaFriendList& Friends) - { - FNakamaFriendList InvitedList = Friends; - - if(InvitedList.NakamaUsers.Num() == 0) - { - NAKAMA_LOG_ERROR("empty invited list 1"); - TestFalse("empty invited list 1", true); - StopTest(); - } - - FString ReturnedFriendId1 = Friends.NakamaUsers[0].NakamaUser.Id; - - auto ListFriends2Success = [&] (const FNakamaFriendList& Friends2) - { - if(Friends2.NakamaUsers.Num() == 0) - { - NAKAMA_LOG_ERROR("Empty invited list 2"); - TestFalse("Empty invited list 2", true); - StopTest(); - } - - FString ReturnedFriendId2 = Friends2.NakamaUsers[0].NakamaUser.Id; - TestTrue("List Friends Test Passed", ReturnedFriendId1 != ReturnedFriendId2); - StopTest(); - }; - - auto ListFriends2Error = [&] (const FNakamaError& error) - { - // If this is reached then the cursor is most likely invalid - UE_LOG(LogTemp, Display, TEXT("ListFriends2Error: %s"), *FString(error.Message)); - TestFalse("ListFriends2Error", true); - StopTest(); - }; - - // Passing cursor into function - Client->ListFriends(Session, ListFriendsLimit, ENakamaFriendState::INVITE_SENT, InvitedList.Cursor, ListFriends2Success, ListFriends2Error); - - }; - - Client->ListFriends(Session, ListFriendsLimit, ENakamaFriendState::INVITE_SENT, "", ListFriendsSuccess, {}); - - }; - - // Test that using cursor gives a different friend. - Client->AddFriends(Session, FriendIds, {}, FriendsAddedSuccess, {}); - } - }; - - Client->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, FriendAuthenticateSuccess, {}); - } - - }; - - // Define error callback - auto AuthenticateError = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("List Friends Test Failed", true); - StopTest(); - }; - - Client->AuthenticateCustom(FGuid::NewGuid().ToString(), "", true, {}, AuthenticateSuccess, AuthenticateError); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} \ No newline at end of file diff --git a/Nakama/Source/NakamaTests/Private/Tests/Test_GetAccount.cpp b/Nakama/Source/NakamaTests/Private/Tests/Test_GetAccount.cpp deleted file mode 100644 index bf07af00d..000000000 --- a/Nakama/Source/NakamaTests/Private/Tests/Test_GetAccount.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaTestBase.h" -#include "Misc/AutomationTest.h" - -// Get Account -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(GetAccount, FNakamaTestBase, "Nakama.Base.Users.GetAccount", NAKAMA_MODULE_TEST_MASK) -inline bool GetAccount::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Setup test variables - TMap InVars; - InVars.Add(TEXT("test1"), TEXT("testValue1")); - InVars.Add(TEXT("test2"), TEXT("testValue2")); - - // Define success callback - auto successCallback = [this](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - UE_LOG (LogTemp, Warning, TEXT("Session Token: %s"), *Session->GetAuthToken()); - - // Get Account: - auto successCallback = [&](const FNakamaAccount& Account) - { - UE_LOG(LogTemp, Warning, TEXT("Account ID: %s"), *Account.User.Id); - UE_LOG(LogTemp, Warning, TEXT("Account Created: %ls"), *Account.User.CreatedAt.ToString()); - - //TestTrue("Get Account Test Passed", !Account.User.Id.IsEmpty()); - //StopTest(); - - auto UpdateAccountSuccessCallback = [this]() - { - UE_LOG(LogTemp, Display, TEXT("Account Was Updated")); - TestTrue("Get Account Test Passed - Account was Upated", true); - StopTest(); - }; - - auto UpdateAccountErrorCallback = [this](const FNakamaError& Error) - { - UE_LOG(LogTemp, Error, TEXT("Account retrieval error. ErrorMessage: %s"), *Error.Message); - TestFalse("Account update error.", true); - StopTest(); - }; - - const FString NewDisplayName = TEXT("Nakama-test"); - - Client->UpdateAccount( - Session, - {}, - NewDisplayName, // Update Display Name - {}, - {}, - {}, - {}, - UpdateAccountSuccessCallback, - UpdateAccountErrorCallback - ); - }; - - auto errorCallback = [this](const FNakamaError& Error) - { - UE_LOG(LogTemp, Error, TEXT("Account retrieval error. ErrorMessage: %s"), *Error.Message); - TestFalse("Account retrieval error.", true); - StopTest(); - }; - - Client->GetAccount(Session, successCallback, errorCallback); - - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Get Account Test Failed: Authentication", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateEmail("account-test@example.com", "12345678", "get-account-test", true, InVars, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} \ No newline at end of file diff --git a/Nakama/Source/NakamaTests/Private/Tests/Test_GetUsers.cpp b/Nakama/Source/NakamaTests/Private/Tests/Test_GetUsers.cpp deleted file mode 100644 index ec4ba4f91..000000000 --- a/Nakama/Source/NakamaTests/Private/Tests/Test_GetUsers.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaTestBase.h" -#include "Misc/AutomationTest.h" - -// Authenticate Email -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(GetUsers, FNakamaTestBase, "Nakama.Base.Users.GetUsers", NAKAMA_MODULE_TEST_MASK) -inline bool GetUsers::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Setup test variables - TMap InVars; - InVars.Add(TEXT("test1"), TEXT("testValue1")); - InVars.Add(TEXT("test2"), TEXT("testValue2")); - - // Define success callback - auto successCallback = [this](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - // Get Users: - auto successCallback = [&](const FNakamaUserList& Users) - { - //UE_LOG(LogTemp, Warning, TEXT("Account ID: %s"), *Account.User.Id); - //UE_LOG(LogTemp, Warning, TEXT("Account Created: %ls"), *Account.User.CreatedAt.ToString()); - UE_LOG(LogTemp, Display, TEXT("Get Users Success. Users: %d"), Users.Users.Num()); - - for (auto& User : Users.Users) - { - UE_LOG(LogTemp, Warning, TEXT("User ID: %s"), *User.Id); - UE_LOG(LogTemp, Warning, TEXT("UserName: %s"), *User.Username); - UE_LOG(LogTemp, Warning, TEXT("User Created: %ls"), *User.CreatedAt.ToString()); - } - - if(Users.Users.Num() <= 0) - { - TestFalse("Get Users Error. No Users Found", true); - return; - } - - TestTrue("Get Users Success. Users Test Passed", Users.Users.Num() >= 1 && !Users.Users[0].Id.IsEmpty() ); - StopTest(); - }; - - auto errorCallback = [&](const FNakamaError& Error) - { - UE_LOG(LogTemp, Error, TEXT("Get Users Error. ErrorMessage: %s"), *Error.Message); - TestFalse("Get Users Error.", true); - StopTest(); - }; - - TArray UserIds; - //UserIds.Add("9f4218bf-a894-44bd-9d58-2ad18735fde6"); - //UserIds.Add("6613f4fa-2684-4859-a10d-854047c14b77"); - UserIds.Add(Session->GetUserId()); // Add Self - - Client->GetUsers(Session, UserIds, {}, {}, successCallback, errorCallback); - - }; - - // Define error callback - auto errorCallback = [&](const FNakamaError& Error) - { - // Test fails if there is an authentication error - TestFalse("Authentication Test Failed", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateEmail("account-test@example.com", "12345678", "get-account-test", true, InVars, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} \ No newline at end of file diff --git a/Nakama/Source/NakamaTests/Private/Tests/Test_Groups.cpp b/Nakama/Source/NakamaTests/Private/Tests/Test_Groups.cpp deleted file mode 100644 index 75d7e08c9..000000000 --- a/Nakama/Source/NakamaTests/Private/Tests/Test_Groups.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Tests/Test_Groups.h" -#include "Misc/AutomationTest.h" - -// List Groups -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(ListGroups, FNakamaGroupsTestBase, "Nakama.Base.Groups.List", NAKAMA_MODULE_TEST_MASK) -inline bool ListGroups::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Auth Success - auto successCallback = [this](UNakamaSession* session) - { - // Set the session for later use - Session = session; - UE_LOG(LogTemp, Display, TEXT("Session Token: %s"), *Session->GetAuthToken()); - - // List Groups - DoListGroups(); - }; - - // Call the AuthenticateEmail functions - Client->AuthenticateDevice("mytestdevice0000", true, {}, {}, successCallback, {}); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} - -void FNakamaGroupsTestBase::DoListGroups() -{ - auto successCallback = [this](const FNakamaGroupList& Groups) - { - UE_LOG(LogTemp, Display, TEXT("List Groups Success. Groups: %d"), Groups.Groups.Num()); - - if (Groups.Groups.Num() > 0) - { - // Group exists, update it - UpdateGroup(Groups.Groups[0].Id); - } - else - { - // Group does not exist, create it - CreateGroup(); - } - }; - - auto errorCallback = [this](const FNakamaError& Error) - { - UE_LOG(LogTemp, Error, TEXT("List Groups Error: %s"), *Error.Message); - TestFalse("List Groups Test Failed", true); - StopTest(); - }; - - Client->ListGroups(Session, GroupName, 0, "", successCallback, errorCallback); -} - -void FNakamaGroupsTestBase::CreateGroup() -{ - - auto successCallback = [this] (const FNakamaGroup& Group) - { - UpdateGroup(Group.Id); - }; - - auto errorCallback = [this] (const FNakamaError& Error) - { - UE_LOG(LogTemp, Error, TEXT("Create Group Error: %s"), *Error.Message); - TestFalse("Create Group Test Failed", true); - StopTest(); - }; - - FString Description = TEXT("Nakama is cool!"); - - Client->CreateGroup( - Session, - GroupName, - Description, - "", // Avatar URL - "", - true, // Open - {}, - successCallback, - errorCallback - ); -} - -void FNakamaGroupsTestBase::UpdateGroup(const FString& GroupId) -{ - auto successCallback = [this, GroupId]() - { - UE_LOG(LogTemp, Display, TEXT("Group Updated. Group ID: %s"), *GroupId); - // Proceed to list group users - ListGroupUsers(GroupId); - }; - - auto errorCallback = [this](const FNakamaError& Error) - { - UE_LOG(LogTemp, Error, TEXT("Update Group Error: %s"), *Error.Message); - TestFalse("Update Group Test Failed", true); - StopTest(); - }; - - FString Description = TEXT("Nakama is cool!"); - - Client->UpdateGroup( - Session, - GroupId, - {}, - Description, - {}, - {}, // LangTag - {}, - successCallback, - errorCallback - ); -} - -void FNakamaGroupsTestBase::ListGroupUsers(const FString& GroupId) -{ - auto successCallback = [this](const FNakamaGroupUsersList& GroupUsers) - { - UE_LOG(LogTemp, Display, TEXT("List Group Users Success. GroupUsers: %d"), GroupUsers.GroupUsers.Num()); - - if (GroupUsers.GroupUsers.Num() <= 0) - { - UE_LOG(LogTemp, Display, TEXT("List Group Users Error. No GroupUsers Found")); - TestFalse("List Group Users Error. No GroupUsers Found", true); - StopTest(); - return; - } - - TestTrue("List Group Users Test Passed", GroupUsers.GroupUsers.Num() >= 1 && !GroupUsers.GroupUsers[0].User.Id.IsEmpty()); - StopTest(); - }; - - auto errorCallback = [this](const FNakamaError& Error) - { - UE_LOG(LogTemp, Error, TEXT("List Group Users Error: %s"), *Error.Message); - TestFalse("List Group Users Test Failed", true); - StopTest(); - }; - - Client->ListGroupUsers(Session, GroupId, 30, ENakamaGroupState::SUPERADMIN, "", successCallback, errorCallback); -} \ No newline at end of file diff --git a/Nakama/Source/NakamaTests/Private/Tests/Test_Internals.cpp b/Nakama/Source/NakamaTests/Private/Tests/Test_Internals.cpp deleted file mode 100644 index 32d37aec0..000000000 --- a/Nakama/Source/NakamaTests/Private/Tests/Test_Internals.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "GenericPlatform/GenericPlatformHttp.h" -#include "Misc/AutomationTest.h" - -IMPLEMENT_SIMPLE_AUTOMATION_TEST(Test_Internals, "Nakama.Base.Internals.UriEncode", - EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) - -// It is important to use TEXT macro, otherwise this test would fail! -bool Test_Internals::RunTest(const FString& Parameters) -{ - FString Input = TEXT("βσκαταη3"); - FString Expected = TEXT("%CE%B2%CF%83%CE%BA%CE%B1%CF%84%CE%B1%CE%B73"); - - const FString EncodedString = FGenericPlatformHttp::UrlEncode(Input); - - if( EncodedString != Expected ) - { - UE_LOG(LogTemp, Display, TEXT("EncodedString: %s"), *EncodedString); - UE_LOG(LogTemp, Display, TEXT("Expected: %s"), *Expected); - TestFalse("UriEncode Test Failed", true); - } - else - { - UE_LOG(LogTemp, Display, TEXT("EncodedString: %s"), *EncodedString); - UE_LOG(LogTemp, Display, TEXT("Expected: %s"), *Expected); - TestTrue("UriEncode Test Passed", true); - } - - // Make the test pass by returning true, or fail by returning false. - return true; -} diff --git a/Nakama/Source/NakamaTests/Private/Tests/Test_ListMatches.cpp b/Nakama/Source/NakamaTests/Private/Tests/Test_ListMatches.cpp deleted file mode 100644 index cfe987705..000000000 --- a/Nakama/Source/NakamaTests/Private/Tests/Test_ListMatches.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaTestBase.h" -#include "Misc/AutomationTest.h" -#include "Tests/AutomationCommon.h" - -// List Matches -// TODO: Disabled because this Test Fails, it only works with a delay after the RPC call, could be that matches are not created before RPC returns success -/*IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(ListMatches, FNakamaTestBase, "Nakama.Base.Matches.ListMatches", NAKAMA_MODULE_TEST_MASK) -inline bool ListMatches::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Auth Success - auto successCallback = [this](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - // List Matches - auto RPCSuccessCallback = [this](const FNakamaRPC& Rpc) - { - auto ListMatchesSuccessCallback = [this] (const FNakamaMatchList& MatchList) - { - UE_LOG(LogTemp, Display, TEXT("Expecting match count to be 2. Actual count: %d"), MatchList.Matches.Num()); - TestTrue("List Matches Test Passed", MatchList.Matches.Num() == 2); - StopTest(); - }; - - auto ListMatchesErrorCallback = [this] (const FNakamaError& Error) - { - UE_LOG(LogTemp, Error, TEXT("List Matches Error: %s"), *Error.Message); - TestFalse("List Matches Test Failed: List Matches Error", true); - StopTest(); - }; - - int MinPlayers = 0; - int MaxPlayers = 10; - int Limit = 10; - bool Authoritative = true; - FString Label = TEXT(""); - FString Query = TEXT("+label.type:freeforall +label.difficulty:>1"); - - Client->ListMatches(Session, MinPlayers, MaxPlayers, Limit, Label, Query, Authoritative, ListMatchesSuccessCallback, ListMatchesErrorCallback); - }; - - auto RPCErrorCallback = [this](const FNakamaError& Error) - { - UE_LOG(LogTemp, Error, TEXT("List Matches RPC Error: %s"), *Error.Message); - TestFalse("List Matches Test Failed: RPC Failed", true); - StopTest(); - }; - - Client->RPC(Session, TEXT("create_matches"), {}, RPCSuccessCallback, RPCErrorCallback); - }; - - // Auth Error - auto errorCallback = [this](const FNakamaError& Error) - { - UE_LOG(LogTemp, Error, TEXT("List Matches Error: %s"), *Error.Message); - TestFalse("List Matches Test Failed: Auth Error", true); - StopTest(); - }; - - // Call the AuthenticateEmail function - Client->AuthenticateDevice("mytestdevice0000", true, {}, {}, successCallback, errorCallback); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -}*/ \ No newline at end of file diff --git a/Nakama/Source/NakamaTests/Private/Tests/Test_RestoreSession.cpp b/Nakama/Source/NakamaTests/Private/Tests/Test_RestoreSession.cpp deleted file mode 100644 index 12ff7b727..000000000 --- a/Nakama/Source/NakamaTests/Private/Tests/Test_RestoreSession.cpp +++ /dev/null @@ -1,179 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaTestBase.h" - -// Restore Session check with example JWT tokens -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(RestoreSession, FNakamaTestBase, "Nakama.Base.Sessions.RestoreSession", NAKAMA_MODULE_TEST_MASK) -inline bool RestoreSession::RunTest(const FString& Parameters) -{ - // First Token - const FString FirstToken = TEXT("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MTY5MTA5NzMsInVpZCI6ImY0MTU4ZjJiLTgwZjMtNDkyNi05NDZiLWE4Y2NmYzE2NTQ5MCIsInVzbiI6InZUR2RHSHl4dmwifQ.gzLaMQPaj5wEKoskOSALIeJLOYXEVFoPx3KY0Jm1EVU"); - UNakamaSession* FirstSession = UNakamaSession::RestoreSession(FirstToken, TEXT("")); - bool bDidFirstTokenFail = false; - - if(FirstSession->GetAuthToken() != FirstToken) - { - UE_LOG(LogTemp, Warning, TEXT("Restore Session Test Failed: AuthToken")); - TestFalse("Restore Session Test Failed: AuthToken", true); - bDidFirstTokenFail = true; - } - if(FirstSession->GetVariables().Num() != 0) - { - UE_LOG(LogTemp, Warning, TEXT("Restore Session Test Failed: Variables")); - TestFalse("Restore Session Test Failed: Variables", true); - bDidFirstTokenFail = true; - } - - if(bDidFirstTokenFail) - { - StopTest(); - } - - // Second Token (with variables and more checks) - const FString SecondToken = TEXT("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MTY5MTA5NzMsInVpZCI6ImY0MTU4ZjJiLTgwZjMtNDkyNi05NDZiLWE4Y2NmYzE2NTQ5MCIsInVzbiI6InZUR2RHSHl4dmwiLCJ2cnMiOnsiazEiOiJ2MSIsImsyIjoidjIifX0.Hs9ltsNmtrTJXi2U21jjuXcd-3DMsyv4W6u1vyDBMTo"); - UNakamaSession* SecondSession = UNakamaSession::RestoreSession(SecondToken, TEXT("")); - bool bDidSecondTokenFail = false; - - if(SecondSession->GetAuthToken() != SecondToken) - { - UE_LOG(LogTemp, Warning, TEXT("Restore Session Test Failed: AuthToken")); - TestFalse("Restore Session Test Failed: AuthToken", true); - bDidSecondTokenFail = true; - } - if(SecondSession->GetUsername() != TEXT("vTGdGHyxvl")) - { - UE_LOG(LogTemp, Warning, TEXT("Restore Session Test Failed: Username")); - TestFalse("Restore Session Test Failed: Username", true); - bDidSecondTokenFail = true; - } - if(SecondSession->GetUserId() != TEXT("f4158f2b-80f3-4926-946b-a8ccfc165490")) - { - UE_LOG(LogTemp, Warning, TEXT("Restore Session Test Failed: UserId")); - TestFalse("Restore Session Test Failed: UserId", true); - bDidSecondTokenFail = true; - } - if(SecondSession->GetVariable(TEXT("k1")) != TEXT("v1")) - { - UE_LOG(LogTemp, Warning, TEXT("Restore Session Test Failed: Variable k1")); - TestFalse("Restore Session Test Failed: Variable k1", true); - bDidSecondTokenFail = true; - } - if(SecondSession->GetVariable(TEXT("k2")) != TEXT("v2")) - { - UE_LOG(LogTemp, Warning, TEXT("Restore Session Test Failed: Variable k2")); - TestFalse("Restore Session Test Failed: Variable k2", true); - bDidSecondTokenFail = true; - } - - if(bDidSecondTokenFail) - { - StopTest(); - } - - if(!bDidFirstTokenFail && !bDidSecondTokenFail) - { - UE_LOG(LogTemp, Warning, TEXT("Restore Session Test Passed: Both Tokens")); - TestTrue("Restore Session Test Passed: Both Tokens", true); - StopTest(); - } - - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} - -// Error Unauthenticated -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(RestoreSession2, FNakamaTestBase, "Nakama.Base.Sessions.RestoreSession2", NAKAMA_MODULE_TEST_MASK) -inline bool RestoreSession2::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - auto successCallback = [this](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - const int ExpirePeriodMinutes = 121; - const int RefreshExpirePeriodMinutes = 7201; - - UNakamaSession* RestoredSession = UNakamaSession::RestoreSession(Session->GetAuthToken(), Session->GetRefreshToken()); - UE_LOG (LogTemp, Display, TEXT("Session Token: %s"), *Session->GetAuthToken()); - UE_LOG (LogTemp, Display, TEXT("Session IsExpired: %hhd"), Session->IsExpired()); - - // Get the current timestamp in milliseconds - //int64 CurrentTimestampMs = FPlatformTime::Seconds() * 1000; - - // Calculate the expiration time in milliseconds - //FDateTime ExpireTimeMs = RestoredSession->GetExpireTime(); // Assuming getExpireTime() returns the expiration time in milliseconds - - if(Session->IsRefreshExpired()) - { - UE_LOG(LogTemp, Warning, TEXT("Original session refresh token must not be expired")); - TestFalse("RestoreSession Failed: Session Refresh Token Expired", true); - StopTest(); - } - else if(Session->IsExpired()) - { - UE_LOG(LogTemp, Warning, TEXT("Original session token must not be expired")); - TestFalse("RestoreSession Failed: Session Auth Token Expired", true); - StopTest(); - } - else if(!RestoredSession->IsExpiredTime(FDateTime::UtcNow() + FTimespan::FromMinutes(ExpirePeriodMinutes))) - { - UE_LOG(LogTemp, Warning, TEXT("Restored session token must be expired after: %s minutes"), *FString::FromInt(ExpirePeriodMinutes)); - TestFalse("RestoreSession Failed: Auth Token", true); - StopTest(); - } - else if(!RestoredSession->IsRefreshExpiredTime(FDateTime::UtcNow() + FTimespan::FromMinutes(RefreshExpirePeriodMinutes))) - { - UE_LOG(LogTemp, Warning, TEXT("Restored session token must be expired after: %s minutes"), *FString::FromInt(RefreshExpirePeriodMinutes)); - TestFalse("RestoreSession Failed: Refresh Token", true); - StopTest(); - } - else - { - // Test Account Request - auto GetAccountSuccessCallback = [&](const FNakamaAccount& Account) - { - UE_LOG(LogTemp, Warning, TEXT("Account ID: %s"), *Account.User.Id); - UE_LOG(LogTemp, Warning, TEXT("Account Retreived: %ls"), *Account.User.CreatedAt.ToString()); - - // Successful Test: - TestTrue("RestoreSession Test Passed", !Account.User.Id.IsEmpty()); - StopTest(); - }; - - auto GetAccountErrorCallback = [&](const FNakamaError& Error) - { - UE_LOG(LogTemp, Error, TEXT("Account retrieval error. ErrorMessage: %s"), *Error.Message); - TestFalse("RestoreSession Test Failed: Final Account Step", true); - StopTest(); - }; - - Client->GetAccount(RestoredSession, GetAccountSuccessCallback, GetAccountErrorCallback); - } - }; - - Client->AuthenticateDevice("mytestdevice0000", true, {}, {}, successCallback, {}); - - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} \ No newline at end of file diff --git a/Nakama/Source/NakamaTests/Private/Tests/Test_Storage.cpp b/Nakama/Source/NakamaTests/Private/Tests/Test_Storage.cpp deleted file mode 100644 index 8d96ad127..000000000 --- a/Nakama/Source/NakamaTests/Private/Tests/Test_Storage.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Tests/Test_Storage.h" - -#include "NakamaTestBase.h" - -// Write Storage Invalid Argument -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(WriteStorageInvalidArgument, FNakamaTestBase, "Nakama.Base.Storage.InvalidArgument", NAKAMA_MODULE_TEST_MASK) -inline bool WriteStorageInvalidArgument::RunTest(const FString& Parameters) -{ - // TODO: This test ends up being quite slow - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [this](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - auto errorCallback = [this](const FNakamaError& Error) - { - UE_LOG(LogTemp, Display, TEXT("WriteStorageInvalidArgument. ErrorMessage: %s"), *Error.Message); - UE_LOG(LogTemp, Display, TEXT("WriteStorageInvalidArgument. ErrorCode: %d"), Error.Code); - TestTrue("Write Storage Invalid Argument Passed.", Error.Code == ENakamaErrorCode::InvalidArgument); - StopTest(); - }; - - // Object to write - TArray Objects; - - FNakamaStoreObjectWrite Object1; - Object1.Collection = TEXT("candies"); - Object1.Key = TEXT("test"); - Object1.Value = TEXT("25"); // Invalid Json! - Objects.Add(Object1); - - // Only want to check error? - Client->WriteStorageObjects(Session, Objects, {}, errorCallback); - }; - - Client->AuthenticateDevice("mytestdevice0000", true, {}, {}, successCallback, {}); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} - -// Write Storage Object then verify read -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(WriteStorage, FNakamaTestBase, "Nakama.Base.Storage.Write", NAKAMA_MODULE_TEST_MASK) -inline bool WriteStorage::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [this](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - auto WriteErrorCallback = [this](const FNakamaError& Error) - { - UE_LOG(LogTemp, Display, TEXT("Write Storage Object Failed. ErrorMessage: %s"), *Error.Message); - TestFalse("Write Storage Object Failed.", true); - StopTest(); - }; - - auto WriteSuccessCallback = [this](const FNakamaStoreObjectAcks& Acks) - { - if(Acks.StorageObjects.Num() == 1) - { - UE_LOG(LogTemp, Display, TEXT("Write Ok. Version: %s"), *Acks.StorageObjects[0].Version); - - auto ListErrorCallback = [this](const FNakamaError& Error) - { - UE_LOG(LogTemp, Display, TEXT("List Storage Object Failed. ErrorMessage: %s"), *Error.Message); - TestFalse("List Storage Object Failed.", true); - StopTest(); - }; - - auto ListSuccessCallback = [this](const FNakamaStorageObjectList& ObjectList) - { - UE_LOG(LogTemp, Display, TEXT("List Storage Object Success. Objects: %d"), ObjectList.Objects.Num()); - TestTrue("List Storage Object Success.", ObjectList.Objects.Num() > 0); - StopTest(); - }; - - Client->ListUsersStorageObjects(Session, "candies", Session->GetUserId(), {}, {}, ListSuccessCallback, ListErrorCallback); - } - else - { - UE_LOG(LogTemp, Display, TEXT("Write Storage Object Failed.")); - TestFalse("Write Storage Object Failed.", true); - StopTest(); - } - }; - - // Object to write - TArray Objects; - - FNakamaStoreObjectWrite Object1; - Object1.Collection = TEXT("candies"); - Object1.Key = TEXT("Ice cream"); - Object1.Value = TEXT("{ \"price\": 25 }"); - Object1.PermissionRead = ENakamaStoragePermissionRead::OWNER_READ; - Object1.PermissionWrite = ENakamaStoragePermissionWrite::OWNER_WRITE; - Objects.Add(Object1); - - // Only want to check error? - Client->WriteStorageObjects(Session, Objects, WriteSuccessCallback, WriteErrorCallback); - }; - - Client->AuthenticateDevice("mytestdevice0000", true, {}, {}, successCallback, {}); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} - -// Write Storage Object then verify cursor -IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(WriteStorageCursor, FNakamaStorageTestBase, "Nakama.Base.Storage.WriteCursor", NAKAMA_MODULE_TEST_MASK) -inline bool WriteStorageCursor::RunTest(const FString& Parameters) -{ - // Initiates the test - InitiateTest(); - - // Define success callback - auto successCallback = [this](UNakamaSession* session) - { - // Set the session for later use - Session = session; - - auto WriteErrorCallback = [this](const FNakamaError& Error) - { - UE_LOG(LogTemp, Display, TEXT("Write Storage Object Failed. ErrorMessage: %s"), *Error.Message); - TestFalse("Write Storage Object Failed.", true); - StopTest(); - }; - - auto WriteSuccessCallback = [this](const FNakamaStoreObjectAcks& Acks) - { - if(Acks.StorageObjects.Num() == NumCandies) - { - UE_LOG(LogTemp, Display, TEXT("Write Ok. Version: %s"), *Acks.StorageObjects[0].Version); - - auto ListErrorCallback = [this](const FNakamaError& Error) - { - UE_LOG(LogTemp, Display, TEXT("List Storage Object Failed. ErrorMessage: %s"), *Error.Message); - TestFalse("List Storage Object Failed.", true); - StopTest(); - }; - - auto ListFirstSuccessCallback = [this](const FNakamaStorageObjectList& FirstObjectList) - { - UE_LOG(LogTemp, Display, TEXT("Cursor: %d"), FirstObjectList.Objects.Num()); - - // Second: - auto ListSecondSuccessCallback = [this](const FNakamaStorageObjectList& SecondObjectList) - { - UE_LOG(LogTemp, Display, TEXT("List Second Storage Object Success. Objects: %d"), SecondObjectList.Objects.Num()); - TestTrue("Storage Object Cursor Passed.", SecondObjectList.Objects.Num() > 0); - StopTest(); - }; - - auto ListSecondErrorCallback = [this](const FNakamaError& Error) - { - // This typically means the cursor is invalid - UE_LOG(LogTemp, Display, TEXT("List Storage Object Second Failed. ErrorMessage: %s"), *Error.Message); - TestFalse("List Storage Object Second Failed.", true); - StopTest(); - }; - - Client->ListUsersStorageObjects(Session, "candies", Session->GetUserId(), 10, FirstObjectList.Cursor, ListSecondSuccessCallback, ListSecondErrorCallback); - }; - - Client->ListUsersStorageObjects(Session, "candies", Session->GetUserId(), {}, {}, ListFirstSuccessCallback, ListErrorCallback); - } - else - { - UE_LOG(LogTemp, Display, TEXT("Write Storage Object Failed.")); - TestFalse("Write Storage Object Failed.", true); - StopTest(); - } - }; - - // Object to write - TArray Objects; - - for (int i = 0; i < NumCandies; i++) - { - FNakamaStoreObjectWrite Object; - Object.Collection = TEXT("candies"); - Object.Key = FString::Printf(TEXT("Ice cream%d"), i); - Object.Value = TEXT("{ \"price\": 25 }"); - Object.PermissionRead = ENakamaStoragePermissionRead::OWNER_READ; - Object.PermissionWrite = ENakamaStoragePermissionWrite::OWNER_WRITE; - Objects.Add(Object); - } - - // Only want to check error? - Client->WriteStorageObjects(Session, Objects, WriteSuccessCallback, WriteErrorCallback); - }; - - Client->AuthenticateDevice("mytestdevice0000", true, {}, {}, successCallback, {}); - - // Wait for authentication to complete - ADD_LATENT_AUTOMATION_COMMAND(FWaitForAsyncQueries(this)); - - // Return true to indicate the test is complete - return true; -} \ No newline at end of file diff --git a/Nakama/Source/NakamaTests/Public/NakamaTestBase.h b/Nakama/Source/NakamaTests/Public/NakamaTestBase.h deleted file mode 100644 index d0974318b..000000000 --- a/Nakama/Source/NakamaTests/Public/NakamaTestBase.h +++ /dev/null @@ -1,150 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaClient.h" -#include "NakamaLogger.h" -#include "Misc/AutomationTest.h" -#include "Misc/CommandLine.h" -#include "Misc/EngineVersionComparison.h" - -#if UE_VERSION_OLDER_THAN(5, 5, 0) -#define NAKAMA_MODULE_TEST_MASK EAutomationTestFlags::ApplicationContextMask | EAutomationTestFlags::ProductFilter -#else -#define NAKAMA_MODULE_TEST_MASK EAutomationTestFlags_ApplicationContextMask | EAutomationTestFlags::ProductFilter -#endif - - -// The base class for the tests -class FNakamaTestBase : public FAutomationTestBase -{ -public: - FNakamaTestBase (const FString& InName, const bool bInComplexTask) : FAutomationTestBase(InName, bInComplexTask), Client(nullptr), Socket(nullptr), Session(nullptr) - { - bHasFinished = false; - } - - virtual bool SuppressLogWarnings() override { return true; } // Toggle this to see warnings! - void StopTest() { bHasFinished = true; } - bool IsFinished() const { return bHasFinished; } - void InitiateTest() - { - if(FCommandLine::IsInitialized()) - { - FString GetHostName; - if (FParse::Value(FCommandLine::Get(), TEXT("hostname="), GetHostName)) - { - Hostname = GetHostName; - } - - FString GetServerKey; - if (FParse::Value(FCommandLine::Get(), TEXT("serverkey="), GetServerKey)) - { - ServerKey = GetServerKey; - } - - int32 GetPort; - if (FParse::Value(FCommandLine::Get(), TEXT("port="), GetPort)) - { - Port = GetPort; - } - - bool GetUseSSL; - if (FParse::Bool(FCommandLine::Get(), TEXT("useSSL"), GetUseSSL)) - { - UseSSL = true; - } - - FString GetServerHttpKey; - if (FParse::Value(FCommandLine::Get(), TEXT("serverhttpkey="), GetServerHttpKey)) - { - ServerHttpKey = GetServerHttpKey; - } - - double GetTimeout; - if (FParse::Value(FCommandLine::Get(), TEXT("timeout="), GetTimeout)) - { - Timeout = GetTimeout; - } - } - else - { - UE_LOG(LogTemp, Warning, TEXT("Command Line is not initialized!")); - } - - bHasFinished = false; - Client = CreateClient(); - - UNakamaLogger::EnableLogging(true); - UNakamaLogger::SetLogLevel(ENakamaLogLevel::Debug); - } - double StartTestTime = 0; - - // Set Timeout accordingly - double Timeout = 60.0; - - UPROPERTY() - UNakamaClient *Client; - - UPROPERTY() - UNakamaRealtimeClient *Socket; - - UPROPERTY() - UNakamaSession *Session; - - UNakamaClient* CreateClient() const - { - return UNakamaClient::CreateDefaultClient(ServerKey, Hostname, Port, UseSSL, true); - } - - // Parameters - FString ServerKey = TEXT("defaultkey"); - FString Hostname = TEXT("127.0.0.1"); - int32 Port = 7350; - bool UseSSL = false; - FString ServerHttpKey = TEXT("defaulthttpkey"); - -private: - bool bHasFinished = false; -}; - -// Helper class to wait for async queries to complete (will wait forever) -DEFINE_LATENT_AUTOMATION_COMMAND_ONE_PARAMETER(FWaitForAsyncQueries, FNakamaTestBase*, Base); -inline bool FWaitForAsyncQueries::Update() -{ - // Check if the timer has been initialized - if (Base->StartTestTime == 0) - { - // Get the current time - Base->StartTestTime = FPlatformTime::Seconds(); - } - - // Get the current time - double CurrentTime = FPlatformTime::Seconds(); - - // Calculate elapsed time since the timer was started - double ElapsedTime = CurrentTime - Base->StartTestTime; - - // If X seconds have passed, end the test - if (ElapsedTime >= Base->Timeout) - { - Base->StopTest(); - } - - return Base->IsFinished(); -} \ No newline at end of file diff --git a/Nakama/Source/NakamaTests/Public/Tests/Realtime/Test_AuthoritativeMatch.h b/Nakama/Source/NakamaTests/Public/Tests/Realtime/Test_AuthoritativeMatch.h deleted file mode 100644 index 478600151..000000000 --- a/Nakama/Source/NakamaTests/Public/Tests/Realtime/Test_AuthoritativeMatch.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaTestBase.h" - -// The base class for the tests -class FNakamaAuthoritativeMatchTestBase : public FNakamaTestBase -{ -public: - FNakamaAuthoritativeMatchTestBase(const FString& InName, bool bInComplexTask) : FNakamaTestBase(InName, bInComplexTask), Client2(nullptr), Socket2(nullptr), Session2(nullptr) - { - - } - - virtual bool SuppressLogWarnings() override { return true; } - - UNakamaClient* Client2; - UNakamaRealtimeClient* Socket2; - UNakamaSession* Session2; - - FString GetMatchIdFromJsonString(const FString& JsonString) const; - -}; \ No newline at end of file diff --git a/Nakama/Source/NakamaTests/Public/Tests/Realtime/Test_FollowUsers.h b/Nakama/Source/NakamaTests/Public/Tests/Realtime/Test_FollowUsers.h deleted file mode 100644 index dbc730b19..000000000 --- a/Nakama/Source/NakamaTests/Public/Tests/Realtime/Test_FollowUsers.h +++ /dev/null @@ -1,35 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaTestBase.h" - -class FNakamaFollowUsersTestBase : public FNakamaTestBase -{ -public: - FNakamaFollowUsersTestBase(const FString& InName, bool bInComplexTask) : FNakamaTestBase(InName, bInComplexTask), Client2(nullptr), Socket2(nullptr), Session2(nullptr) - { - - } - - virtual bool SuppressLogWarnings() override { return true; } - - UNakamaClient* Client2; - UNakamaRealtimeClient* Socket2; - UNakamaSession* Session2; -}; \ No newline at end of file diff --git a/Nakama/Source/NakamaTests/Public/Tests/Realtime/Test_Match.h b/Nakama/Source/NakamaTests/Public/Tests/Realtime/Test_Match.h deleted file mode 100644 index 2419dd68c..000000000 --- a/Nakama/Source/NakamaTests/Public/Tests/Realtime/Test_Match.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaTestBase.h" - -// The base class for the tests -class FNakamaMatchTestBase : public FNakamaTestBase -{ -public: - FNakamaMatchTestBase(const FString& InName, bool bInComplexTask) : FNakamaTestBase(InName, bInComplexTask), Client2(nullptr), Socket2(nullptr) - { - - } - virtual bool SuppressLogWarnings() override { return true; } - - UNakamaClient* Client2; - UNakamaRealtimeClient* Socket2; - UNakamaSession* Session2; - - TOptional MinCount = 2; - TOptional MaxCount = 2; - const FString Query = "*"; - const TMap StringProperties = {}; - const TMap NumericProperties = {}; - const int CountMultiple = 1; -}; \ No newline at end of file diff --git a/Nakama/Source/NakamaTests/Public/Tests/Realtime/Test_Parties.h b/Nakama/Source/NakamaTests/Public/Tests/Realtime/Test_Parties.h deleted file mode 100644 index d7d00a84e..000000000 --- a/Nakama/Source/NakamaTests/Public/Tests/Realtime/Test_Parties.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaTestBase.h" - -// The base class for the tests -class FNakamaPartiesTestBase : public FNakamaTestBase -{ -public: - FNakamaPartiesTestBase(const FString& InName, bool bInComplexTask) : FNakamaTestBase(InName, bInComplexTask), Client2(nullptr), Socket2(nullptr), Session2(nullptr) - { - - } - - virtual bool SuppressLogWarnings() override { return true; } - - UNakamaClient* Client2; - UNakamaRealtimeClient* Socket2; - UNakamaSession* Session2; - - FNakamaParty Party; - - void SetupClient2AndJoinParty(); - void SetupClient2AndRequestJoinParty(); - void SetupClient2AndReceiveRequestJoinParty(); -}; \ No newline at end of file diff --git a/Nakama/Source/NakamaTests/Public/Tests/Realtime/Test_Tournament.h b/Nakama/Source/NakamaTests/Public/Tests/Realtime/Test_Tournament.h deleted file mode 100644 index 190c73a30..000000000 --- a/Nakama/Source/NakamaTests/Public/Tests/Realtime/Test_Tournament.h +++ /dev/null @@ -1,52 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaTestBase.h" - -// The base class for the tests -class FNakamaTournamentTestBase : public FNakamaTestBase -{ -public: - FNakamaTournamentTestBase(const FString& InName, bool bInComplexTask) : FNakamaTestBase(InName, bInComplexTask), Client2(nullptr), Socket2(nullptr) - { - - } - virtual bool SuppressLogWarnings() override { return true; } - - UNakamaClient* Client2; - UNakamaRealtimeClient* Socket2; - UNakamaSession* Session2; - - static uint64 GetCurrentUnixTimestampInSeconds() - { - return FDateTime::Now().ToUnixTimestamp(); - } - - int64 StartTime = GetCurrentUnixTimestampInSeconds(); // starts now in seconds - int32 Duration = 5; // in seconds - FString Operator = "best"; // one of : "best", "set", "incr" - FString ResetSchedule = ""; // none - int64 EndTime = StartTime + 5; // end after 5 sec - int32 MaxSize = 10000; // first 10,000 players who join - int32 MaxNumScore = 3; // each player can have 3 attempts to score - bool JoinRequired = true; // must join to compete - - FString GetTournamentIdFromJsonString(const FString& JsonString) const; - -}; \ No newline at end of file diff --git a/Nakama/Source/NakamaTests/Public/Tests/Test_Friends.h b/Nakama/Source/NakamaTests/Public/Tests/Test_Friends.h deleted file mode 100644 index 6eb52787e..000000000 --- a/Nakama/Source/NakamaTests/Public/Tests/Test_Friends.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaTestBase.h" - -// The base class for the tests -class FNakamaFriendsTestBase : public FNakamaTestBase -{ -public: - FNakamaFriendsTestBase(const FString& InName, bool bInComplexTask) : FNakamaTestBase(InName, bInComplexTask) { } - virtual bool SuppressLogWarnings() override { return true; } - - // Other - UPROPERTY() - TArray FriendIds; - - int FriendsToAdd = 5; - int ListFriendsLimit = 1; - -}; - diff --git a/Nakama/Source/NakamaTests/Public/Tests/Test_Groups.h b/Nakama/Source/NakamaTests/Public/Tests/Test_Groups.h deleted file mode 100644 index 4159aa866..000000000 --- a/Nakama/Source/NakamaTests/Public/Tests/Test_Groups.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaTestBase.h" - -// The base class for the group tests -// Some tests were moved into one -class FNakamaGroupsTestBase : public FNakamaTestBase -{ -public: - FNakamaGroupsTestBase(const FString& InName, bool bInComplexTask) : FNakamaTestBase(InName, bInComplexTask) { } - virtual bool SuppressLogWarnings() override { return true; } - - FString GroupName = TEXT("We're-Nakama-Lovers"); - //FString GroupName = TEXT("Test7"); - - void DoListGroups(); - void CreateGroup(); - void UpdateGroup(const FString& GroupId); - void ListGroupUsers(const FString& GroupId); -}; \ No newline at end of file diff --git a/Nakama/Source/NakamaTests/Public/Tests/Test_Storage.h b/Nakama/Source/NakamaTests/Public/Tests/Test_Storage.h deleted file mode 100644 index fdb50d36b..000000000 --- a/Nakama/Source/NakamaTests/Public/Tests/Test_Storage.h +++ /dev/null @@ -1,32 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaTestBase.h" - -// The base class for the tests -class FNakamaStorageTestBase : public FNakamaTestBase -{ -public: - FNakamaStorageTestBase(const FString& InName, bool bInComplexTask) : FNakamaTestBase(InName, bInComplexTask) { } - virtual bool SuppressLogWarnings() override { return true; } - - - int NumCandies = 25; - -}; \ No newline at end of file diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaAccount.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaAccount.cpp deleted file mode 100644 index 5210a9b90..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaAccount.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaAccount.h" -#include "NakamaUtils.h" - -FNakamaAccount::FNakamaAccount() : VerifyTime(FDateTime::MinValue()), DisableTime(FDateTime::MinValue()) -{ -} - -FNakamaAccount::FNakamaAccount(const FString& JsonString) -{ - TSharedPtr JsonObject; - TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - if (!FJsonSerializer::Deserialize(JsonReader, JsonObject) || !JsonObject.IsValid()) - { - // Failed to deserialize JSON or JSON object is invalid - return; - } - - - const TSharedPtr* UserJsonObject; - if (JsonObject->TryGetObjectField(TEXT("user"), UserJsonObject)) - { - User = FNakamaUser(*UserJsonObject); - } - - JsonObject->TryGetStringField(TEXT("wallet"), Wallet); - JsonObject->TryGetStringField(TEXT("email"), Email); - JsonObject->TryGetStringField(TEXT("custom_id"), CustomId); - - if (JsonObject->HasTypedField(TEXT("devices"))) - { - const TArray>* DevicesJsonArray; - if (JsonObject->TryGetArrayField(TEXT("devices"), DevicesJsonArray)) - { - for (const TSharedPtr& DeviceJson : *DevicesJsonArray) - { - if (TSharedPtr DeviceJsonObject = DeviceJson->AsObject()) - { - FNakamaAccountDevice Device(DeviceJsonObject); - Devices.Add(Device); - } - } - } - } - - FString VerifyTimeString; - if (JsonObject->TryGetStringField(TEXT("verify_time"), VerifyTimeString)) - { - FDateTime::ParseIso8601(*VerifyTimeString, VerifyTime); - } - else - { - VerifyTime = FDateTime(); // Set to default value when the field is not found - } - - FString DisableTimeString; - if (JsonObject->TryGetStringField(TEXT("disable_time"), DisableTimeString)) - { - FDateTime::ParseIso8601(*DisableTimeString, DisableTime); - } - else - { - DisableTime = FDateTime(); // Set to default value when the field is not found - } - -} diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaAccountDevice.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaAccountDevice.cpp deleted file mode 100644 index d868c65c8..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaAccountDevice.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaAccountDevice.h" - -#include "NakamaUtils.h" - -FNakamaAccountDevice::FNakamaAccountDevice(const FString& JsonString) : FNakamaAccountDevice(FNakamaUtils::DeserializeJsonObject(JsonString)) { -} - -FNakamaAccountDevice::FNakamaAccountDevice(const TSharedPtr JsonObject) -{ - if (JsonObject.IsValid()) - { - if (JsonObject->HasField(TEXT("id"))) - { - JsonObject->TryGetStringField(TEXT("id"), Id); - } - - const TSharedPtr* VarsJsonObject = nullptr; - if (JsonObject->TryGetObjectField(TEXT("vars"), VarsJsonObject)) - { - Vars.Empty(); - - for (const auto& Entry : (*VarsJsonObject)->Values) - { - FString Key = Entry.Key; - FString Value = Entry.Value->AsString(); - - Vars.Add(Key, Value); - } - } - } -} - -FNakamaAccountDevice::FNakamaAccountDevice() -{ - -} diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaChannelTypes.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaChannelTypes.cpp deleted file mode 100644 index 63a1cd7f5..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaChannelTypes.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaChannelTypes.h" -#include "NakamaUtils.h" - -FNakamaChannelMessage::FNakamaChannelMessage(const FString& JsonString) : FNakamaChannelMessage(FNakamaUtils::DeserializeJsonObject(JsonString)) { -} - -FNakamaChannelMessage::FNakamaChannelMessage(const TSharedPtr JsonObject) -{ - if (JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("channel_id"), ChannelId); - JsonObject->TryGetStringField(TEXT("message_id"), MessageId); - JsonObject->TryGetNumberField(TEXT("code"), code); - JsonObject->TryGetStringField(TEXT("sender_id"), SenderId); - JsonObject->TryGetStringField(TEXT("username"), Username); - JsonObject->TryGetStringField(TEXT("content"), Content); - - FString CreateTimeString; - if (JsonObject->TryGetStringField(TEXT("create_time"), CreateTimeString)) { - FDateTime::ParseIso8601(*CreateTimeString, CreateTime); - } - - FString UpdateTimeString; - if (JsonObject->TryGetStringField(TEXT("update_time"), UpdateTimeString)) { - FDateTime::ParseIso8601(*UpdateTimeString, UpdateTime); - } - - JsonObject->TryGetBoolField(TEXT("persistent"), Persistent); - JsonObject->TryGetStringField(TEXT("room_name"), RoomName); - JsonObject->TryGetStringField(TEXT("group_id"), GroupId); - JsonObject->TryGetStringField(TEXT("user_id_one"), UserIdOne); - JsonObject->TryGetStringField(TEXT("user_id_two"), UserIdTwo); - } -} - -FNakamaChannelMessage::FNakamaChannelMessage() - : CreateTime(FDateTime::MinValue()), UpdateTime(FDateTime::MinValue()), code(0), Persistent(false) -{ - -} - -FNakamaChannelMessageAck::FNakamaChannelMessageAck(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - const TSharedPtr* ChannelMessageObject; - if (JsonObject->TryGetObjectField(TEXT("channel_message_ack"), ChannelMessageObject)) { - - (*ChannelMessageObject)->TryGetStringField(TEXT("channel_id"), ChannelId); - (*ChannelMessageObject)->TryGetStringField(TEXT("message_id"), MessageId); - (*ChannelMessageObject)->TryGetStringField(TEXT("username"), Username); - (*ChannelMessageObject)->TryGetNumberField(TEXT("code"), code); - - FString CreateTimeString; - if ((*ChannelMessageObject)->TryGetStringField(TEXT("create_time"), CreateTimeString)) { - FDateTime::ParseIso8601(*CreateTimeString, CreateTime); - } - - FString UpdateTimeString; - if ((*ChannelMessageObject)->TryGetStringField(TEXT("update_time"), UpdateTimeString)) { - FDateTime::ParseIso8601(*UpdateTimeString, UpdateTime); - } - - (*ChannelMessageObject)->TryGetBoolField(TEXT("persistent"), Persistent); - (*ChannelMessageObject)->TryGetStringField(TEXT("room_name"), RoomName); - } - } - -} - -FNakamaChannelMessageAck::FNakamaChannelMessageAck() - : CreateTime(FDateTime::MinValue()), UpdateTime(FDateTime::MinValue()), code(0), Persistent(false) -{ -} - -FNakamaChannelMessageList::FNakamaChannelMessageList(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - const TArray>* MessagesJsonArray; - if (JsonObject->TryGetArrayField(TEXT("messages"), MessagesJsonArray)) - { - for (const TSharedPtr& MessageJsonValue : *MessagesJsonArray) - { - if (TSharedPtr MessageJsonObject = MessageJsonValue->AsObject()) - { - FNakamaChannelMessage Message(MessageJsonObject); - Messages.Add(Message); - } - } - } - - JsonObject->TryGetStringField(TEXT("next_cursor"), NextCursor); - JsonObject->TryGetStringField(TEXT("prev_cursor"), PrevCursor); - } -} - - -FNakamaChannelMessageList::FNakamaChannelMessageList() -{ -} - - -FNakamaChannelPresenceEvent::FNakamaChannelPresenceEvent(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("channel_id"), ChannelId); - - const TArray>* JoinsJsonArray; - if (JsonObject->TryGetArrayField(TEXT("joins"), JoinsJsonArray)) - { - for (const TSharedPtr& JoinJsonValue : *JoinsJsonArray) - { - if (TSharedPtr JoinJsonObject = JoinJsonValue->AsObject()) - { - FNakamaUserPresence JoinPresence(JoinJsonObject); - Joins.Add(JoinPresence); - } - } - } - - const TArray>* LeavesJsonArray; - if (JsonObject->TryGetArrayField(TEXT("leaves"), LeavesJsonArray)) - { - for (const TSharedPtr& LeaveJsonValue : *LeavesJsonArray) - { - if (TSharedPtr LeaveJsonObject = LeaveJsonValue->AsObject()) - { - FNakamaUserPresence LeavePresence(LeaveJsonObject); - Leaves.Add(LeavePresence); - } - } - } - - JsonObject->TryGetStringField(TEXT("room_name"), RoomName); - JsonObject->TryGetStringField(TEXT("group_id"), GroupId); - JsonObject->TryGetStringField(TEXT("user_id_one"), UserIdOne); - JsonObject->TryGetStringField(TEXT("user_id_two"), UserIdTwo); - } -} - -FNakamaChannelPresenceEvent::FNakamaChannelPresenceEvent() -{ - -} diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaChat.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaChat.cpp deleted file mode 100644 index 91918de0a..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaChat.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaChat.h" -#include "Dom/JsonObject.h" -#include "Serialization/JsonSerializer.h" - -FNakamaChannel::FNakamaChannel() -{ - -} - -FNakamaChannel::FNakamaChannel(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - const TSharedPtr* ChannelObjectPtr; - if (JsonObject->TryGetObjectField(TEXT("channel"), ChannelObjectPtr)) - { - TSharedPtr ChannelObject = *ChannelObjectPtr; - - ChannelObject->TryGetStringField(TEXT("id"), Id); - ChannelObject->TryGetStringField(TEXT("room_name"), RoomName); - ChannelObject->TryGetStringField(TEXT("group_id"), GroupId); - ChannelObject->TryGetStringField(TEXT("user_id_one"), UserIdOne); - ChannelObject->TryGetStringField(TEXT("user_id_two"), UserIdTwo); - - const TArray>* PresencesJsonArray; - if (ChannelObject->TryGetArrayField(TEXT("presences"), PresencesJsonArray)) - { - for (const TSharedPtr& PresenceJsonValue : *PresencesJsonArray) - { - if (TSharedPtr PresenceJsonObject = PresenceJsonValue->AsObject()) - { - FNakamaUserPresence Presence(PresenceJsonObject); - Presences.Add(Presence); - } - } - } - - const TSharedPtr* SelfObjectPtr; - if (ChannelObject->TryGetObjectField(TEXT("self"), SelfObjectPtr)) - { - Me = FNakamaUserPresence(*SelfObjectPtr); - } - } - } -} \ No newline at end of file diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaClient.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaClient.cpp deleted file mode 100644 index fe831f84a..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaClient.cpp +++ /dev/null @@ -1,9298 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaClient.h" -#include "NakamaUtils.h" -#include "NakamaRealtimeClient.h" -#include "NakamaSession.h" -#include "NakamaLogger.h" -#include "GenericPlatform/GenericPlatformHttp.h" -#include "Interfaces/IHttpResponse.h" -#include "Misc/Optional.h" - -void UNakamaClient::InitializeClient(const FString& InHostname, int32 InPort, const FString& InServerKey, - bool bInUseSSL) -{ - Hostname = InHostname; - Port = InPort; - ServerKey = InServerKey; - bUseSSL = bInUseSSL; -} - -void UNakamaClient::InitializeSystem(const FString& InServerKey, const FString& Host, int32 InPort, bool UseSSL, - bool EnableDebug) -{ - if (EnableDebug) - { - bEnableDebug = true; - } - - InitializeClient(Host, InPort, InServerKey, UseSSL); - bIsActive = true; -} - -void UNakamaClient::Disconnect() -{ - if(IsValidLowLevel()) - { - CancelAllRequests(); - } -} - -void UNakamaClient::Destroy() -{ - bIsActive = false; - ConditionalBeginDestroy(); -} - -void UNakamaClient::SetTimeout(float InTimeout) -{ - Timeout = InTimeout; -} - -float UNakamaClient::GetTimeout() -{ - return Timeout; -} - -void UNakamaClient::BeginDestroy() -{ - UObject::BeginDestroy(); - bIsActive = false; -} - -UNakamaClient* UNakamaClient::CreateDefaultClient( - const FString& ServerKey, - const FString& Host, - int32 Port, - bool UseSSL, - bool EnableDebug) -{ - UNakamaClient* NewClient = NewObject((UObject*)GetTransientPackage(), UNakamaClient::StaticClass()); - NewClient->InitializeSystem(ServerKey, Host, Port, UseSSL, EnableDebug); - - if (EnableDebug) - { - UNakamaLogger::EnableLogging(true); - UNakamaLogger::SetLogLevel(ENakamaLogLevel::Debug); - } - - return NewClient; -} - -/** - * Authentication - */ - -void UNakamaClient::AuthenticateCustom(const FString& UserID, - const FString& Username, - bool CreateAccount, - const TMap& Vars, - FOnAuthUpdate Success, - FOnError Error) -{ - auto successCallback = [this, Success](UNakamaSession* session) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(session); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - // A custom identifier must contain alphanumeric - // characters with dashesand be between 6 and 128 bytes. - - AuthenticateCustom(UserID, Username, CreateAccount, Vars, successCallback, errorCallback); -} - -void UNakamaClient::AuthenticateEmail( - const FString& Email, - const FString& Password, - const FString& Username, - bool CreateAccount, - const TMap& Vars, - FOnAuthUpdate Success, - FOnError Error) -{ - auto successCallback = [this, Success](UNakamaSession* session) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(session); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - // An email address must be valid as defined by RFC-5322 and passwords must be at least 8 characters. - - AuthenticateEmail(Email, Password, Username, CreateAccount, Vars, successCallback, errorCallback); -} - -void UNakamaClient::AuthenticateDevice( - const FString& DeviceID, - const FString& Username, - bool CreateAccount, - const TMap& Vars, - const FOnAuthUpdate& Success, - const FOnError& Error) -{ - auto successCallback = [this, Success](UNakamaSession* session) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(session); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - // A device identifier must contain alphanumeric characters with dashes and be between 10 and 128 bytes. - - const auto OptUsername = FNakamaUtils::CreateOptional(Username, FString()); - - AuthenticateDevice(DeviceID, CreateAccount, OptUsername, Vars, successCallback, errorCallback); -} - -void UNakamaClient::AuthenticateSteam( - const FString& SteamToken, - const FString& Username, - bool CreateAccount, - bool ImportFriends, - const TMap& Vars, - FOnAuthUpdate Success, - FOnError Error) -{ - auto successCallback = [this, Success](UNakamaSession* session) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(session); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - AuthenticateSteam(SteamToken, Username, CreateAccount, ImportFriends, Vars, successCallback, errorCallback); -} - -void UNakamaClient::AuthenticateGoogle( - const FString& AccessToken, - const FString& Username, - bool CreateAccount, - const TMap& Vars, - FOnAuthUpdate Success, - FOnError Error) -{ - - auto successCallback = [this, Success](UNakamaSession* session) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(session); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - AuthenticateGoogle(AccessToken, Username, CreateAccount, Vars, successCallback, errorCallback); -} - -void UNakamaClient::AuthenticateGameCenter( - const FString& PlayerId, - const FString& BundleId, - int64 TimeStampSeconds, - const FString& Salt, - const FString& Signature, - const FString& PublicKeyUrl, - const FString& Username, - bool CreateAccount, - const TMap& Vars, - FOnAuthUpdate Success, - FOnError Error) -{ - auto successCallback = [this, Success](UNakamaSession* session) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(session); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - AuthenticateGameCenter( - PlayerId, - BundleId, - TimeStampSeconds, - Salt, - Signature, - PublicKeyUrl, - Username, - CreateAccount, - Vars, - successCallback, - errorCallback - ); -} - -void UNakamaClient::AuthenticateFacebook( - const FString& AccessToken, - const FString& Username, - bool CreateAccount, - bool ImportFriends, - const TMap& Vars, - FOnAuthUpdate Success, - FOnError Error) -{ - auto successCallback = [this, Success](UNakamaSession* session) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(session); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - AuthenticateFacebook(AccessToken, Username, CreateAccount, ImportFriends, Vars, successCallback, errorCallback); -} - -void UNakamaClient::AuthenticateApple( - const FString& Token, - const FString& Username, - bool CreateAccount, - const TMap& Vars, - FOnAuthUpdate Success, - FOnError Error) -{ - auto successCallback = [this, Success](UNakamaSession* session) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(session); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - AuthenticateApple(Token, Username, CreateAccount, Vars, successCallback, errorCallback); -} - -void UNakamaClient::AuthenticateRefresh( - UNakamaSession* Session, - FOnAuthUpdate Success, - FOnError Error) -{ - auto successCallback = [this, Success](UNakamaSession* session) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(session); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - AuthenticateRefresh(Session, successCallback, errorCallback); -} - -/** - * Sessions - */ - -void UNakamaClient::RestoreSession( - const FString& Token, - const FString& RefreshToken, - UNakamaSession*& RestoredSession) -{ - UNakamaSession* Session = UNakamaSession::RestoreSession(Token, RefreshToken); - RestoredSession = Session; -} - -/** - * Linking Accounts - */ - -void UNakamaClient::LinkCustom( - UNakamaSession *Session, - const FString& CustomId, - FOnLinkSuccess Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - LinkCustom(Session, CustomId, successCallback, errorCallback); -} - -void UNakamaClient::LinkDevice( - UNakamaSession *Session, - const FString& DeviceId, - FOnLinkSuccess Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - LinkDevice(Session, DeviceId, successCallback, errorCallback); -} - -void UNakamaClient::LinkEmail( - UNakamaSession *Session, - const FString& Email, - const FString& Password, - FOnLinkSuccess Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - LinkEmail(Session, Email, Password, successCallback, errorCallback); -} - -void UNakamaClient::LinkFacebook( - UNakamaSession *Session, - const FString& AccessToken, - bool ImportFriends, - FOnLinkSuccess Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - LinkFacebook(Session, AccessToken, ImportFriends, successCallback, errorCallback); -} - -void UNakamaClient::LinkGameCenter( - UNakamaSession *Session, - const FString& PlayerId, - const FString& BundleId, - int64 TimeStampSeconds, - const FString& Salt, - const FString& Signature, - const FString& PublicKeyUrl, - FOnLinkSuccess Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - LinkGameCenter( - Session, - PlayerId, - BundleId, - TimeStampSeconds, - Salt, - Signature, - PublicKeyUrl, - successCallback, - errorCallback - ); -} - -void UNakamaClient::LinkGoogle( - UNakamaSession *Session, - const FString& AccessToken, - FOnLinkSuccess Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - LinkGoogle(Session, AccessToken, successCallback, errorCallback); -} - -void UNakamaClient::LinkSteam( - UNakamaSession *Session, - const FString& SteamToken, - FOnLinkSuccess Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - LinkSteam(Session, SteamToken, successCallback, errorCallback); -} - -void UNakamaClient::LinkApple( - UNakamaSession *Session, - const FString& Token, - FOnLinkSuccess Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - LinkApple(Session, Token, successCallback, errorCallback); -} - -/** - * Unlinking Account - */ - -void UNakamaClient::UnLinkCustom( - UNakamaSession *Session, - const FString& CustomId, - FOnLinkSuccess Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - UnLinkCustom(Session, CustomId, successCallback, errorCallback); -} - -void UNakamaClient::UnLinkDevice( - UNakamaSession *Session, - const FString& DeviceId, - FOnLinkSuccess Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - UnLinkDevice(Session, DeviceId, successCallback, errorCallback); -} - -void UNakamaClient::UnLinkEmail( - UNakamaSession *Session, - const FString& Email, - const FString& Password, - FOnLinkSuccess Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - UnLinkEmail(Session, Email, Password, successCallback, errorCallback); -} - -void UNakamaClient::UnLinkFacebook( - UNakamaSession *Session, - const FString& AccessToken, - FOnLinkSuccess Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - UnLinkFacebook(Session, AccessToken, successCallback, errorCallback); -} - -void UNakamaClient::UnLinkGameCenter( - UNakamaSession *Session, - const FString& PlayerId, - const FString& BundleId, - int64 TimeStampSeconds, - const FString& Salt, - const FString& Signature, - const FString& PublicKeyUrl, - FOnLinkSuccess Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - UnLinkGameCenter( - Session, - PlayerId, - BundleId, - TimeStampSeconds, - Salt, - Signature, - PublicKeyUrl, - successCallback, - errorCallback - ); -} - -void UNakamaClient::UnLinkGoogle( - UNakamaSession *Session, - const FString& AccessToken, - FOnLinkSuccess Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - UnLinkGoogle(Session, AccessToken, successCallback, errorCallback); -} - -void UNakamaClient::UnLinkSteam( - UNakamaSession *Session, - const FString& SteamToken, - FOnLinkSuccess Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - UnLinkSteam(Session, SteamToken, successCallback, errorCallback); -} - -void UNakamaClient::UnLinkApple( - UNakamaSession *Session, - const FString& Token, - FOnLinkSuccess Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - UnLinkApple(Session, Token, successCallback, errorCallback); -} - -/** - * Refresh Session - */ - -void UNakamaClient::RefreshSession( - UNakamaSession *Session, - FOnAuthRefresh Success, - FOnAuthRefreshError Error) -{ - auto successCallback = [this, Success](UNakamaSession* Session) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(Session); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - AuthenticateRefresh(Session, successCallback, errorCallback); -} - -/** - * Import Facebook Friends - */ - -void UNakamaClient::ImportFacebookFriends( - UNakamaSession* Session, - const FString& Token, - bool Reset, - FOnImportFacebookFriends Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - ImportFacebookFriends(Session, Token, Reset, successCallback, errorCallback); -} - -/** - * Import Steam Friends - */ - -void UNakamaClient::ImportSteamFriends( - UNakamaSession* Session, - const FString& SteamToken, - bool Reset, - FOnImportSteamFriends Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - ImportSteamFriends(Session, SteamToken, Reset, successCallback, errorCallback); -} - -/** - * Get Account Info - */ - -void UNakamaClient::GetUserAccount( - UNakamaSession *Session, - FOnUserAccountInfo Success, - FOnError Error) -{ - GetAccount(Session, Success, Error); -} - -void UNakamaClient::GetAccount( - UNakamaSession *Session, - FOnUserAccountInfo Success, - FOnError Error) -{ - auto successCallback = [this, Success](const FNakamaAccount& account) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(account); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - GetAccount(Session, successCallback, errorCallback); -} - -/** - * Get Users - */ - -void UNakamaClient::GetUsers( - UNakamaSession *Session, - const TArray& UserIds, - const TArray& Usernames, - const TArray& FacebookIds, - FOnGetUsers Success, - FOnError Error) -{ - auto successCallback = [this, Success](const FNakamaUserList& UserList) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(UserList.Users); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - GetUsers(Session, UserIds, Usernames, FacebookIds, successCallback, errorCallback); -} - -void UNakamaClient::UpdateAccount( - UNakamaSession *Session, - const FString& Username, - const FString& DisplayName, - const FString& AvatarUrl, - const FString& LanguageTag, - const FString& Location, - const FString& Timezone, - FOnUpdateAccount Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - UpdateAccount( - Session, - Username, - DisplayName, - AvatarUrl, - LanguageTag, - Location, - Timezone, - successCallback, - errorCallback - ); -} - -void UNakamaClient::DeleteUser(UNakamaSession* Session, FOnDeleteUser Success, FOnError Error) -{ - auto successCallback = [this, Success]() - { - if (!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if (!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - DeleteUser(Session, successCallback, errorCallback); -} - -/** - * Setup Realtime Client (rtClient = Socket) - */ - -UNakamaRealtimeClient* UNakamaClient::SetupRealtimeClient() -{ - UNakamaRealtimeClient* NewClient = NewObject(); - NewClient->Initialize(Hostname, Port, bUseSSL); - NewClient->bIsActive = true; - - return NewClient; -} - -void UNakamaClient::ListMatches( - UNakamaSession *Session, - int32 MinSize, - int32 MaxSize, - int32 Limit, - const FString& Label, - const FString& Query, - bool Authoritative, - FOnMatchlist Success, - FOnError Error) -{ - auto successCallback = [this, Success](const FNakamaMatchList& MatchList) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(MatchList); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - const auto OptLabel = FNakamaUtils::CreateOptional(Label, FString()); - const auto OptQuery = FNakamaUtils::CreateOptional(Query, FString()); - const auto OptMinSize = FNakamaUtils::CreateOptional(MinSize, 0); - const auto OptMaxSize = FNakamaUtils::CreateOptional(MaxSize, 0); - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - //const auto OptAuthoritative = FNakamaUtils::CreateOptional(Authoritative, false); - - ListMatches( - Session, - OptMinSize, - OptMaxSize, - OptLimit, - OptLabel, - OptQuery, - Authoritative, - successCallback, - errorCallback - ); -} - - -/** - * Friends System - */ - -void UNakamaClient::GetFriends( - UNakamaSession* Session, - int32 Limit, - ENakamaFriendState State, - const FString& Cursor, - FOnFriendsList Success, - FOnError Error) -{ - ListFriends(Session, Limit, State, Cursor, Success, Error); - -} - -void UNakamaClient::ListFriends( - UNakamaSession* Session, - int32 Limit, - ENakamaFriendState State, - const FString& Cursor, - FOnFriendsList Success, - FOnError Error) -{ - auto successCallback = [this, Success](const FNakamaFriendList& Friends) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(Friends); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - - // If "All" Friend States are requested, we return an empty Enum Object - if(State == ENakamaFriendState::ALL) - { - ListFriends(Session, OptLimit, {}, Cursor, successCallback, errorCallback); - } - else - { - ListFriends(Session, OptLimit, State, Cursor, successCallback, errorCallback); - } -} - -void UNakamaClient::AddFriends( - UNakamaSession* Session, - const TArray& Ids, - const TArray& Usernames, - FOnAddedFriend Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - AddFriends(Session, Ids, Usernames, successCallback, errorCallback); -} - -void UNakamaClient::RemoveFriends( - UNakamaSession* Session, - const TArray& Ids, - const TArray& Usernames, - FOnRemovedFriends Success, - FOnError Error) -{ - DeleteFriends(Session, Ids, Usernames, Success, Error); -} - -void UNakamaClient::DeleteFriends( - UNakamaSession* Session, - const TArray& Ids, - const TArray& Usernames, - FOnRemovedFriends Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - DeleteFriends(Session, Ids, Usernames, successCallback, errorCallback); -} - -void UNakamaClient::BlockFriends( - UNakamaSession* Session, - const TArray& Ids, - const TArray& Usernames, - FOnBlockedFriends Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - BlockFriends(Session, Ids, Usernames, successCallback, errorCallback); -} - -/** - * Group System - */ - - -void UNakamaClient::CreateGroup( - UNakamaSession* Session, - const FString& GroupName, - const FString& Description, - const FString& AvatarUrl, - const FString& LanguageTag, - bool Open, - int32 MaxMembers, - FOnCreateGroup Success, - FOnError Error) -{ - auto successCallback = [this, Success](const FNakamaGroup& Group) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(Group); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - const auto OptMaxCount = FNakamaUtils::CreateOptional(MaxMembers, 0); - - CreateGroup( - Session, - GroupName, - Description, - AvatarUrl, - LanguageTag, - Open, - OptMaxCount, - successCallback, - errorCallback - ); -} - -void UNakamaClient::ListGroups( - UNakamaSession* Session, - const FString& GroupNameFilter, - int32 Limit, - const FString& Cursor, - FOnGroupsList Success, - FOnError Error) -{ - auto successCallback = [this, Success](const FNakamaGroupList& Groups) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(Groups); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - ListGroups(Session, GroupNameFilter, Limit, Cursor, successCallback, errorCallback); -} - -void UNakamaClient::JoinGroup( - UNakamaSession* Session, - const FString& GroupId, - FOnJoinedGroup Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - JoinGroup(Session, GroupId, successCallback, errorCallback); -} - -// Note: Does not get members! -void UNakamaClient::ListUserGroups( - UNakamaSession* Session, - const FString& UserId, - int32 Limit, - ENakamaGroupState State, - const FString& Cursor, - FOnUserGroups Success, - FOnError Error) -{ - auto successCallback = [this, Success](const FNakamaUserGroupList& UserGroupList) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(UserGroupList); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - - // If "All" States are requested, we return an empty Enum Object - if(State == ENakamaGroupState::ALL) - { - ListUserGroups(Session, OptLimit, {}, Cursor, successCallback, errorCallback); - } - else - { - ListUserGroups(Session, OptLimit, State, Cursor, successCallback, errorCallback); - } -} - -void UNakamaClient::ListGroupUsers( - UNakamaSession* Session, - const FString& GroupId, - int32 Limit, - ENakamaGroupState State, - const FString& Cursor, - FOnListGroupMembers Success, - FOnError Error) -{ - auto successCallback = [this, Success](const FNakamaGroupUsersList& GroupUsersList) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(GroupUsersList); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - - // If "All" States are requested, we return an empty Enum Object - if(State == ENakamaGroupState::ALL) - { - ListGroupUsers(Session, GroupId, OptLimit, {}, Cursor, successCallback, errorCallback); - } - else - { - ListGroupUsers(Session, GroupId, OptLimit, State, Cursor, successCallback, errorCallback); - } - -} - -void UNakamaClient::UpdateGroup( - UNakamaSession* Session, - const FString& GroupId, - const FString& Name, - const FString& Description, - const FString& AvatarUrl, - const FString& LanguageTag, - bool Open, - FOnUpdateGroup Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - const auto OptName = FNakamaUtils::CreateOptional(Name, FString()); - const auto OptDescription = FNakamaUtils::CreateOptional(Description, FString()); - const auto OptAvatarUrl = FNakamaUtils::CreateOptional(AvatarUrl, FString()); - const auto OptLanguageTag = FNakamaUtils::CreateOptional(LanguageTag, FString()); - - UpdateGroup( - Session, - GroupId, - OptName, - OptDescription, - OptAvatarUrl, - OptLanguageTag, - Open, - successCallback, - errorCallback - ); -} - -void UNakamaClient::LeaveGroup( - UNakamaSession* Session, - const FString& GroupId, - FOnLeaveGroup Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - LeaveGroup(Session, GroupId, successCallback, errorCallback); -} - -void UNakamaClient::AddGroupUsers( - UNakamaSession* Session, - const FString& GroupId, - const TArray& UserIds, - FOnAddGroupUsers Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - AddGroupUsers(Session, GroupId, UserIds, successCallback, errorCallback); -} - -void UNakamaClient::PromoteGroupUsers( - UNakamaSession* Session, - const FString& GroupId, - const TArray& UserIds, - FOnPromoteGroupUsers Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - PromoteGroupUsers(Session, GroupId, UserIds, successCallback, errorCallback); -} - -void UNakamaClient::KickGroupUsers( - UNakamaSession* Session, - const FString& GroupId, - const TArray& UserIds, - FOnKickGroupUsers Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - KickGroupUsers(Session, GroupId, UserIds, successCallback, errorCallback); -} - -void UNakamaClient::BanGroupUsers( - UNakamaSession* Session, - const FString& GroupId, - const TArray& UserIds, - FOnBanGroupUsers Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - BanGroupUsers(Session, GroupId, UserIds, successCallback, errorCallback); -} - -void UNakamaClient::DemoteGroupUsers( - UNakamaSession* Session, - const FString& GroupId, - const TArray& UserIds, - FOnDemoteGroupUsers Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - DemoteGroupUsers(Session, GroupId, UserIds, successCallback, errorCallback); -} - -void UNakamaClient::DeleteGroup( - UNakamaSession* Session, - const FString& GroupId, - FOnRemoveGroup Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - DeleteGroup(Session, GroupId, successCallback, errorCallback); -} - -/** - * Notification System - */ - -void UNakamaClient::ListNotifications( - UNakamaSession* Session, - int32 Limit, - const FString& Cursor, - FOnListNotifications Success, - FOnError Error) -{ - auto successCallback = [this, Success](const FNakamaNotificationList& NotificationList) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(NotificationList); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - const auto OptCacheableCursor = FNakamaUtils::CreateOptional(Cursor, FString()); - - ListNotifications(Session, OptLimit, OptCacheableCursor, successCallback, errorCallback); -} - -void UNakamaClient::DeleteNotifications( - UNakamaSession* Session, - const TArray& NotificationIds, - FOnDeleteNotifications Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - DeleteNotifications(Session, NotificationIds, successCallback, errorCallback); -} - -/** - * Storage System - */ - -void UNakamaClient::WriteStorageObjects( - UNakamaSession* Session, - const TArray& StorageObjectsData, - FOnStorageObjectAcks Success, - FOnError Error) -{ - auto successCallback = [this, Success](const FNakamaStoreObjectAcks& StorageObjectAcks) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(StorageObjectAcks); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - WriteStorageObjects(Session, StorageObjectsData, successCallback, errorCallback); -} - -void UNakamaClient::ReadStorageObjects( - UNakamaSession* Session, - const TArray& StorageObjectsData, - FOnStorageObjectsRead Success, - FOnError Error) -{ - auto successCallback = [this, Success](const FNakamaStorageObjectList& StorageObjectList) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(StorageObjectList); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - ReadStorageObjects(Session, StorageObjectsData, successCallback, errorCallback); -} - -void UNakamaClient::ListStorageObjects( - UNakamaSession* Session, - const FString& Collection, - const FString& UserId, - int32 Limit, - const FString& Cursor, - FOnStorageObjectsListed Success, - FOnError Error) -{ - auto successCallback = [this, Success](const FNakamaStorageObjectList& StorageObjectList) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(StorageObjectList); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - const auto OptCursor = FNakamaUtils::CreateOptional(Cursor, FString()); - - if(UserId.IsEmpty()) - { - ListStorageObjects(Session, Collection, OptLimit, OptCursor, successCallback, errorCallback); - } - else - { - ListUsersStorageObjects(Session, Collection, UserId, OptLimit, OptCursor, successCallback, errorCallback); - } -} - -void UNakamaClient::RemoveStorageObjects( - UNakamaSession* Session, - const TArray& StorageObjectsData, - FOnRemovedStorageObjects Success, - FOnError Error) -{ - DeleteStorageObjects(Session, StorageObjectsData, Success, Error); -} - -void UNakamaClient::DeleteStorageObjects( - UNakamaSession* Session, - const TArray& StorageObjectsData, - FOnRemovedStorageObjects Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - DeleteStorageObjects(Session, StorageObjectsData, successCallback, errorCallback); -} - - -void UNakamaClient::ListParties ( - UNakamaSession* Session, - int32 Limit, - bool Open, - const FString& Query, - const FString& Cursor, - FOnListedParties Success, - FOnError Error -) -{ - auto successCallback = [this, Success](const FNakamaPartyList& PartyList) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(PartyList); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - const auto OptQuery = FNakamaUtils::CreateOptional(Query, FString()); - const auto OptCursor = FNakamaUtils::CreateOptional(Cursor, FString()); - - ListParties(Session, OptLimit, Open, OptQuery, OptCursor, successCallback, errorCallback); -} - -/** - * RPC - */ - -bool UNakamaClient::RPC( - UNakamaSession* Session, - const FString& FunctionId, - const FString& Payload, - FOnRPC Success, - FOnError Error) -{ - auto successCallback = [this, Success](FNakamaRPC&& Rpc) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(MoveTemp(Rpc)); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - return RPCm(Session, FunctionId, TOptional(Payload), successCallback, errorCallback); -} - -/** - * RPCHttpKey - */ - -bool UNakamaClient::RPCHttpKey( - const FString& HttpKey, - const FString& FunctionId, - const FString& Payload, - FOnRPC Success, - FOnError Error) -{ - auto successCallback = [this, Success](const FNakamaRPC& Rpc) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Success.Broadcast(Rpc); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - return RPC(HttpKey, FunctionId, Payload, successCallback, errorCallback); -} - -/** - * List Channel Messages - */ - -void UNakamaClient::ListChannelMessages( - UNakamaSession* Session, - const FString& ChannelId, - int32 Limit, - const FString& Cursor, - bool Forward, - FOnListChannelMessages Success, - FOnError Error) -{ - auto successCallback = [this, Success](const FNakamaChannelMessageList& ChannelMessageList) - { - if(!FNakamaUtils::IsClientActive(this)) - return; - - Success.Broadcast(ChannelMessageList); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - return; - - Error.Broadcast(error); - }; - - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - const auto OptCursor = FNakamaUtils::CreateOptional(Cursor, FString()); - - ListChannelMessages(Session, ChannelId, OptLimit, OptCursor, Forward, successCallback, errorCallback); -} - -/** - * Leaderboards System - */ - -void UNakamaClient::WriteLeaderboardRecord( - UNakamaSession* Session, - const FString& LeaderboardId, - int64 Score, - int64 SubScore, - const FString& Metadata, - FOnWriteLeaderboardRecord Success, - FOnError Error) -{ - auto successCallback = [this, Success](const FNakamaLeaderboardRecord& LeaderboardRecord) - { - if(!FNakamaUtils::IsClientActive(this)) - return; - - Success.Broadcast(LeaderboardRecord); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - return; - - Error.Broadcast(error); - }; - - const auto OptSubScore = FNakamaUtils::CreateOptional(SubScore, 0); - const auto OptMetadata = FNakamaUtils::CreateOptional(Metadata, FString()); - - WriteLeaderboardRecord( - Session, - LeaderboardId, - Score, - OptSubScore, - OptMetadata, - successCallback, - errorCallback - ); -} - -void UNakamaClient::ListLeaderboardRecords( - UNakamaSession* Session, - const FString& LeaderboardId, - const TArray& OwnerIds, - int32 Limit, - const FString& Cursor, - ENakamaLeaderboardListBy ListBy, - FOnListLeaderboardRecords Success, - FOnError Error) -{ - auto successCallback = [this, Success](const FNakamaLeaderboardRecordList& LeaderboardRecords) - { - if(!FNakamaUtils::IsClientActive(this)) - return; - - Success.Broadcast(LeaderboardRecords); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - return; - - Error.Broadcast(error); - }; - - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - const auto OptCursor = FNakamaUtils::CreateOptional(Cursor, FString()); - - if (ListBy == ENakamaLeaderboardListBy::BY_SCORE) - { - ListLeaderboardRecords( - Session, - LeaderboardId, - {}, // None because of listing by score - OptLimit, - OptCursor, - successCallback, - errorCallback - ); - } - else if (ListBy == ENakamaLeaderboardListBy::BY_FRIENDS) - { - ListLeaderboardRecords( - Session, - LeaderboardId, - OwnerIds, // OwnerIds, Can be empty - OptLimit, - OptCursor, - successCallback, - errorCallback - ); - } -} - -void UNakamaClient::ListLeaderboardRecordsAroundOwner( - UNakamaSession* Session, - const FString& LeaderboardId, - const FString& OwnerId, - int32 Limit, - FOnListLeaderboardRecords Success, - FOnError Error) -{ - auto successCallback = [this, Success](const FNakamaLeaderboardRecordList& LeaderboardRecords) - { - if(!FNakamaUtils::IsClientActive(this)) - return; - - Success.Broadcast(LeaderboardRecords); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - return; - - Error.Broadcast(error); - }; - - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - - ListLeaderboardRecordsAroundOwner(Session, LeaderboardId, OwnerId, OptLimit, successCallback, errorCallback); -} - -void UNakamaClient::DeleteLeaderboardRecord( - UNakamaSession* Session, - const FString& LeaderboardId, - FOnDeletedLeaderboardRecord Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - return; - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - return; - - Error.Broadcast(error); - }; - - DeleteLeaderboardRecord(Session, LeaderboardId, successCallback, errorCallback); -} - -/** - * Tournaments System - */ - -void UNakamaClient::WriteTournamentRecord( - UNakamaSession* Session, - const FString& TournamentId, - int64 Score, - int64 SubScore, - const FString& Metadata, - FOnWriteLeaderboardRecord Success, - FOnError Error) -{ - auto successCallback = [this, Success](const FNakamaLeaderboardRecord& LeaderboardRecord) - { - if(!FNakamaUtils::IsClientActive(this)) - return; - - Success.Broadcast(LeaderboardRecord); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - return; - - Error.Broadcast(error); - }; - - const auto OptSubScore = FNakamaUtils::CreateOptional(SubScore, 0); - const auto OptMetadata = FNakamaUtils::CreateOptional(Metadata, FString()); - - WriteTournamentRecord(Session, TournamentId, Score, OptSubScore, OptMetadata, successCallback, errorCallback); -} - -void UNakamaClient::ListTournamentRecords( - UNakamaSession* Session, - const FString& TournamentId, - int32 Limit, - const FString& Cursor, - const TArray& OwnerIds, - ENakamaLeaderboardListBy ListBy, - FOnListTournamentRecords Success, - FOnError Error) -{ - auto successCallback = [this, Success](const FNakamaTournamentRecordList& TournamentRecordList) - { - if(!FNakamaUtils::IsClientActive(this)) - return; - - Success.Broadcast(TournamentRecordList); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - return; - - Error.Broadcast(error); - }; - - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - const auto OptCursor = FNakamaUtils::CreateOptional(Cursor, FString()); - - ListTournamentRecords( - Session, - TournamentId, - OptLimit, - OptCursor, - OwnerIds, - successCallback, - errorCallback - ); -} - -void UNakamaClient::ListTournamentRecordsAroundOwner( - UNakamaSession* Session, - const FString& TournamentId, - const FString& OwnerId, - int32 Limit, - FOnListTournamentRecords Success, - FOnError Error) -{ - auto successCallback = [this, Success](const FNakamaTournamentRecordList& TournamentRecordList) - { - if(!FNakamaUtils::IsClientActive(this)) - return; - - Success.Broadcast(TournamentRecordList); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - return; - - Error.Broadcast(error); - }; - - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - - ListTournamentRecordsAroundOwner( - Session, - TournamentId, - OwnerId, - OptLimit, - successCallback, - errorCallback - ); -} - -void UNakamaClient::JoinTournament( - UNakamaSession* Session, - const FString& TournamentId, - FOnJoinedTournament Success, - FOnError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsClientActive(this)) - return; - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - return; - - Error.Broadcast(error); - }; - - JoinTournament(Session, TournamentId, successCallback, errorCallback); -} - -void UNakamaClient::ListTournaments( - UNakamaSession* Session, - int32 CategoryStart, - int32 CategoryEnd, - int32 StartTime, - int32 EndTime, - int32 Limit, - const FString& Cursor, - FOnListTournaments Success, - FOnError Error) -{ - auto successCallback = [this, Success](const FNakamaTournamentList& TournamentList) - { - if(!FNakamaUtils::IsClientActive(this)) - return; - - Success.Broadcast(TournamentList); - }; - - auto errorCallback = [this, Error](const FNakamaError& error) - { - if(!FNakamaUtils::IsClientActive(this)) - return; - - Error.Broadcast(error); - }; - - const auto OptCategoryStart = FNakamaUtils::CreateOptional(CategoryStart, 0); - const auto OptCategoryEnd = FNakamaUtils::CreateOptional(CategoryEnd, 0); - const auto OptStartTime = FNakamaUtils::CreateOptional(StartTime, 0); - const auto OptEndTime = FNakamaUtils::CreateOptional(EndTime, 0); - const auto OptLimit = FNakamaUtils::CreateOptional(Limit, 0); - const auto OptCursor = FNakamaUtils::CreateOptional(Cursor, FString()); - - ListTournaments( - Session, - OptCategoryStart, - OptCategoryEnd, - OptStartTime, - OptEndTime, - OptLimit, - OptCursor, - successCallback, - errorCallback - ); -} - -void UNakamaClient::AuthenticateDevice( - const FString& DeviceId, - const TOptional bCreate, - const TOptional& Username, - const TMap& Vars, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account/authenticate/device"); - - // Setup the query parameters - TMultiMap QueryParams; - - if (bCreate.IsSet()) - { - QueryParams.Add(TEXT("create"), FNakamaUtils::BoolToString(bCreate.GetValue())); - } - - if (Username.IsSet()) - { - // Encode the username - const FString EncodedUsername = FGenericPlatformHttp::UrlEncode(Username.GetValue()); - QueryParams.Add(TEXT("username"), EncodedUsername); - } - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("id"), DeviceId); - FNakamaUtils::AddVarsToJson(ContentJson, Vars); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, QueryParams, ""); - - // Set the basic authorization header - FNakamaUtils::FNakamaUtils::SetBasicAuthorizationHeader(HttpRequest, ServerKey); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(UNakamaSession::SetupSession(ResponseBody)); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::AuthenticateEmail( - const FString& Email, - const FString& Password, - const FString& Username, - bool bCreate, - const TMap& Vars, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account/authenticate/email"); - - // Encode the username - const FString EncodedUsername = FGenericPlatformHttp::UrlEncode(Username); - - // Setup the query parameters - TMultiMap QueryParams; - QueryParams.Add(TEXT("create"), FNakamaUtils::BoolToString(bCreate)); - QueryParams.Add(TEXT("username"), EncodedUsername); - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("email"), Email); - ContentJson->SetStringField(TEXT("password"), Password); - FNakamaUtils::AddVarsToJson(ContentJson, Vars); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, QueryParams, ""); - - // Set the basic authorization header - FNakamaUtils::SetBasicAuthorizationHeader(HttpRequest, ServerKey); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(UNakamaSession::SetupSession(ResponseBody)); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::AuthenticateCustom( - const FString& CustomId, - const FString& Username, - bool bCreate, - const TMap& Vars, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account/authenticate/custom"); - - // Encode the username - const FString EncodedUsername = FGenericPlatformHttp::UrlEncode(Username); - - // Setup the query parameters - TMultiMap QueryParams; - QueryParams.Add(TEXT("create"), FNakamaUtils::BoolToString(bCreate)); - QueryParams.Add(TEXT("username"), EncodedUsername); - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("id"), CustomId); - FNakamaUtils::AddVarsToJson(ContentJson, Vars); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, QueryParams, ""); - - // Set the basic authorization header - FNakamaUtils::SetBasicAuthorizationHeader(HttpRequest, ServerKey); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(UNakamaSession::SetupSession(ResponseBody)); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::AuthenticateApple( - const FString& Token, - const FString& Username, - bool bCreate, - const TMap& Vars, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account/authenticate/apple"); - - // Encode the username - const FString EncodedUsername = FGenericPlatformHttp::UrlEncode(Username); - - // Setup the query parameters - TMultiMap QueryParams; - QueryParams.Add(TEXT("create"), FNakamaUtils::BoolToString(bCreate)); - QueryParams.Add(TEXT("username"), EncodedUsername); - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("token"), Token); - FNakamaUtils::AddVarsToJson(ContentJson, Vars); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, QueryParams, ""); - - // Set the basic authorization header - FNakamaUtils::SetBasicAuthorizationHeader(HttpRequest, ServerKey); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(UNakamaSession::SetupSession(ResponseBody)); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::AuthenticateFacebook( - const FString& Token, - const FString& Username, - bool bCreate, - bool bImport, - const TMap& Vars, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account/authenticate/facebook"); - - // Encode the username - const FString EncodedUsername = FGenericPlatformHttp::UrlEncode(Username); - - // Setup the query parameters - TMultiMap QueryParams; - QueryParams.Add(TEXT("create"), FNakamaUtils::BoolToString(bCreate)); - QueryParams.Add(TEXT("username"), EncodedUsername); - QueryParams.Add(TEXT("import"), FNakamaUtils::BoolToString(bImport)); - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("token"), Token); - FNakamaUtils::AddVarsToJson(ContentJson, Vars); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, QueryParams, ""); - - // Set the basic authorization header - FNakamaUtils::SetBasicAuthorizationHeader(HttpRequest, ServerKey); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(UNakamaSession::SetupSession(ResponseBody)); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::AuthenticateGoogle( - const FString& Token, - const FString& Username, - bool bCreate, - const TMap& Vars, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account/authenticate/google"); - - // Encode the username - const FString EncodedUsername = FGenericPlatformHttp::UrlEncode(Username); - - // Setup the query parameters - TMultiMap QueryParams; - QueryParams.Add(TEXT("create"), FNakamaUtils::BoolToString(bCreate)); - QueryParams.Add(TEXT("username"), EncodedUsername); - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("token"), Token); - FNakamaUtils::AddVarsToJson(ContentJson, Vars); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, QueryParams, ""); - - // Set the basic authorization header - FNakamaUtils::SetBasicAuthorizationHeader(HttpRequest, ServerKey); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(UNakamaSession::SetupSession(ResponseBody)); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::AuthenticateGameCenter( - const FString& PlayerId, - const FString& BundleId, - int64 TimestampSeconds, - const FString& Salt, - const FString& Signature, - const FString& PublicKeyUrl, - const FString& Username, - bool bCreate, - const TMap& Vars, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account/authenticate/gamecenter"); - - // Encode the username - const FString EncodedUsername = FGenericPlatformHttp::UrlEncode(Username); - - // Setup the query parameters - TMultiMap QueryParams; - QueryParams.Add(TEXT("create"), FNakamaUtils::BoolToString(bCreate)); - QueryParams.Add(TEXT("username"), EncodedUsername); - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("player_id"), PlayerId); - ContentJson->SetStringField(TEXT("bundle_id"), BundleId); - ContentJson->SetNumberField(TEXT("timestamp_seconds"), TimestampSeconds); - ContentJson->SetStringField(TEXT("salt"), Salt); - ContentJson->SetStringField(TEXT("signature"), Signature); - ContentJson->SetStringField(TEXT("public_key_url"), PublicKeyUrl); - FNakamaUtils::AddVarsToJson(ContentJson, Vars); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, QueryParams, ""); - - // Set the basic authorization header - FNakamaUtils::SetBasicAuthorizationHeader(HttpRequest, ServerKey); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(UNakamaSession::SetupSession(ResponseBody)); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::AuthenticateSteam( - const FString& Token, - const FString& Username, - bool bCreate, - bool bImport, - const TMap& Vars, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account/authenticate/steam"); - - // Encode the username - const FString EncodedUsername = FGenericPlatformHttp::UrlEncode(Username); - - // Setup the query parameters - TMultiMap QueryParams; - QueryParams.Add(TEXT("create"), FNakamaUtils::BoolToString(bCreate)); - QueryParams.Add(TEXT("username"), EncodedUsername); - QueryParams.Add(TEXT("sync"), FNakamaUtils::BoolToString(bImport)); - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("token"), Token); - FNakamaUtils::AddVarsToJson(ContentJson, Vars); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, QueryParams, ""); - - // Set the basic authorization header - FNakamaUtils::SetBasicAuthorizationHeader(HttpRequest, ServerKey); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(UNakamaSession::SetupSession(ResponseBody)); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::AuthenticateRefresh( - UNakamaSession* Session, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - // NOTE: Endpoint taken from from C++ Client - Docs say: /v2/session/refresh/ - const FString Endpoint = TEXT("/v2/account/session/refresh"); - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("token"), Session->GetRefreshToken()); - FNakamaUtils::AddVarsToJson(ContentJson, Session->GetVariables()); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, TMultiMap(), ""); - - // Set the basic authorization header - FNakamaUtils::SetBasicAuthorizationHeader(HttpRequest, ServerKey); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(UNakamaSession::SetupSession(ResponseBody)); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::LinkDevice( - UNakamaSession* Session, - const FString& Id, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account/link/device"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("id"), Id); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::LinkEmail( - UNakamaSession* Session, - const FString& Email, - const FString& Password, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account/link/email"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("email"), Email); - ContentJson->SetStringField(TEXT("password"), Password); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::LinkCustom( - UNakamaSession* Session, - const FString& Id, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account/link/custom"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("id"), Id); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::LinkApple( - UNakamaSession* Session, - const FString& Token, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account/link/apple"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("token"), Token); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::LinkFacebook( - UNakamaSession* Session, - const FString& Token, - TOptional bImport, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account/link/facebook"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - if (bImport.IsSet()) - { - QueryParams.Add(TEXT("import"), FNakamaUtils::BoolToString(bImport.GetValue())); - } - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("token"), Token); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::LinkGoogle( - UNakamaSession* Session, - const FString& Token, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account/link/google"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("token"), Token); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::LinkGameCenter( - UNakamaSession* Session, - const FString& PlayerId, - const FString& BundleId, - int64 TimestampSeconds, - const FString& Salt, - const FString& Signature, - const FString& PublicKeyUrl, - TFunction SuccessCallback, TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account/link/gamecenter"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("player_id"), PlayerId); - ContentJson->SetStringField(TEXT("bundle_id"), BundleId); - ContentJson->SetNumberField(TEXT("timestamp_seconds"), TimestampSeconds); - ContentJson->SetStringField(TEXT("salt"), Salt); - ContentJson->SetStringField(TEXT("signature"), Signature); - ContentJson->SetStringField(TEXT("public_key_url"), PublicKeyUrl); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::LinkSteam( - UNakamaSession* Session, - const FString& Token, - //bool bImport, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account/link/steam"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("token"), Token); - //ContentJson->SetBoolField(TEXT("import"), bImport); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::UnLinkDevice( - UNakamaSession* Session, - const FString& Id, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account/unlink/device"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("id"), Id); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::UnLinkEmail( - UNakamaSession* Session, - const FString& Email, - const FString& Password, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account/unlink/email"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("email"), Email); - ContentJson->SetStringField(TEXT("password"), Password); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::UnLinkCustom( - UNakamaSession* Session, - const FString& Id, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account/unlink/custom"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("id"), Id); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::UnLinkApple( - UNakamaSession* Session, - const FString& Token, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account/unlink/apple"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("token"), Token); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::UnLinkFacebook( - UNakamaSession* Session, - const FString& Token, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account/unlink/facebook"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("token"), Token); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::UnLinkGoogle( - UNakamaSession* Session, - const FString& Token, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account/unlink/google"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("token"), Token); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::UnLinkGameCenter( - UNakamaSession* Session, - const FString& PlayerId, - const FString& BundleId, - int64 TimestampSeconds, - const FString& Salt, - const FString& Signature, - const FString& PublicKeyUrl, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account/unlink/gamecenter"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("player_id"), PlayerId); - ContentJson->SetStringField(TEXT("bundle_id"), BundleId); - ContentJson->SetNumberField(TEXT("timestamp_seconds"), TimestampSeconds); - ContentJson->SetStringField(TEXT("salt"), Salt); - ContentJson->SetStringField(TEXT("signature"), Signature); - ContentJson->SetStringField(TEXT("public_key_url"), PublicKeyUrl); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::UnLinkSteam( - UNakamaSession* Session, - const FString& Token, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account/unlink/steam"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("token"), Token); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::ImportFacebookFriends( - UNakamaSession* Session, - const FString& Token, - const TOptional bReset, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/friend/facebook"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // NOTE: Use Query Params for Reset? Docs say json but C++ SDK uses Body/json - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("token"), Token); - if (bReset.IsSet()) - { - ContentJson->SetBoolField(TEXT("reset"), bReset.GetValue()); - } - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::ImportSteamFriends( - UNakamaSession* Session, - const FString& SteamToken, - const TOptional bReset, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/friend/steam"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - - if (bReset.IsSet()) - { - QueryParams.Add(TEXT("reset"), FNakamaUtils::BoolToString(bReset.GetValue())); - } - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("token"), SteamToken); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - - -void UNakamaClient::GetAccount( - UNakamaSession* Session, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::GET, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FNakamaAccount NakamaAccount = FNakamaAccount(ResponseBody); - SuccessCallback(NakamaAccount); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::UpdateAccount( - UNakamaSession* Session, - const TOptional& Username, - const TOptional& DisplayName, - const TOptional& AvatarUrl, - const TOptional& LangTag, - const TOptional& Location, - const TOptional& TimeZone, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - if (Username.IsSet() && !Username.GetValue().IsEmpty()) - { - ContentJson->SetStringField(TEXT("username"), Username.GetValue()); - } - if (DisplayName.IsSet() && !DisplayName.GetValue().IsEmpty()) - { - ContentJson->SetStringField(TEXT("display_name"), DisplayName.GetValue()); - } - if (AvatarUrl.IsSet() && !AvatarUrl.GetValue().IsEmpty()) - { - ContentJson->SetStringField(TEXT("avatar_url"), AvatarUrl.GetValue()); - } - if (LangTag.IsSet() && !LangTag.GetValue().IsEmpty()) - { - ContentJson->SetStringField(TEXT("lang_tag"), LangTag.GetValue()); - } - if (Location.IsSet() && !Location.GetValue().IsEmpty()) - { - ContentJson->SetStringField(TEXT("location"), Location.GetValue()); - } - if (TimeZone.IsSet() && !TimeZone.GetValue().IsEmpty()) - { - ContentJson->SetStringField(TEXT("timezone"), TimeZone.GetValue()); - } - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::PUT, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - - -void UNakamaClient::DeleteUser( - UNakamaSession* Session, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/account"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - const FNakamaError Error("Invalid session on DeleteUser call."); - ErrorCallback(Error); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::DEL, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - if (!IsValidLowLevel()) - { - const FNakamaError Error("Invalid http request when lambda executes on DeleteUser call."); - ErrorCallback(Error); - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - if (!HttpRequest->ProcessRequest()) - { - const FNakamaError Error("Failed to process request on DeleteUser call."); - ErrorCallback(Error); - } -} - -void UNakamaClient::GetUsers( - UNakamaSession* Session, - const TArray& UserIds, - const TArray& Usernames, - const TArray& FacebookIds, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/user"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - if (UserIds.Num() > 0) - { - for (const FString& UserId : UserIds) - { - QueryParams.Add(TEXT("ids"), UserId); - } - } - if (Usernames.Num() > 0) - { - for (const FString& Username : Usernames) - { - const FString EncodedUsername = FGenericPlatformHttp::UrlEncode(Username); - QueryParams.Add(TEXT("usernames"), EncodedUsername); - } - } - if (FacebookIds.Num() > 0) - { - for (const FString& FacebookId : FacebookIds) - { - QueryParams.Add(TEXT("facebook_ids"), FacebookId); - } - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::GET, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FNakamaUserList UserList = FNakamaUserList(ResponseBody); - SuccessCallback(UserList); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::AddFriends( - UNakamaSession* Session, - const TArray& UserIds, - const TArray& Usernames, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/friend"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - if (UserIds.Num() > 0) - { - for (const FString& UserId : UserIds) - { - QueryParams.Add(TEXT("ids"), UserId); - } - } - if (Usernames.Num() > 0) - { - for (const FString& Username : Usernames) - { - const FString EncodedUsername = FGenericPlatformHttp::UrlEncode(Username); - QueryParams.Add(TEXT("usernames"), EncodedUsername); - } - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::POST, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::DeleteFriends( - UNakamaSession* Session, - const TArray& UserIds, - const TArray& Usernames, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/friend"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - if (UserIds.Num() > 0) - { - for (const FString& UserId : UserIds) - { - QueryParams.Add(TEXT("ids"), UserId); - } - } - if (Usernames.Num() > 0) - { - for (const FString& Username : Usernames) - { - const FString EncodedUsername = FGenericPlatformHttp::UrlEncode(Username); - QueryParams.Add(TEXT("usernames"), EncodedUsername); - } - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::DEL, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::BlockFriends( - UNakamaSession* Session, - const TArray& UserIds, - const TArray& Usernames, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/friend/block"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - if (UserIds.Num() > 0) - { - for (const FString& UserId : UserIds) - { - QueryParams.Add(TEXT("ids"), UserId); - } - } - if (Usernames.Num() > 0) - { - for (const FString& Username : Usernames) - { - const FString EncodedUsername = FGenericPlatformHttp::UrlEncode(Username); - QueryParams.Add(TEXT("usernames"), EncodedUsername); - } - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::POST, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::ListFriends( - UNakamaSession* Session, - const TOptional& Limit, - TOptional State, - const FString& Cursor, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/friend"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - if (Limit.IsSet() && Limit.GetValue() > 0) - { - QueryParams.Add(TEXT("limit"), FString::FromInt(Limit.GetValue())); - } - if (State.IsSet() && State.GetValue() != ENakamaFriendState::ALL) - { - const FString StateString = FNakamaUtils::GetEnumValueAsIntString(State.GetValue()); - QueryParams.Add(TEXT("state"), StateString); - } - if(!Cursor.IsEmpty()) - { - const FString EncodedCursor = FGenericPlatformHttp::UrlEncode(Cursor); - QueryParams.Add(TEXT("cursor"), EncodedCursor); - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::GET, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FNakamaFriendList FriendsList = FNakamaFriendList(ResponseBody); - SuccessCallback(FriendsList); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::CreateGroup( - UNakamaSession *Session, - const FString& Name, - const FString& Description, - const FString& AvatarUrl, - const FString& LangTag, - const bool bOpen, - const TOptional& MaxCount, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/group"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("name"), Name); - ContentJson->SetStringField(TEXT("description"), Description); - ContentJson->SetStringField(TEXT("avatar_url"), AvatarUrl); - ContentJson->SetStringField(TEXT("lang_tag"), LangTag); - ContentJson->SetBoolField(TEXT("open"), bOpen); - - if (MaxCount.IsSet()) - { - ContentJson->SetNumberField(TEXT("max_count"), MaxCount.GetValue()); - } - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FNakamaGroup Group = FNakamaGroup(ResponseBody); - SuccessCallback(Group); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::DeleteGroup( - UNakamaSession* Session, - const FString& GroupId, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = FString::Printf(TEXT("/v2/group/%s"), *GroupId); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::DEL, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::AddGroupUsers( - UNakamaSession* Session, - const FString& GroupId, - const TArray& UserIds, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = FString::Printf(TEXT("/v2/group/%s/add"), *GroupId); - - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - if (UserIds.Num() > 0) - { - for (const FString& UserId : UserIds) - { - QueryParams.Add(TEXT("user_ids"), UserId); - } - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::POST, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::ListGroupUsers( - UNakamaSession* Session, - const FString& GroupId, - const TOptional& Limit, - TOptional State, - FString Cursor, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = FString::Printf(TEXT("/v2/group/%s/user"), *GroupId); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - if (Limit.IsSet()) - { - QueryParams.Add(TEXT("limit"), FString::FromInt(Limit.GetValue())); - } - if (State.IsSet()) - { - const FString StateString = FNakamaUtils::GetEnumValueAsIntString(State.GetValue()); - QueryParams.Add(TEXT("state"), StateString); - } - if(!Cursor.IsEmpty()) - { - const FString EncodedCursor = FGenericPlatformHttp::UrlEncode(Cursor); - QueryParams.Add(TEXT("cursor"), EncodedCursor); - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::GET, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FNakamaGroupUsersList GroupUsersList = FNakamaGroupUsersList(ResponseBody); - SuccessCallback(GroupUsersList); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::KickGroupUsers( - UNakamaSession* Session, - const FString& GroupId, - const TArray& UserIds, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = FString::Printf(TEXT("/v2/group/%s/kick"), *GroupId); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - if (UserIds.Num() > 0) - { - for (const FString& UserId : UserIds) - { - QueryParams.Add(TEXT("user_ids"), UserId); - } - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::POST, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::BanGroupUsers( - UNakamaSession* Session, - const FString& GroupId, - const TArray& UserIds, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = FString::Printf(TEXT("/v2/group/%s/ban"), *GroupId); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - if (UserIds.Num() > 0) - { - for (const FString& UserId : UserIds) - { - QueryParams.Add(TEXT("user_ids"), UserId); - } - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::POST, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::JoinGroup( - UNakamaSession* Session, - const FString& GroupId, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = FString::Printf(TEXT("/v2/group/%s/join"), *GroupId); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::POST, {}, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::LeaveGroup( - UNakamaSession* Session, - const FString& GroupId, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = FString::Printf(TEXT("/v2/group/%s/leave"), *GroupId); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::POST, {}, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::ListGroups( - UNakamaSession* Session, - const FString& Name, - int32 Limit, - const FString& Cursor, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/group"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - if (!Name.IsEmpty()) - { - QueryParams.Add(TEXT("name"), Name); - } - if (!Cursor.IsEmpty()) - { - const FString EncodedCursor = FGenericPlatformHttp::UrlEncode(Cursor); - QueryParams.Add(TEXT("cursor"), EncodedCursor); - } - if (Limit > 0) - { - QueryParams.Add(TEXT("limit"), FString::FromInt(Limit)); - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::GET, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FNakamaGroupList GroupList = FNakamaGroupList(ResponseBody); - SuccessCallback(GroupList); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -// Used instead of passing in UserId, gets UserId from the Session -void UNakamaClient::ListUserGroups( - UNakamaSession* Session, - const TOptional& Limit, - const TOptional& State, - const FString& Cursor, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - if(Session && Session->IsValidLowLevel()) - { - ListUserGroups(Session, Session->GetUserId(), Limit, State, Cursor, SuccessCallback, ErrorCallback); - } - else - { - if (ErrorCallback) - { - FNakamaError Error; - Error.Code = ENakamaErrorCode::InvalidArgument; - Error.Message = TEXT("No session"); - - ErrorCallback(Error); - } - } -} - -void UNakamaClient::ListUserGroups( - UNakamaSession* Session, - const FString& UserId, - const TOptional& Limit, - const TOptional& State, - const FString& Cursor, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = FString::Printf(TEXT("/v2/user/%s/group"), *UserId); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - if (Limit.IsSet()) - { - QueryParams.Add(TEXT("limit"), FString::FromInt(Limit.GetValue())); - } - if (State.IsSet()) - { - const FString StateString = FNakamaUtils::GetEnumValueAsIntString(State.GetValue()); - QueryParams.Add(TEXT("state"), StateString); - } - if (!Cursor.IsEmpty()) - { - const FString EncodedCursor = FGenericPlatformHttp::UrlEncode(Cursor); - QueryParams.Add(TEXT("cursor"), EncodedCursor); - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::GET, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FNakamaUserGroupList UserGroupList = FNakamaUserGroupList(ResponseBody); - SuccessCallback(UserGroupList); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::PromoteGroupUsers(UNakamaSession* Session, - const FString& GroupId, - const TArray& UserIds, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = FString::Printf(TEXT("/v2/group/%s/promote"), *GroupId); - - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - if (UserIds.Num() > 0) - { - for (const FString& UserId : UserIds) - { - QueryParams.Add(TEXT("user_ids"), UserId); - } - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::POST, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::DemoteGroupUsers( - UNakamaSession* Session, - const FString& GroupId, - const TArray& UserIds, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = FString::Printf(TEXT("/v2/group/%s/demote"), *GroupId); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - if (UserIds.Num() > 0) - { - for (const FString& UserId : UserIds) - { - QueryParams.Add(TEXT("user_ids"), UserId); - } - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::POST, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::UpdateGroup( - UNakamaSession* Session, - const FString& GroupId, - const TOptional& Name, - const TOptional& Description, - const TOptional& AvatarUrl, - const TOptional& LangTag, - const TOptional bOpen, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = FString::Printf(TEXT("/v2/group/%s"), *GroupId); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("group_id"), GroupId); - if (Name.IsSet()) - { - ContentJson->SetStringField(TEXT("name"), Name.GetValue()); - } - if (Description.IsSet()) - { - ContentJson->SetStringField(TEXT("description"), Description.GetValue()); - } - if (AvatarUrl.IsSet()) - { - ContentJson->SetStringField(TEXT("avatar_url"), AvatarUrl.GetValue()); - } - if (LangTag.IsSet()) - { - ContentJson->SetStringField(TEXT("lang_tag"), LangTag.GetValue()); - } - if (bOpen.IsSet()) - { - ContentJson->SetBoolField(TEXT("open"), bOpen.GetValue()); - } - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::PUT, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::ListLeaderboardRecords( - UNakamaSession* Session, - const FString& LeaderboardId, - const TArray& OwnerIds, - const TOptional& Limit, - const TOptional& Cursor, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = FString::Printf(TEXT("/v2/leaderboard/%s"), *LeaderboardId); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - - if (OwnerIds.Num() > 0) - { - for (const FString& UserId : OwnerIds) - { - QueryParams.Add(TEXT("owner_ids"), UserId); - } - } - - if (Limit.IsSet()) - { - QueryParams.Add(TEXT("limit"), FString::FromInt(Limit.GetValue())); - } - if(Cursor.IsSet()) - { - const FString EncodedCursor = FGenericPlatformHttp::UrlEncode(Cursor.GetValue()); - QueryParams.Add(TEXT("cursor"), EncodedCursor); - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::GET, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FNakamaLeaderboardRecordList LeaderboardRecords = FNakamaLeaderboardRecordList(ResponseBody); - SuccessCallback(LeaderboardRecords); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::ListLeaderboardRecordsAroundOwner( - UNakamaSession* Session, - const FString& LeaderboardId, - const FString& OwnerId, - const TOptional& Limit, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = FString::Printf(TEXT("/v2/leaderboard/%s/owner/%s"), *LeaderboardId, *OwnerId); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - - if (Limit.IsSet()) - { - QueryParams.Add(TEXT("limit"), FString::FromInt(Limit.GetValue())); - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::GET, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FNakamaLeaderboardRecordList LeaderboardRecords = FNakamaLeaderboardRecordList(ResponseBody); - SuccessCallback(LeaderboardRecords); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::WriteLeaderboardRecord( - UNakamaSession* Session, - const FString& LeaderboardId, - int64 Score, - const TOptional& Subscore, - const TOptional& Metadata, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = FString::Printf(TEXT("/v2/leaderboard/%s"), *LeaderboardId); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the request content - TSharedPtr ContentJson = MakeShareable(new FJsonObject); - ContentJson->SetNumberField(TEXT("score"), Score); - if (Subscore.IsSet()) - { - ContentJson->SetNumberField(TEXT("subscore"), Subscore.GetValue()); - } - if (Metadata.IsSet()) - { - ContentJson->SetStringField(TEXT("metadata"), Metadata.GetValue()); - } - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FNakamaLeaderboardRecord LeaderboardRecord = FNakamaLeaderboardRecord(ResponseBody); - SuccessCallback(LeaderboardRecord); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::DeleteLeaderboardRecord(UNakamaSession* Session, - const FString& LeaderboardId, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = FString::Printf(TEXT("/v2/leaderboard/%s"), *LeaderboardId); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::DEL, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::ListMatches(UNakamaSession* Session, - const TOptional& MinSize, - const TOptional& MaxSize, - const TOptional& Limit, - const TOptional& Label, - const TOptional& Query, - const TOptional Authoritative, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/match"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - if (MinSize.IsSet()) - { - QueryParams.Add(TEXT("min_size"), FString::FromInt(MinSize.GetValue())); - } - if (MaxSize.IsSet()) - { - QueryParams.Add(TEXT("max_size"), FString::FromInt(MaxSize.GetValue())); - } - if (Limit.IsSet()) - { - QueryParams.Add(TEXT("limit"), FString::FromInt(Limit.GetValue())); - } - if (Label.IsSet()) - { - const FString EncodedLabel = FGenericPlatformHttp::UrlEncode(Label.GetValue()); - QueryParams.Add(TEXT("label"), EncodedLabel); - } - if (Query.IsSet()) - { - const FString EncodedQuery = FGenericPlatformHttp::UrlEncode(Query.GetValue()); - QueryParams.Add(TEXT("query"), EncodedQuery); - } - if (Authoritative.IsSet()) - { - QueryParams.Add(TEXT("authoritative"), FNakamaUtils::BoolToString(Authoritative.GetValue())); - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::GET, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FNakamaMatchList MatchList = FNakamaMatchList(ResponseBody); - SuccessCallback(MatchList); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::ListNotifications(UNakamaSession* Session, - const TOptional& Limit, - const TOptional& CacheableCursor, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/notification"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - if (Limit.IsSet()) - { - QueryParams.Add(TEXT("limit"), FString::FromInt(Limit.GetValue())); - } - if (CacheableCursor.IsSet()) - { - const FString EncodedCursor = FGenericPlatformHttp::UrlEncode(CacheableCursor.GetValue()); - QueryParams.Add(TEXT("cacheable_cursor"), EncodedCursor); - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::GET, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FNakamaNotificationList NotificationList = FNakamaNotificationList(ResponseBody); - SuccessCallback(NotificationList); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::DeleteNotifications( - UNakamaSession* Session, - const TArray& NotificationIds, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/notification"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - if (NotificationIds.Num() > 0) - { - for (const FString& NotificationId : NotificationIds) - { - QueryParams.Add(TEXT("ids"), NotificationId); - } - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::DEL, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::ListChannelMessages(UNakamaSession* Session, - const FString& ChannelId, - const TOptional& Limit, - const TOptional& Cursor, - const TOptional Forward, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = FString::Printf(TEXT("/v2/channel/%s"), *ChannelId); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - if (Limit.IsSet()) - { - QueryParams.Add(TEXT("limit"), FString::FromInt(Limit.GetValue())); - } - if (Cursor.IsSet()) - { - const FString EncodedCursor = FGenericPlatformHttp::UrlEncode(Cursor.GetValue()); - QueryParams.Add(TEXT("cursor"), EncodedCursor); - } - if (Forward.IsSet()) - { - QueryParams.Add(TEXT("forward"), FNakamaUtils::BoolToString(Forward.GetValue())); - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::GET, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FNakamaChannelMessageList ChannelMessageList = FNakamaChannelMessageList(ResponseBody); - SuccessCallback(ChannelMessageList); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::ListTournaments(UNakamaSession* Session, - const TOptional& CategoryStart, - const TOptional& CategoryEnd, - const TOptional& StartTime, - const TOptional& EndTime, - const TOptional& Limit, - const TOptional& Cursor, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/tournament"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - if (CategoryStart.IsSet()) - { - QueryParams.Add(TEXT("category_start"), FString::FromInt(CategoryStart.GetValue())); - } - if (CategoryEnd.IsSet()) - { - QueryParams.Add(TEXT("category_end"), FString::FromInt(CategoryEnd.GetValue())); - } - if (StartTime.IsSet()) - { - QueryParams.Add(TEXT("start_time"), FString::FromInt(StartTime.GetValue())); - } - if (EndTime.IsSet()) - { - QueryParams.Add(TEXT("end_time"), FString::FromInt(EndTime.GetValue())); - } - if (Limit.IsSet()) - { - QueryParams.Add(TEXT("limit"), FString::FromInt(Limit.GetValue())); - } - if (Cursor.IsSet()) - { - const FString EncodedCursor = FGenericPlatformHttp::UrlEncode(Cursor.GetValue()); - QueryParams.Add(TEXT("cursor"), EncodedCursor); - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::GET, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FNakamaTournamentList TournamentList = FNakamaTournamentList(ResponseBody); - SuccessCallback(TournamentList); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::ListTournamentRecords(UNakamaSession* Session, - const FString& TournamentId, - const TOptional& Limit, - const TOptional& Cursor, - const TArray& OwnerIds, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = FString::Printf(TEXT("/v2/tournament/%s"), *TournamentId); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - if (Limit.IsSet()) - { - QueryParams.Add(TEXT("limit"), FString::FromInt(Limit.GetValue())); - } - if (Cursor.IsSet()) - { - const FString EncodedCursor = FGenericPlatformHttp::UrlEncode(Cursor.GetValue()); - QueryParams.Add(TEXT("cursor"), EncodedCursor); - } - if (OwnerIds.Num() > 0) - { - for (const FString& OwnerId : OwnerIds) - { - QueryParams.Add(TEXT("owner_ids"), OwnerId); - } - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::GET, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FNakamaTournamentRecordList TournamentRecordList = FNakamaTournamentRecordList(ResponseBody); - SuccessCallback(TournamentRecordList); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::ListTournamentRecordsAroundOwner( - UNakamaSession* Session, const FString& TournamentId, - const FString& OwnerId, const TOptional& Limit, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = FString::Printf(TEXT("/v2/tournament/%s/owner/%s"), *TournamentId, *OwnerId); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - if (Limit.IsSet()) - { - QueryParams.Add(TEXT("limit"), FString::FromInt(Limit.GetValue())); - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::GET, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FNakamaTournamentRecordList TournamentRecordList = FNakamaTournamentRecordList(ResponseBody); - SuccessCallback(TournamentRecordList); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::WriteTournamentRecord(UNakamaSession* Session, - const FString& TournamentId, int64 Score, - const TOptional& Subscore, - const TOptional& Metadata, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = FString::Printf(TEXT("/v2/tournament/%s"), *TournamentId); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the request content - TSharedPtr ContentJson = MakeShareable(new FJsonObject); - ContentJson->SetNumberField(TEXT("score"), Score); - if (Subscore.IsSet()) - { - ContentJson->SetNumberField(TEXT("subscore"), Subscore.GetValue()); - } - if (Metadata.IsSet()) - { - ContentJson->SetStringField(TEXT("metadata"), Metadata.GetValue()); - } - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::PUT, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FNakamaLeaderboardRecord TournamentRecord = FNakamaLeaderboardRecord(ResponseBody); - SuccessCallback(TournamentRecord); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::JoinTournament( - UNakamaSession* Session, - const FString& TournamentId, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = FString::Printf(TEXT("/v2/tournament/%s/join"), *TournamentId); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::POST, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::ListStorageObjects(UNakamaSession* Session, - const FString& Collection, - const TOptional& Limit, - const TOptional& Cursor, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = FString::Printf(TEXT("/v2/storage/%s"), *Collection); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - if (Limit.IsSet()) - { - QueryParams.Add(TEXT("limit"), FString::FromInt(Limit.GetValue())); - } - if (Cursor.IsSet()) - { - const FString EncodedCursor = FGenericPlatformHttp::UrlEncode(Cursor.GetValue()); - QueryParams.Add(TEXT("cursor"), EncodedCursor); - } - - //QueryParams.Add(TEXT("user_id"), Session->GetUserId()); - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::GET, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FNakamaStorageObjectList StorageObjectList = FNakamaStorageObjectList(ResponseBody); - SuccessCallback(StorageObjectList); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::ListUsersStorageObjects(UNakamaSession* Session, - const FString& Collection, - const FString& UserId, - const TOptional& Limit, - const TOptional& Cursor, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = FString::Printf(TEXT("/v2/storage/%s"), *Collection); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - QueryParams.Add( TEXT("user_id"), UserId); - if (Limit.IsSet()) - { - QueryParams.Add(TEXT("limit"), FString::FromInt(Limit.GetValue())); - } - if (Cursor.IsSet()) - { - const FString EncodedCursor = FGenericPlatformHttp::UrlEncode(Cursor.GetValue()); - QueryParams.Add(TEXT("cursor"), EncodedCursor); - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::GET, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FNakamaStorageObjectList StorageObjectList = FNakamaStorageObjectList(ResponseBody); - SuccessCallback(StorageObjectList); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::WriteStorageObjects( - UNakamaSession* Session, - const TArray& Objects, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/storage"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the request body - TArray> ObjectsJson; - for (const FNakamaStoreObjectWrite& Object : Objects) - { - TSharedPtr ObjectJson = MakeShareable(new FJsonObject()); - ObjectJson->SetStringField(TEXT("collection"), Object.Collection); - ObjectJson->SetStringField(TEXT("key"), Object.Key); - ObjectJson->SetStringField(TEXT("value"), Object.Value); - ObjectJson->SetStringField(TEXT("version"), Object.Version); - - // Note: Should these be optional? - const FString PermissionRead = FNakamaUtils::GetEnumValueAsIntString(Object.PermissionRead); - ObjectJson->SetStringField(TEXT("permission_read"), PermissionRead); - - const FString PermissionWrite = FNakamaUtils::GetEnumValueAsIntString(Object.PermissionWrite); - ObjectJson->SetStringField(TEXT("permission_write"), PermissionWrite); - - ObjectsJson.Add(MakeShareable(new FJsonValueObject(ObjectJson))); - } - - TSharedPtr RequestBodyJson = MakeShareable(new FJsonObject()); - RequestBodyJson->SetArrayField(TEXT("objects"), ObjectsJson); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(RequestBodyJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::PUT, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FNakamaStoreObjectAcks StoreObjectAcks = FNakamaStoreObjectAcks(ResponseBody); - SuccessCallback(StoreObjectAcks); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::ReadStorageObjects( - UNakamaSession* Session, - const TArray& ObjectIds, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/storage"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the request body - TArray> ObjectsJson; - for (const FNakamaReadStorageObjectId& Object : ObjectIds) - { - TSharedPtr ObjectJson = MakeShareable(new FJsonObject()); - ObjectJson->SetStringField(TEXT("collection"), Object.Collection); - ObjectJson->SetStringField(TEXT("key"), Object.Key); - ObjectJson->SetStringField(TEXT("user_id"), Object.UserId); - - ObjectsJson.Add(MakeShareable(new FJsonValueObject(ObjectJson))); - } - - TSharedPtr RequestBodyJson = MakeShareable(new FJsonObject()); - RequestBodyJson->SetArrayField(TEXT("object_ids"), ObjectsJson); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(RequestBodyJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::POST, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FNakamaStorageObjectList StorageObjectList = FNakamaStorageObjectList(ResponseBody); - SuccessCallback(StorageObjectList); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::DeleteStorageObjects( - UNakamaSession* Session, - const TArray& ObjectIds, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/storage/delete"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the request body - TArray> ObjectsJson; - for (const FNakamaDeleteStorageObjectId& Object : ObjectIds) - { - TSharedPtr ObjectJson = MakeShareable(new FJsonObject()); - ObjectJson->SetStringField(TEXT("collection"), Object.Collection); - ObjectJson->SetStringField(TEXT("key"), Object.Key); - ObjectJson->SetStringField(TEXT("version"), Object.Version); - - ObjectsJson.Add(MakeShareable(new FJsonValueObject(ObjectJson))); - } - - TSharedPtr RequestBodyJson = MakeShareable(new FJsonObject()); - RequestBodyJson->SetArrayField(TEXT("object_ids"), ObjectsJson); - - // Serialize the request content - FString Content; - if (!FNakamaUtils::SerializeJsonObject(RequestBodyJson, Content)) - { - // Handle JSON serialization failure - FNakamaUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ENakamaRequestMethod::PUT, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void UNakamaClient::ListParties ( - UNakamaSession* Session, - const TOptional& Limit, - const TOptional& Open, - const TOptional& Query, - const TOptional& Cursor, - TFunction SuccessCallback, - TFunction ErrorCallback -) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v2/party"); - - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - if (Limit.IsSet()) - { - QueryParams.Add(TEXT("limit"), FString::FromInt(Limit.GetValue())); - } - if (Open.IsSet()) - { - QueryParams.Add(TEXT("open"), FNakamaUtils::BoolToString(Open.GetValue())); - } - if (Query.IsSet()) - { - const FString EncodedQuery = FGenericPlatformHttp::UrlEncode(Query.GetValue()); - QueryParams.Add(TEXT("query"), EncodedQuery); - } - if (Cursor.IsSet()) - { - const FString EncodedCursor = FGenericPlatformHttp::UrlEncode(Cursor.GetValue()); - QueryParams.Add(TEXT("cursor"), EncodedCursor); - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::GET, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FNakamaUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FNakamaPartyList PartyList = FNakamaPartyList(ResponseBody); - SuccessCallback(PartyList); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FNakamaError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FNakamaError RequestError = FNakamaUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -bool UNakamaClient::RPC( - UNakamaSession* Session, - const FString& Id, - const TOptional& Payload, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return false; - } - - // Call the SendRPC function - return SendRPC(Session, Id, Payload, {}, SuccessCallback, ErrorCallback); -} - -bool UNakamaClient::RPC( - const FString& HttpKey, - const FString& Id, - const FString& Payload, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - TMultiMap QueryParams; - //QueryParams.Add(TEXT("http_key"), HttpKey); - const FString EncodedHttpKey = FGenericPlatformHttp::UrlEncode(HttpKey); - QueryParams.Add(TEXT("http_key"), EncodedHttpKey); - - - // Sends Empty Session - return SendRPC({}, Id, TOptional(Payload), QueryParams, SuccessCallback, ErrorCallback); -} - -bool UNakamaClient::RPCm( - UNakamaSession* Session, - const FString& Id, - TOptional&& Payload, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Verify the session - if (!FNakamaUtils::IsSessionValid(Session, ErrorCallback)) - { - return false; - } - - // Call the SendRPC function - return SendRPCm(Session, Id, MoveTemp(Payload), {}, SuccessCallback, ErrorCallback); -} - -bool UNakamaClient::RPCm(const FString& HttpKey, const FString& Id, FString&& Payload, - TFunction SuccessCallback, TFunction ErrorCallback) -{ - TMultiMap QueryParams; - //QueryParams.Add(TEXT("http_key"), HttpKey); - const FString EncodedHttpKey = FGenericPlatformHttp::UrlEncode(HttpKey); - QueryParams.Add(TEXT("http_key"), EncodedHttpKey); - - - // Sends Empty Session - return SendRPCm({}, Id, TOptional(MoveTemp(Payload)), QueryParams, SuccessCallback, ErrorCallback); -} - -// End of TFunctions - -FString UNakamaClient::ConstructURL(const FString& Endpoint) -{ - FString Protocol = bUseSSL ? TEXT("https") : TEXT("http"); - FString URL = FString::Printf(TEXT("%s://%s:%d%s"), *Protocol, *Hostname, Port, *Endpoint); - - return URL; -} - -TSharedRef UNakamaClient::MakeRequest(const FString& Endpoint, - const FString& Content, ENakamaRequestMethod RequestMethod, const TMultiMap& QueryParams, - const FString& SessionToken) -{ - // Append query parameters to the endpoint - FString ModifiedEndpoint = Endpoint; - if (QueryParams.Num() > 0) - { - FString QueryString = FNakamaUtils::BuildQueryString(QueryParams); - ModifiedEndpoint += "?" + QueryString; - } - - // Construct the URL - FString URL = ConstructURL(ModifiedEndpoint); - - return FNakamaUtils::MakeRequest(URL, Content, RequestMethod, SessionToken, Timeout); -} - -bool UNakamaClient::IsClientValid() const -{ - return IsValidLowLevel(); - //return IsValid(this); -} - -bool UNakamaClient::SendRPC(UNakamaSession* Session, const FString& Id, const TOptional& Payload, - TMultiMap QueryParams, TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = FString("/v2/rpc/") + Id; - - if (Payload.IsSet() && !Payload.GetValue().IsEmpty()) - { - // This part is important - const FString EscapedPayload = EscapeJsonString(Payload.GetValue()); - - FString SessionToken; - if (Session != nullptr) - { - SessionToken = Session->GetAuthToken(); - } - - // Make the POST request with payload - const auto HttpRequest = MakeRequest(Endpoint, EscapedPayload, ENakamaRequestMethod::POST, QueryParams, SessionToken); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - FNakamaUtils::ProcessRequestCompleteMove(Request, Response, bSuccess, [SuccessCallback](FString&& ResponseBody) { - if (SuccessCallback) - { - SuccessCallback(FNakamaRPC(MoveTemp(ResponseBody))); - } - }, [ErrorCallback](const FNakamaError& Error) { - if (ErrorCallback) - { - ErrorCallback(Error); - } - }); - }); - - // Process the request - return HttpRequest->ProcessRequest(); - } - else - { - // Add the RPC ID to the query parameters - QueryParams.Add(TEXT("id"), Id); - - FString SessionToken; - if (Session != nullptr) - { - SessionToken = Session->GetAuthToken(); - } - - // Make the GET request without payload - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::GET, QueryParams, SessionToken); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - FNakamaUtils::ProcessRequestCompleteMove(Request, Response, bSuccess, [SuccessCallback](FString&& ResponseBody) { - if (SuccessCallback) - { - SuccessCallback(FNakamaRPC(MoveTemp(ResponseBody))); - } - }, [ErrorCallback](const FNakamaError& Error) { - if (ErrorCallback) - { - ErrorCallback(Error); - } - }); - }); - - // Process the request - return HttpRequest->ProcessRequest(); - } -} - -bool UNakamaClient::SendRPCm(UNakamaSession* Session, const FString& Id, const TOptional& Payload, - TMultiMap QueryParams, TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = FString("/v2/rpc/") + Id; - - if (Payload.IsSet() && !Payload.GetValue().IsEmpty()) - { - // This part is important - const FString EscapedPayload = EscapeJsonString(Payload.GetValue()); - - FString SessionToken; - if (Session != nullptr) - { - SessionToken = Session->GetAuthToken(); - } - - // Make the POST request with payload - const auto HttpRequest = MakeRequest(Endpoint, EscapedPayload, ENakamaRequestMethod::POST, QueryParams, SessionToken); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - FNakamaUtils::ProcessRequestCompleteMove(Request, Response, bSuccess, [SuccessCallback](FString&& ResponseBody) { - if (SuccessCallback) - { - SuccessCallback(FNakamaRPC(MoveTemp(ResponseBody))); - } - }, [ErrorCallback](const FNakamaError& Error) { - if (ErrorCallback) - { - ErrorCallback(Error); - } - }); - }); - - // Process the request - return HttpRequest->ProcessRequest(); - } - else - { - // Add the RPC ID to the query parameters - QueryParams.Add(TEXT("id"), Id); - - FString SessionToken; - if (Session != nullptr) - { - SessionToken = Session->GetAuthToken(); - } - - // Make the GET request without payload - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ENakamaRequestMethod::GET, QueryParams, SessionToken); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - FNakamaUtils::ProcessRequestCompleteMove(Request, Response, bSuccess, [SuccessCallback](FString&& ResponseBody) { - if (SuccessCallback) - { - SuccessCallback(FNakamaRPC(MoveTemp(ResponseBody))); - } - }, [ErrorCallback](const FNakamaError& Error) { - if (ErrorCallback) - { - ErrorCallback(Error); - } - }); - }); - - // Process the request - return HttpRequest->ProcessRequest(); - } -} - -void UNakamaClient::CancelAllRequests() -{ - if(!IsValidLowLevel()) - { - return; - } - - // Lock the mutex to protect access to ActiveRequests - FScopeLock Lock(&ActiveRequestsMutex); - - // Iterate over the active requests and cancel each one - for (const FHttpRequestPtr& Request : ActiveRequests) - { - Request->OnProcessRequestComplete().Unbind(); - Request->CancelRequest(); - } - - // Clear the ActiveRequests array - ActiveRequests.Empty(); -} - diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaError.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaError.cpp deleted file mode 100644 index 0740c1753..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaError.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaError.h" -#include "NakamaUtils.h" - -ENakamaErrorCode FNakamaError::ConvertNakamaErrorCode(int32 CodeValue) -{ - switch (CodeValue) - { - case 0: - return ENakamaErrorCode::Ok; - case 1: - return ENakamaErrorCode::Cancelled; - case 2: - return ENakamaErrorCode::Unknown; - case 3: - return ENakamaErrorCode::InvalidArgument; - case 4: - return ENakamaErrorCode::DeadlineExceeded; - case 5: - return ENakamaErrorCode::NotFound; - case 6: - return ENakamaErrorCode::AlreadyExists; - case 7: - return ENakamaErrorCode::PermissionDenied; - case 8: - return ENakamaErrorCode::ResourceExhausted; - case 9: - return ENakamaErrorCode::FailedPrecondition; - case 10: - return ENakamaErrorCode::Aborted; - case 11: - return ENakamaErrorCode::OutOfRange; - case 12: - return ENakamaErrorCode::Unimplemented; - case 13: - return ENakamaErrorCode::Internal; - case 14: - return ENakamaErrorCode::Unavailable; - case 15: - return ENakamaErrorCode::DataLoss; - case 16: - return ENakamaErrorCode::Unauthenticated; - default: - return ENakamaErrorCode::Unknown; - } -} - -FNakamaError::FNakamaError(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - if (FJsonSerializer::Deserialize(JsonReader, JsonObject)) - { - if (JsonObject->HasField(TEXT("message"))) - { - Message = JsonObject->GetStringField(TEXT("message")); - } - else - { - Message = TEXT("Invalid or missing 'message' field"); - } - - int32 CodeValue; - if (JsonObject->TryGetNumberField(TEXT("code"), CodeValue)) - { - //Code = static_cast(CodeValue); - Code = ConvertNakamaErrorCode(CodeValue); - } - else - { - Code = ENakamaErrorCode::Unknown; - } - } - else - { - Message = TEXT("Failed to parse JSON"); - Code = ENakamaErrorCode::Unknown; - } -} diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaFriend.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaFriend.cpp deleted file mode 100644 index 56378d7b0..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaFriend.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaFriend.h" -#include "NakamaUtils.h" - - -FNakamaFriend::FNakamaFriend(const FString& JsonString) : FNakamaFriend(FNakamaUtils::DeserializeJsonObject(JsonString)) { -} - -FNakamaFriend::FNakamaFriend(const TSharedPtr JsonObject) -{ - if (JsonObject.IsValid()) - { - const TSharedPtr* UserJsonObject; - if (JsonObject->TryGetObjectField(TEXT("user"), UserJsonObject)) - { - NakamaUser = FNakamaUser(*UserJsonObject); - } - - FString StateString; - if (JsonObject->TryGetStringField(TEXT("state"), StateString)) - { - UserState = GetFriendStateFromString(StateString); - } - - FString UpdateTimeString; - if (JsonObject->TryGetStringField(TEXT("update_time"), UpdateTimeString)) - { - FDateTime::ParseIso8601(*UpdateTimeString, UpdateTime); - } - } -} - -FNakamaFriend::FNakamaFriend() : UpdateTime(FDateTime::MinValue()), UserState(ENakamaFriendState::FRIEND) -{ - -} - -ENakamaFriendState FNakamaFriend::GetFriendStateFromString(const FString& StateString) -{ - if (StateString == "0") - return ENakamaFriendState::FRIEND; - else if (StateString == "1") - return ENakamaFriendState::INVITE_SENT; - else if (StateString == "2") - return ENakamaFriendState::INVITE_RECEIVED; - else if (StateString == "3") - return ENakamaFriendState::BLOCKED; - - return ENakamaFriendState::FRIEND; // Default to FRIEND state if the string doesn't match any known value -} - -FNakamaFriendList::FNakamaFriendList(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - const TArray>* FriendsJsonArray; - if (JsonObject->TryGetArrayField(TEXT("friends"), FriendsJsonArray)) - { - for (const TSharedPtr& FriendJsonValue : *FriendsJsonArray) - { - if (TSharedPtr FriendJsonObject = FriendJsonValue->AsObject()) - { - FNakamaFriend Friend(FriendJsonObject); - NakamaUsers.Add(Friend); - } - } - } - - JsonObject->TryGetStringField(TEXT("cursor"), Cursor); - } -} - -FNakamaFriendList::FNakamaFriendList() -{ - -} diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaGroup.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaGroup.cpp deleted file mode 100644 index b74e1ffd0..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaGroup.cpp +++ /dev/null @@ -1,221 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaGroup.h" -#include "NakamaUtils.h" - -ENakamaGroupState GetGroupStateFromString(const FString& StateString) -{ - if (StateString == "0") - { - return ENakamaGroupState::SUPERADMIN; - } - else if (StateString == "1") - { - return ENakamaGroupState::ADMIN; - } - else if (StateString == "2") - { - return ENakamaGroupState::MEMBER; - } - else if (StateString == "3") - { - return ENakamaGroupState::JOIN_REQUEST; - } - else - { - return ENakamaGroupState::SUPERADMIN; - } -} - - -FNakamaGroup::FNakamaGroup(const FString& JsonString) : FNakamaGroup(FNakamaUtils::DeserializeJsonObject(JsonString)) -{} - -FNakamaGroup::FNakamaGroup(const TSharedPtr JsonObject) -{ - if (JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("id"), Id); - JsonObject->TryGetStringField(TEXT("creator_id"), CreatorId); - JsonObject->TryGetStringField(TEXT("name"), Name); - JsonObject->TryGetStringField(TEXT("description"), Description); - JsonObject->TryGetStringField(TEXT("lang_tag"), Language); - JsonObject->TryGetStringField(TEXT("metadata"), MetaData); - JsonObject->TryGetStringField(TEXT("avatar_url"), AvatarUrl); - - JsonObject->TryGetBoolField(TEXT("open"), open); - - double Tmp; - if (JsonObject->TryGetNumberField(TEXT("edge_count"), Tmp)) - { - EdgeCount = static_cast(Tmp); - } - if (JsonObject->TryGetNumberField(TEXT("max_count"), Tmp)) - { - MaxCount = static_cast(Tmp); - } - - FString CreateTimeString; - if (JsonObject->TryGetStringField(TEXT("create_time"), CreateTimeString)) - { - FDateTime::ParseIso8601(*CreateTimeString, CreateTime); - } - FString UpdateTimeString; - if (JsonObject->TryGetStringField(TEXT("update_time"), UpdateTimeString)) - { - FDateTime::ParseIso8601(*UpdateTimeString, UpdateTime); - } - } -} - -FNakamaGroup::FNakamaGroup() : CreateTime(FDateTime::MinValue()), UpdateTime(FDateTime::MinValue()), EdgeCount(0), MaxCount(0), open(false) -{ - -} - - -FNakamaGroupUser::FNakamaGroupUser(const FString& JsonString) : FNakamaGroupUser(FNakamaUtils::DeserializeJsonObject(JsonString)) -{ -} - -FNakamaGroupUser::FNakamaGroupUser(const TSharedPtr JsonObject) -{ - if (JsonObject.IsValid()) - { - const TSharedPtr* UserJsonObject; - if (JsonObject->TryGetObjectField(TEXT("user"), UserJsonObject)) - { - User = FNakamaUser(*UserJsonObject); - } - - FString StateString; - if (JsonObject->TryGetStringField(TEXT("state"), StateString)) - { - State = GetGroupStateFromString(StateString); - } - } -} - -FNakamaGroupUser::FNakamaGroupUser(): State(ENakamaGroupState::SUPERADMIN) -{ - -} - -FNakamaGroupUsersList::FNakamaGroupUsersList(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - const TArray>* GroupUsersJsonArray; - if (JsonObject->TryGetArrayField(TEXT("group_users"), GroupUsersJsonArray)) - { - for (const TSharedPtr& GroupUserJsonValue : *GroupUsersJsonArray) - { - if (TSharedPtr GroupUserJsonObject = GroupUserJsonValue->AsObject()) - { - GroupUsers.Add(FNakamaGroupUser(GroupUserJsonObject)); - } - } - } - - JsonObject->TryGetStringField(TEXT("cursor"), Cursor); - } -} - -FNakamaGroupList::FNakamaGroupList(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - const TArray>* GroupsJsonArray; - if (JsonObject->TryGetArrayField(TEXT("groups"), GroupsJsonArray)) - { - for (const TSharedPtr& GroupJsonValue : *GroupsJsonArray) - { - if (TSharedPtr GroupJsonObject = GroupJsonValue->AsObject()) - { - Groups.Add(FNakamaGroup(GroupJsonObject)); - } - } - } - - JsonObject->TryGetStringField(TEXT("cursor"), Cursor); - } -} - - -FNakamaUserGroup::FNakamaUserGroup(const FString& JsonString) : FNakamaUserGroup(FNakamaUtils::DeserializeJsonObject(JsonString)) -{ -} - -FNakamaUserGroup::FNakamaUserGroup(const TSharedPtr JsonObject) -{ - if (JsonObject.IsValid()) - { - if (JsonObject->HasField(TEXT("group")) && JsonObject->GetField(TEXT("group"))) - { - const TSharedPtr* GroupJsonObject; - if (JsonObject->TryGetObjectField(TEXT("group"), GroupJsonObject)) - { - Group = FNakamaGroup(*GroupJsonObject); - } - } - - FString StateString; - if (JsonObject->TryGetStringField(TEXT("state"), StateString)) - { - State = GetGroupStateFromString(StateString); - } - } -} - -FNakamaUserGroup::FNakamaUserGroup(): State(ENakamaGroupState::SUPERADMIN) -{ - -} - -FNakamaUserGroupList::FNakamaUserGroupList(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - const TArray>* UserGroupsJsonArray; - if (JsonObject->TryGetArrayField(TEXT("user_groups"), UserGroupsJsonArray)) - { - for (const TSharedPtr& UserGroupJsonValue : *UserGroupsJsonArray) - { - if (TSharedPtr UserGroupJsonObject = UserGroupJsonValue->AsObject()) - { - UserGroups.Add(FNakamaUserGroup(UserGroupJsonObject)); - } - } - } - - JsonObject->TryGetStringField(TEXT("cursor"), Cursor); - } -} - -FNakamaUserGroupList::FNakamaUserGroupList() -{ - -} diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaLeaderboard.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaLeaderboard.cpp deleted file mode 100644 index 5ffc08fa8..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaLeaderboard.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaLeaderboard.h" -#include "NakamaUtils.h" - -FNakamaLeaderboardRecord::FNakamaLeaderboardRecord(const FString& JsonString) : FNakamaLeaderboardRecord(FNakamaUtils::DeserializeJsonObject(JsonString)) { -} - -FNakamaLeaderboardRecord::FNakamaLeaderboardRecord(const TSharedPtr JsonObject) -{ - if (JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("leaderboard_id"), LeaderboardId); - JsonObject->TryGetStringField(TEXT("owner_id"), OwnerId); - JsonObject->TryGetStringField(TEXT("username"), Username); - JsonObject->TryGetNumberField(TEXT("score"), Score); - JsonObject->TryGetNumberField(TEXT("subscore"), SubScore); - JsonObject->TryGetNumberField(TEXT("num_score"), NumScore); - JsonObject->TryGetNumberField(TEXT("max_num_score"), MaxNumScore); - JsonObject->TryGetStringField(TEXT("metadata"), Metadata); - - FString CreateTimeString; - if (JsonObject->TryGetStringField(TEXT("create_time"), CreateTimeString)) - { - FDateTime::ParseIso8601(*CreateTimeString, CreateTime); - } - - FString UpdateTimeString; - if (JsonObject->TryGetStringField(TEXT("update_time"), UpdateTimeString)) - { - FDateTime::ParseIso8601(*UpdateTimeString, UpdateTime); - } - - FString ExpiryTimeString; - if (JsonObject->TryGetStringField(TEXT("expiry_time"), ExpiryTimeString)) - { - FDateTime::ParseIso8601(*ExpiryTimeString, ExpiryTime); - } - - JsonObject->TryGetNumberField(TEXT("rank"), Rank); - } -} - -FNakamaLeaderboardRecord::FNakamaLeaderboardRecord() - : CreateTime(FDateTime::MinValue()), UpdateTime(FDateTime::MinValue()), ExpiryTime(FDateTime::MinValue()), - Score(0), SubScore(0), NumScore(0), Rank(0), MaxNumScore(0) -{ -} - -FNakamaLeaderboardRecordList::FNakamaLeaderboardRecordList(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - const TArray>* RecordsJsonArray; - if (JsonObject->TryGetArrayField(TEXT("records"), RecordsJsonArray)) - { - for (const TSharedPtr& RecordJsonValue : *RecordsJsonArray) - { - if (TSharedPtr RecordJsonObject = RecordJsonValue->AsObject()) - { - Records.Add(FNakamaLeaderboardRecord(RecordJsonObject)); - } - } - } - - const TArray>* OwnerRecordsJsonArray; - if (JsonObject->TryGetArrayField(TEXT("owner_records"), OwnerRecordsJsonArray)) - { - for (const TSharedPtr& OwnerRecordJsonValue : *OwnerRecordsJsonArray) - { - if (TSharedPtr OwnerRecordJsonObject = OwnerRecordJsonValue->AsObject()) - { - OwnerRecords.Add(FNakamaLeaderboardRecord(OwnerRecordJsonObject)); - } - } - } - - JsonObject->TryGetStringField(TEXT("next_cursor"), NextCursor); - JsonObject->TryGetStringField(TEXT("prev_cursor"), PrevCursor); - } -} - -FNakamaLeaderboardRecordList::FNakamaLeaderboardRecordList() -{ - -} diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaLogger.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaLogger.cpp deleted file mode 100644 index ce74611d5..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaLogger.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaLogger.h" - -DEFINE_LOG_CATEGORY(LogNakamaUnreal); - -ENakamaLogLevel UNakamaLogger::CurrentLogLevel = ENakamaLogLevel::Info; -bool UNakamaLogger::bLoggingEnabled = false; - -UNakamaLogger::UNakamaLogger() -{ - -} - -void UNakamaLogger::SetLogLevel(ENakamaLogLevel InLogLevel) -{ - CurrentLogLevel = InLogLevel; -} - -bool UNakamaLogger::IsLoggable(ENakamaLogLevel InLogLevel) -{ - // Logging Disabled check - if (!bLoggingEnabled) - return false; - - // Debug - if(CurrentLogLevel == ENakamaLogLevel::Debug) - { - if(InLogLevel == ENakamaLogLevel::Debug) - return true; - - if(InLogLevel == ENakamaLogLevel::Info) - return true; - - if(InLogLevel == ENakamaLogLevel::Warn) - return true; - - if(InLogLevel == ENakamaLogLevel::Error) - return true; - - if(InLogLevel == ENakamaLogLevel::Fatal) - return true; - } - - // Info - if(CurrentLogLevel == ENakamaLogLevel::Info) - { - if(InLogLevel == ENakamaLogLevel::Info) - return true; - - if(InLogLevel == ENakamaLogLevel::Warn) - return true; - - if(InLogLevel == ENakamaLogLevel::Error) - return true; - - if(InLogLevel == ENakamaLogLevel::Fatal) - return true; - } - - // Warn - if(CurrentLogLevel == ENakamaLogLevel::Warn) - { - if(InLogLevel == ENakamaLogLevel::Warn) - return true; - - if(InLogLevel == ENakamaLogLevel::Error) - return true; - - if(InLogLevel == ENakamaLogLevel::Fatal) - return true; - } - - // Error - if(CurrentLogLevel == ENakamaLogLevel::Error) - { - if(InLogLevel == ENakamaLogLevel::Error) - return true; - - if(InLogLevel == ENakamaLogLevel::Fatal) - return true; - } - - // Fatal - if(CurrentLogLevel == ENakamaLogLevel::Fatal) - { - if(InLogLevel == ENakamaLogLevel::Fatal) - return true; - } - - return false; -} - -void UNakamaLogger::Log(ENakamaLogLevel InLogLevel, const FString& Message) -{ - if (IsLoggable(InLogLevel)) - { - switch (InLogLevel) - { - case ENakamaLogLevel::Debug: - UE_LOG(LogNakamaUnreal, Verbose, TEXT("%s"), *Message); - break; - case ENakamaLogLevel::Info: - UE_LOG(LogNakamaUnreal, Display, TEXT("%s"), *Message); - break; - case ENakamaLogLevel::Warn: - UE_LOG(LogNakamaUnreal, Warning, TEXT("%s"), *Message); - break; - case ENakamaLogLevel::Error: - UE_LOG(LogNakamaUnreal, Error, TEXT("%s"), *Message); - break; - case ENakamaLogLevel::Fatal: - UE_LOG(LogNakamaUnreal, Fatal, TEXT("%s"), *Message); - break; - default: - break; - } - } -} - -void UNakamaLogger::EnableLogging(bool bEnable) -{ - bLoggingEnabled = bEnable; -} diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaMatch.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaMatch.cpp deleted file mode 100644 index fb2bb1be1..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaMatch.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaMatch.h" -#include "NakamaUtils.h" - -FNakamaMatch::FNakamaMatch(const FString& JsonString) : FNakamaMatch(FNakamaUtils::DeserializeJsonObject(JsonString)) { -} - -FNakamaMatch::FNakamaMatch(const TSharedPtr JsonObject) -{ - if (JsonObject.IsValid()) - { - // Try to get the "match" object from the parsed JSON - const TSharedPtr* MatchObjectPtr = nullptr; - JsonObject->TryGetObjectField(TEXT("match"), MatchObjectPtr); - - // If "match" object is not found, use the root object - TSharedPtr MatchObject = MatchObjectPtr ? *MatchObjectPtr : JsonObject; - - // NOTE: Server can respond with 'tick_rate' and 'handler_name' as well - - // Extract and assign the individual fields - MatchObject->TryGetStringField(TEXT("match_id"), MatchId); - MatchObject->TryGetBoolField(TEXT("authoritative"), Authoritative); - MatchObject->TryGetStringField(TEXT("label"), Label); - MatchObject->TryGetNumberField(TEXT("size"), Size); - - const TArray>* JoinsJsonArray; - if (MatchObject->TryGetArrayField(TEXT("presences"), JoinsJsonArray)) - { - for (const TSharedPtr& UserPresence : *JoinsJsonArray) - { - if (TSharedPtr UserPresenceJsonObject = UserPresence->AsObject()) - { - Pressences.Add(FNakamaUserPresence(UserPresenceJsonObject)); - } - } - } - - // Try to get the "self" object from the match object - const TSharedPtr* SelfObjectPtr; - if (MatchObject->TryGetObjectField(TEXT("self"), SelfObjectPtr)) - { - Me = FNakamaUserPresence(*SelfObjectPtr); - } - } -} - -FNakamaMatchData::FNakamaMatchData(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("match_id"), MatchId); - - const TSharedPtr* PresenceJsonObject; - if (JsonObject->TryGetObjectField(TEXT("presence"), PresenceJsonObject)) - { - Presence = FNakamaUserPresence(*PresenceJsonObject); - } - - JsonObject->TryGetNumberField(TEXT("op_code"), OpCode); - - if(JsonObject->HasField(TEXT("data"))) - { - FNakamaUtils::Base64Decode(JsonObject->GetStringField(TEXT("data")), Data); - } - - } -} - -FNakamaMatchData::FNakamaMatchData(): OpCode(0) -{ -} - -FNakamaMatchList::FNakamaMatchList(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - const TArray>* MatchesJsonArray; - if (JsonObject->TryGetArrayField(TEXT("matches"), MatchesJsonArray)) - { - for (const TSharedPtr& MatchJsonValue : *MatchesJsonArray) - { - if (TSharedPtr MatchJsonObject = MatchJsonValue->AsObject()) - { - Matches.Add(FNakamaMatch(MatchJsonObject)); - } - } - } - } -} - -FNakamaMatchList::FNakamaMatchList() -{ - -} diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaMatchTypes.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaMatchTypes.cpp deleted file mode 100644 index 49f83f1fd..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaMatchTypes.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaMatchTypes.h" -#include "NakamaUtils.h" - -FNakamaMatchmakerUser::FNakamaMatchmakerUser(const FString& JsonString) : FNakamaMatchmakerUser(FNakamaUtils::DeserializeJsonObject(JsonString)) { -} - -FNakamaMatchmakerUser::FNakamaMatchmakerUser(const TSharedPtr JsonObject) -{ - if (JsonObject.IsValid()) - { - const TSharedPtr* PresenceObject; - if (JsonObject->TryGetObjectField(TEXT("presence"), PresenceObject)) - { - Presence = FNakamaUserPresence(*PresenceObject); - } - - const TSharedPtr* StringPropertiesObject; - if (JsonObject->TryGetObjectField(TEXT("string_properties"), StringPropertiesObject)) - { - for (const auto& Entry : (*StringPropertiesObject)->Values) - { - StringProperties.Add(Entry.Key, Entry.Value->AsString()); - } - } - - const TSharedPtr* NumericPropertiesObject; - if (JsonObject->TryGetObjectField(TEXT("numeric_properties"), NumericPropertiesObject)) - { - for (const auto& Entry : (*NumericPropertiesObject)->Values) - { - NumericProperties.Add(Entry.Key, Entry.Value->AsNumber()); - } - } - } -} - -FNakamaMatchmakerUser::FNakamaMatchmakerUser() -{ - -} - -FNakamaMatchmakerMatched::FNakamaMatchmakerMatched(const FString& JsonString) -{ - TSharedPtr JsonObject; - TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("ticket"), Ticket); - JsonObject->TryGetStringField(TEXT("match_id"), MatchId); - JsonObject->TryGetStringField(TEXT("token"), Token); - - const TArray>* UsersJsonArray; - if (JsonObject->TryGetArrayField(TEXT("users"), UsersJsonArray)) - { - for (const TSharedPtr& UserJson : *UsersJsonArray) - { - if (TSharedPtr UserJsonObject = UserJson->AsObject()) - { - Users.Add(FNakamaMatchmakerUser(UserJsonObject)); - } - } - } - - const TSharedPtr* MeJsonObject; - if (JsonObject->TryGetObjectField(TEXT("self"), MeJsonObject)) - { - Me = FNakamaMatchmakerUser(*MeJsonObject); - } - } -} - -FNakamaMatchmakerMatched::FNakamaMatchmakerMatched() -{ - -} - -FNakamaMatchPresenceEvent::FNakamaMatchPresenceEvent(const FString& JsonString) -{ - TSharedPtr JsonObject; - TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("match_id"), MatchId); - - const TArray>* JoinsJsonArray; - if (JsonObject->TryGetArrayField(TEXT("joins"), JoinsJsonArray)) - { - for (const TSharedPtr& JoinJson : *JoinsJsonArray) - { - if (TSharedPtr JoinJsonObject = JoinJson->AsObject()) - { - Joins.Add(FNakamaUserPresence(JoinJsonObject)); - } - } - } - - const TArray>* LeavesJsonArray; - if (JsonObject->TryGetArrayField(TEXT("leaves"), LeavesJsonArray)) - { - for (const TSharedPtr& LeaveJson : *LeavesJsonArray) - { - if (TSharedPtr LeaveJsonObject = LeaveJson->AsObject()) - { - Leaves.Add(FNakamaUserPresence(LeaveJsonObject)); - } - } - } - } -} - -FNakamaMatchPresenceEvent::FNakamaMatchPresenceEvent() -{ - -} - -FNakamaMatchmakerTicket::FNakamaMatchmakerTicket(const FString& JsonString) -{ - TSharedPtr JsonObject; - TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - TSharedPtr MatchmakerTicketObject = JsonObject->GetObjectField(TEXT("matchmaker_ticket")); - if (MatchmakerTicketObject.IsValid()) - { - MatchmakerTicketObject->TryGetStringField(TEXT("ticket"), TicketId); - } - } -} - -FNakamaMatchmakerTicket::FNakamaMatchmakerTicket() -{ - -} diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaNotification.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaNotification.cpp deleted file mode 100644 index d1e63a166..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaNotification.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaNotification.h" -#include "NakamaUtils.h" - -FNakamaNotification::FNakamaNotification(const FString& JsonString) : FNakamaNotification(FNakamaUtils::DeserializeJsonObject(JsonString)) { -} - -FNakamaNotification::FNakamaNotification(const TSharedPtr JsonObject) -{ - if (JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("id"), Id); - JsonObject->TryGetStringField(TEXT("subject"), Subject); - JsonObject->TryGetStringField(TEXT("content"), Content); - JsonObject->TryGetNumberField(TEXT("code"), Code); - JsonObject->TryGetStringField(TEXT("sender_id"), SenderId); - - FString CreateTimeString; - if (JsonObject->TryGetStringField(TEXT("create_time"), CreateTimeString)) - { - FDateTime::ParseIso8601(*CreateTimeString, CreateTime); - } - - JsonObject->TryGetBoolField(TEXT("persistent"), Persistent); - } -} - -FNakamaNotification::FNakamaNotification() : CreateTime(FDateTime::MinValue()), Code(0), Persistent(false) -{ -} - -FNakamaNotificationList::FNakamaNotificationList(const FString& JsonString) -{ - TSharedPtr JsonObject; - TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - const TArray>* NotificationsJsonArray; - if (JsonObject->TryGetArrayField(TEXT("notifications"), NotificationsJsonArray)) - { - for (const TSharedPtr& NotificationJson : *NotificationsJsonArray) - { - if (TSharedPtr NotificationJsonObject = NotificationJson->AsObject()) - { - Notifications.Add(FNakamaNotification(NotificationJsonObject)); - } - } - } - - JsonObject->TryGetStringField(TEXT("cacheable_cursor"), CacheableCursor); - } -} - -FNakamaNotificationList::FNakamaNotificationList() -{ - -} - \ No newline at end of file diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaParty.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaParty.cpp deleted file mode 100644 index dfce0935b..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaParty.cpp +++ /dev/null @@ -1,274 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaParty.h" -#include "NakamaUtils.h" - -FNakamaParty::FNakamaParty(const FString& JsonString) : FNakamaParty(FNakamaUtils::DeserializeJsonObject(JsonString)) { -} - -FNakamaParty::FNakamaParty(const TSharedPtr JsonObject) -{ - if (JsonObject.IsValid()) - { - // Get the appropriate object based on whether "party" is present or not - TSharedPtr PartyObject = JsonObject->HasField(TEXT("party")) ? JsonObject->GetObjectField(TEXT("party")) : JsonObject; - - if (!PartyObject->TryGetStringField(TEXT("party_id"), PartyId)) - { - // TODO: Check which is the one actually sent by the server - PartyObject->TryGetStringField(TEXT("partyId"), PartyId); - } - PartyObject->TryGetBoolField(TEXT("open"), Open); - PartyObject->TryGetBoolField(TEXT("hidden"), Hidden); - if (!PartyObject->TryGetNumberField(TEXT("max_size"), MaxSize)) - { - // TODO: Check which is the one actually sent by the server - PartyObject->TryGetNumberField(TEXT("maxSize"), MaxSize); - } - PartyObject->TryGetStringField(TEXT("label"), Label); - - FString SelfJsonString; - const TSharedPtr* SelfJsonObject; - if (PartyObject->TryGetObjectField(TEXT("self"), SelfJsonObject)) - { - Me = FNakamaUserPresence(*SelfJsonObject); - } - - FString LeaderJsonString; - const TSharedPtr* LeaderJsonObject; - if (PartyObject->TryGetObjectField(TEXT("leader"), LeaderJsonObject)) - { - Leader = FNakamaUserPresence(*LeaderJsonObject); - } - - const TArray>* PresencesJsonArray; - if (PartyObject->TryGetArrayField(TEXT("presences"), PresencesJsonArray)) - { - for (const TSharedPtr& PresenceJson : *PresencesJsonArray) - { - if (TSharedPtr PresenceObject = PresenceJson->AsObject()) - { - Presences.Add(FNakamaUserPresence(PresenceObject)); - } - } - } - } - -} - -FNakamaParty::FNakamaParty() : MaxSize(0), Open(false), Hidden(true) -{ -} - -FNakamaPartyJoinRequest::FNakamaPartyJoinRequest(const FString& JsonString) -{ - TSharedPtr JsonObject; - TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - // Get the appropriate object based on whether "party_join_request" is present or not. - TSharedPtr PartyJoinRequestJsonObject = JsonObject->HasField(TEXT("party_join_request")) ? JsonObject->GetObjectField(TEXT("party_join_request")) : JsonObject; - - PartyJoinRequestJsonObject->TryGetStringField(TEXT("party_id"), PartyId); - - const TArray>* PresencesJsonArray; - if (PartyJoinRequestJsonObject->TryGetArrayField(TEXT("presences"), PresencesJsonArray)) - { - for (const TSharedPtr& PresenceJson : *PresencesJsonArray) - { - if (TSharedPtr PresenceObject = PresenceJson->AsObject()) - { - Presences.Add(FNakamaUserPresence(PresenceObject)); - } - } - } - } -} - -FNakamaPartyJoinRequest::FNakamaPartyJoinRequest() -{ - -} - -FNakamaPartyMatchmakerTicket::FNakamaPartyMatchmakerTicket(const FString& JsonString) -{ - TSharedPtr JsonObject; - TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - if (!FJsonSerializer::Deserialize(JsonReader, JsonObject)) - { - return; - } - - TSharedPtr PartyTicketObject = JsonObject->GetObjectField(TEXT("party_matchmaker_ticket")); - if (!PartyTicketObject.IsValid()) - { - return; - } - - PartyTicketObject->TryGetStringField(TEXT("ticket"), Ticket); - PartyTicketObject->TryGetStringField(TEXT("party_id"), PartyId); -} - -FNakamaPartyMatchmakerTicket::FNakamaPartyMatchmakerTicket() -{ - -} - - -FNakamaPartyClose::FNakamaPartyClose(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - Id = JsonObject->GetStringField(TEXT("id")); - } -} - -FNakamaPartyClose::FNakamaPartyClose() -{ - -} - -FNakamaPartyData::FNakamaPartyData(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("party_id"), PartyId); - - const TSharedPtr* PresenceJsonObject; - if (JsonObject->TryGetObjectField(TEXT("presence"), PresenceJsonObject)) - { - Presence = FNakamaUserPresence(*PresenceJsonObject); - } - else - { - Presence = FNakamaUserPresence(); - } - - JsonObject->TryGetNumberField(TEXT("op_code"), OpCode); - - if(JsonObject->HasField(TEXT("data"))) - { - FBase64::Decode(JsonObject->GetStringField(TEXT("data")), Data); - } - } -} - -FNakamaPartyData::FNakamaPartyData(): OpCode(0) -{ -} - -FNakamaPartyLeader::FNakamaPartyLeader(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("party_id"), PartyId); - - const TSharedPtr* PresenceJsonObject; - if (JsonObject->TryGetObjectField(TEXT("presence"), PresenceJsonObject)) - { - Presence = FNakamaUserPresence(*PresenceJsonObject); - } - else - { - Presence = FNakamaUserPresence(); - } - } -} - -FNakamaPartyLeader::FNakamaPartyLeader() -{ - -} - -FNakamaPartyPresenceEvent::FNakamaPartyPresenceEvent(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("party_id"), PartyId); - - const TArray>* JoinsJsonArray; - if (JsonObject->TryGetArrayField(TEXT("joins"), JoinsJsonArray)) - { - for (const TSharedPtr& PresenceJson : *JoinsJsonArray) - { - if (TSharedPtr PresenceObject = PresenceJson->AsObject()) - { - Joins.Add(FNakamaUserPresence(PresenceObject)); - } - } - } - - const TArray>* LeavesJsonArray; - if (JsonObject->TryGetArrayField(TEXT("leaves"), LeavesJsonArray)) - { - for (const TSharedPtr& PresenceJson : *LeavesJsonArray) - { - if (TSharedPtr PresenceObject = PresenceJson->AsObject()) - { - Leaves.Add(FNakamaUserPresence(PresenceObject)); - } - } - } - } -} - -FNakamaPartyPresenceEvent::FNakamaPartyPresenceEvent() -{ - -} - - -FNakamaPartyList::FNakamaPartyList(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - const TArray>* PartiesJsonArray; - if (JsonObject->TryGetArrayField(TEXT("parties"), PartiesJsonArray)) - { - for (const TSharedPtr& PartyJsonValue : *PartiesJsonArray) - { - if (TSharedPtr PartyJsonObject = PartyJsonValue->AsObject()) - { - FNakamaParty Party(PartyJsonObject); - Parties.Add(Party); - } - } - } - - JsonObject->TryGetStringField(TEXT("cursor"), Cursor); - } -} - -FNakamaPartyList::FNakamaPartyList() -{ -} diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaPresence.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaPresence.cpp deleted file mode 100644 index 83bed12ce..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaPresence.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaPresence.h" -#include "NakamaUtils.h" - -FNakamaUserPresence::FNakamaUserPresence(const FString& JsonString) : FNakamaUserPresence(FNakamaUtils::DeserializeJsonObject(JsonString)) { -} - -FNakamaUserPresence::FNakamaUserPresence(const TSharedPtr JsonObject) -{ - if (JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("user_id"), UserID); - JsonObject->TryGetStringField(TEXT("session_id"), SessionID); - JsonObject->TryGetStringField(TEXT("username"), Username); - JsonObject->TryGetBoolField(TEXT("persistence"), Persistence); - JsonObject->TryGetStringField(TEXT("status"), Status); - } -} - - -FNakamaUserPresence::FNakamaUserPresence() : Persistence(false) -{ - -} \ No newline at end of file diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaRPC.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaRPC.cpp deleted file mode 100644 index e1b4dfa3c..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaRPC.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaRPC.h" - -#include "NakamaUtils.h" - -FNakamaRPC::FNakamaRPC(const FString& JsonString) -{ - TSharedPtr RootJsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, RootJsonObject) && RootJsonObject.IsValid()) - { - // Check if the "rpc" object exists - if (RootJsonObject->HasField(TEXT("rpc"))) - { - TSharedPtr JsonObject = RootJsonObject->GetObjectField(TEXT("rpc")); - - // Now extract the fields from the "rpc" object - if (JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("id"), Id); - JsonObject->TryGetStringField(TEXT("payload"), Payload); - JsonObject->TryGetStringField(TEXT("http_key"), HttpKey); - } - } - else - { - RootJsonObject->TryGetStringField(TEXT("id"), Id); - RootJsonObject->TryGetStringField(TEXT("payload"), Payload); - RootJsonObject->TryGetStringField(TEXT("http_key"), HttpKey); - } - } -} - -FNakamaRPC::FNakamaRPC(FString&& JsonString) -{ - TSharedPtr RootJsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(MoveTemp(JsonString)); - - if (FJsonSerializer::Deserialize(JsonReader, RootJsonObject) && RootJsonObject.IsValid()) - { - // Check if the "rpc" object exists - if (RootJsonObject->HasField(TEXT("rpc"))) - { - TSharedPtr JsonObject = RootJsonObject->GetObjectField(TEXT("rpc")); - - // Now extract the fields from the "rpc" object - if (JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("id"), Id); - JsonObject->TryGetStringField(TEXT("payload"), Payload); - JsonObject->TryGetStringField(TEXT("http_key"), HttpKey); - } - } - else - { - RootJsonObject->TryGetStringField(TEXT("id"), Id); - RootJsonObject->TryGetStringField(TEXT("payload"), Payload); - RootJsonObject->TryGetStringField(TEXT("http_key"), HttpKey); - } - } -} - -FNakamaRPC::FNakamaRPC() -{ - -} diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaRealtimeClient.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaRealtimeClient.cpp deleted file mode 100644 index 45b058f10..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaRealtimeClient.cpp +++ /dev/null @@ -1,3124 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaRealtimeClient.h" - -#include "NakamaUtils.h" -#include "NakamaChannelTypes.h" -#include "NakamaRtError.h" -#include "NakamaMatch.h" -#include "NakamaMatchTypes.h" -#include "NakamaNotification.h" -#include "NakamaParty.h" -#include "NakamaStatus.h" -#include "NakamaStreams.h" -#include "WebSocketsModule.h" -#include "GenericPlatform/GenericPlatformHttp.h" - -void UNakamaRealtimeClient::Initialize(const FString& InHost, int32 InPort, bool InSSL) -{ - Host = InHost; - Port = InPort; - bUseSSL = InSSL; - - NAKAMA_LOG_INFO(FString::Printf(TEXT("Nakama Realtime Client Created on Port: %d"), InPort)); -} - -void UNakamaRealtimeClient::UseCustomWebsocket(TSharedPtr CustomWebSocket) -{ - // Check if a previous WebSocket object exists (safety check) - if (WebSocket) - { - CancelAllRequests(ENakamaRtErrorCode::DISCONNECTED); - CleanupWebSocket(); - } - - bIsCustomWebsocketSet = CustomWebSocket.IsValid(); - WebSocket = CustomWebSocket; -} - -void UNakamaRealtimeClient::Connect( - UNakamaSession* Session, - bool bCreateStatus, - const FOnRealtimeClientConnected& Success, - const FOnRealtimeClientConnectionError& ConnectionError) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, ConnectionError](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - ConnectionError.Broadcast(error); - }; - - Connect(Session, bCreateStatus, successCallback, errorCallback); -} - -void UNakamaRealtimeClient::Connect( - UNakamaSession* Session, - bool bCreateStatus, - TFunction Success, - TFunction ConnectionError) -{ - // Start by checking session validity - if (!Session || !Session->IsValidLowLevel()) - { - NAKAMA_LOG_WARN(TEXT("Session is invalid. Aborting connection attempt.")); - - FNakamaRtError InvalidSessionError; - InvalidSessionError.Message = TEXT("Realtime Client Connect. Session is invalid."); - InvalidSessionError.Code = ENakamaRtErrorCode::BAD_INPUT; - - // Execute the provided error callback - if(ConnectionError) - { - ConnectionError(InvalidSessionError); - } - - // Broadcast Multicast Delegate Event - ConnectionErrorEvent.Broadcast(InvalidSessionError); - ConnectionErrorEventNative.Broadcast(InvalidSessionError); - - return; - } - - // Check if connetion is ongoing - // remove this block if you want to create a new socket connection while one is already active - if (ConnectionState != EConnectionState::Disconnected) - { - NAKAMA_LOG_WARN(TEXT("A connection process is currently active. Aborting new connection attempt.")); - - FNakamaRtError ExistingConnectionError; - ExistingConnectionError.Message = TEXT("A connection process is already active."); - ExistingConnectionError.Code = ENakamaRtErrorCode::CONNECT_ERROR; - - // Execute Lambda - if(ConnectionError) - { - ConnectionError(ExistingConnectionError); - } - - // Broadcast Multicast Delegate Event - ConnectionErrorEvent.Broadcast(ExistingConnectionError); - ConnectionErrorEventNative.Broadcast(ExistingConnectionError); - - return; - } - - ConnectionState = EConnectionState::Connecting; - - if(!FModuleManager::Get().IsModuleLoaded("WebSockets")) - { - FModuleManager::Get().LoadModule("WebSockets"); - } - - // Check if a previous WebSocket object exists (safety check) - if (WebSocket) - { - CancelAllRequests(ENakamaRtErrorCode::DISCONNECTED); - CleanupWebSocket(); - } - - if (!bIsCustomWebsocketSet) - { - FString Url; - - if (bUseSSL) - { - Url = TEXT("wss://"); - } - else - { - Url = TEXT("ws://"); - } - - const FString EncodedToken = FGenericPlatformHttp::UrlEncode(Session->GetAuthToken()); - Url += FString(Host + TEXT(":") + FString::FromInt(Port) + TEXT("/ws")); - Url += TEXT("?token=") + EncodedToken; - Url += TEXT("&status=") + FNakamaUtils::BoolToString(bCreateStatus); - - WebSocket = FWebSocketsModule::Get().CreateWebSocket(Url); - } - - WebSocket->OnConnected().AddLambda([this, Success]() - { - NAKAMA_LOG_INFO(TEXT("Realtime Client Connected")); - - ConnectionState = EConnectionState::Connected; - - // Handle callbacks - if(FNakamaUtils::IsRealtimeClientActive(this)) - { - // This function (Connect) - if(Success) - { - Success(); - } - - // Call Lambda - if(OnConnect) - { - OnConnect(); - } - - // Broadcast Event Multicast Delegate - ConnectedEvent.Broadcast(); - ConnectedEventNative.Broadcast(); - } - }); - - WebSocket->OnConnectionError().AddLambda([this, ConnectionError](const FString& Error) - { - NAKAMA_LOG_ERROR(FString::Printf(TEXT("Realtime Client Connection Error: %s"), *Error)); - // Call connection error callback if listener is set and OnConnectionError is bound - // Checking validity is important here - - ConnectionState = EConnectionState::Disconnected; - - FNakamaRtError ConnectionRtError; - ConnectionRtError.Message = Error; - ConnectionRtError.Code = ENakamaRtErrorCode::CONNECT_ERROR; - - // Handle Callbacks - if(FNakamaUtils::IsRealtimeClientActive(this)) - { - // This function (Connect) - if(ConnectionError) - { - ConnectionError(ConnectionRtError); - } - - // Call Lambda - if(OnConnectionError) - { - OnConnectionError(ConnectionRtError); - } - - // Broadcast Event Multicast Delegate - ConnectionErrorEvent.Broadcast(ConnectionRtError); - ConnectionErrorEventNative.Broadcast(ConnectionRtError); - } - }); - - WebSocket->OnClosed().AddLambda([this](int32 StatusCode, const FString& Reason, bool bWasClean) - { - NAKAMA_LOG_INFO(FString::Printf(TEXT("Realtime Client Connection closed with status code: %d, reason: %s, was clean: %d"), StatusCode, *Reason, bWasClean)); - - ConnectionState = EConnectionState::Disconnected; - - CancelAllRequests(ENakamaRtErrorCode::DISCONNECTED); - - // Call disconnect callback if OnDisconnect is bound and DisconnectedEvent is bound - // This validity check is important - if(IsValidLowLevel()) - { - FNakamaDisconnectInfo DisconnectInfo; - DisconnectInfo.Code = DisconnectInfo.ConvertIntToDisconnectCode(StatusCode); - DisconnectInfo.Reason = Reason; - - // Handle remote disconnect - if(!bLocalDisconnectInitiated) - { - DisconnectInfo.Remote = true; - } - else - { - DisconnectInfo.Remote = false; - bLocalDisconnectInitiated = false; // Reset for future use - } - - // Call Lambda - if(OnDisconnect) - { - OnDisconnect(DisconnectInfo); - } - - // Broadcast Event Multicast Delegate - DisconnectedEvent.Broadcast(DisconnectInfo); - DisconnectedEventNative.Broadcast(DisconnectInfo); - } - }); - - WebSocket->OnMessage().AddLambda([this](const FString& MessageString) - { - // Parse the message - HandleReceivedMessage(MessageString); - - // Update the last message timestamp - LastMessageTimestamp = FPlatformTime::Seconds(); - }); - - WebSocket->OnMessageSent().AddLambda([](const FString& MessageString) - { - // Only print message if not ping - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(MessageString); - if (FJsonSerializer::Deserialize(JsonReader, JsonObject)) - { - if (!JsonObject->HasField(TEXT("ping"))) - { - //NAKAMA_LOG_INFO(TEXT("...")); - NAKAMA_LOG_DEBUG(FString::Printf(TEXT("Realtime Client: Sent Message: %s"), *MessageString)); - } - } - }); - - bIsActive = true; - - WebSocket->Connect(); -} - - -/** - * Listen on All Events - */ - -void UNakamaRealtimeClient::SetListenerAllCallbacks() -{ - NAKAMA_LOG_DEBUG(TEXT("You no longer need to call 'SetListenerAllCallbacks()' bind directly to the events instead")); -} - -/** - * Setup Specific Listeners - */ - -void UNakamaRealtimeClient::SetListenerConnectCallback() -{ - NAKAMA_LOG_DEBUG(TEXT("You no longer need to call 'SetListenerConnectCallback()' bind directly to the 'ConnectedEvent' event instead")); -} - -void UNakamaRealtimeClient::SetListenerConnectionErrorCallback() -{ - NAKAMA_LOG_DEBUG(TEXT("You no longer need to call 'SetListenerConnectionErrorCallback()' bind to the 'ConnectionErrorEvent' event instead")); -} - -void UNakamaRealtimeClient::SetListenerDisconnectCallback() -{ - NAKAMA_LOG_DEBUG(TEXT("You no longer need to call 'SetListenerDisconnectCallback()' bind to the 'DisconnectedEvent' event instead")); -} - -void UNakamaRealtimeClient::SetListenerErrorCallback() -{ - NAKAMA_LOG_DEBUG(TEXT("You no longer need to call 'SetListenerErrorCallback()' bind to the 'ErrorEvent' event instead")); -} - -void UNakamaRealtimeClient::SetListenerChannelMessageCallback() -{ - NAKAMA_LOG_DEBUG(TEXT("You no longer need to call 'SetListenerChannelMessageCallback()' bind to the 'ChannelMessageReceived' event instead")); -} - -void UNakamaRealtimeClient::SetListenerChannelPresenceCallback() -{ - NAKAMA_LOG_DEBUG(TEXT("You no longer need to call 'SetListenerChannelPresenceCallback()' bind to the 'ChannelPresenceEventReceived' event instead")); -} - -void UNakamaRealtimeClient::SetListenerMatchmakerMatchedCallback() -{ - NAKAMA_LOG_DEBUG(TEXT("You no longer need to call 'SetListenerMatchmakerMatchedCallback()' bind to the 'MatchmakerMatchMatched' event instead")); -} - -void UNakamaRealtimeClient::SetListenerMatchDataCallback() -{ - NAKAMA_LOG_DEBUG(TEXT("You no longer need to call 'SetListenerMatchDataCallback()' bind to the 'MatchDataCallback' event instead")); -} - -void UNakamaRealtimeClient::SetListenerMatchPresenceCallback() -{ - NAKAMA_LOG_DEBUG(TEXT("You no longer need to call 'SetListenerMatchPresenceCallback()' bind to the 'MatchmakerPresenceCallback' event instead")); -} - -void UNakamaRealtimeClient::SetListenerNotificationsCallback() -{ - NAKAMA_LOG_DEBUG(TEXT("You no longer need to call 'SetListenerNotificationsCallback()' bind to the 'NotificationReceived' event instead")); -} - -void UNakamaRealtimeClient::SetListenerPartyCallback() -{ - NAKAMA_LOG_DEBUG(TEXT("You no longer need to call 'SetListenerPartyCallback()' bind to the 'PartyReceived' event instead")); -} - -void UNakamaRealtimeClient::SetListenerPartyCloseCallback() -{ - NAKAMA_LOG_DEBUG(TEXT("You no longer need to call 'SetListenerPartyCloseCallback()' bind to the 'PartyCloseReceived' event instead")); -} - -void UNakamaRealtimeClient::SetListenerPartyDataCallback() -{ - NAKAMA_LOG_DEBUG(TEXT("You no longer need to call 'SetListenerPartyDataCallback()' bind to the 'PartyDataReceived' event instead")); -} - -void UNakamaRealtimeClient::SetListenerPartyJoinRequestCallback() -{ - NAKAMA_LOG_DEBUG(TEXT("You no longer need to call 'SetListenerPartyJoinRequestCallback()' bind to the 'PartyJoinRequestReceived' event instead")); -} - -void UNakamaRealtimeClient::SetListenerPartyLeaderCallback() -{ - NAKAMA_LOG_DEBUG(TEXT("You no longer need to call 'SetListenerPartyLeaderCallback()' bind to the 'PartyLeaderReceived' event instead")); -} - -void UNakamaRealtimeClient::SetListenerPartyMatchmakerTicketCallback() -{ - NAKAMA_LOG_DEBUG(TEXT("You no longer need to call 'SetListenerPartyMatchmakerTicketCallback()' bind to the 'PartyMatchmakerTicketReceived' event instead")); -} - -void UNakamaRealtimeClient::SetListenerPartyPresenceCallback() -{ - NAKAMA_LOG_DEBUG(TEXT("You no longer need to call 'SetListenerPartyPresenceCallback()' bind to the 'PartyPresenceReceived' event instead")); -} - -void UNakamaRealtimeClient::SetListenerStatusPresenceCallback() -{ - NAKAMA_LOG_DEBUG(TEXT("You no longer need to call 'SetListenerStatusPresenceCallback()' bind to the 'PresenceStatusReceived' event instead")); -} - -void UNakamaRealtimeClient::SetListenerStreamPresenceCallback() -{ - NAKAMA_LOG_DEBUG(TEXT("You no longer need to call 'SetListenerStreamPresenceCallback()' bind to the 'StreamPresenceEventReceived' event instead")); -} - -void UNakamaRealtimeClient::SetListenerStreamDataCallback() -{ - NAKAMA_LOG_DEBUG(TEXT("You no longer need to call 'SetListenerStreamDataCallback()' bind to the 'StreamPresenceDataReceived' event instead")); -} - -void UNakamaRealtimeClient::Destroy() -{ - bIsActive = false; - ConditionalBeginDestroy(); -} - -void UNakamaRealtimeClient::BeginDestroy() -{ - bIsActive = false; - - // Clear the request contexts in a thread-safe manner. - CancelAllRequests(ENakamaRtErrorCode::DISCONNECTED); - - CleanupWebSocket(); - - Super::BeginDestroy(); -} - -void UNakamaRealtimeClient::Disconnect() -{ - if (ConnectionState != EConnectionState::Connected) - { - NAKAMA_LOG_WARN(TEXT("Not currently connected. Aborting disconnect attempt.")); - return; - } - - ConnectionState = EConnectionState::Disconnecting; - - if(!WebSocket.IsValid()) - { - return; - } - - bLocalDisconnectInitiated = true; - - // NOTE: We do NOT clear binding for 'OnClosed' because it will clean up the Socket Connection - WebSocket->OnConnectionError().Clear(); - WebSocket->OnRawMessage().Clear(); - WebSocket->OnConnected().Clear(); - WebSocket->OnMessage().Clear(); - WebSocket->OnMessageSent().Clear(); - WebSocket->Close(); - - // TODO: We could also set 'ConnectionState' to 'Disconnected' here instead of waiting for the 'OnClosed' callback - // if you want that behaviour move the code from 'OnClosed' to here - //ConnectionState = EConnectionState::Disconnected; - //CancelAllRequests(ENakamaRtErrorCode::DISCONNECTED); - // Then broadcast Disconnected Lambda/Multicast Delegate -} - -/** - * Messaging - */ - -void UNakamaRealtimeClient::SendMessage( - const FString& ChannelId, - const FString& Content, - FOnWriteChatMessage Success, - FOnRtError Error) -{ - WriteChatMessage(ChannelId, Content, Success, Error); -} - -void UNakamaRealtimeClient::WriteChatMessage( - const FString& ChannelId, - const FString& Content, - FOnWriteChatMessage Success, - FOnRtError Error) -{ - auto successCallback = [this, Success](const FNakamaChannelMessageAck& ChannelMessageAck) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(ChannelMessageAck); - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - WriteChatMessage(ChannelId, Content, successCallback, errorCallback); -} - -void UNakamaRealtimeClient::SendDirectMessage( - const FString& UserID, - const FString& Content, - FOnWriteChatMessage Success, - FOnRtError Error) -{ - auto successCallback = [this, Success](const FNakamaChannelMessageAck& ChannelMessageAck) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(ChannelMessageAck); - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - WriteChatMessage(UserID, Content, successCallback, errorCallback); -} - -void UNakamaRealtimeClient::UpdateChatMessage( - const FString& ChannelId, - const FString& MessageId, - const FString& Content, - FOnWriteChatMessage Success, - FOnRtError Error) -{ - auto successCallback = [this, Success](const FNakamaChannelMessageAck& ChannelMessageAck) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(ChannelMessageAck); - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - UpdateChatMessage(ChannelId, MessageId, Content, successCallback, errorCallback); -} - -void UNakamaRealtimeClient::RemoveChatMessage( - const FString& ChannelId, - const FString& MessageId, - FOnWriteChatMessage Success, - FOnRtError Error) -{ - auto successCallback = [this, Success](const FNakamaChannelMessageAck& ChannelMessageAck) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(ChannelMessageAck); - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - RemoveChatMessage(ChannelId, MessageId, successCallback, errorCallback); -} - - -/** - * Chat System - */ - -void UNakamaRealtimeClient::JoinChat( - const FString& ChatId, - ENakamaChannelType ChannelType, - bool Persistence, - bool Hidden, - FOnJoinChat Success, - FOnRtError Error) -{ - auto successCallback = [this, Success](const FNakamaChannel& Channel) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(Channel); - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - JoinChat(ChatId, ChannelType, Persistence, Hidden, successCallback, errorCallback); -} - -void UNakamaRealtimeClient::LeaveChat( - const FString& ChannelId, - FOnLeaveChat Success, - FOnRtError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - LeaveChat(ChannelId, successCallback, errorCallback); -} - -/** - * Matchmaking System - */ - -void UNakamaRealtimeClient::AddMatchmaker( - int32 MinCount, - int32 MaxCount, - const FString& Query, - const TMap& StringProperties, - const TMap& NumericProperties, - int32 CountMultiple, - bool IgnoreCountMultiple, - FOnMatchmakerTicket Success, - FOnRtError Error) -{ - auto successCallback = [this, Success](const FNakamaMatchmakerTicket& MatchmakerTicket) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(MatchmakerTicket.TicketId); - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - auto OptMinCount = FNakamaUtils::CreateOptional(MinCount, 0); - auto OptMaxCount = FNakamaUtils::CreateOptional(MaxCount, 0); - const auto OptQuery = FNakamaUtils::CreateOptional(Query, FString()); - const auto OptCountMultiple = FNakamaUtils::CreateOptional(CountMultiple, 0); - - AddMatchmaker( - OptMinCount, - OptMaxCount, - OptQuery, - StringProperties, - NumericProperties, - OptCountMultiple, - successCallback, - errorCallback - ); -} - -void UNakamaRealtimeClient::LeaveMatchmaker( - const FString& Ticket, - FOnRemovedMatchmakerTicket Success, - FOnRtError Error) -{ - RemoveMatchmaker(Ticket, Success, Error); -} - -void UNakamaRealtimeClient::RemoveMatchmaker( - const FString& Ticket, - FOnRemovedMatchmakerTicket Success, - FOnRtError Error) -{ - auto successCallback = [this, Success, Ticket]() - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(Ticket); // Deviation from the C++ SDK by returning the ticket - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - RemoveMatchmaker(Ticket, successCallback, errorCallback); -} - -/** - * Status System - */ - -void UNakamaRealtimeClient::UpdateStatus( - const FString& StatusMessage, - FOnSetStatus Success, - FOnRtError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - UpdateStatus(StatusMessage, successCallback, errorCallback); -} - -void UNakamaRealtimeClient::SetAppearOffline( - FOnSetStatus Success, - FOnRtError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - UpdateStatus("", successCallback, errorCallback); // "Invisible" Status -} - -void UNakamaRealtimeClient::FollowUsers( - const TArray& UserIds, - FOnFollowUsers Success, - FOnRtError Error) -{ - auto successCallback = [this, Success](const FNakamaStatus& Status) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(Status); - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - FollowUsers(UserIds, successCallback, errorCallback); -} - -void UNakamaRealtimeClient::UnFollowUsers( - const TArray& UserIds, - FOnUnFollowUsers Success, - FOnRtError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - UnfollowUsers(UserIds, successCallback, errorCallback); -} - -/** - * Match System - */ - -void UNakamaRealtimeClient::CreateMatch( - FOnCreateMatch Success, - FOnRtError Error) -{ - auto successCallback = [this, Success](const FNakamaMatch& Match) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(Match); - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - CreateMatch(successCallback, errorCallback); -} - -void UNakamaRealtimeClient::JoinMatch( - const FString& MatchId, - const TMap& MetaData, - FOnCreateMatch Success, - FOnRtError Error) -{ - auto successCallback = [this, Success](const FNakamaMatch& Match) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(Match); - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - JoinMatch(MatchId, MetaData, successCallback, errorCallback); -} - -void UNakamaRealtimeClient::JoinMatchByToken( - const FString& Token, - FOnCreateMatch Success, - FOnRtError Error) -{ - auto successCallback = [this, Success](const FNakamaMatch& Match) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(Match); - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - JoinMatchByToken(Token, successCallback, errorCallback); -} - -void UNakamaRealtimeClient::SendMatchData( - const FString& MatchId, - int64 OpCode, - const FString& Data, - const TArray& Presences) -{ - // Setup the json object - const TSharedPtr MatchDataSend = MakeShareable(new FJsonObject()); - MatchDataSend->SetStringField(TEXT("match_id"), MatchId); - MatchDataSend->SetNumberField(TEXT("op_code"), OpCode); - - FString EncodeData = FNakamaUtils::Base64Encode(Data); - MatchDataSend->SetStringField(TEXT("data"), EncodeData); - - if (Presences.Num() > 0) - { - TArray> PresencesJsonArray; - for (const FNakamaUserPresence& Presence : Presences) - { - if (Presence.UserID.IsEmpty()) - { - NAKAMA_LOG_WARN(TEXT("Please set 'UserID' for user presence")); - continue; - } - - if (Presence.SessionID.IsEmpty()) - { - NAKAMA_LOG_WARN(TEXT("Please set 'SessionID' for user presence")); - continue; - } - - TSharedPtr PresenceJson = MakeShared(); - PresenceJson->SetStringField(TEXT("user_id"), Presence.UserID); - PresenceJson->SetStringField(TEXT("session_id"), Presence.SessionID); - - if (!Presence.Username.IsEmpty()) - { - PresenceJson->SetStringField(TEXT("username"), Presence.Username); - } - - PresencesJsonArray.Add(MakeShared(PresenceJson)); - } - - MatchDataSend->SetArrayField(TEXT("presences"), PresencesJsonArray); - } - - // This does not have callbacks - SendMessageWithEnvelope(TEXT("match_data_send"), MatchDataSend, {}, {}); -} - -void UNakamaRealtimeClient::LeaveMatch( - const FString& MatchId, - FOnLeaveMatch Success, - FOnRtError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - LeaveMatch(MatchId, successCallback, errorCallback); -} - -/** - * Party System - */ - - -void UNakamaRealtimeClient::CreateParty( - bool Open, - int32 MaxSize, - FOnCreateParty Success, - FOnRtError Error) -{ - auto successCallback = [this, Success](const FNakamaParty& Party) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(Party); - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - CreateParty(Open, MaxSize, successCallback, errorCallback); -} - -void UNakamaRealtimeClient::JoinParty( - const FString& PartyId, - FOnJoinParty Success, - FOnRtError Error) -{ - auto successCallback = [this, Success, PartyId]() - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(PartyId); // Deviation from C++ SDK by passing PartyId - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - JoinParty(PartyId, successCallback, errorCallback); -} - -void UNakamaRealtimeClient::LeaveParty( - const FString& PartyId, - FOnLeaveParty Success, - FOnRtError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - LeaveParty(PartyId, successCallback, errorCallback); -} - -void UNakamaRealtimeClient::ListPartyJoinRequests( - const FString& PartyId, - FOnListPartyJoinRequests Success, - FOnRtError Error) -{ - auto successCallback = [this, Success](const FNakamaPartyJoinRequest& PartyJoinRequest) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(PartyJoinRequest); - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - ListPartyJoinRequests(PartyId, successCallback, errorCallback); -} - -void UNakamaRealtimeClient::PromotePartyMember( - const FString& PartyId, - const FNakamaUserPresence& PartyMember, - FOnPromotePartyMember Success, - FOnRtError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - PromotePartyMember(PartyId, PartyMember, successCallback, errorCallback); -} - -void UNakamaRealtimeClient::RemoveMatchMakerParty( - const FString& PartyId, - const FString& Ticket, - FOnRemoveMatchmakerParty Success, - FOnRtError Error) -{ - auto successCallback = [this, Success, Ticket]() - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(Ticket); // Deviation from C++ SDK by passing Ticket - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - RemoveMatchmakerParty(PartyId, Ticket, successCallback, errorCallback); -} - -void UNakamaRealtimeClient::RemovePartyMember( - const FString& PartyId, - const FNakamaUserPresence& Presence, - FOnRemovePartyMember Success, - FOnRtError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - RemovePartyMember(PartyId, Presence, successCallback, errorCallback); -} - -void UNakamaRealtimeClient::AcceptPartyMember( - const FString& PartyId, - const FNakamaUserPresence& Presence, - FOnAcceptPartyMember Success, - FOnRtError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - AcceptPartyMember(PartyId, Presence, successCallback, errorCallback); -} - -void UNakamaRealtimeClient::AddMatchmakerParty( - const FString& PartyId, - const FString& Query, - int32 MinCount, - int32 MaxCount, - const TMap& StringProperties, - const TMap& NumericProperties, - int32 CountMultiple, - bool IgnoreCountMultiple, - FOnAddMatchmakerParty Success, - FOnRtError Error) -{ - auto successCallback = [this, Success](const FNakamaPartyMatchmakerTicket& MatchmakerTicket) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(MatchmakerTicket); - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - auto OptMinCount = FNakamaUtils::CreateOptional(MinCount, 0); - auto OptMaxCount = FNakamaUtils::CreateOptional(MaxCount, 0); - const auto OptQuery = FNakamaUtils::CreateOptional(Query, FString()); - const auto OptCountMultiple = FNakamaUtils::CreateOptional(CountMultiple, 0); - - AddMatchmakerParty( - PartyId, - OptMinCount, - OptMaxCount, - OptQuery, - StringProperties, - NumericProperties, - OptCountMultiple, - successCallback, - errorCallback - ); -} - -void UNakamaRealtimeClient::CloseParty( - const FString& PartyId, - FOnCloseParty Success, - FOnRtError Error) -{ - auto successCallback = [this, Success]() - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - CloseParty(PartyId, successCallback, errorCallback); -} - -void UNakamaRealtimeClient::RPC(const FString& FunctionId, const FString& Payload, FOnRtRPC Success, FOnRtError Error) -{ - auto successCallback = [this, Success](const FNakamaRPC& rpc) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Success.Broadcast(rpc); - }; - - auto errorCallback = [this, Error](const FNakamaRtError& error) - { - if(!FNakamaUtils::IsRealtimeClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - RPC(FunctionId, Payload, successCallback, errorCallback); -} - -void UNakamaRealtimeClient::JoinChat( - const FString& Target, - ENakamaChannelType ChannelType, - TOptional Persistence, - TOptional Hidden, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the json object - const TSharedPtr ChannelJoin = MakeShareable(new FJsonObject()); - ChannelJoin->SetStringField(TEXT("target"), Target); - ChannelJoin->SetNumberField(TEXT("type"), static_cast(ChannelType)); - if (Persistence.IsSet()) - { - ChannelJoin->SetBoolField(TEXT("persistence"), Persistence.GetValue()); - } - if (Hidden.IsSet()) - { - ChannelJoin->SetBoolField(TEXT("hidden"), Hidden.GetValue()); - } - - SendMessageWithEnvelope(TEXT("channel_join"), ChannelJoin, - [SuccessCallback](const FNakamaRealtimeEnvelope& Envelope) - { - if (SuccessCallback) - { - FNakamaChannel Channel = FNakamaChannel(Envelope.Payload); - SuccessCallback(Channel); - } - }, - [ErrorCallback](const FNakamaRtError& Error) - { - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - ); - -} - -void UNakamaRealtimeClient::LeaveChat( - const FString& ChannelId, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the json object - const TSharedPtr ChannelLeave = MakeShareable(new FJsonObject()); - ChannelLeave->SetStringField(TEXT("channel_id"), ChannelId); - - SendMessageWithEnvelope(TEXT("channel_leave"), ChannelLeave, - [SuccessCallback](const FNakamaRealtimeEnvelope& Envelope) - { - if (SuccessCallback) - { - SuccessCallback(); - } - }, - [ErrorCallback](const FNakamaRtError& Error) - { - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - ); -} - -void UNakamaRealtimeClient::WriteChatMessage( - const FString& ChannelId, - const FString& Content, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the json object - const TSharedPtr ChannelMessageSend = MakeShareable(new FJsonObject()); - ChannelMessageSend->SetStringField(TEXT("channel_id"), ChannelId); - ChannelMessageSend->SetStringField(TEXT("content"), Content); - - SendMessageWithEnvelope(TEXT("channel_message_send"), ChannelMessageSend, - [SuccessCallback](const FNakamaRealtimeEnvelope& Envelope) - { - if (SuccessCallback) - { - FNakamaChannelMessageAck ChannelMessageAck = FNakamaChannelMessageAck(Envelope.Payload); - SuccessCallback(ChannelMessageAck); - } - }, - [ErrorCallback](const FNakamaRtError& Error) - { - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - ); -} - -void UNakamaRealtimeClient::UpdateChatMessage( - const FString& ChannelId, - const FString& MessageId, - const FString& Content, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the json object - const TSharedPtr ChannelMessageUpdate = MakeShareable(new FJsonObject()); - ChannelMessageUpdate->SetStringField(TEXT("channel_id"), ChannelId); - ChannelMessageUpdate->SetStringField(TEXT("message_id"), MessageId); - ChannelMessageUpdate->SetStringField(TEXT("content"), Content); - - SendMessageWithEnvelope(TEXT("channel_message_update"), ChannelMessageUpdate, - [SuccessCallback](const FNakamaRealtimeEnvelope& Envelope) - { - if (SuccessCallback) - { - FNakamaChannelMessageAck ChannelMessageAck = FNakamaChannelMessageAck(Envelope.Payload); - SuccessCallback(ChannelMessageAck); - } - }, - [ErrorCallback](const FNakamaRtError& Error) - { - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - ); -} - -void UNakamaRealtimeClient::RemoveChatMessage(const FString& ChannelId, - const FString& MessageId, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the json object - const TSharedPtr ChannelMessageRemove = MakeShareable(new FJsonObject()); - ChannelMessageRemove->SetStringField(TEXT("channel_id"), ChannelId); - ChannelMessageRemove->SetStringField(TEXT("message_id"), MessageId); - - SendMessageWithEnvelope(TEXT("channel_message_remove"), ChannelMessageRemove, - [SuccessCallback](const FNakamaRealtimeEnvelope& Envelope) - { - if (SuccessCallback) - { - FNakamaChannelMessageAck ChannelMessageAck = FNakamaChannelMessageAck(Envelope.Payload); - SuccessCallback(ChannelMessageAck); - } - }, - [ErrorCallback](const FNakamaRtError& Error) - { - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - ); -} - -void UNakamaRealtimeClient::CreateMatch( - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - SendMessageWithEnvelope(TEXT("match_create"), {}, - [SuccessCallback](const FNakamaRealtimeEnvelope& Envelope) - { - if (SuccessCallback) - { - FNakamaMatch Match = FNakamaMatch(Envelope.Payload); - SuccessCallback(Match); - } - }, - [ErrorCallback](const FNakamaRtError& Error) - { - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - ); -} - -void UNakamaRealtimeClient::JoinMatch( - const FString& MatchId, - const TMap& Metadata, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the json object - const TSharedPtr MatchJoin = MakeShareable(new FJsonObject()); - MatchJoin->SetStringField(TEXT("match_id"), MatchId); - - const TSharedPtr MetadataJson = MakeShareable(new FJsonObject()); - for (auto& Entry : Metadata) - { - MetadataJson->SetStringField(Entry.Key, Entry.Value); - } - - MatchJoin->SetObjectField(TEXT("metadata"), MetadataJson); - - SendMessageWithEnvelope(TEXT("match_join"), MatchJoin, - [SuccessCallback](const FNakamaRealtimeEnvelope& Envelope) - { - if (SuccessCallback) - { - FNakamaMatch Match = FNakamaMatch(Envelope.Payload); - SuccessCallback(Match); - } - }, - [ErrorCallback](const FNakamaRtError& Error) - { - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - ); -} - -void UNakamaRealtimeClient::JoinMatchByToken( - const FString& Token, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the json object - const TSharedPtr MatchJoin = MakeShareable(new FJsonObject()); - MatchJoin->SetStringField(TEXT("token"), Token); - - SendMessageWithEnvelope(TEXT("match_join"), MatchJoin, - [SuccessCallback](const FNakamaRealtimeEnvelope& Envelope) - { - if (SuccessCallback) - { - FNakamaMatch Match = FNakamaMatch(Envelope.Payload); - SuccessCallback(Match); - } - }, - [ErrorCallback](const FNakamaRtError& Error) - { - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - ); -} - -void UNakamaRealtimeClient::LeaveMatch( - const FString& MatchId, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the json object - const TSharedPtr MatchLeave = MakeShareable(new FJsonObject()); - MatchLeave->SetStringField(TEXT("match_id"), MatchId); - - SendMessageWithEnvelope(TEXT("match_leave"), MatchLeave, - [SuccessCallback](const FNakamaRealtimeEnvelope& Envelope) - { - if (SuccessCallback) - { - SuccessCallback(); - } - }, - [ErrorCallback](const FNakamaRtError& Error) - { - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - ); -} - -void UNakamaRealtimeClient::AddMatchmaker( - TOptional& MinCount, - TOptional& MaxCount, - const TOptional& Query, - const TMap& StringProperties, - const TMap& NumericProperties, - const TOptional& CountMultiple, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the json object - const TSharedPtr MatchmakerAdd = MakeShareable(new FJsonObject()); - if (MinCount.IsSet()) - { - MatchmakerAdd->SetNumberField(TEXT("min_count"), MinCount.GetValue()); - } - if (MaxCount.IsSet()) - { - MatchmakerAdd->SetNumberField(TEXT("max_count"), MaxCount.GetValue()); - } - if (Query.IsSet()) - { - MatchmakerAdd->SetStringField(TEXT("query"), Query.GetValue()); - } - if (CountMultiple.IsSet()) - { - MatchmakerAdd->SetNumberField(TEXT("count_multiple"), CountMultiple.GetValue()); - } - - if (StringProperties.Num() > 0) - { - const TSharedPtr StringPropertiesJson = MakeShareable(new FJsonObject()); - for (auto& Entry : StringProperties) - { - if(!Entry.Key.IsEmpty() && !Entry.Value.IsEmpty()) - { - StringPropertiesJson->SetStringField(Entry.Key, Entry.Value); - } - } - MatchmakerAdd->SetObjectField(TEXT("string_properties"), StringPropertiesJson); - } - - if (NumericProperties.Num() > 0) - { - const TSharedPtr NumericPropertiesJson = MakeShareable(new FJsonObject()); - for (auto& Entry : NumericProperties) - { - if(!Entry.Key.IsEmpty()) - { - NumericPropertiesJson->SetNumberField(Entry.Key, Entry.Value); - } - } - MatchmakerAdd->SetObjectField(TEXT("numeric_properties"), NumericPropertiesJson); - } - - SendMessageWithEnvelope(TEXT("matchmaker_add"), MatchmakerAdd, - [SuccessCallback](const FNakamaRealtimeEnvelope& Envelope) - { - if (SuccessCallback) - { - FNakamaMatchmakerTicket MatchmakerTicket = FNakamaMatchmakerTicket(Envelope.Payload); - SuccessCallback(MatchmakerTicket); - } - }, - [ErrorCallback](const FNakamaRtError& Error) - { - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - ); -} - -void UNakamaRealtimeClient::RemoveMatchmaker( - const FString& Ticket, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the json object - const TSharedPtr MatchmakerRemove = MakeShareable(new FJsonObject()); - MatchmakerRemove->SetStringField(TEXT("ticket"), Ticket); - - SendMessageWithEnvelope(TEXT("matchmaker_remove"), MatchmakerRemove, - [SuccessCallback](const FNakamaRealtimeEnvelope& Envelope) - { - if (SuccessCallback) - { - SuccessCallback(); - } - }, - [ErrorCallback](const FNakamaRtError& Error) - { - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - ); -} - -void UNakamaRealtimeClient::FollowUsers( - const TArray& UserIds, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the json object - const TSharedPtr StatusFollowUsers = MakeShared(); - TArray> UserIdsJsonArray; - - for (const FString& UserId : UserIds) - { - UserIdsJsonArray.Add(MakeShared(UserId)); - } - - StatusFollowUsers->SetArrayField(TEXT("user_ids"), UserIdsJsonArray); - - SendMessageWithEnvelope(TEXT("status_follow"), StatusFollowUsers, - [SuccessCallback](const FNakamaRealtimeEnvelope& Envelope) - { - if (SuccessCallback) - { - FNakamaStatus Status = FNakamaStatus(Envelope.Payload); - SuccessCallback(Status); - } - }, - [ErrorCallback](const FNakamaRtError& Error) - { - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - ); -} - -void UNakamaRealtimeClient::UnfollowUsers( - const TArray& UserIds, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the json object - const TSharedPtr StatusUnfollowUsers = MakeShared(); - TArray> UserIdsJsonArray; - - for (const FString& UserId : UserIds) - { - UserIdsJsonArray.Add(MakeShared(UserId)); - } - - StatusUnfollowUsers->SetArrayField(TEXT("user_ids"), UserIdsJsonArray); - - SendMessageWithEnvelope(TEXT("status_unfollow"), StatusUnfollowUsers, - [SuccessCallback](const FNakamaRealtimeEnvelope& Envelope) - { - if (SuccessCallback) - { - SuccessCallback(); - } - }, - [ErrorCallback](const FNakamaRtError& Error) - { - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - ); -} - -void UNakamaRealtimeClient::UpdateStatus( - const FString& Status, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the json object - const TSharedPtr StatusUpdate = MakeShared(); - StatusUpdate->SetStringField(TEXT("status"), Status); - - SendMessageWithEnvelope(TEXT("status_update"), StatusUpdate, - [SuccessCallback](const FNakamaRealtimeEnvelope& Envelope) - { - if (SuccessCallback) - { - SuccessCallback(); - } - }, - [ErrorCallback](const FNakamaRtError& Error) - { - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - ); -} - -void UNakamaRealtimeClient::RPC( - const FString& Id, - const TOptional& Payload, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the json object - const TSharedPtr RpcSend = MakeShared(); - RpcSend->SetStringField(TEXT("id"), Id); - - if (Payload.IsSet()) - { - RpcSend->SetStringField(TEXT("payload"), Payload.GetValue()); - } - - SendMessageWithEnvelope(TEXT("rpc"), RpcSend, - [SuccessCallback](const FNakamaRealtimeEnvelope& Envelope) - { - if (SuccessCallback) - { - FNakamaRPC Rpc = FNakamaRPC(Envelope.Payload); - SuccessCallback(Rpc); - } - }, - [ErrorCallback](const FNakamaRtError& Error) - { - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - ); -} - -void UNakamaRealtimeClient::AcceptPartyMember( - const FString& PartyId, - const FNakamaUserPresence& Presence, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the json object - const TSharedPtr PartyAccept = MakeShared(); - PartyAccept->SetStringField(TEXT("party_id"), PartyId); - - // Create the presence JSON object - const TSharedPtr PresenceJson = MakeShared(); - PresenceJson->SetStringField(TEXT("user_id"), Presence.UserID); - PresenceJson->SetBoolField(TEXT("persistence"), Presence.Persistence); - PresenceJson->SetStringField(TEXT("session_id"), Presence.SessionID); - PresenceJson->SetStringField(TEXT("username"), Presence.Username); - - // Set the presence JSON object in the PartyAccept JSON object - PartyAccept->SetObjectField(TEXT("presence"), PresenceJson); - - SendMessageWithEnvelope(TEXT("party_accept"), PartyAccept, - [SuccessCallback](const FNakamaRealtimeEnvelope& Envelope) - { - if (SuccessCallback) - { - SuccessCallback(); - } - }, - [ErrorCallback](const FNakamaRtError& Error) - { - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - ); -} - -void UNakamaRealtimeClient::AddMatchmakerParty( - const FString& PartyId, - TOptional& MinCount, - TOptional& MaxCount, - const TOptional& Query, - const TMap& StringProperties, - const TMap& NumericProperties, - const TOptional& CountMultiple, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the json object - const TSharedPtr PartyMatchmakerAdd = MakeShared(); - PartyMatchmakerAdd->SetStringField(TEXT("party_id"), PartyId); - - if (MinCount.IsSet()) - { - PartyMatchmakerAdd->SetNumberField(TEXT("min_count"), MinCount.GetValue()); - } - - if (MaxCount.IsSet()) - { - PartyMatchmakerAdd->SetNumberField(TEXT("max_count"), MaxCount.GetValue()); - } - - if (Query.IsSet()) - { - PartyMatchmakerAdd->SetStringField(TEXT("query"), Query.GetValue()); - } - - if (StringProperties.Num() > 0) - { - const TSharedPtr StringPropertiesJson = MakeShared(); - for (const auto& StringProperty : StringProperties) - { - StringPropertiesJson->SetStringField(StringProperty.Key, StringProperty.Value); - } - - PartyMatchmakerAdd->SetObjectField(TEXT("string_properties"), StringPropertiesJson); - } - - if (NumericProperties.Num() > 0) - { - const TSharedPtr NumericPropertiesJson = MakeShared(); - for (const auto& NumericProperty : NumericProperties) - { - NumericPropertiesJson->SetNumberField(NumericProperty.Key, NumericProperty.Value); - } - - PartyMatchmakerAdd->SetObjectField(TEXT("numeric_properties"), NumericPropertiesJson); - } - - if (CountMultiple.IsSet()) - { - PartyMatchmakerAdd->SetNumberField(TEXT("count_multiple"), CountMultiple.GetValue()); - } - - SendMessageWithEnvelope(TEXT("party_matchmaker_add"), PartyMatchmakerAdd, - [SuccessCallback](const FNakamaRealtimeEnvelope& Envelope) - { - if (SuccessCallback) - { - FNakamaPartyMatchmakerTicket MatchmakerTicket = FNakamaPartyMatchmakerTicket(Envelope.Payload); - SuccessCallback(MatchmakerTicket); - } - }, - [ErrorCallback](const FNakamaRtError& Error) - { - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - ); -} - -void UNakamaRealtimeClient::CloseParty( - const FString& PartyId, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the json object - const TSharedPtr PartyClose = MakeShared(); - PartyClose->SetStringField(TEXT("party_id"), PartyId); - - SendMessageWithEnvelope(TEXT("party_close"), PartyClose, - [SuccessCallback](const FNakamaRealtimeEnvelope& Envelope) - { - if (SuccessCallback) - { - SuccessCallback(); - } - }, - [ErrorCallback](const FNakamaRtError& Error) - { - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - ); -} - -void UNakamaRealtimeClient::CreateParty( - bool bOpen, - int32 MaxSize, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the json object - const TSharedPtr PartyCreate = MakeShared(); - PartyCreate->SetBoolField(TEXT("open"), bOpen); - PartyCreate->SetNumberField(TEXT("max_size"), MaxSize); - - SendMessageWithEnvelope(TEXT("party_create"), PartyCreate, - [SuccessCallback](const FNakamaRealtimeEnvelope& Envelope) - { - if (SuccessCallback) - { - FNakamaParty Party = FNakamaParty(Envelope.Payload); - SuccessCallback(Party); - } - }, - [ErrorCallback](const FNakamaRtError& Error) - { - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - ); -} - -void UNakamaRealtimeClient::JoinParty( - const FString& PartyId, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the json object - const TSharedPtr PartyJoin = MakeShared(); - PartyJoin->SetStringField(TEXT("party_id"), PartyId); - - SendMessageWithEnvelope(TEXT("party_join"), PartyJoin, - [SuccessCallback](const FNakamaRealtimeEnvelope& Envelope) - { - if (SuccessCallback) - { - SuccessCallback(); - } - }, - [ErrorCallback](const FNakamaRtError& Error) - { - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - ); -} - -void UNakamaRealtimeClient::LeaveParty( - const FString& PartyId, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the json object - const TSharedPtr PartyLeave = MakeShared(); - PartyLeave->SetStringField(TEXT("party_id"), PartyId); - - SendMessageWithEnvelope(TEXT("party_leave"), PartyLeave, - [SuccessCallback](const FNakamaRealtimeEnvelope& Envelope) - { - if (SuccessCallback) - { - SuccessCallback(); - } - }, - [ErrorCallback](const FNakamaRtError& Error) - { - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - ); -} - -void UNakamaRealtimeClient::ListPartyJoinRequests( - const FString& PartyId, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the json object - const TSharedPtr PartyJoinRequestList = MakeShared(); - PartyJoinRequestList->SetStringField(TEXT("party_id"), PartyId); - - SendMessageWithEnvelope(TEXT("party_join_request_list"), PartyJoinRequestList, - [SuccessCallback](const FNakamaRealtimeEnvelope& Envelope) - { - if (SuccessCallback) - { - FNakamaPartyJoinRequest PartyJoinRequest = FNakamaPartyJoinRequest(Envelope.Payload); - SuccessCallback(PartyJoinRequest); - } - }, - [ErrorCallback](const FNakamaRtError& Error) - { - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - ); -} - -void UNakamaRealtimeClient::PromotePartyMember( - const FString& PartyId, - const FNakamaUserPresence& PartyMember, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the json object - const TSharedPtr PartyPromote = MakeShared(); - PartyPromote->SetStringField(TEXT("party_id"), PartyId); - - // Create the presence JSON object - const TSharedPtr PresenceJson = MakeShared(); - PresenceJson->SetStringField(TEXT("user_id"), PartyMember.UserID); - PresenceJson->SetBoolField(TEXT("persistence"), PartyMember.Persistence); - PresenceJson->SetStringField(TEXT("session_id"), PartyMember.SessionID); - PresenceJson->SetStringField(TEXT("username"), PartyMember.Username); - - // Set the presence JSON object in the PartyAccept JSON object - PartyPromote->SetObjectField(TEXT("presence"), PresenceJson); - - SendMessageWithEnvelope(TEXT("party_promote"), PartyPromote, - [SuccessCallback](const FNakamaRealtimeEnvelope& Envelope) - { - if (SuccessCallback) - { - SuccessCallback(); - } - }, - [ErrorCallback](const FNakamaRtError& Error) - { - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - ); -} - -void UNakamaRealtimeClient::RemoveMatchmakerParty( - const FString& PartyId, - const FString& Ticket, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the json object - const TSharedPtr PartyRemoveMatchmaker = MakeShared(); - PartyRemoveMatchmaker->SetStringField(TEXT("party_id"), PartyId); - PartyRemoveMatchmaker->SetStringField(TEXT("ticket"), Ticket); - - SendMessageWithEnvelope(TEXT("party_matchmaker_remove"), PartyRemoveMatchmaker, - [SuccessCallback](const FNakamaRealtimeEnvelope& Envelope) - { - if (SuccessCallback) - { - SuccessCallback(); - } - }, - [ErrorCallback](const FNakamaRtError& Error) - { - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - ); -} - -void UNakamaRealtimeClient::RemovePartyMember( - const FString& PartyId, - const FNakamaUserPresence& Presence, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the json object - const TSharedPtr PartyRemoveMember = MakeShared(); - PartyRemoveMember->SetStringField(TEXT("party_id"), PartyId); - - // Create the presence JSON object - const TSharedPtr PresenceJson = MakeShared(); - PresenceJson->SetStringField(TEXT("user_id"), Presence.UserID); - PresenceJson->SetBoolField(TEXT("persistence"), Presence.Persistence); - PresenceJson->SetStringField(TEXT("session_id"), Presence.SessionID); - PresenceJson->SetStringField(TEXT("username"), Presence.Username); - - // Set the presence JSON object in the PartyAccept JSON object - PartyRemoveMember->SetObjectField(TEXT("presence"), PresenceJson); - - SendMessageWithEnvelope(TEXT("party_remove"), PartyRemoveMember, - [SuccessCallback](const FNakamaRealtimeEnvelope& Envelope) - { - if (SuccessCallback) - { - SuccessCallback(); - } - }, - [ErrorCallback](const FNakamaRtError& Error) - { - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - ); -} - -void UNakamaRealtimeClient::SendPartyData( - const FString& PartyId, - int64 OpCode, - const FString& Data) -{ - // Setup the json object - const TSharedPtr PartySendData = MakeShared(); - PartySendData->SetStringField(TEXT("party_id"), PartyId); - PartySendData->SetNumberField(TEXT("op_code"), OpCode); - PartySendData->SetStringField(TEXT("data"), Data); - - const FString EncodeData = FNakamaUtils::Base64Encode(Data); - PartySendData->SetStringField(TEXT("data"), EncodeData); - - // This does not have callbacks - SendMessageWithEnvelope(TEXT("party_data_send"), PartySendData, {}, {}); -} - - -bool UNakamaRealtimeClient::IsConnected() const -{ - if(!WebSocket) - { - return false; - } - - return WebSocket->IsConnected(); -} - -int32 UNakamaRealtimeClient::GetHeartbeatIntervalMs() const -{ - return HeartbeatIntervalMs; -} - -void UNakamaRealtimeClient::SetHeartbeatIntervalMs(int32 IntervalMs) -{ - HeartbeatIntervalMs = IntervalMs; -} - -TObjectPtr UNakamaRealtimeClient::CreateReqContext(FNakamaRealtimeEnvelope& envelope) -{ - FScopeLock Lock(&ReqContextsLock); - - if (ReqContexts.Num() == 0 && NextCid > 9) - { - // Reset just to be one digit - // We can reset because there are no pending requests - NextCid = 0; - } - - TObjectPtr ReqContext = NewObject(); - - int32_t Cid = 0; - bool Inserted = false; - - int32_t NumTries = 10; - while (!Inserted && --NumTries > 0) - { - Cid = NextCid++; - Inserted = (ReqContexts.Add(Cid, ReqContext) != nullptr); - if (!Inserted) - { - NAKAMA_LOG_ERROR(FString::Printf(TEXT("Creating request with already assigned CID=%d"), Cid)); - } - } - - envelope.CID = Cid; - ReqContext->CID = Cid; - - return ReqContext; -} - -void UNakamaRealtimeClient::SendMessageWithEnvelope(const FString& FieldName, - const TSharedPtr& ObjectField, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Check WebSocket before sending Data - if (!WebSocket || !WebSocket->IsConnected()) - { - FNakamaRtError Error; - Error.Message = TEXT("WebSocket is not valid or not connected."); - Error.Code = ENakamaRtErrorCode::TRANSPORT_ERROR; - NAKAMA_LOG_ERROR(Error.Message); - - if(ErrorCallback) - { - ErrorCallback(Error); - } - - return; - } - - // Create the Envelope with the object field - const TSharedPtr Envelope = MakeShareable(new FJsonObject()); - //Envelope->SetObjectField(FieldName, ObjectField); - Envelope->SetObjectField(FieldName, ObjectField != nullptr ? ObjectField : MakeShareable(new FJsonObject())); - - // Make the Envelope - FNakamaRealtimeEnvelope NakamaEnvelope; - - // Create Context from the Envelope - TObjectPtr ReqContext = CreateReqContext(NakamaEnvelope); - Envelope->SetStringField(TEXT("cid"), FString::FromInt(ReqContext->CID)); - - // Envelope is basically just holding a reference to the Payload in the SuccessCallback (makes it generic) - // It is being set in the HandleMessage function - // Finally: Call the SuccessCallback with the Envelope - ReqContext->SuccessCallback.BindLambda([this, SuccessCallback](const FNakamaRealtimeEnvelope& ChannelEnvelope) - { - if(SuccessCallback) - { - SuccessCallback(ChannelEnvelope); - } - }); - - // Made into FNakamaRtError inside the HandleMessage function - ReqContext->ErrorCallback.BindLambda([this, ErrorCallback](const FNakamaRtError& Error) - { - if(ErrorCallback) - { - ErrorCallback(Error); - } - }); - - // Set the payload - NakamaEnvelope.Payload = FNakamaUtils::EncodeJson(Envelope); - - // Send Message - const FString Message = NakamaEnvelope.Payload; - WebSocket->Send(Message); - - NAKAMA_LOG_INFO(FString::Printf(TEXT("Realtime Client - Request %s sent with CID: %d"), *FieldName, ReqContext->CID)); -} - -void UNakamaRealtimeClient::SendMessageWithEnvelopeMove(const FString& FieldName, - const TSharedPtr& ObjectField, TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Check WebSocket before sending Data - if (!WebSocket || !WebSocket->IsConnected()) - { - FNakamaRtError Error; - Error.Message = TEXT("WebSocket is not valid or not connected."); - Error.Code = ENakamaRtErrorCode::TRANSPORT_ERROR; - NAKAMA_LOG_ERROR(Error.Message); - - if(ErrorCallback) - { - ErrorCallback(Error); - } - - return; - } - - // Create the Envelope with the object field - const TSharedPtr Envelope = MakeShareable(new FJsonObject()); - //Envelope->SetObjectField(FieldName, ObjectField); - Envelope->SetObjectField(FieldName, ObjectField != nullptr ? ObjectField : MakeShareable(new FJsonObject())); - - // Make the Envelope - FNakamaRealtimeEnvelope NakamaEnvelope; - - // Create Context from the Envelope - TObjectPtr ReqContext = CreateReqContext(NakamaEnvelope); - Envelope->SetStringField(TEXT("cid"), FString::FromInt(ReqContext->CID)); - - // Envelope is basically just holding a reference to the Payload in the SuccessCallback (makes it generic) - // It is being set in the HandleMessage function - // Finally: Call the SuccessCallback with the Envelope - ReqContext->SuccessCallbackMove.BindLambda([this, SuccessCallback](FNakamaRealtimeEnvelope&& ChannelEnvelope) - { - if(SuccessCallback) - { - SuccessCallback(MoveTemp(ChannelEnvelope)); - } - }); - - // Made into FNakamaRtError inside the HandleMessage function - ReqContext->ErrorCallback.BindLambda([this, ErrorCallback](const FNakamaRtError& Error) - { - if(ErrorCallback) - { - ErrorCallback(Error); - } - }); - - // Set the payload - NakamaEnvelope.Payload = FNakamaUtils::EncodeJson(Envelope); - - // Send Message - const FString Message = NakamaEnvelope.Payload; - WebSocket->Send(Message); - - NAKAMA_LOG_INFO(FString::Printf(TEXT("Realtime Client - Request %s sent with CID: %d"), *FieldName, ReqContext->CID)); -} - -void UNakamaRealtimeClient::SendDataWithEnvelope(const FString& FieldName, const TSharedPtr& ObjectField) -{ - // Check WebSocket before sending Data - if (!WebSocket || !WebSocket->IsConnected()) - { - NAKAMA_LOG_ERROR(TEXT("WebSocket is not valid or not connected.")); - return; - } - - // Create the Envelope with the object field - const TSharedPtr Envelope = MakeShareable(new FJsonObject()); - //Envelope->SetObjectField(FieldName, ObjectField); - Envelope->SetObjectField(FieldName, ObjectField != nullptr ? ObjectField : MakeShareable(new FJsonObject())); - - // Make the Envelope - FNakamaRealtimeEnvelope NakamaEnvelope; - - // Create Context from the Envelope - const TObjectPtr ReqContext = CreateReqContext(NakamaEnvelope); - Envelope->SetStringField(TEXT("cid"), FString::FromInt(ReqContext->CID)); - - // Set the payload - FString JsonPayload = FNakamaUtils::EncodeJson(Envelope); - - const FString EncodeData = FBase64::Encode(JsonPayload); - NakamaEnvelope.Payload = EncodeData; - - // Send Message - const FString Message = NakamaEnvelope.Payload; - WebSocket->Send(Message); - - NAKAMA_LOG_DEBUG(FString::Printf(TEXT("%s request sent with CID=%d"), *FieldName, ReqContext->CID)); -} - -void UNakamaRealtimeClient::CleanupWebSocket() -{ - if (!WebSocket.IsValid()) - { - return; - } - - // Indicate that the disconnect was initiated locally. - bLocalDisconnectInitiated = true; - - // If the WebSocket is still connected, close the connection. - if (WebSocket->IsConnected()) - { - WebSocket->Close(); - } - - // Clear all bound event delegates to ensure no callbacks are made after this point. - WebSocket->OnClosed().Clear(); - WebSocket->OnConnectionError().Clear(); - WebSocket->OnRawMessage().Clear(); - WebSocket->OnConnected().Clear(); - WebSocket->OnMessage().Clear(); - WebSocket->OnMessageSent().Clear(); - - // Reset the WebSocket pointer. - WebSocket.Reset(); -} - -void UNakamaRealtimeClient::SendPing() -{ - TSharedPtr PingRequest = MakeShareable(new FJsonObject()); - SendMessage(TEXT("ping"), PingRequest); -} - -void UNakamaRealtimeClient::Heartbeat() -{ - SendPing(); -} - -void UNakamaRealtimeClient::HandleReceivedMessage(const FString& Data) -{ - // Start by parsing the Json! - TSharedPtr JsonObject; - TSharedRef> JsonReader = TJsonReaderFactory<>::Create(Data); - if (!FJsonSerializer::Deserialize(JsonReader, JsonObject)) - { - OnTransportError(FString::Printf(TEXT("Unable to parse message as JSON: %s"), *Data)); - return; - } - - // Only log if it is not a pong - if (!JsonObject->HasField(TEXT("pong"))) - { - NAKAMA_LOG_DEBUG(FString::Printf(TEXT("Realtime Client - Received message: %s"), *Data)); - } - - FNakamaRtError Error; - if (JsonObject->HasField(TEXT("error"))) - { - FString JsonString; - if (SerializeJsonObject(JsonObject->GetObjectField(TEXT("error")), JsonString)) - { - Error = FNakamaRtError(JsonString); - } - else - { - NAKAMA_LOG_ERROR("Realtime Client - Failed to serialize 'error' as JSON string."); - } - } - - // Check if CID is empty - FString CidStr; - if (!JsonObject->TryGetStringField(TEXT("cid"), CidStr)) - { - NAKAMA_LOG_DEBUG(FString::Printf(TEXT("Received message with no CID: %s"), *Data)); - - // Handle Events here - if(FNakamaUtils::IsRealtimeClientActive(this)) - { - if (JsonObject->HasField(TEXT("error"))) - { - FString JsonString; - if (SerializeJsonObject(JsonObject->GetObjectField(TEXT("error")), JsonString)) - { - // TODO: Look into if we need to get Error from JsonString (using ReturnedError) or use 'Error' from above - FNakamaRtError ReturnedError = FNakamaRtError(MoveTemp(JsonString)); - - // Handle Lambda Callback - if(OnError) - { - OnError(ReturnedError); - } - - // Handle Multicast Delegate - ErrorEvent.Broadcast(ReturnedError); - ErrorEventNative.Broadcast(ReturnedError); - } - else - { - NAKAMA_LOG_ERROR("Realtime Client - Failed to deserialize 'error' from JSON string."); - } - } - else if (JsonObject->HasField(TEXT("channel_message"))) - { - FString JsonString; - if (SerializeJsonObject(JsonObject->GetObjectField(TEXT("channel_message")), JsonString)) - { - FNakamaChannelMessage ChannelMessage = FNakamaChannelMessage(MoveTemp(JsonString)); - - // Handle Lambda Callback - if(OnChannelMessage) - { - OnChannelMessage(ChannelMessage); - } - - // Handle Multicast Delegate - ChannelMessageReceived.Broadcast(ChannelMessage); - ChannelMessageReceivedNative.Broadcast(ChannelMessage); - } - else - { - NAKAMA_LOG_ERROR("Realtime Client - Failed to deserialize 'channel_message' from JSON string."); - } - } - else if (JsonObject->HasField(TEXT("channel_presence_event"))) - { - FString JsonString; - if (SerializeJsonObject(JsonObject->GetObjectField(TEXT("channel_presence_event")), JsonString)) - { - FNakamaChannelPresenceEvent ChannelPresenceEvent = FNakamaChannelPresenceEvent(MoveTemp(JsonString)); - - // Handle Lambda Callback - if(OnChannelPresenceEvent) - { - OnChannelPresenceEvent(ChannelPresenceEvent); - } - - // Handle Multicast Delegate - ChannelPresenceEventReceived.Broadcast(ChannelPresenceEvent); - ChannelPresenceEventReceivedNative.Broadcast(ChannelPresenceEvent); - } - else - { - NAKAMA_LOG_ERROR("Realtime Client - Failed to deserialize 'channel_presence_event' from JSON string."); - } - } - else if (JsonObject->HasField(TEXT("match_data"))) - { - FString JsonString; - if (SerializeJsonObject(JsonObject->GetObjectField(TEXT("match_data")), JsonString)) - { - FNakamaMatchData MatchData = FNakamaMatchData(MoveTemp(JsonString)); - - // Handle Lambda Callback - if(OnMatchData) - { - OnMatchData(MatchData); - } - - // Handle Multicast Delegate - MatchDataCallback.Broadcast(MatchData); - MatchDataCallbackNative.Broadcast(MatchData); - } - else - { - NAKAMA_LOG_ERROR("Realtime Client - Failed to deserialize 'match_data' from JSON string."); - } - } - else if (JsonObject->HasField(TEXT("match_presence_event"))) - { - FString JsonString; - if (SerializeJsonObject(JsonObject->GetObjectField(TEXT("match_presence_event")), JsonString)) - { - FNakamaMatchPresenceEvent MatchPresenceEvent = FNakamaMatchPresenceEvent(MoveTemp(JsonString)); - - // Handle Lambda Callback - if(OnMatchPresenceEvent) - { - OnMatchPresenceEvent(MatchPresenceEvent); - } - - // Handle Multicast Delegate - MatchmakerPresenceCallback.Broadcast(MatchPresenceEvent); - MatchmakerPresenceCallbackNative.Broadcast(MatchPresenceEvent); - } - else - { - NAKAMA_LOG_ERROR("Realtime Client - Failed to deserialize 'match_presence_event' from JSON string."); - } - } - else if (JsonObject->HasField(TEXT("matchmaker_matched"))) - { - FString JsonString; - if (SerializeJsonObject(JsonObject->GetObjectField(TEXT("matchmaker_matched")), JsonString)) - { - FNakamaMatchmakerMatched MatchmakerMatched = FNakamaMatchmakerMatched(MoveTemp(JsonString)); - - // Handle Lambda Callback - if(OnMatchmakerMatched) - { - OnMatchmakerMatched(MatchmakerMatched); - } - - // Handle Multicast Delegate - MatchmakerMatchMatched.Broadcast(MatchmakerMatched); - MatchmakerMatchMatchedNative.Broadcast(MatchmakerMatched); - } - else - { - NAKAMA_LOG_ERROR("Realtime Client - Failed to deserialize 'matchmaker_matched' from JSON string."); - } - } - else if (JsonObject->HasField(TEXT("notifications"))) - { - FString JsonString; - if (SerializeJsonObject(JsonObject->GetObjectField(TEXT("notifications")), JsonString)) - { - FNakamaNotificationList NotificationList = FNakamaNotificationList(MoveTemp(JsonString)); - - // Handle Lambda Callback - if(OnNotifications) - { - OnNotifications(NotificationList); - } - - // Handle Multicast Delegate - NotificationReceived.Broadcast(NotificationList); - NotificationReceivedNative.Broadcast(NotificationList); - } - else - { - NAKAMA_LOG_ERROR("Realtime Client - Failed to deserialize 'notifications' from JSON string."); - } - } - else if (JsonObject->HasField(TEXT("status_presence_event"))) - { - FString JsonString; - if (SerializeJsonObject(JsonObject->GetObjectField(TEXT("status_presence_event")), JsonString)) - { - FNakamaStatusPresenceEvent StatusPresenceEvent = FNakamaStatusPresenceEvent(MoveTemp(JsonString)); - - // Handle Lambda Callback - if(OnStatusPresenceEvent) - { - OnStatusPresenceEvent(StatusPresenceEvent); - } - - // Handle Multicast Delegate - PresenceStatusReceived.Broadcast(StatusPresenceEvent); - PresenceStatusReceivedNative.Broadcast(StatusPresenceEvent); - } - else - { - NAKAMA_LOG_ERROR("Realtime Client - Failed to deserialize 'status_presence_event' from JSON string."); - } - } - else if (JsonObject->HasField(TEXT("stream_data"))) - { - FString JsonString; - if (SerializeJsonObject(JsonObject->GetObjectField(TEXT("stream_data")), JsonString)) - { - FNakamaStreamData StreamData = FNakamaStreamData(MoveTemp(JsonString)); - - // Handle Lambda Callback - if(OnStreamData) - { - OnStreamData(StreamData); - } - - // Handle Multicast Delegate - StreamPresenceDataReceived.Broadcast(StreamData); - StreamPresenceDataReceivedNative.Broadcast(StreamData); - } - else - { - NAKAMA_LOG_ERROR("Realtime Client - Failed to deserialize 'stream_data' from JSON string."); - } - } - else if (JsonObject->HasField(TEXT("stream_presence_event"))) - { - FString JsonString; - if (SerializeJsonObject(JsonObject->GetObjectField(TEXT("stream_presence_event")), JsonString)) - { - FNakamaStreamPresenceEvent StreamPresenceEvent = FNakamaStreamPresenceEvent(MoveTemp(JsonString)); - - // Handle Lambda Callback - if(OnStreamPresenceEvent) - { - OnStreamPresenceEvent(StreamPresenceEvent); - } - - // Handle Multicast Delegate - StreamPresenceEventReceived.Broadcast(StreamPresenceEvent); - StreamPresenceEventReceivedNative.Broadcast(StreamPresenceEvent); - } - else - { - NAKAMA_LOG_ERROR("Realtime Client - Failed to deserialize 'stream_presence_event' from JSON string."); - } - } - else if (JsonObject->HasField(TEXT("party"))) - { - FString JsonString; - if (SerializeJsonObject(JsonObject->GetObjectField(TEXT("party")), JsonString)) - { - FNakamaParty Party = FNakamaParty(MoveTemp(JsonString)); - - // Handle Lambda Callback - if(OnParty) - { - OnParty(Party); - } - - // Handle Multicast Delegate - PartyReceived.Broadcast(Party); - PartyReceivedNative.Broadcast(Party); - } - else - { - NAKAMA_LOG_ERROR("Realtime Client - Failed to deserialize 'party' from JSON string."); - } - } - else if (JsonObject->HasField(TEXT("party_close"))) - { - FString JsonString; - if (SerializeJsonObject(JsonObject->GetObjectField(TEXT("party_close")), JsonString)) - { - FNakamaPartyClose PartyClose = FNakamaPartyClose(MoveTemp(JsonString)); - - // Handle Lambda Callback - if(OnPartyClose) - { - OnPartyClose(PartyClose); - } - - // Handle Multicast Delegate - PartyCloseReceived.Broadcast(PartyClose); - PartyCloseReceivedNative.Broadcast(PartyClose); - } - else - { - NAKAMA_LOG_ERROR("Realtime Client - Failed to deserialize 'party_close' from JSON string."); - } - } - else if (JsonObject->HasField(TEXT("party_data"))) - { - FString JsonString; - if (SerializeJsonObject(JsonObject->GetObjectField(TEXT("party_data")), JsonString)) - { - FNakamaPartyData PartyData = FNakamaPartyData(MoveTemp(JsonString)); - - // Handle Lambda Callback - if(OnPartyData) - { - OnPartyData(PartyData); - } - - // Handle Multicast Delegate - PartyDataReceived.Broadcast(PartyData); - PartyDataReceivedNative.Broadcast(PartyData); - } - else - { - NAKAMA_LOG_ERROR("Realtime Client - Failed to deserialize 'party_data' from JSON string."); - } - } - else if (JsonObject->HasField(TEXT("party_join_request"))) - { - FString JsonString; - if (SerializeJsonObject(JsonObject->GetObjectField(TEXT("party_join_request")), JsonString)) - { - FNakamaPartyJoinRequest PartyJoinRequest = FNakamaPartyJoinRequest(MoveTemp(JsonString)); - - // Handle Lambda Callback - if(OnPartyJoinRequest) - { - OnPartyJoinRequest(PartyJoinRequest); - } - - // Handle Multicast Delegate - PartyJoinRequestReceived.Broadcast(PartyJoinRequest); - PartyJoinRequestReceivedNative.Broadcast(PartyJoinRequest); - } - else - { - NAKAMA_LOG_ERROR("Realtime Client - Failed to deserialize 'party_join_request' from JSON string."); - } - } - else if (JsonObject->HasField(TEXT("party_leader"))) - { - FString JsonString; - if (SerializeJsonObject(JsonObject->GetObjectField(TEXT("party_leader")), JsonString)) - { - FNakamaPartyLeader PartyLeader = FNakamaPartyLeader(MoveTemp(JsonString)); - - // Handle Lambda Callback - if(OnPartyLeader) - { - OnPartyLeader(PartyLeader); - } - - // Handle Multicast Delegate - PartyLeaderReceived.Broadcast(PartyLeader); - PartyLeaderReceivedNative.Broadcast(PartyLeader); - } - else - { - NAKAMA_LOG_ERROR("Realtime Client - Failed to deserialize 'party_leader' from JSON string."); - } - } - else if (JsonObject->HasField(TEXT("party_matchmaker_ticket"))) - { - FString JsonString; - if (SerializeJsonObject(JsonObject->GetObjectField(TEXT("party_matchmaker_ticket")), JsonString)) - { - FNakamaPartyMatchmakerTicket PartyMatchmakerTicket = FNakamaPartyMatchmakerTicket(MoveTemp(JsonString)); - - // Handle Lambda Callback - if(OnPartyMatchmakerTicket) - { - OnPartyMatchmakerTicket(PartyMatchmakerTicket); - } - - // Handle Multicast Delegate - PartyMatchmakerTicketReceived.Broadcast(PartyMatchmakerTicket); - PartyMatchmakerTicketReceivedNative.Broadcast(PartyMatchmakerTicket); - } - else - { - NAKAMA_LOG_ERROR("Realtime Client - Failed to deserialize 'party_matchmaker_ticket' from JSON string."); - } - } - else if (JsonObject->HasField(TEXT("party_presence_event"))) - { - FString JsonString; - if (SerializeJsonObject(JsonObject->GetObjectField(TEXT("party_presence_event")), JsonString)) - { - FNakamaPartyPresenceEvent PartyPresenceEvent = FNakamaPartyPresenceEvent(MoveTemp(JsonString)); - - // Handle Lambda Callback - if(OnPartyPresenceEvent) - { - OnPartyPresenceEvent(PartyPresenceEvent); - } - - // Handle Multicast Delegate - PartyPresenceReceived.Broadcast(PartyPresenceEvent); - PartyPresenceReceivedNative.Broadcast(PartyPresenceEvent); - } - else - { - NAKAMA_LOG_ERROR("Realtime Client - Failed to deserialize 'party_presence_event' from JSON string."); - } - } - else - { - OnTransportError(TEXT("Realtime Client Listener - Unknown message received.")); - } - } - else - { - NAKAMA_LOG_ERROR(TEXT("Realtime Client - Realtime Client was not available to handle event message.")); - } - // End of Events! - } - else - { - // Handle Requests here - int32 Cid = FCString::Atoi(*CidStr); - - // NOTE: This log got a bit too verbose, enable it to see the CID - //NAKAMA_LOG_DEBUG(FString::Printf(TEXT("Received message with CID: %d"), Cid)); - - FNakamaRealtimeSuccessCallback SuccessCallback; - FNakamaRealtimeSuccessCallbackMove SuccessCallbackMove; - FNakamaRealtimeErrorCallback ErrorCallback; - - bool bContextIsValid = false; - - { - FScopeLock Lock(&ReqContextsLock); - TObjectPtr ReqContext = ReqContexts.FindRef(Cid); - if (ReqContext) - { - bContextIsValid = true; - SuccessCallback = ReqContext->SuccessCallback; - SuccessCallbackMove = ReqContext->SuccessCallbackMove; - ErrorCallback = ReqContext->ErrorCallback; - ReqContexts.Remove(Cid); - } - else - { - NAKAMA_LOG_ERROR(FString::Printf(TEXT("Received response for unknown CID=%d"), Cid)); - return; - } - } - - if(bContextIsValid) - { - if (JsonObject->HasField(TEXT("error"))) - { - if (ErrorCallback.IsBound()) - { - ErrorCallback.Execute(Error); - } - else if (OnError || ErrorEvent.IsBound() || ErrorEventNative.IsBound()) // Checks if Error is bound (means it is handled) - { - // Lambda Callback - if(OnError) - { - OnError(Error); - } - - // Multicast Delegate - ErrorEvent.Broadcast(Error); - ErrorEventNative.Broadcast(Error); - } - else - { - // Error was not handled - NAKAMA_LOG_WARN(TEXT("Error not handled.")); - } - } - else - { - bool constRefBound = SuccessCallback.IsBound(); - bool moveBound = SuccessCallbackMove.IsBound(); - if (constRefBound || moveBound) - { - FNakamaRealtimeEnvelope Envelope; - Envelope.CID = Cid; - Envelope.Payload = Data; - if (constRefBound) - { - SuccessCallback.Execute(Envelope); - } - if (moveBound) - { - SuccessCallbackMove.Execute(MoveTemp(Envelope)); - } - } - } - } - else - { - OnTransportError(FString::Printf(TEXT("Request context not found CID: %d"), Cid)); - } - } -} - -void UNakamaRealtimeClient::SendMessage(const FString& FieldName, const TSharedPtr& Object) -{ - // Check WebSocket before sending Data - if (!WebSocket || !WebSocket->IsConnected()) - { - NAKAMA_LOG_ERROR(TEXT("WebSocket is not valid or not connected.")); - return; - } - - TSharedPtr Envelope = MakeShareable(new FJsonObject()); - Envelope->SetObjectField(FieldName, Object); - - // Make our own - FNakamaRealtimeEnvelope NakamaEnvelope; - - // Create Context - const TObjectPtr ReqContext = CreateReqContext(NakamaEnvelope); - Envelope->SetStringField(TEXT("cid"), FString::FromInt(ReqContext->CID)); - - // Set the payload - NakamaEnvelope.Payload = FNakamaUtils::EncodeJson(Envelope); - - // Send Message - FString Message = NakamaEnvelope.Payload; - WebSocket->Send(Message); -} - -void UNakamaRealtimeClient::Tick(float DeltaTime) -{ - AccumulatedDeltaTime += DeltaTime * 1000.0f; // Convert DeltaTime to milliseconds - if (AccumulatedDeltaTime >= HeartbeatIntervalMs) - { - Heartbeat(); - AccumulatedDeltaTime = 0.0f; - } -} - -bool UNakamaRealtimeClient::IsTickable() const -{ - // Tick only when the WebSocket is connected (?) - return WebSocket && WebSocket->IsConnected(); -} - -TStatId UNakamaRealtimeClient::GetStatId() const -{ - RETURN_QUICK_DECLARE_CYCLE_STAT(UNakamaRealtimeClient, STATGROUP_Tickables); -} - -bool UNakamaRealtimeClient::SerializeJsonObject(const TSharedPtr& JsonObject, FString& OutSerializedJson) -{ - if (!JsonObject.IsValid()) - { - return false; - } - - const TSharedRef> JsonWriter = TJsonWriterFactory<>::Create(&OutSerializedJson); - if (!FJsonSerializer::Serialize(JsonObject.ToSharedRef(), JsonWriter)) - { - JsonWriter->Close(); - return false; - } - - JsonWriter->Close(); - - return true; -} - -void UNakamaRealtimeClient::CancelAllRequests(const ENakamaRtErrorCode& ErrorCode) -{ - if(!IsValidLowLevel()) - { - return; - } - - FNakamaRtError Error; - Error.Code = ErrorCode; - Error.Message = TEXT(""); - - FScopeLock Lock(&ReqContextsLock); - - for (const auto& Pair : ReqContexts) - { - UNakamaRealtimeRequestContext* Context = Pair.Value; - - if (Context) - { - if(Context->ErrorCallback.IsBound()) - { - Context->ErrorCallback.Execute(Error); - } - } - } - - // Clear the Array - ReqContexts.Empty(); -} - -void UNakamaRealtimeClient::OnTransportError(const FString& Description) -{ - FNakamaRtError Error; - Error.Message = Description; - Error.Code = WebSocket->IsConnected() ? ENakamaRtErrorCode::TRANSPORT_ERROR : ENakamaRtErrorCode::CONNECT_ERROR; - - NAKAMA_LOG_ERROR(FString::Printf(TEXT("Realtime Client Transport Error (Code: %s): %s"), - WebSocket->IsConnected() ? TEXT("TRANSPORT_ERROR") : TEXT("CONNECT_ERROR"), *Description)); - - // Handle Callbacks - if(FNakamaUtils::IsRealtimeClientActive(this)) - { - // Lambda Callback - if(OnError) - { - OnError(Error); - } - - // Multicast Delegate - ErrorEvent.Broadcast(Error); - ErrorEventNative.Broadcast(Error); - } -} - diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaRtError.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaRtError.cpp deleted file mode 100644 index bd82e72f1..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaRtError.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaRtError.h" -#include "NakamaUtils.h" - -FNakamaRtError::FNakamaRtError(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("message"), Message); - - int32 CodeValue; - if (JsonObject->TryGetNumberField(TEXT("code"), CodeValue)) - { - Code = static_cast(CodeValue); - } - else - { - Code = ENakamaRtErrorCode::UNKNOWN_JSON; - } - - const TSharedPtr* ContextJsonObject; - if (JsonObject->TryGetObjectField(TEXT("context"), ContextJsonObject)) - { - for (auto& Pair : (*ContextJsonObject)->Values) - { - Context.Add(Pair.Key, Pair.Value->AsString()); - } - } - } -} - -ENakamaDisconnectCode FNakamaDisconnectInfo::ConvertIntToDisconnectCode(int32 Value) -{ - switch (Value) - { - case 1000: - return ENakamaDisconnectCode::NORMAL_CLOSURE; - case 1001: - return ENakamaDisconnectCode::GOING_AWAY; - case 1002: - return ENakamaDisconnectCode::PROTOCOL_ERROR; - case 1003: - return ENakamaDisconnectCode::UNSUPPORTED_DATA; - case 1005: - return ENakamaDisconnectCode::NO_STATUS_RCVD; - case 1006: - return ENakamaDisconnectCode::ABNORMAL_CLOSURE; - case 1007: - return ENakamaDisconnectCode::INVALID_FRAME_PAYLOAD_DATA; - case 1008: - return ENakamaDisconnectCode::POLICY_VIOLATION; - case 1009: - return ENakamaDisconnectCode::MESSAGE_TOO_BIG; - case 1010: - return ENakamaDisconnectCode::MANDATORY_EXT; - case 1011: - return ENakamaDisconnectCode::INTERNAL_SERVER_ERROR; - case 1015: - return ENakamaDisconnectCode::TLS_HANDSHAKE; - case 4000: - return ENakamaDisconnectCode::HEARTBEAT_FAILURE; - case 4001: - return ENakamaDisconnectCode::TRANSPORT_ERROR; - default: - return ENakamaDisconnectCode::NORMAL_CLOSURE; - } -} diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaSession.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaSession.cpp deleted file mode 100644 index ef294e317..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaSession.cpp +++ /dev/null @@ -1,253 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaSession.h" - -#include "NakamaUtils.h" - - -UNakamaSession* UNakamaSession::SetupSession(const FString& AuthResponse) -{ - UNakamaSession* ResultSession = NewObject(); - TSharedPtr JsonObject = MakeShareable(new FJsonObject()); - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(AuthResponse); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject)) - { - FString Token = JsonObject->GetStringField(TEXT("token")); - FString RefreshToken = JsonObject->GetStringField(TEXT("refresh_token")); - - ResultSession->SessionData.AuthToken = Token; - ResultSession->_AuthToken = Token; - ResultSession->SessionData.RefreshToken = RefreshToken; - ResultSession->_RefreshToken = RefreshToken; - - // Check if the "created" field is available and set IsCreated accordingly - bool IsSessionCreated; - if (JsonObject->TryGetBoolField(TEXT("created"), IsSessionCreated)) - { - ResultSession->SessionData.IsCreated = IsSessionCreated; - ResultSession->_IsCreated = IsSessionCreated; - } - - TSharedPtr PayloadJson; - if (ParseJwtPayload(Token, PayloadJson)) - { - FString UserId; - FString Username; - PayloadJson->TryGetStringField(TEXT("uid"), UserId); - PayloadJson->TryGetStringField(TEXT("usn"), Username); - - ResultSession->SessionData.Username = Username; - ResultSession->_Username = Username; - ResultSession->SessionData.UserId = UserId; - ResultSession->_UserId = UserId; - - TMap InVars; - if (PayloadJson->HasField(TEXT("vrs"))) - { - const TSharedPtr& VarsJson = PayloadJson->GetObjectField(TEXT("vrs")); - for (const auto& Entry : VarsJson->Values) - { - FString Key = Entry.Key; - FString Value = Entry.Value->AsString(); - ResultSession->SessionData.Variables.Add(Key, Value); - ResultSession->_Variables.Add(Key, Value); - } - } - - int64 Expires; - if (PayloadJson->TryGetNumberField(TEXT("exp"), Expires)) - { - FDateTime ExpireTime = FDateTime::FromUnixTimestamp(Expires); - ResultSession->SessionData.ExpireTime = ExpireTime; - ResultSession->_ExpireTime = ExpireTime; - ResultSession->SessionData.IsExpired = (FDateTime::UtcNow() >= ExpireTime); - ResultSession->_IsExpired = (FDateTime::UtcNow() >= ExpireTime); - } - - // Parse and check expiration time of the refresh_token - TSharedPtr RefreshPayloadJson; - if (ParseJwtPayload(RefreshToken, RefreshPayloadJson)) - { - int64 RefreshExpires; - if (RefreshPayloadJson->TryGetNumberField(TEXT("exp"), RefreshExpires)) - { - FDateTime RefreshExpireTime = FDateTime::FromUnixTimestamp(RefreshExpires); - ResultSession->SessionData.RefreshExpireTime = RefreshExpireTime; - ResultSession->_RefreshExpireTime = RefreshExpireTime; - ResultSession->SessionData.IsRefreshExpired = (FDateTime::UtcNow() >= RefreshExpireTime); - ResultSession->_IsRefreshExpired = (FDateTime::UtcNow() >= RefreshExpireTime); - } - } - - ResultSession->SessionData.CreateTime = FDateTime::UtcNow(); - ResultSession->_CreateTime = FDateTime::UtcNow(); - return ResultSession; - } - else - { - NAKAMA_LOG_ERROR(TEXT("Failed to parse JWT payload")); - } - } - else - { - NAKAMA_LOG_ERROR(TEXT("Failed to deserialize Session JSON object")); - } - return nullptr; -} - -const FString UNakamaSession::GetAuthToken() const -{ - return _AuthToken; -} - -const FString UNakamaSession::GetRefreshToken() const -{ - return _RefreshToken; -} - -bool UNakamaSession::IsCreated() const -{ - return _IsCreated; -} - -const FString UNakamaSession::GetUsername() const -{ - return _Username; -} - -const FString UNakamaSession::GetUserId() const -{ - return _UserId; -} - -const FDateTime UNakamaSession::GetCreateTime() const -{ - return _CreateTime; -} - -const FDateTime UNakamaSession::GetExpireTime() const -{ - return _ExpireTime; -} - -const FDateTime UNakamaSession::GetRefreshExpireTime() const -{ - return _RefreshExpireTime; -} - -bool UNakamaSession::IsExpired() const -{ - return FDateTime::UtcNow() >= _ExpireTime; -} - -bool UNakamaSession::IsExpiredTime(FDateTime Time) const -{ - return Time >= _ExpireTime; -} - -bool UNakamaSession::IsRefreshExpired() const -{ - return FDateTime::UtcNow() >= _RefreshExpireTime; -} - -bool UNakamaSession::IsRefreshExpiredTime(FDateTime Time) const -{ - return Time >= _RefreshExpireTime; -} - -TMap UNakamaSession::GetVariables() const -{ - return _Variables; -} - -FString UNakamaSession::GetVariable(FString Name) const -{ - return _Variables.FindRef(Name); -} - -UNakamaSession* UNakamaSession::RestoreSession(FString Token, FString RefreshToken) -{ - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - ContentJson->SetStringField(TEXT("token"), Token); - ContentJson->SetStringField(TEXT("refresh_token"), RefreshToken); - ContentJson->SetBoolField(TEXT("created"), false); - - // Convert the JSON object to a JSON string - FString JsonString; - TSharedRef> JsonWriter = TJsonWriterFactory<>::Create(&JsonString); - bool bSerializationSuccessful = FJsonSerializer::Serialize(ContentJson.ToSharedRef(), JsonWriter); - - // Check if serialization was successful - if (bSerializationSuccessful) - { - return UNakamaSession::SetupSession(JsonString); - } - else - { - NAKAMA_LOG_ERROR("Restore Session: Failed to serialize Restore Session JSON object"); - } - return nullptr; -} - -bool UNakamaSession::ParseJwtPayload(const FString& jwt, TSharedPtr& payloadJson) -{ - // Split the JWT into its three parts - TArray jwtParts; - jwt.ParseIntoArray(jwtParts, TEXT(".")); - - if (jwtParts.Num() != 3) - { - // Invalid JWT format - return false; - } - - // Convert Base64Url to Base64 - FString payloadString = jwtParts[1]; - payloadString.ReplaceInline(TEXT("-"), TEXT("+")); - payloadString.ReplaceInline(TEXT("_"), TEXT("/")); - - // Handle padding - int32 mod = payloadString.Len() % 4; - if (mod != 0) { - for (int32 i = 0; i < (4 - mod); ++i) { - payloadString += TEXT("="); - } - } - - // Decode to bytes - TArray decodedBytes; - FBase64::Decode(payloadString, decodedBytes); - - // Ensure null termination - decodedBytes.Add(0); - - // Convert UTF-8 bytes to FString to handle special characters - FString decodedPayloadString = FString(UTF8_TO_TCHAR(reinterpret_cast(decodedBytes.GetData()))); - - // Parse the decoded payload as a JSON object - TSharedRef> reader = TJsonReaderFactory<>::Create(decodedPayloadString); - if (!FJsonSerializer::Deserialize(reader, payloadJson)) - { - // Failed to parse JSON - return false; - } - - return true; -} - diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaStatus.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaStatus.cpp deleted file mode 100644 index 62804755a..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaStatus.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaStatus.h" - -#include "NakamaUtils.h" -#include "NakamaAccount.h" - -FNakamaStatus::FNakamaStatus(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - const TSharedPtr* StatusJsonObject; - if (JsonObject->TryGetObjectField(TEXT("status"), StatusJsonObject)) - { - const TArray>* PresencesJsonArray; - if ((*StatusJsonObject)->TryGetArrayField(TEXT("presences"), PresencesJsonArray)) - { - for (const TSharedPtr& PresenceJson : *PresencesJsonArray) - { - if (TSharedPtr PresenceJsonObject = PresenceJson->AsObject()) - { - Presences.Add(FNakamaUserPresence(PresenceJsonObject)); - } - } - } - } - } -} - -FNakamaStatus::FNakamaStatus() -{ - -} - -FNakamaStatusPresenceEvent::FNakamaStatusPresenceEvent(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - const TArray>* JoinsJsonArray; - if (JsonObject->TryGetArrayField(TEXT("joins"), JoinsJsonArray)) - { - for (const TSharedPtr& UserPresence : *JoinsJsonArray) - { - if (TSharedPtr UserPresenceJsonObject = UserPresence->AsObject()) - { - Joins.Add(FNakamaUserPresence(UserPresenceJsonObject)); - } - } - } - - const TArray>* LeavesJsonArray; - if (JsonObject->TryGetArrayField(TEXT("leaves"), LeavesJsonArray)) - { - for (const TSharedPtr& UserPresence : *LeavesJsonArray) - { - if (TSharedPtr UserPresenceJsonObject = UserPresence->AsObject()) - { - Leaves.Add(FNakamaUserPresence(UserPresenceJsonObject)); - } - } - } - } -} - - -FNakamaStatusPresenceEvent::FNakamaStatusPresenceEvent() -{ - -} diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaStorageObject.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaStorageObject.cpp deleted file mode 100644 index 55a0bfed9..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaStorageObject.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaStorageObject.h" -#include "NakamaUtils.h" - -FNakamaStoreObjectData::FNakamaStoreObjectData(const FString& JsonString) : FNakamaStoreObjectData(FNakamaUtils::DeserializeJsonObject(JsonString)) { -} - -FNakamaStoreObjectData::FNakamaStoreObjectData(const TSharedPtr JsonObject) -{ - if (JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("collection"), Collection); - JsonObject->TryGetStringField(TEXT("key"), Key); - JsonObject->TryGetStringField(TEXT("user_id"), UserId); - JsonObject->TryGetStringField(TEXT("value"), Value); - JsonObject->TryGetStringField(TEXT("version"), Version); - - int32 PermissionReadValue; - if (JsonObject->TryGetNumberField(TEXT("permission_read"), PermissionReadValue)) - { - PermissionRead = static_cast(PermissionReadValue); - } - - int32 PermissionWriteValue; - if (JsonObject->TryGetNumberField(TEXT("permission_write"), PermissionWriteValue)) - { - PermissionWrite = static_cast(PermissionWriteValue); - } - - FString CreatedAtString; - if (JsonObject->TryGetStringField(TEXT("create_time"), CreatedAtString)) - { - FDateTime::ParseIso8601(*CreatedAtString, CreateTime); - } - - FString UpdatedAtString; - if (JsonObject->TryGetStringField(TEXT("update_time"), UpdatedAtString)) - { - FDateTime::ParseIso8601(*UpdatedAtString, UpdateTime); - } - } -} - -FNakamaStoreObjectData::FNakamaStoreObjectData() - : CreateTime(FDateTime::MinValue()), UpdateTime(FDateTime::MinValue()), - PermissionRead(ENakamaStoragePermissionRead::NO_READ), PermissionWrite(ENakamaStoragePermissionWrite::NO_WRITE) -{ - -} - -FNakamaStoreObjectWrite::FNakamaStoreObjectWrite(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("collection"), Collection); - JsonObject->TryGetStringField(TEXT("key"), Key); - JsonObject->TryGetStringField(TEXT("value"), Value); - JsonObject->TryGetStringField(TEXT("version"), Version); - - int32 PermissionReadValue; - if (JsonObject->TryGetNumberField(TEXT("permission_read"), PermissionReadValue)) - { - PermissionRead = static_cast(PermissionReadValue); - } - - int32 PermissionWriteValue; - if (JsonObject->TryGetNumberField(TEXT("permission_write"), PermissionWriteValue)) - { - PermissionWrite = static_cast(PermissionWriteValue); - } - } -} - -FNakamaStoreObjectWrite::FNakamaStoreObjectWrite() - : PermissionRead(ENakamaStoragePermissionRead::NO_READ), PermissionWrite(ENakamaStoragePermissionWrite::NO_WRITE) -{ -} - -FNakamaReadStorageObjectId::FNakamaReadStorageObjectId(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("collection"), Collection); - JsonObject->TryGetStringField(TEXT("key"), Key); - JsonObject->TryGetStringField(TEXT("user_id"), UserId); - } -} - -FNakamaReadStorageObjectId::FNakamaReadStorageObjectId() -{ - -} - -FNakamaDeleteStorageObjectId::FNakamaDeleteStorageObjectId(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("collection"), Collection); - JsonObject->TryGetStringField(TEXT("key"), Key); - JsonObject->TryGetStringField(TEXT("version"), Version); - } -} - -FNakamaDeleteStorageObjectId::FNakamaDeleteStorageObjectId() -{ - -} - -FNakamaStoreObjectAck::FNakamaStoreObjectAck(const FString& JsonString) : FNakamaStoreObjectAck(FNakamaUtils::DeserializeJsonObject(JsonString)) { -} - -FNakamaStoreObjectAck::FNakamaStoreObjectAck(const TSharedPtr JsonObject) -{ - if (JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("collection"), Collection); - JsonObject->TryGetStringField(TEXT("key"), Key); - JsonObject->TryGetStringField(TEXT("version"), Version); - JsonObject->TryGetStringField(TEXT("user_id"), UserId); - } -} - - -FNakamaStoreObjectAck::FNakamaStoreObjectAck() -{ - -} - -FNakamaStoreObjectAcks::FNakamaStoreObjectAcks(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - const TArray>* StorageJobjectsJsonArray; - if (JsonObject->TryGetArrayField(TEXT("acks"), StorageJobjectsJsonArray)) - { - for (const TSharedPtr& StorageJson : *StorageJobjectsJsonArray) - { - if (TSharedPtr StorageJsonObject = StorageJson->AsObject()) - { - StorageObjects.Add(FNakamaStoreObjectAck(StorageJsonObject)); - } - } - } - } -} - - -FNakamaStoreObjectAcks::FNakamaStoreObjectAcks() -{ - -} - -FNakamaStorageObjectList::FNakamaStorageObjectList(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - const TArray>* StorageJobjectsJsonArray; - if (JsonObject->TryGetArrayField(TEXT("objects"), StorageJobjectsJsonArray)) - { - for (const TSharedPtr& StorageJson : *StorageJobjectsJsonArray) - { - if (TSharedPtr StorageJsonObject = StorageJson->AsObject()) - { - Objects.Add(FNakamaStoreObjectData(StorageJsonObject)); - } - } - } - - JsonObject->TryGetStringField(TEXT("cursor"), Cursor); - } -} - -FNakamaStorageObjectList::FNakamaStorageObjectList() -{ - -} diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaStreams.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaStreams.cpp deleted file mode 100644 index 37089eecc..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaStreams.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaStreams.h" -#include "NakamaUtils.h" - -FNakamaStream::FNakamaStream(const FString& JsonString) : FNakamaStream(FNakamaUtils::DeserializeJsonObject(JsonString)) { -} - -FNakamaStream::FNakamaStream(const TSharedPtr JsonObject) -{ - if (JsonObject.IsValid()) - { - JsonObject->TryGetNumberField(TEXT("mode"), Mode); - JsonObject->TryGetStringField(TEXT("subject"), Subject); - JsonObject->TryGetStringField(TEXT("subcontext"), SubContext); - JsonObject->TryGetStringField(TEXT("label"), Label); - } -} - -FNakamaStream::FNakamaStream() : Mode(0) -{ - -} - -FNakamaStreamData::FNakamaStreamData(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - const TSharedPtr* StreamJsonObject; - if (JsonObject->TryGetObjectField(TEXT("stream"), StreamJsonObject)) - { - Stream = FNakamaStream(*StreamJsonObject); - } - - const TSharedPtr* SenderJsonObject; - if (JsonObject->TryGetObjectField(TEXT("sender"), SenderJsonObject)) - { - Sender = FNakamaUserPresence(*SenderJsonObject); - } - - JsonObject->TryGetStringField(TEXT("data"), Data); - } -} - -FNakamaStreamData::FNakamaStreamData() -{ - -} - -FNakamaStreamPresenceEvent::FNakamaStreamPresenceEvent(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - const TSharedPtr* StreamJsonObject; - if (JsonObject->TryGetObjectField(TEXT("stream"), StreamJsonObject)) - { - Stream = FNakamaStream(*StreamJsonObject); - } - - const TArray>* JoinsJsonArray; - if (JsonObject->TryGetArrayField(TEXT("joins"), JoinsJsonArray)) - { - for (const TSharedPtr& JoinJsonValue : *JoinsJsonArray) - { - if (TSharedPtr JoinJsonObject = JoinJsonValue->AsObject()) - { - Joins.Add(FNakamaUserPresence(JoinJsonObject)); - } - } - } - - const TArray>* LeavesJsonArray; - if (JsonObject->TryGetArrayField(TEXT("leaves"), LeavesJsonArray)) - { - for (const TSharedPtr& LeaveJsonValue : *LeavesJsonArray) - { - if (TSharedPtr LeaveJsonObject = LeaveJsonValue->AsObject()) - { - Leaves.Add(FNakamaUserPresence(LeaveJsonObject)); - } - } - } - - } -} - -FNakamaStreamPresenceEvent::FNakamaStreamPresenceEvent() -{ - -} diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaTournament.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaTournament.cpp deleted file mode 100644 index 62a8d99fa..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaTournament.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaTournament.h" -#include "NakamaUtils.h" - -FNakamaTournament::FNakamaTournament(const FString& JsonString) : FNakamaTournament(FNakamaUtils::DeserializeJsonObject(JsonString)) { -} - -FNakamaTournament::FNakamaTournament(const TSharedPtr JsonObject) -{ - if (JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("id"), Id); - JsonObject->TryGetStringField(TEXT("title"), Title); - JsonObject->TryGetStringField(TEXT("description"), Description); - JsonObject->TryGetNumberField(TEXT("category"), Category); - JsonObject->TryGetNumberField(TEXT("sort_order"), SortOrder); - JsonObject->TryGetNumberField(TEXT("size"), Size); - JsonObject->TryGetNumberField(TEXT("max_size"), MaxSize); - JsonObject->TryGetNumberField(TEXT("max_num_score"), MaxNumScore); - JsonObject->TryGetBoolField(TEXT("can_enter"), CanEnter); - - FString CreateTimeString; - if (JsonObject->TryGetStringField(TEXT("create_time"), CreateTimeString)) - { - FDateTime::ParseIso8601(*CreateTimeString, CreateTime); - } - - FString StartTimeString; - if (JsonObject->TryGetStringField(TEXT("start_time"), StartTimeString)) - { - FDateTime::ParseIso8601(*StartTimeString, StartTime); - } - - FString EndTimeString; - if (JsonObject->TryGetStringField(TEXT("end_time"), EndTimeString)) - { - FDateTime::ParseIso8601(*EndTimeString, EndTime); - } - - JsonObject->TryGetNumberField(TEXT("end_active"), EndActive); - JsonObject->TryGetNumberField(TEXT("next_reset"), NextReset); - JsonObject->TryGetNumberField(TEXT("duration"), Duration); - JsonObject->TryGetNumberField(TEXT("start_active"), StartActive); - - JsonObject->TryGetStringField(TEXT("metadata"), Metadata); - } -} - -FNakamaTournament::FNakamaTournament() - : CreateTime(FDateTime::MinValue()), StartTime(FDateTime::MinValue()), EndTime(FDateTime::MinValue()), - Category(0), SortOrder(0), Size(0), MaxSize(0), MaxNumScore(0), EndActive(0), - NextReset(0), Duration(0), StartActive(0), CanEnter(false) -{ -} - -FNakamaTournamentRecordList::FNakamaTournamentRecordList(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - const TArray>* RecordsJsonArray; - if (JsonObject->TryGetArrayField(TEXT("records"), RecordsJsonArray)) - { - for (const TSharedPtr& RecordJson : *RecordsJsonArray) - { - if (TSharedPtr RecordJsonObject = RecordJson->AsObject()) - { - Records.Add(FNakamaLeaderboardRecord(RecordJsonObject)); - } - } - } - - const TArray>* OwnerRecordsJsonArray; - if (JsonObject->TryGetArrayField(TEXT("owner_records"), OwnerRecordsJsonArray)) - { - for (const TSharedPtr& OwnerRecordJson : *OwnerRecordsJsonArray) - { - if (TSharedPtr OwnerRecordJsonObject = OwnerRecordJson->AsObject()) - { - OwnerRecords.Add(FNakamaLeaderboardRecord(OwnerRecordJsonObject)); - } - } - } - - JsonObject->TryGetStringField(TEXT("next_cursor"), NextCursor); - JsonObject->TryGetStringField(TEXT("prev_cursor"), PrevCursor); - } - -} - -FNakamaTournamentRecordList::FNakamaTournamentRecordList() -{ - -} - -FNakamaTournamentList::FNakamaTournamentList(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - const TArray>* TournamentsJsonArray; - if (JsonObject->TryGetArrayField(TEXT("tournaments"), TournamentsJsonArray)) - { - for (const TSharedPtr& TournamentJson : *TournamentsJsonArray) - { - if (TSharedPtr TournamentJsonObject = TournamentJson->AsObject()) - { - Tournaments.Add(FNakamaTournament(TournamentJsonObject)); - } - } - } - - JsonObject->TryGetStringField(TEXT("cursor"), Cursor); - } -} - - -FNakamaTournamentList::FNakamaTournamentList() -{ - -} diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaUnreal.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaUnreal.cpp deleted file mode 100644 index a4aa967d1..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaUnreal.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaUnreal.h" -#include "Modules/ModuleManager.h" - -void FNakamaUnrealModule::StartupModule() -{ - // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module - - //NLogger::init(std::make_shared(), NLogLevel::Debug); - -} - -void FNakamaUnrealModule::ShutdownModule() -{ - // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, - // we call this function before unloading the module. - -} - -#undef LOCTEXT_NAMESPACE - -IMPLEMENT_MODULE(FNakamaUnrealModule, NakamaUnreal) diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaUser.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaUser.cpp deleted file mode 100644 index 36b48835e..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaUser.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaUser.h" -#include "NakamaUtils.h" - -FNakamaUserList::FNakamaUserList(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - const TArray>* UsersJsonArray; - if (JsonObject->TryGetArrayField(TEXT("users"), UsersJsonArray)) - { - for (const TSharedPtr& UserJson : *UsersJsonArray) - { - if (TSharedPtr UserJsonObject = UserJson->AsObject()) - { - Users.Add(FNakamaUser(UserJsonObject)); - } - } - } - } -} - - -FNakamaUser::FNakamaUser(const FString& JsonString) : FNakamaUser(FNakamaUtils::DeserializeJsonObject(JsonString)) { -} - -FNakamaUser::FNakamaUser(const TSharedPtr JsonObject) -{ - if (JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("id"), Id); - JsonObject->TryGetStringField(TEXT("username"), Username); - JsonObject->TryGetStringField(TEXT("display_name"), DisplayName); - JsonObject->TryGetStringField(TEXT("avatar_url"), AvatarUrl); - JsonObject->TryGetStringField(TEXT("lang_tag"), Language); - JsonObject->TryGetStringField(TEXT("location"), Location); - JsonObject->TryGetStringField(TEXT("timezone"), TimeZone); - JsonObject->TryGetStringField(TEXT("metadata"), MetaData); - JsonObject->TryGetStringField(TEXT("facebook_id"), FacebookId); - JsonObject->TryGetStringField(TEXT("google_id"), GoogleId); - JsonObject->TryGetStringField(TEXT("gamecenter_id"), GameCenterId); - JsonObject->TryGetStringField(TEXT("apple_id"), AppleId); - JsonObject->TryGetStringField(TEXT("steam_id"), SteamId); - - JsonObject->TryGetBoolField(TEXT("online"), Online); - JsonObject->TryGetNumberField(TEXT("edge_count"), EdgeCount); - - FString CreatedAtString; - if (JsonObject->TryGetStringField(TEXT("create_time"), CreatedAtString)) - { - FDateTime::ParseIso8601(*CreatedAtString, CreatedAt); - } - - FString UpdatedAtString; - if (JsonObject->TryGetStringField(TEXT("update_time"), UpdatedAtString)) - { - FDateTime::ParseIso8601(*UpdatedAtString, updatedAt); - } - } -} - diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaUserSession.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaUserSession.cpp deleted file mode 100644 index d7590e6ea..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaUserSession.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaUserSession.h" - -FNakamaUserSession::FNakamaUserSession() - : CreateTime(FDateTime::MinValue()), ExpireTime(FDateTime::MinValue()), RefreshExpireTime(FDateTime::MinValue()), - IsCreated(false), IsExpired(false), IsRefreshExpired(false) -{ - -} \ No newline at end of file diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaUtils.cpp b/Nakama/Source/NakamaUnreal/Private/NakamaUtils.cpp deleted file mode 100644 index c24ae37ab..000000000 --- a/Nakama/Source/NakamaUnreal/Private/NakamaUtils.cpp +++ /dev/null @@ -1,195 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NakamaUtils.h" -#include "NakamaUser.h" -#include "NakamaLogger.h" -#include "Dom/JsonObject.h" -#include "Misc/EngineVersionComparison.h" -#include "Interfaces/IHttpResponse.h" - -DEFINE_LOG_CATEGORY_STATIC(LogNakamaUtils, Log, Log); - -void FNakamaUtils::ProcessRequestComplete(FHttpRequestPtr Request, const FHttpResponsePtr& Response, bool bSuccess, const TFunction& SuccessCallback, const TFunction& ErrorCallback) -{ - if (bSuccess && Response.IsValid()) - { - const int32 ResponseCode = Response->GetResponseCode(); - const FString ResponseBody = Response->GetContentAsString(); - - if (ResponseCode == 200) - { - NAKAMA_LOG_DEBUG(FString::Printf(TEXT("Request Successful: %s"), *ResponseBody)); - if (SuccessCallback) - { - SuccessCallback(ResponseBody); - } - } - else - { - NAKAMA_LOG_WARN(FString::Printf(TEXT("Response (Code: %d) - Contents: %s"), ResponseCode, *ResponseBody)); - const FNakamaError Error(ResponseBody); - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - } - else - { - // Handle request failure - NAKAMA_LOG_ERROR(TEXT("Failed to process request.")); - - if (Request.IsValid()) - { - NAKAMA_LOG_DEBUG(FString::Printf(TEXT("Request URL: %s"), *(Request->GetURL()))); - } - - FNakamaError Error; - Error.Code = ENakamaErrorCode::Unknown; - Error.Message = TEXT("Failed to proccess request. Request failed."); - - if (ErrorCallback) - { - ErrorCallback(Error); - } - } -} - -void FNakamaUtils::ProcessRequestCompleteMove(FHttpRequestPtr Request, const FHttpResponsePtr& Response, bool bSuccess, - const TFunction& SuccessCallback, const TFunction& ErrorCallback) -{ - if (bSuccess && Response.IsValid()) - { - const int32 ResponseCode = Response->GetResponseCode(); - FString ResponseBody = Response->GetContentAsString(); - - if (ResponseCode == 200) - { - NAKAMA_LOG_DEBUG(FString::Printf(TEXT("Request Successful: %s"), *ResponseBody)); - if (SuccessCallback) - { - SuccessCallback(MoveTemp(ResponseBody)); - } - } - else - { - NAKAMA_LOG_WARN(FString::Printf(TEXT("Response (Code: %d) - Contents: %s"), ResponseCode, *ResponseBody)); - const FNakamaError Error(ResponseBody); - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - } - else - { - // Handle request failure - NAKAMA_LOG_ERROR(TEXT("Failed to process request.")); - - if (Request.IsValid()) - { - NAKAMA_LOG_DEBUG(FString::Printf(TEXT("Request URL: %s"), *(Request->GetURL()))); - } - - FNakamaError Error; - Error.Code = ENakamaErrorCode::Unknown; - Error.Message = TEXT("Failed to proccess request. Request failed."); - - if (ErrorCallback) - { - ErrorCallback(Error); - } - } -} - -void FNakamaUtils::HandleJsonSerializationFailure(TFunction ErrorCallback) - { - NAKAMA_LOG_ERROR(TEXT("Failed to generate request content.")); - FNakamaError Error; - Error.Code = ENakamaErrorCode::Unknown; - Error.Message = TEXT("Failed to generate request content."); - ErrorCallback(Error); - } - - bool FNakamaUtils::IsSessionValid(const UNakamaSession* Session, TFunction ErrorCallback) - { - if (!Session || Session->SessionData.AuthToken.IsEmpty()) - { - NAKAMA_LOG_ERROR("Invalid session or session data."); - - FNakamaError Error; - Error.Message = "Invalid session or session data."; - ErrorCallback(Error); - return false; - } - - return true; - } - - bool FNakamaUtils::IsResponseSuccessful(int32 ResponseCode) - { - return ResponseCode == 200; - } - - FNakamaError FNakamaUtils::CreateRequestFailureError() - { - NAKAMA_LOG_ERROR(TEXT("Failed to proccess request. Request failed.")); - FNakamaError Error; - Error.Code = ENakamaErrorCode::Unknown; - Error.Message = TEXT("Failed to proccess request. Request failed."); - return Error; - } - - TSharedRef FNakamaUtils::MakeRequest(const FString& URL, const FString& Content, ENakamaRequestMethod RequestMethod, const FString& SessionToken, float Timeout) - { - FHttpModule* HttpModule = &FHttpModule::Get(); - - // Create the HttpRequest -#if ENGINE_MAJOR_VERSION <= 4 && ENGINE_MINOR_VERSION <= 25 - TSharedRef Request = HttpModule->CreateRequest(); -#else - TSharedRef HttpRequest = HttpModule->CreateRequest(); -#endif - - HttpRequest->SetURL(URL); - HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json")); - HttpRequest->SetTimeout(Timeout); // Exposed to end user - - FString VerbString = ENakamaRequesMethodToFString(RequestMethod); - if (!VerbString.IsEmpty()) - { - HttpRequest->SetVerb(VerbString); - } - - // Set the content if it is not empty - if (!Content.IsEmpty()) - { - HttpRequest->SetContentAsString(Content); - } - - // Add authorization header if session token is provided - if (!SessionToken.IsEmpty()) - { - FString AuthorizationHeader = FString::Printf(TEXT("Bearer %s"), *SessionToken); - HttpRequest->SetHeader(TEXT("Authorization"), AuthorizationHeader); - } - - //NAKAMA_LOG_INFO(TEXT("...")); - //NAKAMA_LOG_INFO(FString::Printf(TEXT("Making Request to %s"), *Endpoint)); - NAKAMA_LOG_INFO(FString::Printf(TEXT("Making %s request to %s with content: %s"), *VerbString, *URL, *Content)); - return HttpRequest; - } diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaAccount.h b/Nakama/Source/NakamaUnreal/Public/NakamaAccount.h deleted file mode 100644 index 45f20306a..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaAccount.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaAccountDevice.h" -#include "NakamaUser.h" -#include "NakamaAccount.generated.h" - -namespace Nakama -{ - struct NAccount; -} - -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaAccount -{ - GENERATED_BODY() - - // The user object. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Account", meta = (JsonProperty = "user")) - FNakamaUser User; - - // The devices which belong to the user's account. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Account", meta = (JsonProperty = "devices")) - TArray Devices; - - // The user's wallet data. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Account", meta = (JsonProperty = "wallet")) - FString Wallet; - - // The email address of the user. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Account", meta = (JsonProperty = "email")) - FString Email; - - // The custom id in the user's account. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Account", meta = (JsonProperty = "custom_id")) - FString CustomId; - - // The UNIX time when the user's email was verified. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Account", meta = (JsonProperty = "verify_time")) - FDateTime VerifyTime; - - // The UNIX time when the user's account was disabled/banned. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Account", meta = (JsonProperty = "disable_time")) - FDateTime DisableTime; - - FNakamaAccount(); - - FNakamaAccount(const FString& JsonString); - -}; \ No newline at end of file diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaAccountDevice.h b/Nakama/Source/NakamaUnreal/Public/NakamaAccountDevice.h deleted file mode 100644 index 128a25fe2..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaAccountDevice.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaAccountDevice.generated.h" - - -// Used with authenticate/link/unlink and user. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaAccountDevice -{ - GENERATED_BODY() - - // Extra information that will be bundled in the session token. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Account") - TMap Vars; - - // A device identifier. Should be obtained by a platform-specific device API. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Account") - FString Id; - - FNakamaAccountDevice(const FString& JsonString); - FNakamaAccountDevice(const TSharedPtr JsonObject); - FNakamaAccountDevice(); -}; \ No newline at end of file diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaChannelTypes.h b/Nakama/Source/NakamaUnreal/Public/NakamaChannelTypes.h deleted file mode 100644 index 41f6abd5d..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaChannelTypes.h +++ /dev/null @@ -1,213 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaPresence.h" - -#include "NakamaChannelTypes.generated.h" - -//Chat Category// -UENUM(BlueprintType) -enum class ENakamaChannelType : uint8 -{ - // Default case. Assumed as ROOM type. - TYPE_UNSPECIFIED UMETA(DisplayName = "Unspecified"), - // A chat room which can be created dynamically with a name. - ROOM UMETA(DisplayName = "Room"), - // A private chat between two users. - DIRECT_MESSAGE UMETA(DisplayName = "Direct Message"), - // A chat within a group on the server. - GROUP UMETA(DisplayName = "Group"), -}; - -// A message sent on a channel. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaChannelMessage -{ - GENERATED_BODY() - - // The channel this message belongs to. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FString ChannelId; - - // The unique ID of this message. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FString MessageId; - - // Message sender, usually a user ID. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FString SenderId; - - // The username of the message sender, if any. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FString Username; - - // The content payload. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FString Content; - - // The name of the chat room, or an empty string if this message was not sent through a chat room. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FString RoomName; - - // The ID of the group, or an empty string if this message was not sent through a group channel. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FString GroupId; - - // The ID of the first DM user, or an empty string if this message was not sent through a DM chat. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FString UserIdOne; - - // The ID of the second DM user, or an empty string if this message was not sent through a DM chat. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FString UserIdTwo; - - // The UNIX time when the message was created. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FDateTime CreateTime; - - // The UNIX time when the message was last updated. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FDateTime UpdateTime; - - // The code representing a message type or category. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - int32 code; - - // True if the message was persisted to the channel's history, false otherwise. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - bool Persistent; - - FNakamaChannelMessage(const FString& JsonString); - FNakamaChannelMessage(const TSharedPtr JsonObject); - FNakamaChannelMessage(); // Default Constructor -}; - -// A receipt reply from a channel message send operation. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaChannelMessageAck -{ - GENERATED_BODY() - - // The channel the message was sent to. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FString ChannelId; - - // The unique ID assigned to the message. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FString MessageId; - - // Username of the message sender. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FString Username; - - // The name of the chat room, or an empty string if this message was not sent through a chat room. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FString RoomName; - - // The ID of the group, or an empty string if this message was not sent through a group channel. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FString GroupId; - - // The ID of the first DM user, or an empty string if this message was not sent through a DM chat. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FString UserIdOne; - - // The ID of the second DM user, or an empty string if this message was not sent through a DM chat. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FString UserIdTwo; - - // The UNIX time when the message was created. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FDateTime CreateTime; - - // The UNIX time when the message was last updated. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FDateTime UpdateTime; - - // The code representing a message type or category. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - int32 code; - - // True if the message was persisted to the channel's history, false otherwise. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - bool Persistent; - - FNakamaChannelMessageAck(const FString& JsonString); - FNakamaChannelMessageAck(); // Default Constructor -}; - -// A list of channel messages, usually a result of a list operation. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaChannelMessageList -{ - GENERATED_BODY() - - // A list of messages. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - TArray Messages; - - // The cursor to send when retireving the next page, if any. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FString NextCursor; - - // The cursor to send when retrieving the previous page, if any. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FString PrevCursor; - - FNakamaChannelMessageList(const FString& JsonString); - FNakamaChannelMessageList(); - -}; - -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaChannelPresenceEvent -{ - GENERATED_BODY() - - // Presences joining the channel as part of this event, if any. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - TArray Joins; - - // Presences leaving the channel as part of this event, if any. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - TArray Leaves; - - // The channel identifier this event is for. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FString ChannelId; - - // The name of the chat room, or an empty string if this message was not sent through a chat room. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FString RoomName; - - // The ID of the group, or an empty string if this message was not sent through a group channel. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FString GroupId; - - // The ID of the first DM user, or an empty string if this message was not sent through a DM chat. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FString UserIdOne; - - // The ID of the second DM user, or an empty string if this message was not sent through a DM chat. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Chat|Channel") - FString UserIdTwo; - - FNakamaChannelPresenceEvent(const FString& JsonString); - FNakamaChannelPresenceEvent(); // Default Constructor -}; \ No newline at end of file diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaChat.h b/Nakama/Source/NakamaUnreal/Public/NakamaChat.h deleted file mode 100644 index ff71e0173..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaChat.h +++ /dev/null @@ -1,108 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaChannelTypes.h" -#include "NakamaPresence.h" -#include "NakamaChat.generated.h" - -namespace Nakama -{ - struct NChannel; -} - -// For sending messages (Internal to plugin, no need to convert) -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaChatMessage -{ - GENERATED_BODY() - - // The Chat Message. - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nakama Chat") - FString ChatMessage; - - // Group Name to Send To. - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nakama Chat") - FString GroupName; - - // The Message Type. - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nakama Chat") - ENakamaChannelType MessageType; - - FNakamaChatMessage() : MessageType(ENakamaChannelType::DIRECT_MESSAGE) - { } -}; - - -// Channels -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaChannel -{ - GENERATED_BODY() - - // A reference to the current user's presence in the channel. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, DisplayName = "Self", Category = "Nakama Chat") - FNakamaUserPresence Me; - - // The users currently in the channel. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama Chat") - TArray Presences; - - // The ID of the channel. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama Chat") - FString Id; - - // The name of the chat room, or an empty string if this message was not sent through a chat room. - //UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama Chat") - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama Chat", meta = (JsonProperty = "room_name")) - FString RoomName; - - // The ID of the group, or an empty string if this message was not sent through a group channel. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama Chat", meta = (JsonProperty = "group_id")) - FString GroupId; - - // The ID of the first DM user, or an empty string if this message was not sent through a DM chat. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama Chat", meta = (JsonProperty = "user_id_one")) - FString UserIdOne; - - // The ID of the second DM user, or an empty string if this message was not sent through a DM chat. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama Chat", meta = (JsonProperty = "user_id_two")) - FString UserIdTwo; - - FNakamaChannel(const FString& JsonString); - FNakamaChannel(); -}; - -// For sending messages (Internal to plugin, no need to convert) -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaPrivateChat -{ - GENERATED_BODY() - - // User Id One - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama Chat") - FString UserIdOne; - - // User Id Two - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama Chat") - FString UserIdTwo; - - // Channel Id. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama Chat") - FString ChannelId; -}; \ No newline at end of file diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaClient.h b/Nakama/Source/NakamaUnreal/Public/NakamaClient.h deleted file mode 100644 index 1879c213f..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaClient.h +++ /dev/null @@ -1,3048 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaRealtimeClient.h" -#include "NakamaUser.h" -#include "NakamaAccount.h" -#include "NakamaFriend.h" -#include "NakamaGroup.h" -#include "NakamaError.h" -#include "NakamaNotification.h" -#include "NakamaStorageObject.h" -#include "NakamaLeaderboard.h" -#include "NakamaTournament.h" -#include "NakamaRPC.h" -#include "NakamaMatch.h" -#include "NakamaSession.h" -#include "Interfaces/IHttpRequest.h" -#include "HttpModule.h" - -#include "NakamaClient.generated.h" - -using namespace Nakama; - -// Delegates -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAuthUpdate, UNakamaSession*, LoginData); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnError, const FNakamaError&, ErrorData); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnLinkSuccess); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnUnLinkSuccess); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAuthRefresh, UNakamaSession*, Session); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAuthRefreshError, const FNakamaError&, ErrorData); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnUserAccountInfo, const FNakamaAccount&, AccountData); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGetUsers, const TArray &, Users); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnUpdateAccount); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnDeleteUser); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnFriendsList, FNakamaFriendList, Friends); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReceivedFriendsList, FNakamaFriendList, list); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnAddedFriend); //Add 's here -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnRemovedFriends); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnBlockedFriends); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnJoinGroup); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnRemoveGroup); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnListGroupMembers, const FNakamaGroupUsersList&, members); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnUpdateGroup); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnLeaveGroup); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnAddGroupUsers); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnPromoteGroupUsers); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnDemoteGroupUsers); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnKickGroupUsers); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnBanGroupUsers); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnUserGroups, const FNakamaUserGroupList&, UserGroups); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGroupsList, const FNakamaGroupList&, Groups); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnCreateGroup, FNakamaGroup, Group); //FOnStorageObjectAcks -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnJoinedGroup); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnListNotifications, FNakamaNotificationList, NotificationList); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnDeleteNotifications); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnStorageObjectAcks, const FNakamaStoreObjectAcks&, StorageObjectsAcks); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnStorageObjectsRead, const FNakamaStorageObjectList&, StorageObjects); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnStorageObjectsListed, const FNakamaStorageObjectList&, StorageObjects); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnRemovedStorageObjects); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnListedParties, const FNakamaPartyList&, PartyList); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRPC, const FNakamaRPC&, rpc); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnListChannelMessages, const FNakamaChannelMessageList&, MessageList); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnWriteLeaderboardRecord, const FNakamaLeaderboardRecord&, Record); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnListLeaderboardRecords, const FNakamaLeaderboardRecordList&, RecordsList); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnDeletedLeaderboardRecord); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnListTournamentRecords, const FNakamaTournamentRecordList&, RecordsList); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnListTournaments, const FNakamaTournamentList&, Tournaments); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnJoinedTournament); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnMatchlist, const FNakamaMatchList&, MatchList); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnImportFacebookFriends); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnImportSteamFriends); - -UENUM(BlueprintType) -enum class ENakamaRequestMethod : uint8 -{ - GET, - POST, - DEL, - PUT, -}; - -/** - * - */ -UCLASS(Blueprintable, BlueprintType, meta=(BlueprintSpawnableComponent)) -class NAKAMAUNREAL_API UNakamaClient : public UObject -{ - GENERATED_BODY() - -private: - - // End user can set this - float Timeout = 10.0f; - -protected: - - // Variables - FString Hostname; - int32 Port; - FString ServerKey; - bool bUseSSL; - -public: - - void InitializeClient( - const FString& InHostname, - int32 InPort, - const FString& InServerKey, - bool bInUseSSL - ); - - bool bEnableDebug; - - UPROPERTY(BlueprintAssignable, Category = "Nakama|Events") - FOnDisconnected DisconnectedEvent; - - UPROPERTY() - bool bIsActive; - - // Initialize System, this has to be called first, done via the Library Action instead (removed BlueprintCallable) - UFUNCTION(Category = "Nakama|Initialize") - void InitializeSystem(const FString& InServerKey, const FString& Host, int32 InPort, bool UseSSL, bool EnableDebug); - - /** - * Disconnects the client. This function kills all outgoing exchanges immediately without waiting. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Client", meta=(DeprecatedFunction, DeprecationMessage="Use CancelAllRequests instead")) - void Disconnect(); - - /** - * Cancels all Requests. This function kills all outgoing exchanges immediately without waiting. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Client") - void CancelAllRequests(); - - /** - * Destroys the Client. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Client") - void Destroy(); - - // Manage Timeout - UFUNCTION(BlueprintCallable, Category = "Nakama|Client") - void SetTimeout(float InTimeout); - - UFUNCTION(BlueprintCallable, Category = "Nakama|Client") - float GetTimeout(); - - - // Event that is called on cleanup - virtual void BeginDestroy() override; - - /** - * Creates a default client to interact with Nakama server. - * - * @param ServerKey Server key should match the one on the Nakama Server. - * @param Host The endpoint host name. - * @param Port The port to use, default is 7350. - * @param UseSSL Use "https" scheme if you've setup SSL. - * @param EnableDebug To enable logs output to console with debug logging level. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Client") - static UNakamaClient* CreateDefaultClient( - const FString& ServerKey = "defaultkey", - const FString& Host = "localhost", - int32 Port = 7350, - bool UseSSL = false, - bool EnableDebug = true - ); - - // --- Authentication --- // - - /** - * Authenticate a user with a custom id. - * - * @param UserID A custom identifier usually obtained from an external authentication service. - * @param Username A username used to create the user. - * @param CreateAccount True if the user should be created when authenticated. - * @param Vars Extra information that will be bundled in the session token. - * @param Success Delegate called upon successful authentication, providing the user session. - * @param Error Delegate called on authentication failure with error details. - */ - UFUNCTION(Category = "Nakama|Authentication") - void AuthenticateCustom( - const FString& UserID, - const FString& Username, - bool CreateAccount, - const TMap& Vars, - FOnAuthUpdate Success, - FOnError Error - ); - - /** - * Authenticate a user with an email and password. - * - * @param Email The email address of the user. - * @param Password The password for the user. - * @param Username A username used to create the user. - * @param CreateAccount True if the user should be created when authenticated. - * @param Vars Extra information that will be bundled in the session token. - * @param Success Delegate called upon successful authentication, providing the user session. - * @param Error Delegate called on authentication failure with error details. - */ - UFUNCTION(Category = "Nakama|Authentication") - void AuthenticateEmail( - const FString& Email, - const FString& Password, - const FString& Username, - bool CreateAccount, - const TMap& Vars, - FOnAuthUpdate Success, - FOnError Error - ); - - /** - * Authenticate a user with a device id. - * - * @param DeviceID A device identifier usually obtained from a platform API. - * @param Username A username used to create the user. Defaults to empty string. - * @param CreateAccount True if the user should be created when authenticated. Defaults to false. - * @param Vars Extra information that will be bundled in the session token. - * @param Success Delegate called upon successful authentication, providing the user session. - * @param Error Delegate called on authentication failure with error details. - */ - UFUNCTION(Category = "Nakama|Authentication") - void AuthenticateDevice( - const FString& DeviceID, - const FString& Username, - bool CreateAccount, - const TMap& Vars, - const FOnAuthUpdate& Success, - const FOnError& Error - ); - - // Social Authentication - - /** - * Authenticate a user with a Steam auth token. - * - * @param SteamToken An authentication token from the Steam network. - * @param Username A username used to create the user. - * @param CreateAccount True if the user should be created when authenticated. - * @param ImportFriends True if the Steam friends should be imported. - * @param Vars Extra information that will be bundled in the session token. - * @param Success Delegate called upon successful authentication, providing the user session. - * @param Error Delegate called on authentication failure with error details. - */ - UFUNCTION(Category = "Nakama|Authentication") - void AuthenticateSteam( - const FString& SteamToken, - const FString& Username, - bool CreateAccount, - bool ImportFriends, - const TMap& Vars, - FOnAuthUpdate Success, - FOnError Error - ); - - /** - * Authenticate a user with a Google auth token. - * - * @param AccessToken An OAuth access token from the Google SDK. - * @param Username A username used to create the user. - * @param CreateAccount True if the user should be created when authenticated. - * @param Vars Extra information that will be bundled in the session token. - * @param Success Delegate called upon successful authentication, providing the user session. - * @param Error Delegate called on authentication failure with error details. - */ - UFUNCTION(Category = "Nakama|Authentication") - void AuthenticateGoogle( - const FString& AccessToken, - const FString& Username, - bool CreateAccount, - const TMap& Vars, - FOnAuthUpdate Success, - FOnError Error - ); - - /** - * Authenticate a user with Apple Game Center. - * - * @param PlayerId The player id of the user in Game Center. - * @param BundleId The bundle id of the Game Center application. - * @param TimeStampSeconds The date and time that the signature was created. - * @param Salt A random NSString used to compute the hash and keep it randomized. - * @param Signature The verification signature data generated. - * @param PublicKeyUrl The URL for the public encryption key. - * @param Username A username used to create the user. - * @param CreateAccount True if the user should be created when authenticated. - * @param Vars Extra information that will be bundled in the session token. - * @param Success Delegate called upon successful authentication, providing the user session. - * @param Error Delegate called on authentication failure with error details. - */ - UFUNCTION(Category = "Nakama|Authentication") - void AuthenticateGameCenter( - const FString& PlayerId, - const FString& BundleId, - int64 TimeStampSeconds, - const FString& Salt, - const FString& Signature, - const FString& PublicKeyUrl, - const FString& Username, - bool CreateAccount, - const TMap& Vars, - FOnAuthUpdate Success, - FOnError Error - ); - - /** - * Authenticate a user with a Facebook auth token. - * - * @param AccessToken An OAuth access token from the Facebook SDK. - * @param Username A username used to create the user. - * @param CreateAccount True if the user should be created when authenticated. - * @param ImportFriends True if the Facebook friends should be imported. - * @param Vars Extra information that will be bundled in the session token. - * @param Success Delegate called upon successful authentication, providing the user session. - * @param Error Delegate called on authentication failure with error details. - */ - UFUNCTION(Category = "Nakama|Authentication") - void AuthenticateFacebook( - const FString& AccessToken, - const FString& Username, - bool CreateAccount, - bool ImportFriends, - const TMap& Vars, - FOnAuthUpdate Success, - FOnError Error - ); - - /** - * Authenticate a user with Apple Sign In. - * - * @param Token The ID token received from Apple to validate. - * @param Username A username used to create the user. - * @param CreateAccount True if the user should be created when authenticated. - * @param Vars Extra information that will be bundled in the session token. - * @param Success Delegate called upon successful authentication, providing the user session. - * @param Error Delegate called on authentication failure with error details. - */ - UFUNCTION(Category = "Nakama|Authentication") - void AuthenticateApple( - const FString& Token, - const FString& Username, - bool CreateAccount, - const TMap& Vars, - FOnAuthUpdate Success, - FOnError Error - ); - - /** - * Refresh a user's session using a refresh token retrieved from a previous authentication request. - * @param Session The session of the user. - * @param Success Delegate called upon successful authentication, providing the user session. - * @param Error Delegate called on authentication failure with error details. - **/ - UFUNCTION(Category = "Nakama|Authentication") - void AuthenticateRefresh( - UNakamaSession* Session, - FOnAuthUpdate Success, - FOnError Error - ); - - // --- Restore Session --- // - - /** - * Restore User Session - * - * @param Token Authentication Token from Session - * @param RefreshToken RefreshToken retrieved from Session - * @param RestoredSession Returns restored session - */ - UFUNCTION(Category = "Nakama|Authentication") - void RestoreSession( - const FString& Token, - const FString& RefreshToken, - UNakamaSession*& RestoredSession - ); - - // --- Link Account --- // - - /** - * Link a custom ID to the user account associated with the provided session. - * - * @param CustomId A custom identifier usually obtained from an external authentication service. - * @param Session The session of the user whose account will be linked to the custom ID. - * @param Success Delegate called when the custom ID is successfully linked, providing the updated session information. - * @param Error Delegate called if the linking process fails, detailing the error. - * @param Success Delegate called when the custom ID is successfully linked. - * @param Error Delegate called if the linking process fails, detailing the error. - */ - UFUNCTION(Category = "Nakama|Authentication|Link") - void LinkCustom( - UNakamaSession *Session, - const FString& CustomId, - FOnLinkSuccess Success, - FOnError Error - ); - - /** - * Link a device ID to the user account associated with the provided session. - * - * @param DeviceId A device identifier usually obtained from a platform API. - * @param Session The session of the user whose account will be linked to the device ID. - * @param Success Delegate called when the device ID is successfully linked. - * @param Error Delegate called if the linking process fails, detailing the error. - */ - UFUNCTION(Category = "Nakama|Authentication|Link") - void LinkDevice( - UNakamaSession *Session, - const FString& DeviceId, - FOnLinkSuccess Success, - FOnError Error - ); - - /** - * Link an email with password to the user account associated with the provided session. - * - * @param Email The email address of the user. - * @param Password The password for the user. - * @param Session The session of the user whose account will be linked to the email. - * @param Success Delegate called when the email is successfully linked. - * @param Error Delegate called if the linking process fails, detailing the error. - */ - UFUNCTION(Category = "Nakama|Authentication|Link") - void LinkEmail( - UNakamaSession *Session, - const FString& Email, - const FString& Password, - FOnLinkSuccess Success, - FOnError Error - ); - - /** - * Link a Facebook profile to the user account associated with the provided session. - * - * @param AccessToken An OAuth access token from the Facebook SDK. - * @param ImportFriends True if the Facebook friends should be imported. - * @param Session The session of the user whose account will be linked to the Facebook profile. - * @param Success Delegate called when the Facebook profile is successfully linked. - * @param Error Delegate called if the linking process fails, detailing the error. - */ - UFUNCTION(Category = "Nakama|Authentication|Link") - void LinkFacebook( - UNakamaSession *Session, - const FString& AccessToken, - bool ImportFriends, - FOnLinkSuccess Success, - FOnError Error - ); - - /** - * Link a Game Center profile to the user account associated with the provided session. - * - * @param PlayerId The player id of the user in Game Center. - * @param BundleId The bundle id of the Game Center application. - * @param TimeStampSeconds The date and time that the signature was created. - * @param Salt A random NSString used to compute the hash and keep it randomized. - * @param Signature The verification signature data generated. - * @param PublicKeyUrl The URL for the public encryption key. - * @param Session The session of the user whose account will be linked to the Game Center profile. - * @param Success Delegate called when the Game Center profile is successfully linked. - * @param Error Delegate called if the linking process fails, detailing the error. - */ - UFUNCTION(Category = "Nakama|Authentication|Link") - void LinkGameCenter( - UNakamaSession *Session, - const FString& PlayerId, - const FString& BundleId, - int64 TimeStampSeconds, - const FString& Salt, - const FString& Signature, - const FString& PublicKeyUrl, - FOnLinkSuccess Success, - FOnError Error - ); - - /** - * Link a Google profile to the user account associated with the provided session. - * - * @param AccessToken An OAuth access token from the Google SDK. - * @param Session The session of the user whose account will be linked to the Google profile. - * @param Success Delegate called when the Google profile is successfully linked. - * @param Error Delegate called if the linking process fails, detailing the error. - */ - UFUNCTION(Category = "Nakama|Authentication|Link") - void LinkGoogle( - UNakamaSession *Session, - const FString& AccessToken, - FOnLinkSuccess Success, - FOnError Error - ); - - /** - * Link a Steam profile to the user account associated with the provided session. - * - * @param SteamToken An authentication token from the Steam network. - * @param Session The session of the user whose account will be linked to the Steam profile. - * @param Success Delegate called when the Steam profile is successfully linked. - * @param Error Delegate called if the linking process fails, detailing the error. - */ - UFUNCTION(Category = "Nakama|Authentication|Link") - void LinkSteam( - UNakamaSession *Session, - const FString& SteamToken, - FOnLinkSuccess Success, - FOnError Error - ); - - /** - * Link an Apple ID to the user account associated with the provided session. - * - * @param Token The ID token received from Apple. - * @param Session The session of the user whose account will be linked to the Apple ID. - * @param Success Delegate called when the Apple ID is successfully linked. - * @param Error Delegate called if the linking process fails, detailing the error. - */ - UFUNCTION(Category = "Nakama|Authentication|Link") - void LinkApple( - UNakamaSession *Session, - const FString& Token, - FOnLinkSuccess Success, - FOnError Error - ); - - // --- Unlinking --- // - - /** - * Unlink a custom ID from the user account associated with the provided session. - * - * @param CustomId A custom identifier usually obtained from an external authentication service. - * @param Session The session of the user. - * @param Success Delegate called when the custom ID is successfully unlinked. - * @param Error Delegate called if the unlinking process fails, detailing the error. - */ - UFUNCTION(Category = "Nakama|Authentication|Unlink") - void UnLinkCustom( - UNakamaSession *Session, - const FString& CustomId, - FOnLinkSuccess Success, - FOnError Error - ); - - /** - * Unlink a device ID from the user account associated with the provided session. - * - * @param DeviceId A device identifier usually obtained from a platform API. - * @param Session The session of the user. - * @param Success Delegate called when the device ID is successfully unlinked. - * @param Error Delegate called if the unlinking process fails, detailing the error. - */ - UFUNCTION(Category = "Nakama|Authentication|Unlink") - void UnLinkDevice( - UNakamaSession *Session, - const FString& DeviceId, - FOnLinkSuccess Success, - FOnError Error - ); - - /** - * Unlink an email and its associated password from the user account tied to the session. - * - * @param Email The email address of the user. - * @param Password The password for the user. - * @param Session The session of the user. - * @param Success Delegate called when the email is successfully unlinked. - * @param Error Delegate called if the unlinking process fails, detailing the error. - */ - UFUNCTION(Category = "Nakama|Authentication|Unlink") - void UnLinkEmail( - UNakamaSession *Session, - const FString& Email, - const FString& Password, - FOnLinkSuccess Success, - FOnError Error - ); - - /** - * Unlink a Facebook profile from the user account associated with the provided session. - * - * @param AccessToken An OAuth access token from the Facebook SDK. - * @param Session The session of the user. - * @param Success Delegate called when the Facebook profile is successfully unlinked. - * @param Error Delegate called if the unlinking process fails, detailing the error. - */ - UFUNCTION(Category = "Nakama|Authentication|Unlink") - void UnLinkFacebook( - UNakamaSession *Session, - const FString& AccessToken, - FOnLinkSuccess Success, - FOnError Error - ); - - /** - * Unlink a Game Center profile from the user account tied to the session. - * - * @param PlayerId The player id of the user in Game Center. - * @param BundleId The bundle id of the Game Center application. - * @param TimeStampSeconds The date and time that the signature was created. - * @param Salt A random NSString used to compute the hash and keep it randomized. - * @param Signature The verification signature data generated. - * @param PublicKeyUrl The URL for the public encryption key. - * @param Session The session of the user. - * @param Success Delegate called when the Game Center profile is successfully unlinked. - * @param Error Delegate called if the unlinking process fails, detailing the error. - */ - UFUNCTION(Category = "Nakama|Authentication|Unlink") - void UnLinkGameCenter( - UNakamaSession *Session, - const FString& PlayerId, - const FString& BundleId, - int64 TimeStampSeconds, - const FString& Salt, - const FString& Signature, - const FString& PublicKeyUrl, - FOnLinkSuccess Success, - FOnError Error - ); - - /** - * Unlink a Google profile from the user account tied to the session. - * - * @param AccessToken An OAuth access token from the Google SDK. - * @param Session The session of the user. - * @param Success Delegate called when the Google profile is successfully unlinked. - * @param Error Delegate called if the unlinking process fails, detailing the error. - */ - UFUNCTION(Category = "Nakama|Authentication|Unlink") - void UnLinkGoogle( - UNakamaSession *Session, - const FString& AccessToken, - FOnLinkSuccess Success, - FOnError Error - ); - - /** - * Unlink a Steam profile from the user account tied to the session. - * - * @param SteamToken An authentication token from the Steam network. - * @param Session The session of the user. - * @param Success Delegate called when the Steam profile is successfully unlinked. - * @param Error Delegate called if the unlinking process fails, detailing the error. - */ - UFUNCTION(Category = "Nakama|Authentication|Unlink") - void UnLinkSteam( - UNakamaSession *Session, - const FString& SteamToken, - FOnLinkSuccess Success, - FOnError Error - ); - - /** - * Unlink an Apple profile from the user account associated with the provided session. - * - * @param Token The authentication token from Apple. - * @param Session The session of the user. - * @param Success Delegate called when the Apple profile is successfully unlinked. - * @param Error Delegate called if the unlinking process fails, detailing the error. - */ - UFUNCTION(Category = "Nakama|Authentication|Unlink") - void UnLinkApple( - UNakamaSession *Session, - const FString& Token, - FOnLinkSuccess Success, - FOnError Error - ); - - // --- Functions --- // - - /** - * Refresh a user's session using a refresh token retrieved from a previous authentication request. - * @param Session The session of the user. - * @param Success Delegate called when the user's session is successfully refreshed. Provides session information. - * @param Error Delegate called if the session refresh process fails, detailing the error. - **/ - UFUNCTION(Category = "Nakama|Authentication|Refresh") - void RefreshSession( - UNakamaSession *Session, - FOnAuthRefresh Success, - FOnAuthRefreshError Error - ); - - /** - * Import Facebook friends and add them to the user's account. - * - * The server will import friends when the user authenticates with Facebook. This function can be used to be - * explicit with the import operation. - * - * @param Session The session of the user. - * @param Token An OAuth access token from the Facebook SDK. - * @param Reset True if the Facebook friend import for the user should be reset. - * @param Success Delegate called upon successful import of Facebook friends. - * @param Error Delegate called if the Facebook friends import operation fails, providing error details. - */ - UFUNCTION(Category = "Nakama|Friends") - void ImportFacebookFriends( - UNakamaSession* Session, - const FString& Token, - bool Reset, - FOnImportFacebookFriends Success, - FOnError Error - ); - - /** - * Import Steam friends and add them to the user's account. - * - * The server will import friends when the user authenticates with Steam. This function can be used to be - * explicit with the import operation. - * - * @param Session The session of the user. - * @param SteamToken The Steam token to use. - * @param Reset True if the Steam friend import for the user should be reset. - * @param Success Delegate called upon successful import of Steam friends. - * @param Error Delegate called if the Steam friends import operation fails, providing error details. - */ - UFUNCTION(Category = "Nakama|Friends") - void ImportSteamFriends( - UNakamaSession* Session, - const FString& SteamToken, - bool Reset, - FOnImportSteamFriends Success, - FOnError Error - ); - - // --- Get Account and User Info --- // - - /** - * - * [DEPRECATED] Fetch the user account owned by the session - * @param Session The session of the user. - * @param Success Delegate called upon successfully retrieving user account details. - * @param Error Delegate called if the fetch operation for user account details fails, providing error information. - * - */ - UFUNCTION(Category = "Nakama|Users", meta=(DeprecatedFunction, DeprecationMessage="Use GetAccount instead")) - void GetUserAccount( - UNakamaSession *Session, - FOnUserAccountInfo Success, - FOnError Error - ); - - /** - * - * Fetch the user account owned by the session. - * @param Session The session of the user. - * @param Success Delegate called upon successfully retrieving user account details. - * @param Error Delegate called if the fetch operation for user account details fails, providing error information. - */ - UFUNCTION(Category = "Nakama|Users") - void GetAccount( - UNakamaSession *Session, - FOnUserAccountInfo Success, - FOnError Error - ); - - /** - * Fetch one or more users by id, usernames, and Facebook ids. - * - * @param UserIds List of user IDs. - * @param Usernames List of usernames. - * @param FacebookIds List of Facebook IDs. - * @param Session The session of the user. - * @param Success Delegate called upon successfully retrieving the users' details. - * @param Error Delegate called if the user fetch operation fails, providing error information. - * - */ - UFUNCTION(Category = "Nakama|Users") - void GetUsers( - UNakamaSession *Session, - const TArray& UserIds, - const TArray& Usernames, - const TArray& FacebookIds, - FOnGetUsers Success, - FOnError Error - ); - - /** - * Update the current user's account on the server. - * - * @param Username The new username for the user. - * @param DisplayName A new display name for the user. - * @param AvatarUrl A new avatar url for the user. - * @param LanguageTag A new language tag in BCP-47 format for the user. - * @param Location A new location for the user. - * @param Timezone New timezone information for the user. - * @param Session The session of the user. - * @param Success Delegate called upon successfully updating the user's account details. - * @param Error Delegate called if the update operation fails, detailing the encountered error. - */ - UFUNCTION(Category = "Nakama|Users") - void UpdateAccount( - UNakamaSession *Session, - const FString& Username, - const FString& DisplayName, - const FString& AvatarUrl, - const FString& LanguageTag, - const FString& Location, - const FString& Timezone, - FOnUpdateAccount Success, - FOnError Error - ); - - /** - * Delete the current user from the server. - * - * @param Session The session of the user. - * @param Success Delegate called upon successfully deleting the user's account. - * @param Error Delegate called if the update operation fails, detailing the encountered error. - */ - UFUNCTION(Category = "Nakama|Users") - void DeleteUser( - UNakamaSession* Session, - FOnDeleteUser Success, - FOnError Error - ); - - // --- Realtime Client --- // - - /** - * Setup the Realtime Client (Socket) - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime") // Used for both Blueprints and C++ - UNakamaRealtimeClient* SetupRealtimeClient(); - - /** - * Fetch a list of matches active on the server. - * - * @param Session The session of the user. - * @param MinSize The minimum number of match participants. - * @param MaxSize The maximum number of match participants. - * @param Limit The number of matches to list. - * @param Label The label to filter the match list on. - * @param Query The query to the match listing. - * @param Authoritative True to include authoritative matches. - * @param Success Delegate called when the matches are fetched successfully, returning the match list. - * @param Error Delegate called if the fetching operation encounters an error, providing details on the failure. - */ - UFUNCTION(Category = "Nakama|Realtime") - void ListMatches( - UNakamaSession *Session, - int32 MinSize, - int32 MaxSize, - int32 Limit, - const FString& Label, - const FString& Query, - bool Authoritative, - FOnMatchlist Success, - FOnError Error - ); - - // --- Friends --- // - - /** - * [DEPRECATED] List of friends of the current user. - * - * @param Limit The max number of records to return. Between 1 and 100. - * @param State The friend state to list. - * @param Cursor An optional next page cursor. - * @param Session The session of the user. - * @param Success Delegate called upon successful fetch, returning the list of friends. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Friends", meta=(DeprecatedFunction, DeprecationMessage="Use ListFriends instead")) - void GetFriends( - UNakamaSession* Session, - int32 Limit, - ENakamaFriendState State, - const FString& Cursor, - FOnFriendsList Success, - FOnError Error - ); - - /** - * List of friends of the current user. - * - * @param Limit The max number of records to return. Between 1 and 1000. - * @param State The friend state to list. - * @param Cursor An optional next page cursor. - * @param Session The session of the user. - * @param Success Delegate called upon successful fetch, returning the list of friends. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Friends") - void ListFriends( - UNakamaSession* Session, - int32 Limit, - ENakamaFriendState State, - const FString& Cursor, - FOnFriendsList Success, - FOnError Error - ); - - /** - * Add one or more friends by id. - * - * @param Ids The ids of the users to add or invite as friends. - * @param Usernames The usernames of the users to add as friends. - * @param Session The session of the user. - * @param Success Delegate called upon successful friend addition. - * @param Error Delegate called if an error occurs, detailing the failure. - * - */ - UFUNCTION(Category = "Nakama|Friends") - void AddFriends( - UNakamaSession* Session, - const TArray& Ids, - const TArray& Usernames, - FOnAddedFriend Success, - FOnError Error - ); - - /** - * [DEPRECATED] Delete one more or users by id or username from friends. - * - * @param Ids the user ids to remove as friends. - * @param Usernames The usernames to remove as friends. - * @param Session The session of the user. - * @param Success Delegate called upon successful friend removal. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Friends", meta=(DeprecatedFunction, DeprecationMessage="Use DeleteFriends instead")) - void RemoveFriends( - UNakamaSession* Session, - const TArray& Ids, - const TArray& Usernames, - FOnRemovedFriends Success, - FOnError Error - ); - - /** - * Delete one more or users by id or username from friends. - * - * @param Ids the user ids to remove as friends. - * @param Usernames The usernames to remove as friends. - * @param Session The session of the user. - * @param Success Delegate called upon successful friend removal. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Friends") - void DeleteFriends( - UNakamaSession* Session, - const TArray& Ids, - const TArray& Usernames, - FOnRemovedFriends Success, - FOnError Error - ); - - /** - * Block one or more friends by id. - * - * @param Ids The ids of the users to block. - * @param Usernames The usernames of the users to block. - * @param Session The session of the user. - * @param Success Delegate called upon successfully blocking friends. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Friends") - void BlockFriends( - UNakamaSession* Session, - const TArray& Ids, - const TArray& Usernames, - FOnBlockedFriends Success, - FOnError Error - ); - - // --- Groups --- // - - /** - * Create a group. - * - * @param GroupName The name for the group. - * @param Description A description for the group. - * @param AvatarUrl An avatar url for the group. - * @param LanguageTag A language tag in BCP-47 format for the group. - * @param Open True if the group should have open membership. - * @param MaxMembers Maximum number of group members. - * @param Session The session of the user. - * @param Success Delegate called upon successfully creating the group. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Groups") - void CreateGroup( - UNakamaSession* Session, - const FString& GroupName, - const FString& Description, - const FString& AvatarUrl, - const FString& LanguageTag, - bool Open, - int32 MaxMembers, - FOnCreateGroup Success, - FOnError Error - ); - - /** - * List groups on the server. - * - * @param GroupNameFilter The name filter to apply to the group list. - * @param Limit The number of groups to list. - * @param Cursor A cursor for the current position in the groups to list. - * @param Session The session of the user. - * @param Success Delegate called upon successfully listing the groups. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Groups") - void ListGroups( - UNakamaSession* Session, - const FString& GroupNameFilter, - int32 Limit, - const FString& Cursor, - FOnGroupsList Success, - FOnError Error - ); - - /** - * Join a group if it has open membership or request to join it. - * - * @param GroupId The id of the group to join. - * @param Session The session of the user. - * @param Success Delegate called upon successfully joining the group. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Groups") - void JoinGroup( - UNakamaSession* Session, - const FString& GroupId, - FOnJoinedGroup Success, - FOnError Error - ); - - /** - * List of groups the current user is a member of. - * @param UserId The id of the user whose groups to list. - * @param Limit The max number of records to return. Between 1 and 100. - * @param State The group state to list. - * @param Cursor An optional next page cursor. - * @param Session The session of the user. - * @param Success Delegate called upon successfully listing the user's groups. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Groups") - void ListUserGroups( - UNakamaSession* Session, - const FString& UserId, - int32 Limit, - ENakamaGroupState State, - const FString& Cursor, - FOnUserGroups Success, - FOnError Error - ); - - /** - * List all users part of the group. - * - * @param GroupId The id of the group. - * @param Limit The max number of records to return. Between 1 and 100. - * @param State The group state to list. - * @param Cursor An optional next page cursor. - * @param Session The session of the user. - * @param Success Delegate called upon successfully listing the group members. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Groups") - void ListGroupUsers( - UNakamaSession* Session, - const FString& GroupId, - int32 Limit, - ENakamaGroupState State, - const FString& Cursor, - FOnListGroupMembers Success, - FOnError Error - ); - - /** - * Update a group. - * - * The user must have the correct access permissions for the group. - * - * @param GroupId The id of the group to update. - * @param Name A new name for the group. - * @param Description A new description for the group. - * @param AvatarUrl A new avatar url for the group. - * @param LanguageTag A new language tag in BCP-47 format for the group. - * @param Open True if the group should have open membership. - * @param Session The session of the user. - * @param Success Delegate called upon successfully updating the group. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Groups") - void UpdateGroup( - UNakamaSession* Session, - const FString& GroupId, - const FString& Name, - const FString& Description, - const FString& AvatarUrl, - const FString& LanguageTag, - bool Open, - FOnUpdateGroup Success, - FOnError Error - ); - - /** - * Leave a group by id. - * - * @param GroupId The id of the group to leave. - * @param Session The session of the user. - * @param Success Delegate called upon successfully leaving the group. - * @param Error Delegate called if an error occurs, detailing the failure. - * - */ - UFUNCTION(Category = "Nakama|Groups") - void LeaveGroup( - UNakamaSession* Session, - const FString& GroupId, - FOnLeaveGroup Success, - FOnError Error - ); - - /** - * Add one or more users to the group. - * - * @param GroupId The id of the group to add users into. - * @param UserIds The ids of the users to add or invite to the group. - * @param Session The session of the user. - * @param Success Delegate called upon successfully adding users to the group. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Groups") - void AddGroupUsers( - UNakamaSession* Session, - const FString& GroupId, - const TArray& UserIds, - FOnAddGroupUsers Success, - FOnError Error - ); - - /** - * Promote a set of users in a group to the next role up. - * - * @param GroupId The group ID to promote in. - * @param UserIds The ids of the users to promote. - * @param Session The session of the user. - * @param Success Delegate called upon successfully promoting users in the group. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Groups") - void PromoteGroupUsers( - UNakamaSession* Session, - const FString& GroupId, - const TArray& UserIds, - FOnPromoteGroupUsers Success, - FOnError Error - ); - - /** - * Kick one or more users from the group. - * - * @param GroupId The id of the group. - * @param UserIds The ids of the users to kick. - * @param Session The session of the user. - * @param Success Delegate called upon successfully kicking users from the group. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Groups") - void KickGroupUsers( - UNakamaSession* Session, - const FString& GroupId, - const TArray& UserIds, - FOnKickGroupUsers Success, - FOnError Error - ); - - /** - * Ban one or more users from the group. - * - * @param GroupId The id of the group. - * @param UserIds The ids of the users to ban. - * @param Session The session of the user. - * @param Success Delegate called upon successfully banning users from the group. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Groups") - void BanGroupUsers( - UNakamaSession* Session, - const FString& GroupId, - const TArray& UserIds, - FOnBanGroupUsers Success, - FOnError Error - ); - - /** - * Demote a set of users in a group to the next role down. - * - * @param GroupId The group ID to demote in. - * @param UserIds The ids of the users to demote. - * @param Session The session of the user. - * @param Success Delegate called upon successfully demoting users in the group. - * @param Error Delegate called if an error occurs, detailing the failure. - * - */ - UFUNCTION(Category = "Nakama|Groups") - void DemoteGroupUsers( - UNakamaSession* Session, - const FString& GroupId, - const TArray& UserIds, - FOnDemoteGroupUsers Success, - FOnError Error - ); - - /** - * Delete a group by id. - * - * @param GroupId The group id to to remove. - * @param Session The session of the user. - * @param Success Delegate called upon successfully deleting the group. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Groups") - void DeleteGroup( - UNakamaSession* Session, - const FString& GroupId, - FOnRemoveGroup Success, - FOnError Error - ); - - // --- Notifications --- // - - /** - * List notifications for the user with an optional cursor. - * - * @param Limit The number of notifications to list. - * @param Cursor A cursor for the current position in notifications to list. - * @param Session The session of the user. - * @param Success Delegate called upon successfully listing the notifications. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Notifications") - void ListNotifications( - UNakamaSession* Session, - int32 Limit, - const FString& Cursor, - FOnListNotifications Success, - FOnError Error - ); - - /** - * Delete one or more notifications by id. - * - * @param NotificationIds The notification ids to remove. - * @param Session The session of the user. - * @param Success Delegate called upon successfully deleting the notifications. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Notifications") - void DeleteNotifications( - UNakamaSession* Session, - const TArray& NotificationIds, - FOnDeleteNotifications Success, - FOnError Error - ); - - // --- Storage --- // - - /** - * Write objects to the storage engine. - * - * @param StorageObjectsData The objects to write. - * @param Session The session of the user. - * @param Success Delegate called upon successfully writing storage objects. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Storage") - void WriteStorageObjects( - UNakamaSession* Session, - const TArray& StorageObjectsData, - FOnStorageObjectAcks Success, - FOnError Error - ); - - /** - * Read one or more objects from the storage engine. - * - * @param StorageObjectsData The objects to read. - * @param Session The session of the user. - * @param Success Delegate called upon successfully reading the storage objects. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Storage") - void ReadStorageObjects( - UNakamaSession* Session, - const TArray& StorageObjectsData, - FOnStorageObjectsRead Success, - FOnError Error - ); - - /** - * List storage objects in a collection which belong to a specific user and have public read access. - * - * @param Collection The collection to list over. - * @param UserId The user ID of the user to list objects for. - * @param Limit The number of objects to list. - * @param Cursor A cursor to paginate over the collection. - * @param Session The session of the user. - * @param Success Delegate called upon successfully listing the storage objects. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Storage") - void ListStorageObjects( - UNakamaSession* Session, - const FString& Collection, - const FString& UserId, - int32 Limit, - const FString& Cursor, - FOnStorageObjectsListed Success, - FOnError Error - ); - - /** - * [DEPRECATED] Delete one or more storage objects. - * - * @param StorageObjectsData The ids of the objects to delete. - * @param Session The session of the user. - * @param Success Delegate called upon successfully deleting storage objects. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Storage", meta=(DeprecatedFunction, DeprecationMessage="Use DeleteStorageObjects instead")) - void RemoveStorageObjects( - UNakamaSession* Session, - const TArray& StorageObjectsData, - FOnRemovedStorageObjects Success, - FOnError Error - ); - - /** - * Delete one or more storage objects. - * - * @param StorageObjectsData The ids of the objects to delete. - * @param Session The session of the user. - * @param Success Delegate called upon successfully deleting storage objects. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Storage") - void DeleteStorageObjects ( - UNakamaSession* Session, - const TArray& StorageObjectsData, - FOnRemovedStorageObjects Success, - FOnError Error - ); - - /** - * List parties and optionally filter by matching criteria. - * - * @param Limit Limit the number of returned parties. - * @param Open Optionally filter by open/closed parties. - * @param Query Arbitrary label query. - * @param Cursor Cursor for the next page of results, if any. - * @param Session The session of the user. - * @param Success Delegate called upon successfully deleting storage objects. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Parties") - void ListParties ( - UNakamaSession* Session, - int32 Limit, - bool Open, - const FString& Query, - const FString& Cursor, - FOnListedParties Success, - FOnError Error - ); - - // --- RPC --- // - - /** - * Send an RPC message to the server. - * - * @param FunctionId The ID of the function to execute. - * @param Payload The string content to send to the server. - * @param Session The session of the user. - * @param Success Delegate called upon successfully sending the RPC message and receiving a response. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Realtime|RPC") - bool RPC( - UNakamaSession* Session, - const FString& FunctionId, - const FString& Payload, - FOnRPC Success, - FOnError Error - ); - - // --- RPC HttpKey --- // - - /** - * Send an RPC message to the server using HTTP key. - * - * @param HttpKey The HTTP key for the server. - * @param FunctionId The ID of the function to execute. - * @param Payload The string content to send to the server. - * @param Success Delegate called upon successfully sending the RPC message using the HTTP key and receiving a response. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Realtime|RPC") - bool RPCHttpKey( - const FString& HttpKey, - const FString& FunctionId, - const FString& Payload, - FOnRPC Success, - FOnError Error - ); - - - // --- List Channel Messages --- // - - /** - * List messages from a chat channel. - * - * @param Session The session of the user. - * @param ChannelId A channel identifier. - * @param Limit The number of chat messages to list. - * @param Cursor A cursor for the current position in the messages history to list. - * @param Forward Fetch messages forward from the current cursor (or the start). - * @param Success Delegate called upon successfully retrieving the list of chat messages from the specified channel, returning the messages. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Chat") - void ListChannelMessages( - UNakamaSession* Session, - const FString& ChannelId, - int32 Limit, - const FString& Cursor, - bool Forward, - FOnListChannelMessages Success, - FOnError Error - ); - - // --- Leaderboards --- // - - /** - * Write a record to a leaderboard. - * - * @param LeaderboardId The id of the leaderboard to write. - * @param Score The score for the leaderboard record. - * @param SubScore The subscore for the leaderboard record. - * @param Metadata The metadata for the leaderboard record. - * @param Session The session of the user. - * @param Success Delegate called upon successfully writing the record to the specified leaderboard, confirming the record's acceptance. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Leaderboards") - void WriteLeaderboardRecord( - UNakamaSession* Session, - const FString& LeaderboardId, - int64 Score, - int64 SubScore, - const FString& Metadata, - FOnWriteLeaderboardRecord Success, - FOnError Error - ); - - /** - * List records from a leaderboard. - * - * @param LeaderboardId The id of the leaderboard to list. - * @param OwnerIds Record owners to fetch with the list of records. - * @param Limit The number of records to list. - * @param Cursor A cursor for the current position in the leaderboard records to list. - * @param ListBy List by either Score or Friends - * @param Session The session of the user. - * @param Success Delegate called upon successfully retrieving the list of leaderboard records, returning the specified records. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Leaderboards") - void ListLeaderboardRecords( - UNakamaSession* Session, - const FString& LeaderboardId, - const TArray& OwnerIds, - int32 Limit, - const FString& Cursor, - ENakamaLeaderboardListBy ListBy, - FOnListLeaderboardRecords Success, - FOnError Error - ); - - /** - * List leaderboard records from a given leaderboard around the owner. - * - * @param LeaderboardId The id of the leaderboard to list. - * @param OwnerId The owner to retrieve records around. - * @param Limit Max number of records to return. Between 1 and 100. - * @param Session The session of the user. - * @param Success Delegate called upon successfully retrieving the leaderboard records centered around the specified owner, returning the surrounding records. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Leaderboards") - void ListLeaderboardRecordsAroundOwner( - UNakamaSession* Session, - const FString& LeaderboardId, - const FString& OwnerId, - int32 Limit, - FOnListLeaderboardRecords Success, - FOnError Error - ); - - /** - * Delete a leaderboard record. - * - * @param LeaderboardId The id of the leaderboard with the record to be deleted. - * @param Session The session of the user. - * @param Success Delegate called upon successfully deleting the specified leaderboard record, confirming the deletion. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Leaderboards") - void DeleteLeaderboardRecord( - UNakamaSession* Session, - const FString& LeaderboardId, - FOnDeletedLeaderboardRecord Success, - FOnError Error - ); - - // --- Tournaments --- // - - /** - * A request to submit a score to a tournament. - * - * @param TournamentId The tournament ID to write the record for. - * @param Score The score value to submit. - * @param SubScore An optional secondary value. - * @param Metadata A JSON object of additional properties. - * @param Session The session of the user. - * @param Success Delegate called upon successfully submitting the score to the specified tournament, confirming the submission. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Tournaments") - void WriteTournamentRecord( - UNakamaSession* Session, - const FString& TournamentId, - int64 Score, - int64 SubScore, - const FString& Metadata, - FOnWriteLeaderboardRecord Success, - FOnError Error - ); - - /** - * List tournament records from a given tournament. - * - * @param TournamentId The ID of the tournament to list for. - * @param Limit Max number of records to return. Between 1 and 100. - * @param Cursor A next or previous page cursor. - * @param OwnerIds One or more owners to retrieve records for. - * @param ListBy List By Score or Friends - * @param Session The session of the user. - * @param Success Delegate called with a list of tournament records from the specified tournament. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Tournaments") - void ListTournamentRecords( - UNakamaSession* Session, - const FString& TournamentId, - int32 Limit, - const FString& Cursor, - const TArray& OwnerIds, - ENakamaLeaderboardListBy ListBy, - FOnListTournamentRecords Success, - FOnError Error - ); - - /** - * List tournament records from a given tournament around the owner. - * - * @param TournamentId The ID of the tournament to list for. - * @param OwnerId The owner to retrieve records around. - * @param Limit Max number of records to return. Between 1 and 100. - * @param Session The session of the user. - * @param Success Delegate called with a list of tournament records centered around the specified owner. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Tournaments") - void ListTournamentRecordsAroundOwner( - UNakamaSession* Session, - const FString& TournamentId, - const FString& OwnerId, - int32 Limit, - FOnListTournamentRecords Success, - FOnError Error - ); - - /** - * Join a tournament if it has open membership or request to join it. - * - * @param TournamentId The id of the tournament to join. - * @param Session The session of the user. - * @param Success Delegate called upon successfully joining or requesting to join the specified tournament. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Tournaments") - void JoinTournament( - UNakamaSession* Session, - const FString& TournamentId, - FOnJoinedTournament Success, - FOnError Error - ); - - /** - * List active/upcoming tournaments based on given filters. - * - * @param CategoryStart The start of the categories to include. Defaults to 0. - * @param CategoryEnd The end of the categories to include. Defaults to 128. - * @param StartTime The start time for tournaments. Defaults to current Unix time. - * @param EndTime The end time for tournaments. Defaults to +1 year from current Unix time. - * @param Limit Max number of records to return. Between 1 and 100. - * @param Cursor A next page cursor for listings. - * @param Session The session of the user. - * @param Success Delegate called with a list of active/upcoming tournaments based on the given filters. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Tournaments") - void ListTournaments( - UNakamaSession* Session, - int32 CategoryStart, - int32 CategoryEnd, - int32 StartTime, - int32 EndTime, - int32 Limit, - const FString& Cursor, - FOnListTournaments Success, - FOnError Error - ); - - - // --- TFUNCTIONS (LAMBDAS) SECTIONS --- // - - // --- Authentication --- // - - /** - * Authenticate a user with a device id. - * - * @param DeviceId A device identifier usually obtained from a platform API. - * @param Username A username used to create the user. Defaults to empty string. - * @param bCreate True if the user should be created when authenticated. Defaults to false. - * @param Vars Extra information that will be bundled in the session token. - * @param SuccessCallback Callback invoked upon successful authentication, returning the user's session. - * @param ErrorCallback Callback invoked when an error occurs during authentication, detailing the failure. - */ - void AuthenticateDevice( - const FString& DeviceId, - const TOptional bCreate, - const TOptional& Username, - const TMap& Vars, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Authenticate a user with an email and password. - * - * @param Email The email address of the user. - * @param Password The password for the user. - * @param Username A username used to create the user. - * @param bCreate True if the user should be created when authenticated. - * @param Vars Extra information that will be bundled in the session token. - * @param SuccessCallback Callback invoked upon successful authentication, returning the user's session. - * @param ErrorCallback Callback invoked when an error occurs during authentication, detailing the failure. - */ - void AuthenticateEmail( - const FString& Email, - const FString& Password, - const FString& Username, - bool bCreate, - const TMap& Vars, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Authenticate a user with a custom id. - * - * @param CustomId A custom identifier usually obtained from an external authentication service. - * @param Username A username used to create the user. - * @param bCreate True if the user should be created when authenticated. - * @param Vars Extra information that will be bundled in the session token. - * @param SuccessCallback Callback invoked upon successful authentication, returning the user's session. - * @param ErrorCallback Callback invoked when an error occurs during authentication, detailing the failure. - */ - void AuthenticateCustom( - const FString& CustomId, - const FString& Username, - bool bCreate, - const TMap& Vars, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - // --- Social Authentication --- // - - /** - * Authenticate a user with Apple Sign In. - * - * @param Token The ID token received from Apple to validate. - * @param Username A username used to create the user. - * @param bCreate True if the user should be created when authenticated. - * @param Vars Extra information that will be bundled in the session token. - * @param SuccessCallback Callback invoked upon successful authentication, returning the user's session. - * @param ErrorCallback Callback invoked when an error occurs during authentication, detailing the failure. - */ - void AuthenticateApple( - const FString& Token, - const FString& Username, - bool bCreate, - const TMap& Vars, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Authenticate a user with a Facebook auth token. - * - * @param Token An OAuth access token from the Facebook SDK. - * @param Username A username used to create the user. - * @param bCreate True if the user should be created when authenticated. - * @param bImport True if the Facebook friends should be imported. - * @param Vars Extra information that will be bundled in the session token. - * @param SuccessCallback Callback invoked when authentication succeeds, returning the user session. - * @param ErrorCallback Callback invoked if authentication fails, detailing the error. - */ - void AuthenticateFacebook(const FString& Token, - const FString& Username, - bool bCreate, - bool bImport, - const TMap& Vars, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Authenticate a user with a Google auth token. - * - * @param Token An OAuth access token from the Google SDK. - * @param Username A username used to create the user. - * @param bCreate True if the user should be created when authenticated. - * @param Vars Extra information that will be bundled in the session token. - * @param SuccessCallback Callback invoked upon successful authentication, returning the user's session. - * @param ErrorCallback Callback invoked when an error occurs during authentication, detailing the failure. - */ - void AuthenticateGoogle( - const FString& Token, - const FString& Username, - bool bCreate, - const TMap& Vars, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Authenticate a user with Apple Game Center. - * - * @param PlayerId The player id of the user in Game Center. - * @param BundleId The bundle id of the Game Center application. - * @param TimestampSeconds The date and time that the signature was created. - * @param Salt A random NSString used to compute the hash and keep it randomized. - * @param Signature The verification signature data generated. - * @param PublicKeyUrl The URL for the public encryption key. - * @param Username A username used to create the user. - * @param bCreate True if the user should be created when authenticated. - * @param Vars Extra information that will be bundled in the session token. - * @param SuccessCallback Callback invoked upon successful authentication, returning the user's session. - * @param ErrorCallback Callback invoked when an error occurs during authentication, detailing the failure. - */ - void AuthenticateGameCenter( - const FString& PlayerId, - const FString& BundleId, - int64 TimestampSeconds, - const FString& Salt, - const FString& Signature, - const FString& PublicKeyUrl, - const FString& Username, - bool bCreate, - const TMap& Vars, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Authenticate a user with a Steam auth token. - * - * @param Token An authentication token from the Steam network. - * @param Username A username used to create the user. - * @param bCreate True if the user should be created when authenticated. - * @param bImport True if the Steam friends should be imported. - * @param Vars Extra information that will be bundled in the session token. - * @param SuccessCallback Callback invoked upon successful authentication, returning the user's session. - * @param ErrorCallback Callback invoked when an error occurs during authentication, detailing the failure. - */ - void AuthenticateSteam( - const FString& Token, - const FString& Username, - bool bCreate, - bool bImport, - const TMap& Vars, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Refresh a user's session using a refresh token retrieved from a previous authentication request. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked when authentication succeeds, returning the user session. - * @param ErrorCallback Callback invoked if authentication fails, detailing the error. - * @param SuccessCallback Callback invoked upon successful authentication, returning the user's session. - * @param ErrorCallback Callback invoked when an error occurs during authentication, detailing the failure. - **/ - void AuthenticateRefresh( - UNakamaSession* Session, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - // --- Linking --- // - - /** - * - * Link a device id to the user account owned by the session. - * @param Id A device identifier usually obtained from a platform API. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked when the device ID is successfully linked. - * @param ErrorCallback Callback invoked if the linking process fails, detailing the error. - */ - void LinkDevice( - UNakamaSession* Session, - const FString& Id, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Link an email with password to the user account owned by the session. - * - * @param Email The email address of the user. - * @param Password The password for the user. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked when the email is successfully linked. - * @param ErrorCallback Callback invoked if the linking process fails, detailing the error. - */ - void LinkEmail( - UNakamaSession* Session, - const FString& Email, - const FString& Password, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * - * Link a custom id to the user account owned by the session. - * @param Id A custom identifier usually obtained from an external authentication service. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked when the custom ID is successfully linked. - * @param ErrorCallback Callback invoked if the linking process fails, detailing the error. - */ - void LinkCustom( - UNakamaSession* Session, - const FString& Id, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Link an Apple ID to the social profiles on the current user's account. - * - * @param Token The ID token received from Apple. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked when the Apple ID is successfully linked. - * @param ErrorCallback Callback invoked if the linking process fails, detailing the error. - */ - void LinkApple( - UNakamaSession* Session, - const FString& Token, TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Link a Facebook profile to a user account. - * - * @param Token An OAuth access token from the Facebook SDK. - * @param bImport True if the Facebook friends should be imported. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked when the Facebook profile is successfully linked. - * @param ErrorCallback Callback invoked if the linking process fails, detailing the error. - */ - void LinkFacebook( - UNakamaSession* Session, - const FString& Token, - TOptional bImport, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Link a Google profile to a user account. - * - * @param Token An OAuth access token from the Google SDK. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked when the Google profile is successfully linked. - * @param ErrorCallback Callback invoked if the linking process fails, detailing the error. - */ - void LinkGoogle( - UNakamaSession* Session, - const FString& Token, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Link a Game Center profile to a user account. - * - * @param PlayerId The player id of the user in Game Center. - * @param BundleId The bundle id of the Game Center application. - * @param TimestampSeconds The date and time that the signature was created. - * @param Salt A random NSString used to compute the hash and keep it randomized. - * @param Signature The verification signature data generated. - * @param PublicKeyUrl The URL for the public encryption key. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked when the Game Center profile is successfully linked. - * @param ErrorCallback Callback invoked if the linking process fails, detailing the error. - */ - void LinkGameCenter( - UNakamaSession* Session, - const FString& PlayerId, - const FString& BundleId, - int64 TimestampSeconds, - const FString& Salt, - const FString& Signature, - const FString& PublicKeyUrl, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Link a Steam profile to a user account. - * - * @param Token An authentication token from the Steam network. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked when the Steam profile is successfully linked. - * @param ErrorCallback Callback invoked if the linking process fails, detailing the error. - */ - void LinkSteam( - UNakamaSession* Session, - const FString& Token, - //bool bImport, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - // --- Unlinking --- // - - /** - * Unlink a device id from the user account owned by the session. - * - * @param Id A device identifier usually obtained from a platform API. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked when the device ID is successfully unlinked. - * @param ErrorCallback Callback invoked if the unlinking process fails, detailing the error. - */ - void UnLinkDevice( - UNakamaSession* Session, - const FString& Id, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Unlink an email with password from the user account owned by the session. - * - * @param Email The email address of the user. - * @param Password The password for the user. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked when the email is successfully unlinked. - * @param ErrorCallback Callback invoked if the unlinking process fails, detailing the error. - */ - void UnLinkEmail( - UNakamaSession* Session, - const FString& Email, - const FString& Password, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Unlink a custom id from the user account owned by the session. - * - * @param Id A custom identifier usually obtained from an external authentication service. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked when the custom ID is successfully unlinked. - * @param ErrorCallback Callback invoked if the unlinking process fails, detailing the error. - */ - void UnLinkCustom( - UNakamaSession* Session, - const FString& Id, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Unlink a Apple profile from the user account owned by the session. - * - * @param Token An Apple authentication token. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked when the Apple profile is successfully unlinked. - * @param ErrorCallback Callback invoked if the unlinking process fails, detailing the error. - */ - void UnLinkApple( - UNakamaSession* Session, - const FString& Token, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Unlink a Facebook profile from the user account owned by the session. - * - * @param Token An OAuth access token from the Facebook SDK. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked when the Facebook profile is successfully unlinked. - * @param ErrorCallback Callback invoked if the unlinking process fails, detailing the error. - */ - void UnLinkFacebook( - UNakamaSession* Session, - const FString& Token, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Unlink a Google profile from the user account owned by the session. - * - * @param Token An OAuth access token from the Google SDK. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked when the Google profile is successfully unlinked. - * @param ErrorCallback Callback invoked if the unlinking process fails, detailing the error. - */ - void UnLinkGoogle( - UNakamaSession* Session, - const FString& Token, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Unlink a Game Center profile from the user account owned by the session. - * - * @param PlayerId The player id of the user in Game Center. - * @param BundleId The bundle id of the Game Center application. - * @param TimestampSeconds The date and time that the signature was created. - * @param Salt A random NSString used to compute the hash and keep it randomized. - * @param Signature The verification signature data generated. - * @param PublicKeyUrl The URL for the public encryption key. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked when the Game Center profile is successfully unlinked. - * @param ErrorCallback Callback invoked if the unlinking process fails, detailing the error. - */ - void UnLinkGameCenter( - UNakamaSession* Session, - const FString& PlayerId, - const FString& BundleId, - int64 TimestampSeconds, - const FString& Salt, - const FString& Signature, - const FString& PublicKeyUrl, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Unlink a Steam profile from the user account owned by the session. - * - * @param Token An authentication token from the Steam network. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked when the Steam profile is successfully unlinked. - * @param ErrorCallback Callback invoked if the unlinking process fails, detailing the error. - */ - void UnLinkSteam( - UNakamaSession* Session, - const FString& Token, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - // --- Import Friends --- // - - /** - * Import Facebook friends and add them to the user's account. - * - * The server will import friends when the user authenticates with Facebook. This function can be used to be - * explicit with the import operation. - * - * @param Session The session of the user. - * @param Token An OAuth access token from the Facebook SDK. - * @param bReset True if the Facebook friend import for the user should be reset. - * @param SuccessCallback Callback invoked upon successful import of Facebook friends. - * @param ErrorCallback Callback invoked if the Facebook friends import operation fails, providing error details. - */ - void ImportFacebookFriends( - UNakamaSession* Session, - const FString& Token, - const TOptional bReset, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Import Steam friends and add them to the user's account. - * - * The server will import friends when the user authenticates with Steam. This function can be used to be - * explicit with the import operation. - * - * @param Session The session of the user. - * @param SteamToken The Steam token to use. - * @param bReset True if the Steam friend import for the user should be reset. - * @param SuccessCallback Callback invoked upon successful import of Steam friends. - * @param ErrorCallback Callback invoked if the Steam friends import operation fails, providing error details. - */ - void ImportSteamFriends( - UNakamaSession* Session, - const FString& SteamToken, - const TOptional bReset, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - // --- Accounts --- // - - /** - * - * Fetch the user account owned by the session. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully retrieving user account details. - * @param ErrorCallback Callback invoked if the fetch operation for user account details fails, providing error information. - */ - void GetAccount( - UNakamaSession *Session, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Update the current user's account on the server. - * - * @param Username The new username for the user. - * @param DisplayName A new display name for the user. - * @param AvatarUrl A new avatar url for the user. - * @param LangTag A new language tag in BCP-47 format for the user. - * @param Location A new location for the user. - * @param TimeZone New timezone information for the user. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully updating the user's account details. - * @param ErrorCallback Callback invoked if the update operation fails, detailing the encountered error. - */ - void UpdateAccount( - UNakamaSession *Session, - const TOptional& Username, - const TOptional& DisplayName, - const TOptional& AvatarUrl, - const TOptional& LangTag, - const TOptional& Location, - const TOptional& TimeZone, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - - /** - * Delete the current user from the server. - * - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully deleting the user's account. - * @param ErrorCallback Callback invoked if the delete operation fails, detailing the encountered error. - */ - void DeleteUser(UNakamaSession* Session, TFunction SuccessCallback, - TFunction ErrorCallback); - - // --- Users --- // - - /** - * Fetch one or more users by id, usernames, and Facebook ids. - * - * @param UserIds List of user IDs. - * @param Usernames List of usernames. - * @param FacebookIds List of Facebook IDs. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully retrieving the users' details. - * @param ErrorCallback Callback invoked if the user fetch operation fails, providing error information. - */ - void GetUsers( - UNakamaSession *Session, - const TArray& UserIds, - const TArray& Usernames, - const TArray& FacebookIds, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - // --- Friends --- // - - /** - * Add one or more friends by id. - * - * @param UserIds The ids of the users to add or invite as friends. - * @param Usernames The usernames of the users to add as friends. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successful friend addition. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void AddFriends( - UNakamaSession *Session, - const TArray& UserIds, - const TArray& Usernames, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Delete one more or users by id or username from friends. - * - * @param UserIds the user ids to remove as friends. - * @param Usernames The usernames to remove as friends. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successful friend removal. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - * - */ - void DeleteFriends( - UNakamaSession *Session, - const TArray& UserIds, - const TArray& Usernames, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Block one or more friends by id. - * - * @param UserIds The ids of the users to block. - * @param Usernames The usernames of the users to block. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully blocking friends. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void BlockFriends( - UNakamaSession *Session, - const TArray& UserIds, - const TArray& Usernames, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * List of friends of the current user. - * - * @param Limit The max number of records to return. Between 1 and 1000. - * @param State The friend state to list. - * @param Cursor An optional next page cursor. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successful fetch, returning the list of friends. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void ListFriends( - UNakamaSession *Session, - const TOptional& Limit, - TOptional State, - const FString& Cursor, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - // --- Groups --- // - - /** - * Create a group. - * - * @param Name The name for the group. - * @param Description A description for the group. - * @param AvatarUrl An avatar url for the group. - * @param LangTag A language tag in BCP-47 format for the group. - * @param bOpen True if the group should have open membership. - * @param MaxCount Maximum number of group members. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully creating the group. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void CreateGroup( - UNakamaSession *Session, - const FString& Name, - const FString& Description, - const FString& AvatarUrl, - const FString& LangTag, - const bool bOpen, - const TOptional& MaxCount, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Delete a group by id. - * - * @param GroupId The group id to to remove. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully deleting the group. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void DeleteGroup( - UNakamaSession *Session, - const FString& GroupId, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Add one or more users to the group. - * - * @param GroupId The id of the group to add users into. - * @param UserIds The ids of the users to add or invite to the group. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully adding users to the group. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void AddGroupUsers( - UNakamaSession *Session, - const FString& GroupId, - const TArray& UserIds, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * List all users part of the group. - * - * @param GroupId The id of the group. - * @param Limit The max number of records to return. Between 1 and 100. - * @param State The group state to list. - * @param Cursor An optional next page cursor. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully listing the group members. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void ListGroupUsers( - UNakamaSession *Session, - const FString& GroupId, - const TOptional& Limit, - TOptional State, - FString Cursor, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Kick one or more users from the group. - * - * @param GroupId The id of the group. - * @param UserIds The ids of the users to kick. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully kicking users from the group. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void KickGroupUsers( - UNakamaSession *Session, - const FString& GroupId, - const TArray& UserIds, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - - /** - * Ban one or more users from the group. - * - * @param GroupId The id of the group. - * @param UserIds The ids of the users to ban. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully banning users from the group. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void BanGroupUsers( - UNakamaSession *Session, - const FString& GroupId, - const TArray& UserIds, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Join a group if it has open membership or request to join it. - * - * @param GroupId The id of the group to join. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully joining the group. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void JoinGroup( - UNakamaSession *Session, - const FString& GroupId, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Leave a group by id. - * - * @param GroupId The id of the group to leave. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully leaving the group. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void LeaveGroup( - UNakamaSession *Session, - const FString& GroupId, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * List groups on the server. - * - * @param Name The name filter to apply to the group list. - * @param Limit The number of groups to list. - * @param Cursor A cursor for the current position in the groups to list. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully listing the groups. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void ListGroups( - UNakamaSession *Session, - const FString& Name, - int32 Limit, - const FString& Cursor, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * List of groups the current user (from Session) is a member of. - * @param Limit The max number of records to return. Between 1 and 100. - * @param State The group state to list. - * @param Cursor An optional next page cursor. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully listing the user's groups. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void ListUserGroups( - UNakamaSession *Session, - const TOptional& Limit, - const TOptional& State, - const FString& Cursor, - TFunction SuccessCallback, - TFunction ErrorCallback - ); // Uses UserId from Session - - /** - * List of groups the 'UserId' is a member of. - * @param UserId The id of the user whose groups to list. - * @param Limit The max number of records to return. Between 1 and 100. - * @param State The group state to list. - * @param Cursor An optional next page cursor. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully listing the user's groups. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void ListUserGroups( - UNakamaSession *Session, - const FString& UserId, - const TOptional& Limit, - const TOptional& State, - const FString& Cursor, - TFunction SuccessCallback, - TFunction ErrorCallback - ); // Has UserId - - /** - * Promote a set of users in a group to the next role up. - * - * @param GroupId The group ID to promote in. - * @param UserIds The ids of the users to promote. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully promoting users in the group. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void PromoteGroupUsers( - UNakamaSession *Session, - const FString& GroupId, - const TArray& UserIds, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Demote a set of users in a group to the next role down. - * - * @param GroupId The group ID to demote in. - * @param UserIds The ids of the users to demote. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully demoting users in the group. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void DemoteGroupUsers( - UNakamaSession *Session, - const FString& GroupId, - const TArray& UserIds, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Update a group. - * - * The user must have the correct access permissions for the group. - * - * @param GroupId The id of the group to update. - * @param Name A new name for the group. - * @param Description A new description for the group. - * @param AvatarUrl A new avatar url for the group. - * @param LangTag A new language tag in BCP-47 format for the group. - * @param bOpen True if the group should have open membership. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully demoting users in the group. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void UpdateGroup( - UNakamaSession *Session, - const FString& GroupId, - const TOptional& Name, - const TOptional& Description, - const TOptional& AvatarUrl, - const TOptional& LangTag, - const TOptional bOpen, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - // --- Leaderboards --- // - - /** - * List records from a leaderboard. - * - * @param LeaderboardId The id of the leaderboard to list. - * @param OwnerIds Record owners to fetch with the list of records. - * @param Limit The number of records to list. - * @param Cursor A cursor for the current position in the leaderboard records to list. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully retrieving the list of leaderboard records, returning the specified records. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void ListLeaderboardRecords( - UNakamaSession *Session, - const FString& LeaderboardId, - const TArray& OwnerIds, - const TOptional& Limit, - const TOptional& Cursor, - TFunction - SuccessCallback, TFunction ErrorCallback - ); - - /** - * List leaderboard records from a given leaderboard around the owner. - * - * @param LeaderboardId The id of the leaderboard to list. - * @param OwnerId The owner to retrieve records around. - * @param Limit Max number of records to return. Between 1 and 100. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully retrieving the leaderboard records centered around the specified owner, returning the surrounding records. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void ListLeaderboardRecordsAroundOwner( - UNakamaSession *Session, - const FString& LeaderboardId, - const FString& OwnerId, - const TOptional& Limit, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Write a record to a leaderboard. - * - * @param LeaderboardId The id of the leaderboard to write. - * @param Score The score for the leaderboard record. - * @param Subscore The subscore for the leaderboard record. - * @param Metadata The metadata for the leaderboard record. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully writing the record to the specified leaderboard, confirming the record's acceptance. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void WriteLeaderboardRecord( - UNakamaSession *Session, - const FString& LeaderboardId, - int64 Score, - const TOptional& Subscore, - const TOptional& Metadata, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Delete a leaderboard record. - * - * @param LeaderboardId The id of the leaderboard with the record to be deleted. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully deleting the specified leaderboard record, confirming the deletion. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void DeleteLeaderboardRecord( - UNakamaSession *Session, - const FString& LeaderboardId, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - // --- Matches --- // - - /** - * Fetch a list of matches active on the server. - * - * @param Session The session of the user. - * @param MinSize The minimum number of match participants. - * @param MaxSize The maximum number of match participants. - * @param Limit The number of matches to list. - * @param Label The label to filter the match list on. - * @param Query The query to the match listing. - * @param Authoritative True to include authoritative matches. - * @param SuccessCallback Callback invoked when the matches are fetched successfully, returning the match list. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void ListMatches( - UNakamaSession *Session, - const TOptional& MinSize, - const TOptional& MaxSize, - const TOptional& Limit, - const TOptional& Label, - const TOptional& Query, - const TOptional Authoritative, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - // --- Notifications --- // - - /** - * List notifications for the user with an optional cursor. - * - * @param Limit The number of notifications to list. - * @param CacheableCursor A cursor for the current position in notifications to list. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully listing the notifications. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void ListNotifications( - UNakamaSession *Session, - const TOptional& Limit, - const TOptional& CacheableCursor, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Delete one or more notifications by id. - * - * @param NotificationIds The notification ids to remove. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully deleting the notifications. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void DeleteNotifications( - UNakamaSession *Session, - const TArray& NotificationIds, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - // --- Chat --- // - - /** - * List messages from a chat channel. - * - * @param Session The session of the user. - * @param ChannelId A channel identifier. - * @param Limit The number of chat messages to list. - * @param Cursor A cursor for the current position in the messages history to list. - * @param Forward Fetch messages forward from the current cursor (or the start). - * @param SuccessCallback Callback invoked upon successfully retrieving the list of chat messages from the specified channel, returning the messages. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void ListChannelMessages( - UNakamaSession *Session, - const FString& ChannelId, - const TOptional& Limit, - const TOptional& Cursor, - const TOptional Forward, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - // --- Tournaments --- // - - /** - * List active/upcoming tournaments based on given filters. - * - * @param CategoryStart The start of the categories to include. Defaults to 0. - * @param CategoryEnd The end of the categories to include. Defaults to 128. - * @param StartTime The start time for tournaments. Defaults to current Unix time. - * @param EndTime The end time for tournaments. Defaults to +1 year from current Unix time. - * @param Limit Max number of records to return. Between 1 and 100. - * @param Cursor A next page cursor for listings. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked with a list of active/upcoming tournaments based on the given filters. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void ListTournaments( - UNakamaSession *Session, - const TOptional& CategoryStart, - const TOptional& CategoryEnd, - const TOptional& StartTime, - const TOptional& EndTime, - const TOptional& Limit, - const TOptional& Cursor, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * List tournament records from a given tournament. - * - * @param TournamentId The ID of the tournament to list for. - * @param Limit Max number of records to return. Between 1 and 100. - * @param Cursor A next or previous page cursor. - * @param OwnerIds One or more owners to retrieve records for. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked with a list of tournament records from the specified tournament. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void ListTournamentRecords( - UNakamaSession *Session, - const FString& TournamentId, - const TOptional& Limit, - const TOptional& Cursor, - const TArray& OwnerIds, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * List tournament records from a given tournament around the owner. - * - * @param TournamentId The ID of the tournament to list for. - * @param OwnerId The owner to retrieve records around. - * @param Limit Max number of records to return. Between 1 and 100. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked with a list of tournament records centered around the specified owner. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void ListTournamentRecordsAroundOwner( - UNakamaSession *Session, - const FString& TournamentId, - const FString& OwnerId, - const TOptional& Limit, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * A request to submit a score to a tournament. - * - * @param TournamentId The tournament ID to write the record for. - * @param Score The score value to submit. - * @param Subscore An optional secondary value. - * @param Metadata A JSON object of additional properties. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully submitting the score to the specified tournament, confirming the submission. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void WriteTournamentRecord( - UNakamaSession *Session, - const FString& TournamentId, - int64 Score, - const TOptional& Subscore, - const TOptional& Metadata, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Join a tournament if it has open membership or request to join it. - * - * @param TournamentId The id of the tournament to join. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully joining or requesting to join the specified tournament. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void JoinTournament( - UNakamaSession *Session, - const FString& TournamentId, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - // --- Storage --- // - - /** - * List storage objects in a collection which belong to current user. - * - * @param Collection The collection to list over. - * @param Limit The number of objects to list. - * @param Cursor A cursor to paginate over the collection. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully listing the storage objects. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void ListStorageObjects( - UNakamaSession *Session, - const FString& Collection, - const TOptional& Limit, - const TOptional& Cursor, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * List storage objects in a collection which belong to a specific user and have public read access. - * - * @param Collection The collection to list over. - * @param UserId The user ID of the user to list objects for. - * @param Limit The number of objects to list. - * @param Cursor A cursor to paginate over the collection. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully listing the storage objects. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void ListUsersStorageObjects( - UNakamaSession *Session, - const FString& Collection, - const FString& UserId, - const TOptional& Limit, - const TOptional& Cursor, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Write objects to the storage engine. - * - * @param Objects The objects to write. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully writing storage objects. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void WriteStorageObjects( - UNakamaSession *Session, - const TArray& Objects, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Read one or more objects from the storage engine. - * - * @param ObjectIds The objects to read. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully reading the storage objects. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void ReadStorageObjects( - UNakamaSession *Session, - const TArray& ObjectIds, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Delete one or more storage objects. - * - * @param ObjectIds The ids of the objects to delete. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully deleting storage objects. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void DeleteStorageObjects( - UNakamaSession *Session, - const TArray& ObjectIds, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * List parties and optionally filter by matching criteria. - * - * @param Limit Limit the number of returned parties. - * @param Open Optionally filter by open/closed parties. - * @param Query Arbitrary label query. - * @param Cursor Cursor for the next page of results, if any. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully deleting storage objects. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void ListParties ( - UNakamaSession* Session, - const TOptional& Limit, - const TOptional& Open, - const TOptional& Query, - const TOptional& Cursor, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - // --- RPC --- // - - /** - * Send an RPC message to the server. - * returns true if the call was made. - * - * @param Id The ID of the function to execute. - * @param Payload The string content to send to the server. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully sending the RPC message and receiving a response. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - bool RPC ( - UNakamaSession *Session, - const FString& Id, - const TOptional& Payload, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Send an RPC message to the server using HTTP key. - * returns true if the call was made. - * - * @param HttpKey The HTTP key for the server. - * @param Id The ID of the function to execute. - * @param Payload The string content to send to the server. - * @param SuccessCallback Callback invoked upon successfully sending the RPC message using the HTTP key and receiving a response. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - bool RPC ( // HTTPKey - const FString& HttpKey, - const FString& Id, - const FString& Payload, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Send an RPC message to the server. - * returns true if the call was made. - * - * @param Id The ID of the function to execute. - * @param Payload The string content to send to the server. - * @param Session The session of the user. - * @param SuccessCallback Callback invoked upon successfully sending the RPC message and receiving a response. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - bool RPCm ( - UNakamaSession *Session, - const FString& Id, - TOptional&& Payload, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Send an RPC message to the server using HTTP key. - * returns true if the call was made. - * - * @param HttpKey The HTTP key for the server. - * @param Id The ID of the function to execute. - * @param Payload The string content to send to the server. - * @param SuccessCallback Callback invoked upon successfully sending the RPC message using the HTTP key and receiving a response. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - bool RPCm ( // HTTPKey - const FString& HttpKey, - const FString& Id, - FString&& Payload, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - -private: - - // Utils - FString ConstructURL(const FString& Endpoint); - - // Make HTTP request - TSharedRef MakeRequest( - const FString& Endpoint, - const FString& Content, - ENakamaRequestMethod RequestMethod, - const TMultiMap& QueryParams, - const FString& SessionToken - ); - - // Working with requests - bool IsClientValid() const; - - // Requests for RPC - bool SendRPC( - UNakamaSession* Session, - const FString& Id, - const TOptional& Payload, - TMultiMap QueryParams, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - bool SendRPCm( - UNakamaSession* Session, - const FString& Id, - const TOptional& Payload, - TMultiMap QueryParams, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - // Requests - TArray ActiveRequests; - FCriticalSection ActiveRequestsMutex; - -}; - - - - - - diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaError.h b/Nakama/Source/NakamaUnreal/Public/NakamaError.h deleted file mode 100644 index c1a57f26b..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaError.h +++ /dev/null @@ -1,62 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaError.generated.h" - -// Error Data -UENUM(BlueprintType) -enum class ENakamaErrorCode : uint8 -{ - Ok = 0 UMETA(DisplayName = "Ok"), - Cancelled = 1 UMETA(DisplayName = "Cancelled"), - Unknown = 2 UMETA(DisplayName = "Unknown"), - InvalidArgument = 3 UMETA(DisplayName = "Invalid Argument"), - DeadlineExceeded = 4 UMETA(DisplayName = "Deadline Exceeded"), - NotFound = 5 UMETA(DisplayName = "Not Found"), - AlreadyExists = 6 UMETA(DisplayName = "Already Exists"), - PermissionDenied = 7 UMETA(DisplayName = "Permission Denied"), - ResourceExhausted = 8 UMETA(DisplayName = "Resource Exhausted"), - FailedPrecondition = 9 UMETA(DisplayName = "Failed Precondition"), - Aborted = 10 UMETA(DisplayName = "Aborted"), - OutOfRange = 11 UMETA(DisplayName = "Out Of Range"), - Unimplemented = 12 UMETA(DisplayName = "Unimplemented"), - Internal = 13 UMETA(DisplayName = "Internal"), - Unavailable = 14 UMETA(DisplayName = "Unavailable"), - DataLoss = 15 UMETA(DisplayName = "Data Loss"), - Unauthenticated = 16 UMETA(DisplayName = "Unauthenticated") -}; - -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaError -{ - GENERATED_BODY() - - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama Error") - FString Message; - - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama Error") - ENakamaErrorCode Code; - - FNakamaError(const FString& JsonString); - FNakamaError(): Code(ENakamaErrorCode::Unknown) { } - - ENakamaErrorCode ConvertNakamaErrorCode(int32 CodeValue); - - -}; \ No newline at end of file diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaFriend.h b/Nakama/Source/NakamaUnreal/Public/NakamaFriend.h deleted file mode 100644 index c2deab1ba..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaFriend.h +++ /dev/null @@ -1,91 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaUser.h" -#include "NakamaFriend.generated.h" - -// The friendship status. -UENUM(BlueprintType) -enum class ENakamaFriendState : uint8 -{ - FRIEND UMETA(DisplayName = "Friend"), // The user is a friend of the current user. - INVITE_SENT UMETA(DisplayName = "Invite Sent"), // The current user has sent an invite to the user. - INVITE_RECEIVED UMETA(DisplayName = "Invite Received"), // The current user has received an invite from this user. - BLOCKED UMETA(DisplayName = "Blocked"), // The current user has blocked this user. - ALL UMETA(DisplayName = "All"), // Custom for this Plugin -}; - - -// A friend of a user. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaFriend -{ - GENERATED_BODY() - - // The user object. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Friend") - FNakamaUser NakamaUser; - - // Time of the latest relationship update. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User") - FDateTime UpdateTime; - - // The friend status. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Friend") - ENakamaFriendState UserState; - - FNakamaFriend(const FString& JsonString); - FNakamaFriend(const TSharedPtr JsonObject); - FNakamaFriend(); - - static ENakamaFriendState GetFriendStateFromString(const FString& StateString); -}; - -USTRUCT(BlueprintType) // Internal Unreal Class (No Need to Convert) -struct NAKAMAUNREAL_API FNakamaFriendChat -{ - GENERATED_BODY() - - // User - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Friend") - FNakamaUser NakamaUser; - - // Chat Id. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Friend") - FString ChatId; - -}; - -// A collection of zero or more friends of the user. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaFriendList -{ - GENERATED_BODY() - - // The Friend objects. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Friend") - TArray NakamaUsers; - - // Cursor for the next page of results, if any. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Friend") - FString Cursor; - - FNakamaFriendList(const FString& JsonString); - FNakamaFriendList(); -}; \ No newline at end of file diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaGroup.h b/Nakama/Source/NakamaUnreal/Public/NakamaGroup.h deleted file mode 100644 index cf15638eb..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaGroup.h +++ /dev/null @@ -1,200 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaUser.h" -#include "NakamaGroup.generated.h" - -// The group role status. -UENUM(BlueprintType) -enum class ENakamaUserGroupState : uint8 -{ - SUPERADMIN UMETA(DisplayName = "Superadmin"), // The user is a superadmin with full control of the group. - ADMIN UMETA(DisplayName = "Admin"), // The user is an admin with additional privileges. - MEMBER UMETA(DisplayName = "Member"), // The user is a regular member. - JOIN_REQUEST UMETA(DisplayName = "Join Request"), // The user has requested to join the group - ALL UMETA(DisplayName = "All"), // All group states -}; - -static ENakamaGroupState GetGroupStateFromString(const FString& StateString); - - -// A group in the server. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaGroup -{ - GENERATED_BODY() - - // The id of a group. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Groups") - FString Id; - - // The id of the user who created the group. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Groups") - FString CreatorId; - - // The id of the user who created the group. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Groups") - FString Name; - - // A description for the group. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Groups") - FString Description; - - // The language expected to be a tag which follows the BCP-47 spec. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Groups") - FString Language; - - // Additional information stored as a JSON object. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Groups") - FString MetaData; - - // A URL for an avatar image. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Groups") - FString AvatarUrl; - - // The UNIX time when the group was created. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Groups") - FDateTime CreateTime; - - // The UNIX time when the group was last updated. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Groups") - FDateTime UpdateTime; - - // The current count of all members in the group. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Groups") - int32 EdgeCount; - - // The maximum number of members allowed. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Groups") - int32 MaxCount; - - // Anyone can join open groups, otherwise only admins can accept members. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Groups") - bool open; - - FNakamaGroup(const FString& JsonString); - FNakamaGroup(const TSharedPtr JsonObject); - FNakamaGroup(); -}; - -// Group States -UENUM(BlueprintType) -enum class ENakamaGroupState : uint8 -{ - SUPERADMIN UMETA(DisplayName = "Superadmin"), - ADMIN UMETA(DisplayName = "Admin"), - MEMBER UMETA(DisplayName = "Member"), - JOIN_REQUEST UMETA(DisplayName = "Join Request"), - ALL UMETA(DisplayName = "All"), -}; - -// Group User -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaGroupUser -{ - GENERATED_BODY() - - // User. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Groups") - FNakamaUser User; - - // Their relationship to the group. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Groups") - ENakamaGroupState State; - - FNakamaGroupUser(const FString& JsonString); - FNakamaGroupUser(const TSharedPtr JsonObject); - FNakamaGroupUser(); -}; - -// Group Users list (Members) -// A list of users belonging to a group, along with their role. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaGroupUsersList -{ - GENERATED_BODY() - - // User-role pairs for a group. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Groups") - TArray GroupUsers; - - // Cursor for the next page of results, if any. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Groups") - FString Cursor; - - FNakamaGroupUsersList(const FString& JsonString); - FNakamaGroupUsersList() { } - -}; - -// One or more groups returned from a listing operation. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaGroupList -{ - GENERATED_BODY() - - // One or more groups. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Groups") - TArray Groups; - - // A cursor used to get the next page. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Groups") - FString Cursor; - - FNakamaGroupList(const FString& JsonString); - FNakamaGroupList() { } -}; - -// Owned by a player (I am member of..) -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaUserGroup -{ - GENERATED_BODY() - - // Group. - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nakama|Groups") //VisibleAnywhere, BlueprintReadOnly - FNakamaGroup Group; - - // The user's relationship to the group. - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nakama|Groups") //BlueprintReadOnly - ENakamaGroupState State; - - FNakamaUserGroup(const FString& JsonString); - FNakamaUserGroup(const TSharedPtr JsonObject); - FNakamaUserGroup(); -}; - -// A list of groups belonging to a user, along with the user's role in each group. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaUserGroupList -{ - GENERATED_BODY() - - // Group-role pairs for a user. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Groups") - TArray UserGroups; - - // Cursor for the next page of results, if any. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Groups") - FString Cursor; - - FNakamaUserGroupList(const FString& JsonString); - FNakamaUserGroupList(); - -}; \ No newline at end of file diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaLeaderboard.h b/Nakama/Source/NakamaUnreal/Public/NakamaLeaderboard.h deleted file mode 100644 index 35d656735..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaLeaderboard.h +++ /dev/null @@ -1,132 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaLeaderboard.generated.h" - -// Leaderboardss -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaLeaderboardRecord -{ - GENERATED_BODY() - - // The ID of the leaderboard this score belongs to. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Leaderboards") - FString LeaderboardId; - - // The ID of the score owner, usually a user or group. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Leaderboards") - FString OwnerId; - - // The username of the score owner, if the owner is a user. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Leaderboards") - FString Username; - - // Metadata. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Leaderboards") - FString Metadata; - - // The UNIX time when the leaderboard record was created. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Leaderboards") - FDateTime CreateTime; - - //The UNIX time when the leaderboard record was updated. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Leaderboards") - FDateTime UpdateTime; - - //The UNIX time when the leaderboard record expires. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Leaderboards") - FDateTime ExpiryTime; - - // The score value. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Leaderboards") - int64 Score; - - // An optional subscore value. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Leaderboards") - int64 SubScore; - - // The number of submissions to this score record. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Leaderboards") - int64 NumScore; - - //The rank of this record. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Leaderboards") - int64 Rank; - - // The maximum number of score updates allowed by the owner. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Leaderboards") - int32 MaxNumScore; - - FNakamaLeaderboardRecord(const FString& JsonString); - FNakamaLeaderboardRecord(const TSharedPtr JsonObject); - FNakamaLeaderboardRecord(); -}; - - -UENUM(BlueprintType) -enum class ENakamaLeaderboardListBy : uint8 -{ - // The object is only writable by server runtime - BY_SCORE UMETA(DisplayName = "By Score"), - // Only the user who owns it may write - BY_FRIENDS UMETA(DisplayName = "By Friends"), -}; - - -UENUM(BlueprintType) -enum class ENakamaLeaderboardOperatorOverride : uint8 -{ - // Do not override the leaderboard operator - NO_OVERRIDE UMETA(DisplayName = "NO_OVERRIDE"), - // Override the leaderboard operator with BEST - BEST UMETA(DisplayName = "BEST"), - // Override the leaderboard operator with SET - SET UMETA(DisplayName = "SET"), - // Override the leaderboard operator with INCREMENT - INCREMENT UMETA(DisplayName = "INCREMENT"), - // Override the leaderboard operator with DECREMENT - DECREMENT UMETA(DisplayName = "DECREMENT"), -}; - - -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaLeaderboardRecordList -{ - GENERATED_BODY() - - //A list of leaderboard records. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Leaderboards") - TArray Records; - - //A batched set of leaderboard records belonging to specified owners. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Leaderboards") - TArray OwnerRecords; - - //The cursor to send when retrieving the next page, if any. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Leaderboards") - FString NextCursor; - - //The cursor to send when retrieving the previous page, if any. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Leaderboards") - FString PrevCursor; - - FNakamaLeaderboardRecordList(const FString& JsonString); - FNakamaLeaderboardRecordList(); - -}; diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaLogger.h b/Nakama/Source/NakamaUnreal/Public/NakamaLogger.h deleted file mode 100644 index 237418f76..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaLogger.h +++ /dev/null @@ -1,60 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "UObject/NoExportTypes.h" -#include "NakamaLogger.generated.h" - -//DECLARE_LOG_CATEGORY_EXTERN(LogNakama, Log, All); -DECLARE_LOG_CATEGORY_EXTERN(LogNakamaUnreal, Log, All); - -UENUM(BlueprintType, Category = "Nakama") -enum class ENakamaLogLevel : uint8 -{ - Debug, - Info, - Warn, - Error, - Fatal -}; - -/** - * - */ -UCLASS() -class NAKAMAUNREAL_API UNakamaLogger : public UObject -{ - GENERATED_BODY() - -public: - UNakamaLogger(); - - UFUNCTION(BlueprintCallable, Category = "Nakama") - static void SetLogLevel(ENakamaLogLevel InLogLevel); - - UFUNCTION(BlueprintCallable, Category = "Nakama") - static void Log(ENakamaLogLevel InLogLevel, const FString& Message); - - UFUNCTION(BlueprintCallable, Category = "Nakama") - static void EnableLogging(bool bEnable); - -private: - static ENakamaLogLevel CurrentLogLevel; - static bool bLoggingEnabled; - static bool IsLoggable(ENakamaLogLevel InLogLevel); -}; diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaLoggingMacros.h b/Nakama/Source/NakamaUnreal/Public/NakamaLoggingMacros.h deleted file mode 100644 index a2c689913..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaLoggingMacros.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -// Toggle logging on/off by defining NAKAMA_LOGS_ENABLED -#define NAKAMA_LOGS_ENABLED - -#ifdef NAKAMA_LOGS_ENABLED - - #define NAKAMA_LOG_DEBUG(Message) \ - UNakamaLogger::Log(ENakamaLogLevel::Debug, Message) - - #define NAKAMA_LOG_INFO(Message) \ - UNakamaLogger::Log(ENakamaLogLevel::Info, Message) - - #define NAKAMA_LOG_WARN(Message) \ - UNakamaLogger::Log(ENakamaLogLevel::Warn, Message) - - #define NAKAMA_LOG_ERROR(Message) \ - UNakamaLogger::Log(ENakamaLogLevel::Error, Message) - - #define NAKAMA_LOG_FATAL(Message) \ - UNakamaLogger::Log(ENakamaLogLevel::Fatal, Message) - -#else - - // Define empty macros if logging is disabled - #define NAKAMA_LOG_DEBUG(Message) - #define NAKAMA_LOG_INFO(Message) - #define NAKAMA_LOG_WARN(Message) - #define NAKAMA_LOG_ERROR(Message) - #define NAKAMA_LOG_FATAL(Message) - -#endif // NAKAMA_LOGS_ENABLED \ No newline at end of file diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaMatch.h b/Nakama/Source/NakamaUnreal/Public/NakamaMatch.h deleted file mode 100644 index decf51c8e..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaMatch.h +++ /dev/null @@ -1,95 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaPresence.h" -#include "NakamaMatch.generated.h" - -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaMatch -{ - GENERATED_BODY() // NMatchmakerMatched - - // A reference to the current user's presence in the match. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, DisplayName = "Self", Category = "Nakama|Realtime") - FNakamaUserPresence Me; - - // The users currently in the match. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, DisplayName = "Presences", Category = "Nakama|Realtime") - TArray Pressences; // Typo - - // The ID of the match, can be used to join. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Realtime") - FString MatchId; - - // Match label, if any. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Realtime") - FString Label; - - // True if it's an server-managed authoritative match, false otherwise. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Realtime") - bool Authoritative; - - // Current number of users in the match. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Realtime") - int32 Size; - - FNakamaMatch(const FString& JsonString); - FNakamaMatch(const TSharedPtr JsonObject); - FNakamaMatch() : Authoritative(false), Size(0) { } - -}; - -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaMatchData -{ - GENERATED_BODY() - - // A reference to the user presence that sent this data, if any. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Realtime") - FNakamaUserPresence Presence; - - // The match unique ID. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Realtime") - FString MatchId; - - // Data payload, if any. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Realtime") - FString Data; - - // Op code value. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Realtime") - int64 OpCode; - - FNakamaMatchData(const FString& JsonString); - FNakamaMatchData(); -}; - -// A list of realtime matches. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaMatchList -{ - GENERATED_BODY() - - // A number of matches corresponding to a list operation. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Realtime") - TArray Matches; - - FNakamaMatchList(const FString& JsonString); - FNakamaMatchList(); -}; diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaMatchTypes.h b/Nakama/Source/NakamaUnreal/Public/NakamaMatchTypes.h deleted file mode 100644 index dfd666c14..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaMatchTypes.h +++ /dev/null @@ -1,107 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaPresence.h" -#include "NakamaMatchTypes.generated.h" - -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaMatchmakerUser -{ - GENERATED_BODY() - - // User info. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Matchmaker") - FNakamaUserPresence Presence; - - // String properties. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Matchmaker") - TMap StringProperties; - - // Numeric Properties. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Matchmaker") - TMap NumericProperties; - - FNakamaMatchmakerUser(const FString& JsonString); - FNakamaMatchmakerUser(const TSharedPtr JsonObject); - FNakamaMatchmakerUser(); -}; - -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaMatchmakerMatched -{ - GENERATED_BODY() - - // A reference to the current user and their properties. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Matchmaker") - FNakamaMatchmakerUser Me; - - // The users that have been matched together, and information about their matchmaking data. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Matchmaker") - TArray Users; - - // The matchmaking ticket that has completed. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Matchmaker") - FString Ticket; - - // The match token or match ID to join. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Matchmaker") - FString MatchId; - - // Match join token. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Matchmaker") - FString Token; - - FNakamaMatchmakerMatched(const FString& JsonString); - FNakamaMatchmakerMatched(); -}; - -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaMatchPresenceEvent -{ - GENERATED_BODY() //NMatchmakerMatched - - //User presences that have just joined the match. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Realtime") - TArray Joins; - - //User presences that have just left the match. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Realtime") - TArray Leaves; - - //The match unique ID. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Realtime") - FString MatchId; - - FNakamaMatchPresenceEvent(const FString& JsonString); - FNakamaMatchPresenceEvent(); -}; - -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaMatchmakerTicket -{ - GENERATED_BODY() - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nakama|Matchmaker") - FString TicketId; - - // Might want more properties here later. - - FNakamaMatchmakerTicket(const FString& JsonString); - FNakamaMatchmakerTicket(); -}; diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaNotification.h b/Nakama/Source/NakamaUnreal/Public/NakamaNotification.h deleted file mode 100644 index f70df81a3..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaNotification.h +++ /dev/null @@ -1,78 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaNotification.generated.h" - -// A notification in the server. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaNotification -{ - GENERATED_BODY() - - // ID of the Notification. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Notification") - FString Id; - - // Subject of the notification. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Notification") - FString Subject; - - // Content of the notification in JSON. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Notification") - FString Content; - - // ID of the sender, if a user. Otherwise 'null'. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Notification") - FString SenderId; - - // The UNIX time when the notification was created. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Notification") - FDateTime CreateTime; - - // Category code for this notification. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Notification") - int32 Code; - - // True if this notification was persisted to the database. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Notification") - bool Persistent; - - FNakamaNotification(const FString& JsonString); - FNakamaNotification(const TSharedPtr JsonObject); - FNakamaNotification(); -}; - -// A collection of zero or more notifications. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaNotificationList -{ - GENERATED_BODY() - - // Collection of notifications - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Notification") - TArray Notifications; - - // Use this cursor to paginate notifications. Cache this to catch up to new notifications. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Notification") - FString CacheableCursor; - - FNakamaNotificationList(const FString& JsonString); - FNakamaNotificationList(); - -}; diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaParty.h b/Nakama/Source/NakamaUnreal/Public/NakamaParty.h deleted file mode 100644 index 4792975c8..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaParty.h +++ /dev/null @@ -1,201 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaPresence.h" -#include "NakamaParty.generated.h" - - -// Parties -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaParty -{ - GENERATED_BODY() - - // The current user in this party. i.e. Yourself. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, DisplayName = "Self", Category = "Nakama|Parties") - FNakamaUserPresence Me; - - // The current party leader. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Parties") - FNakamaUserPresence Leader; - - // All members currently in the party. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Parties") - TArray Presences; - - // The unique party identifier. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Parties") - FString PartyId; - - // The party label, if any. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Parties") - FString Label; - - // The maximum number of party members. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Parties") - int32 MaxSize; - - // True if the party is open to join. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Parties") - bool Open; - - // True if the party is hidden from public listings and searches. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Parties") - bool Hidden; - - FNakamaParty(const FString& JsonString); - FNakamaParty(const TSharedPtr JsonObject); - FNakamaParty(); // Default Constructor -}; - -// Incoming notification for one or more new presences attempting to join the party. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaPartyJoinRequest -{ - GENERATED_BODY() - - // Presences attempting to join, or who have joined. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Parties") - TArray Presences; - - // The ID of the party to get a list of join requests for. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Parties") - FString PartyId; - - FNakamaPartyJoinRequest(const FString& JsonString); - FNakamaPartyJoinRequest(); // Default Constructor -}; - -// Incoming notification for one or more new presences attempting to join the party. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaPartyMatchmakerTicket -{ - GENERATED_BODY() - - // The ID of the party. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Parties") - FString PartyId; - - // The ticket that can be used to cancel matchmaking. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Parties") - FString Ticket; - - FNakamaPartyMatchmakerTicket(const FString& JsonString); - FNakamaPartyMatchmakerTicket(); -}; - -// Information about a party close event. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaPartyClose -{ - GENERATED_BODY() - - // The unique party identifier of the closing party. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Parties") - FString Id; - - FNakamaPartyClose(const FString& JsonString); - FNakamaPartyClose(); -}; - -// Incoming party data delivered from the server. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaPartyData -{ - GENERATED_BODY() - - // A reference to the user presence that sent this data, if any. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Parties") - FNakamaUserPresence Presence; - - // The unique party identifier of the closing party. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Parties") - FString PartyId; - - // Incoming Party data, if any, as base64-encoded bytes. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Parties") - FString Data; // NBytes - - // Op code value. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Parties") - int64 OpCode; - - FNakamaPartyData(const FString& JsonString); - FNakamaPartyData(); -}; - - -// Announcement of a new party leader. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaPartyLeader -{ - GENERATED_BODY() - - // The presence of the new party leader. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Parties") - FNakamaUserPresence Presence; - - // The ID of the party to announce the new leader for. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Parties") - FString PartyId; - - FNakamaPartyLeader(const FString& JsonString); - FNakamaPartyLeader(); -}; - - -// Presence update for a particular party. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaPartyPresenceEvent -{ - GENERATED_BODY() - - // The user presences that have just joined the party. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Parties") - TArray Joins; - - // The user presences that have just left the party. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Parties") - TArray Leaves; - - // The ID of the party. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Parties") - FString PartyId; - - FNakamaPartyPresenceEvent(const FString& JsonString); - FNakamaPartyPresenceEvent(); -}; - -// List of realtime parties. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaPartyList -{ - GENERATED_BODY() - - // A number of parties corresponding to a list operation. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Parties") - TArray Parties; - - // A cursor to send when retrieving the next page, if any. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Parties") - FString Cursor; - - FNakamaPartyList(const FString& JsonString); - FNakamaPartyList(); -}; diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaPresence.h b/Nakama/Source/NakamaUnreal/Public/NakamaPresence.h deleted file mode 100644 index 273bb45da..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaPresence.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaPresence.generated.h" - - -UENUM(BlueprintType) -enum class ENakamaPresenceEvent : uint8 -{ - None, - LEAVES UMETA(DisplayName = "Leaves"), - JOINS UMETA(DisplayName = "Joins"), -}; - - -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaUserPresence -{ - GENERATED_BODY() - - // The user this presence belongs to. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User|Presence", meta = (JsonProperty = "user_id")) - FString UserID; - - // A unique session ID identifying the particular connection, because the user may have many. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User|Presence", meta = (JsonProperty = "session_id")) - FString SessionID; - - // The username for display purposes. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User|Presence", meta = (JsonProperty = "username")) - FString Username; - - // A user-set status message for this stream, if applicable. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User|Presence") - FString Status; - - // Whether this presence generates persistent data/messages, if applicable for the stream type. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User|Presence", meta = (JsonProperty = "persistence")) - bool Persistence; - - FNakamaUserPresence(const FString& JsonString); - FNakamaUserPresence(const TSharedPtr JsonObject); - FNakamaUserPresence(); - -}; \ No newline at end of file diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaRPC.h b/Nakama/Source/NakamaUnreal/Public/NakamaRPC.h deleted file mode 100644 index 360f7cd8c..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaRPC.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaRPC.generated.h" - -// RPC -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaRPC -{ - GENERATED_BODY() - - // The identifier of the function. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|RPC") - FString Id; - - // The payload of the function which must be a JSON object. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|RPC") - FString Payload; - - // The authentication key used when executed as a non-client HTTP request. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|RPC") - FString HttpKey; - - FNakamaRPC(const FString& JsonString); - FNakamaRPC(FString&& JsonString); - FNakamaRPC(); -}; diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaRealtimeClient.h b/Nakama/Source/NakamaUnreal/Public/NakamaRealtimeClient.h deleted file mode 100644 index f357d00da..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaRealtimeClient.h +++ /dev/null @@ -1,1435 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaRtError.h" -#include "NakamaChannelTypes.h" -#include "NakamaMatch.h" -#include "NakamaMatchTypes.h" -#include "NakamaNotification.h" -#include "NakamaParty.h" -#include "NakamaPresence.h" -#include "NakamaStatus.h" -#include "NakamaStreams.h" -#include "NakamaChat.h" -#include "Tickable.h" -#include "IWebSocket.h" -#include "NakamaRealtimeRequestContext.h" -#include "NakamaRPC.h" -#include "Engine/TimerHandle.h" - -#include "NakamaRealtimeClient.generated.h" - -// --- Bindable Delegates --- // - -// OnConnect -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnConnect); -DECLARE_MULTICAST_DELEGATE(FOnConnectNative); - -// OnConnectionError -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReceivedConnectionError, const FNakamaRtError&, Error); -DECLARE_MULTICAST_DELEGATE_OneParam(FOnReceivedConnectionErrorNative, const FNakamaRtError&); - -// OnDisconnect -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnDisconnected, const FNakamaDisconnectInfo&, DisconnectInfo); -DECLARE_MULTICAST_DELEGATE_OneParam(FOnDisconnectedNative, const FNakamaDisconnectInfo&); - -// OnError -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReceivedError, const FNakamaRtError&, Error); -DECLARE_MULTICAST_DELEGATE_OneParam(FOnReceivedErrorNative, const FNakamaRtError&); - -// OnChannelMessage -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReceivedChannelMessage, const FNakamaChannelMessage&, ChannelMessage); -DECLARE_MULTICAST_DELEGATE_OneParam(FOnReceivedChannelMessageNative, const FNakamaChannelMessage&); - -// OnChannelPresenceEvent -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReceivedChannelPresenceEvent, const FNakamaChannelPresenceEvent&, ChannelPresenceEvent); -DECLARE_MULTICAST_DELEGATE_OneParam(FOnReceivedChannelPresenceEventNative, const FNakamaChannelPresenceEvent&); - -// OnMatchmakerMatched -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReceivedMatchmakerMatched, const FNakamaMatchmakerMatched&, Match); -DECLARE_MULTICAST_DELEGATE_OneParam(FOnReceivedMatchmakerMatchedNative, const FNakamaMatchmakerMatched&); - -// OnMatchPresenceEvent -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReceivedMatchPresenceCallback, const FNakamaMatchPresenceEvent&, PresenceEvent); -DECLARE_MULTICAST_DELEGATE_OneParam(FOnReceivedMatchPresenceCallbackNative, const FNakamaMatchPresenceEvent&); - -// OnMatchData -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReceivedMatchData, const FNakamaMatchData&, MatchData); -DECLARE_MULTICAST_DELEGATE_OneParam(FOnReceivedMatchDataNative, const FNakamaMatchData&); - -// OnNotifications -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReceivedNotification, const FNakamaNotificationList&, NotificationData); -DECLARE_MULTICAST_DELEGATE_OneParam(FOnReceivedNotificationNative, const FNakamaNotificationList&); - -// OnStatusPresenceEvent -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReceivedStatusPresence, const FNakamaStatusPresenceEvent&, UserPresenceData); -DECLARE_MULTICAST_DELEGATE_OneParam(FOnReceivedStatusPresenceNative, const FNakamaStatusPresenceEvent&); - -// OnStreamPresenceEvent -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReceivedStreamPresenceEvent, const FNakamaStreamPresenceEvent&, StreamPresence); -DECLARE_MULTICAST_DELEGATE_OneParam(FOnReceivedStreamPresenceEventNative, const FNakamaStreamPresenceEvent&); - -// OnStreamData -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReceivedStreamPresenceData, const FNakamaStreamData&, StreamPresenceData); -DECLARE_MULTICAST_DELEGATE_OneParam(FOnReceivedStreamPresenceDataNative, const FNakamaStreamData&); - -// --- Bindable Delegates: Parties --- // - -// OnParty -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReceivedParty, const FNakamaParty&, Party); -DECLARE_MULTICAST_DELEGATE_OneParam(FOnReceivedPartyNative, const FNakamaParty&); - -// OnPartyClose -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReceivedPartyClose, const FNakamaPartyClose&, PartyClose); -DECLARE_MULTICAST_DELEGATE_OneParam(FOnReceivedPartyCloseNative, const FNakamaPartyClose&); - -// OnPartyData -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReceivedPartyData, const FNakamaPartyData&, PartyData); -DECLARE_MULTICAST_DELEGATE_OneParam(FOnReceivedPartyDataNative, const FNakamaPartyData&); - -// OnPartyJoinRequest -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReceivedPartyJoinRequest, const FNakamaPartyJoinRequest&, PartyJoinRequest); -DECLARE_MULTICAST_DELEGATE_OneParam(FOnReceivedPartyJoinRequestNative, const FNakamaPartyJoinRequest&); - -// OnPartyLeader -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReceivedPartyLeader, const FNakamaPartyLeader&, PartyLeader); -DECLARE_MULTICAST_DELEGATE_OneParam(FOnReceivedPartyLeaderNative, const FNakamaPartyLeader&); - -// OnPartyMatchmakerTicket -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReceivedPartyMatchmakerTicket, const FNakamaPartyMatchmakerTicket&, Ticket); -DECLARE_MULTICAST_DELEGATE_OneParam(FOnReceivedPartyMatchmakerTicketNative, const FNakamaPartyMatchmakerTicket&); - -// OnPartyPresence -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnReceivedPartyPresence, const FNakamaPartyPresenceEvent&, Presences); -DECLARE_MULTICAST_DELEGATE_OneParam(FOnReceivedPartyPresenceNative, const FNakamaPartyPresenceEvent&); - - -// Functionality Delegates -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRtError, const FNakamaRtError&, ErrorData); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnJoinChat, FNakamaChannel, Channel); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnWriteChatMessage, FNakamaChannelMessageAck, ChannelMessage); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnLeaveChat); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnMatchmakerTicket, FString, ticket); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRemovedMatchmakerTicket, FString, ticket); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnSetStatus); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnUnFollowUsers); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnFollowUsers, const FNakamaStatus&, Status); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnCreateMatch, FNakamaMatch, Match); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnLeaveMatch); - -// Functionality Delegates - Parties -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnCreateParty, FNakamaParty, Party); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnJoinParty, FString, PartyId); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnLeaveParty); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnListPartyJoinRequests, FNakamaPartyJoinRequest, JoinRequest); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnPromotePartyMember); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRemoveMatchmakerParty, FString, ticket); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnRemovePartyMember); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnAcceptPartyMember); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAddMatchmakerParty, FNakamaPartyMatchmakerTicket, Ticket); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnCloseParty); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnRealtimeClientConnected); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRealtimeClientError, const FNakamaRtError&, ErrorData); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRealtimeClientConnectionError, const FNakamaRtError&, ErrorData); - - -// RPC -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRtRPC, const FNakamaRPC&, rpc); - -/** - * - */ - -UCLASS(Blueprintable, BlueprintType, meta=(BlueprintSpawnableComponent)) -class NAKAMAUNREAL_API UNakamaRealtimeClient : public UObject, public FTickableGameObject -{ - GENERATED_BODY() - -public: - - UPROPERTY() - bool bIsActive; - - // Internal, do not use, use SetupRealtimeClient from NakamaClient instead - void Initialize(const FString& InHost, int32 InPort, bool InSSL); - - // Call this before calling Connect to use your own websocket implementation. - // Pass nullpointer to go back to default UNakamaRealtimeClient behaviour. - // This will disconnect existing websocket connection if UNakamaRealtimeClient was already connected! - // IWebSocket->Connect() should not have been called on this websocket when passing it here. It will be managed by UNakamaRealtimeClient.Connect(...); - void UseCustomWebsocket(TSharedPtr CustomWebSocket); - - /** - * Connect to the Server. - * - * @param Session The Session to use. - * @param bCreateStatus Show as online. - * @param Success Delegate called upon a successful connection to the server. - * @param ConnectionError Delegate called when a connection error occurs. Provides detailed error information. - */ - UFUNCTION(Category = "Nakama|Realtime") - void Connect( - UNakamaSession* Session, - bool bCreateStatus, - const FOnRealtimeClientConnected& Success, - const FOnRealtimeClientConnectionError& ConnectionError - ); - - /** - * Connect to the Server using lambdas - * - * @param Session The Session to use. - * @param bCreateStatus Show as online. - * @param Success Callback invoked when successfully connected to the server. - * @param ConnectionError Callback invoked when a connection error occurs. Provides detailed error information. - * - */ - void Connect( - UNakamaSession* Session, - bool bCreateStatus, - TFunction Success = nullptr, - TFunction ConnectionError = nullptr - ); - - // Events (bindable from blueprints or c++) - - // OnConnect - UPROPERTY(BlueprintAssignable, Category = "Nakama|Events", meta = (DisplayName = "OnConnect")) - FOnConnect ConnectedEvent; - FOnConnectNative ConnectedEventNative; - - // OnConnectionError - UPROPERTY(BlueprintAssignable, Category = "Nakama|Events", meta = (DisplayName = "OnConnectionError")) - FOnReceivedConnectionError ConnectionErrorEvent; - FOnReceivedConnectionErrorNative ConnectionErrorEventNative; - - // OnDisconnect - UPROPERTY(BlueprintAssignable, Category = "Nakama|Events", meta = (DisplayName = "OnDisconnect")) - FOnDisconnected DisconnectedEvent; - FOnDisconnectedNative DisconnectedEventNative; - - // OnError - UPROPERTY(BlueprintAssignable, Category = "Nakama|Events", meta = (DisplayName = "OnError")) - FOnReceivedError ErrorEvent; - FOnReceivedErrorNative ErrorEventNative; - - // OnChannelMessage - UPROPERTY(BlueprintAssignable, Category = "Nakama|Events", meta = (DisplayName = "OnChannelMessage")) - FOnReceivedChannelMessage ChannelMessageReceived; - FOnReceivedChannelMessageNative ChannelMessageReceivedNative; - - // OnChannelPresenceEvent - UPROPERTY(BlueprintAssignable, Category = "Nakama|Events", meta = (DisplayName = "OnChannelPresenceEvent")) - FOnReceivedChannelPresenceEvent ChannelPresenceEventReceived; - FOnReceivedChannelPresenceEventNative ChannelPresenceEventReceivedNative; - - // OnMatchmakerMatched - UPROPERTY(BlueprintAssignable, Category = "Nakama|Events", meta = (DisplayName = "OnMatchmakerMatched")) - FOnReceivedMatchmakerMatched MatchmakerMatchMatched; - FOnReceivedMatchmakerMatchedNative MatchmakerMatchMatchedNative; - - // OnMatchPresenceEvent - UPROPERTY(BlueprintAssignable, Category = "Nakama|Events", meta = (DisplayName = "OnMatchPresenceEvent")) - FOnReceivedMatchPresenceCallback MatchmakerPresenceCallback; - FOnReceivedMatchPresenceCallbackNative MatchmakerPresenceCallbackNative; - - // OnMatchData - UPROPERTY(BlueprintAssignable, Category = "Nakama|Events", meta = (DisplayName = "OnMatchData")) - FOnReceivedMatchData MatchDataCallback; - FOnReceivedMatchDataNative MatchDataCallbackNative; - - // OnNotifications - UPROPERTY(BlueprintAssignable, Category = "Nakama|Events", meta = (DisplayName = "OnNotifications")) - FOnReceivedNotification NotificationReceived; - FOnReceivedNotificationNative NotificationReceivedNative; - - // OnStatusPresenceEvent - UPROPERTY(BlueprintAssignable, Category = "Nakama|Events", meta = (DisplayName = "OnStatusPresenceEvent")) - FOnReceivedStatusPresence PresenceStatusReceived; - FOnReceivedStatusPresenceNative PresenceStatusReceivedNative; - - // OnStreamPresenceEvent - UPROPERTY(BlueprintAssignable, Category = "Nakama|Events", meta = (DisplayName = "OnStreamPresenceEvent")) - FOnReceivedStreamPresenceEvent StreamPresenceEventReceived; - FOnReceivedStreamPresenceEventNative StreamPresenceEventReceivedNative; - - // OnStreamData - UPROPERTY(BlueprintAssignable, Category = "Nakama|Events", meta = (DisplayName = "OnStreamData")) - FOnReceivedStreamPresenceData StreamPresenceDataReceived; - FOnReceivedStreamPresenceDataNative StreamPresenceDataReceivedNative; - - // OnParty - UPROPERTY(BlueprintAssignable, Category = "Nakama|Events", meta = (DisplayName = "OnParty")) - FOnReceivedParty PartyReceived; - FOnReceivedPartyNative PartyReceivedNative; - - // OnPartyClose - UPROPERTY(BlueprintAssignable, Category = "Nakama|Events", meta = (DisplayName = "OnPartyClose")) - FOnReceivedPartyClose PartyCloseReceived; - FOnReceivedPartyCloseNative PartyCloseReceivedNative; - - // OnPartyData - UPROPERTY(BlueprintAssignable, Category = "Nakama|Events", meta = (DisplayName = "OnPartyData")) - FOnReceivedPartyData PartyDataReceived; - FOnReceivedPartyDataNative PartyDataReceivedNative; - - // OnPartyJoinRequest - UPROPERTY(BlueprintAssignable, Category = "Nakama|Events", meta = (DisplayName = "OnPartyJoinRequest")) - FOnReceivedPartyJoinRequest PartyJoinRequestReceived; - FOnReceivedPartyJoinRequestNative PartyJoinRequestReceivedNative; - - // OnPartyLeader - UPROPERTY(BlueprintAssignable, Category = "Nakama|Events", meta = (DisplayName = "OnPartyLeader")) - FOnReceivedPartyLeader PartyLeaderReceived; - FOnReceivedPartyLeaderNative PartyLeaderReceivedNative; - - // OnPartyMatchmakerTicket - UPROPERTY(BlueprintAssignable, Category = "Nakama|Events", meta = (DisplayName = "OnPartyMatchmakerTicket")) - FOnReceivedPartyMatchmakerTicket PartyMatchmakerTicketReceived; - FOnReceivedPartyMatchmakerTicketNative PartyMatchmakerTicketReceivedNative; - - // OnPartyPresence - UPROPERTY(BlueprintAssignable, Category = "Nakama|Events", meta = (DisplayName = "OnPartyPresence")) - FOnReceivedPartyPresence PartyPresenceReceived; - FOnReceivedPartyPresenceNative PartyPresenceReceivedNative; - - // Functionaliy Events - FOnWriteChatMessage ChannelMessageWrite; - - - // [DEPRECATED] Listener Events - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Listeners", - meta=(DeprecatedFunction, DeprecationMessage="You no longer need to call this, bind to the events directly instead")) - void SetListenerAllCallbacks(); - - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Listeners", - meta=(DeprecatedFunction, DeprecationMessage="You no longer need to call this, bind to the events directly instead")) - void SetListenerConnectCallback(); - - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Listeners", - meta=(DeprecatedFunction, DeprecationMessage="You no longer need to call this, bind to the events directly instead")) - void SetListenerConnectionErrorCallback(); - - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Listeners", - meta=(DeprecatedFunction, DeprecationMessage="You no longer need to call this, bind to the events directly instead")) - void SetListenerDisconnectCallback(); - - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Listeners", - meta=(DeprecatedFunction, DeprecationMessage="You no longer need to call this, bind to the events directly instead")) - void SetListenerErrorCallback(); - - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Listeners", - meta=(DeprecatedFunction, DeprecationMessage="You no longer need to call this, bind to the events directly instead")) - void SetListenerChannelMessageCallback(); - - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Listeners", - meta=(DeprecatedFunction, DeprecationMessage="You no longer need to call this, bind to the events directly instead")) - void SetListenerChannelPresenceCallback(); - - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Listeners", - meta=(DeprecatedFunction, DeprecationMessage="You no longer need to call this, bind to the events directly instead")) - void SetListenerMatchmakerMatchedCallback(); - - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Listeners", - meta=(DeprecatedFunction, DeprecationMessage="You no longer need to call this, bind to the events directly instead")) - void SetListenerMatchDataCallback(); - - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Listeners", - meta=(DeprecatedFunction, DeprecationMessage="You no longer need to call this, bind to the events directly instead")) - void SetListenerMatchPresenceCallback(); - - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Listeners", - meta=(DeprecatedFunction, DeprecationMessage="You no longer need to call this, bind to the events directly instead")) - void SetListenerNotificationsCallback(); - - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Listeners", - meta=(DeprecatedFunction, DeprecationMessage="You no longer need to call this, bind to the events directly instead")) - void SetListenerPartyCallback(); - - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Listeners", - meta=(DeprecatedFunction, DeprecationMessage="You no longer need to call this, bind to the events directly instead")) - void SetListenerPartyCloseCallback(); - - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Listeners", - meta=(DeprecatedFunction, DeprecationMessage="You no longer need to call this, bind to the events directly instead")) - void SetListenerPartyDataCallback(); - - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Listeners", - meta=(DeprecatedFunction, DeprecationMessage="You no longer need to call this, bind to the events directly instead")) - void SetListenerPartyJoinRequestCallback(); - - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Listeners", - meta=(DeprecatedFunction, DeprecationMessage="You no longer need to call this, bind to the events directly instead")) - void SetListenerPartyLeaderCallback(); - - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Listeners", - meta=(DeprecatedFunction, DeprecationMessage="You no longer need to call this, bind to the events directly instead")) - void SetListenerPartyMatchmakerTicketCallback(); - - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Listeners", - meta=(DeprecatedFunction, DeprecationMessage="You no longer need to call this, bind to the events directly instead")) - void SetListenerPartyPresenceCallback(); - - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Listeners", - meta=(DeprecatedFunction, DeprecationMessage="You no longer need to call this, bind to the events directly instead")) - void SetListenerStatusPresenceCallback(); - - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Listeners", - meta=(DeprecatedFunction, DeprecationMessage="You no longer need to call this, bind to the events directly instead")) - void SetListenerStreamPresenceCallback(); - - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Listeners", - meta=(DeprecatedFunction, DeprecationMessage="You no longer need to call this, bind to the events directly instead")) - void SetListenerStreamDataCallback(); - - /** - * Destroys the Realtime Client. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Client") - void Destroy(); - - // Event that is called on cleanup - virtual void BeginDestroy() override; - - /** - * Disconnects the client. This function kills all outgoing exchanges immediately without waiting. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Authentication|Disconnect") - void Disconnect(); - - - // --- FUNCTIONALITY --- // - - // --- Messaging --- // - - /** - * [DEPRECATED] Send a chat message to a channel on the server. - * - * @param ChannelId The channel to send on. - * @param Content The content of the chat message. Must be a JSON object. - * @param Success Delegate called on successful chat message delivery. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Chat|Messaging", meta=(DeprecatedFunction, DeprecationMessage="Use WriteChatMessage instead")) - void SendMessage( - const FString& ChannelId, - const FString& Content, - FOnWriteChatMessage Success, - FOnRtError Error - ); - - /** - * Send a chat message to a channel on the server. - * - * @param ChannelId The channel to send on. - * @param Content The content of the chat message. Must be a JSON object. - * @param Success Delegate called on successful chat message delivery. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Chat|Messaging") - void WriteChatMessage( - const FString& ChannelId, - const FString& Content, - FOnWriteChatMessage Success, - FOnRtError Error - ); - - /** - * Send a direct chat message to another user. - * - * @param UserID The user to send to. - * @param Content The content of the chat message. Must be a JSON object. - * @param Success Delegate called on successful chat message delivery. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Chat|Messaging") - void SendDirectMessage( - const FString& UserID, - const FString& Content, - FOnWriteChatMessage Success, - FOnRtError Error - ); - - /** - * Update a chat message to a channel on the server. - * - * @param ChannelId The ID of the chat channel with the message. - * @param MessageId The ID of the message to update. - * @param Content The content update for the message. Must be a JSON object. - * @param Success Delegate called when the chat message is successfully updated. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Chat|Messaging") - void UpdateChatMessage( - const FString& ChannelId, - const FString& MessageId, - const FString& Content, - FOnWriteChatMessage Success, - FOnRtError Error - ); - - /** - * Remove a chat message from a channel on the server. - * - * @param ChannelId The ID of the chat channel with the message. - * @param MessageId The ID of the message to remove. - * @param Success Delegate called when the chat message is successfully removed. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Chat|Messaging") - void RemoveChatMessage( - const FString& ChannelId, - const FString& MessageId, - FOnWriteChatMessage Success, - FOnRtError Error - ); - - // --- Chat --- // - - /** - * Join a chat channel on the server. - * - * @param ChatId The target channel to join. - * @param ChannelType The type of channel to join. - * @param Persistence True if chat messages should be stored. - * @param Hidden True if the user should be hidden on the channel. - * @param Success Delegate called when successfully joined the chat channel. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Chat") - void JoinChat( - const FString& ChatId, - ENakamaChannelType ChannelType, - bool Persistence, - bool Hidden, - FOnJoinChat Success, - FOnRtError Error - ); - - /** - * Leave a chat channel on the server. - * - * @param ChannelId The channel to leave. - * @param Success Delegate called after successfully leaving the chat channel. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Chat") - void LeaveChat( - const FString& ChannelId, - FOnLeaveChat Success, - FOnRtError Error - ); - - // NOTE: List Chat Messages are done in normal client - - // --- Matchmaker --- // - - /** - * Join the matchmaker pool and search for opponents on the server. - * - * @param MinCount The minimum number of players to compete against. - * @param MaxCount The maximum number of players to compete against. - * @param Query A matchmaker query to search for opponents. - * @param StringProperties A set of k/v properties to provide in searches. - * @param NumericProperties A set of k/v numeric properties to provide in searches. - * @param CountMultiple An optional multiple of the matched count that must be satisfied. - * @param IgnoreCountMultiple An optional boolean flag indicating whether the matchmaker should ignore the count multiple during the search. - * @param Success Delegate called when a matchmaker ticket is successfully acquired. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Matchmaker") - void AddMatchmaker( - int32 MinCount, - int32 MaxCount, - const FString& Query, - const TMap& StringProperties, - const TMap& NumericProperties, - int32 CountMultiple, - bool IgnoreCountMultiple, - FOnMatchmakerTicket Success, - FOnRtError Error - ); - - /** - * [DEPRECATED] Leave the matchmaker pool by ticket. - * - * @param Ticket The ticket returned by the matchmaker on join. See NMatchmakerTicket.ticket. - * @param Success Delegate called when successfully removed from the matchmaker using the provided ticket. - * @param Success Delegate called when successfully removed from the matchmaker using the provided ticket. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Matchmaker", meta=(DeprecatedFunction, DeprecationMessage="Use RemoveMatchmaker instead")) - void LeaveMatchmaker( - const FString& Ticket, - FOnRemovedMatchmakerTicket Success, - FOnRtError Error - ); - - /** - * Leave the matchmaker pool by ticket. - * - * @param Ticket The ticket returned by the matchmaker on join. See NMatchmakerTicket.ticket. - * @param Success Delegate called when successfully removed from the matchmaker using the provided ticket. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Matchmaker") - void RemoveMatchmaker( - const FString& Ticket, - FOnRemovedMatchmakerTicket Success, - FOnRtError Error - ); - - // --- Statuses --- // - - /** - * Update the user's status online. - * - * @param StatusMessage The new status of the user. - * @param Success Delegate called when the user's online status is successfully updated. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Status") - void UpdateStatus( - const FString& StatusMessage, - FOnSetStatus Success, - FOnRtError Error - ); - - /** - * Update the user's status to offline, appearing invisible to others. - * - * @param Success Delegate called when the user's status is successfully set to offline. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Status") - void SetAppearOffline( - FOnSetStatus Success, - FOnRtError Error - ); - - /** - * Follow one or more users for status updates. - * - * @param UserIds The user Ids to follow. - * @param Error Delegate called if an error occurs, detailing the failure. - * @param Success Delegate called when the users are successfully followed for status updates. - */ - UFUNCTION(Category = "Nakama|Status") - void FollowUsers( - const TArray& UserIds, - FOnFollowUsers Success, - FOnRtError Error - ); - - /** - * Unfollow status updates for one or more users. - * - * @param UserIds The ids of users to unfollow. - * @param Success Delegate called when status updates for the specified users are successfully unfollowed. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Status") - void UnFollowUsers( - const TArray& UserIds, - FOnUnFollowUsers Success, - FOnRtError Error - ); - - // --- Realtime and Match (To send RPC, please use normal Client) --- // - - /** - * Create a multiplayer match on the server. - * @param Success Delegate called when a multiplayer match is successfully created on the server. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Realtime|Match") - void CreateMatch( - FOnCreateMatch Success, - FOnRtError Error - ); - - /** - * Join a multiplayer match by ID. - * - * @param MatchId A match ID. - * @param MetaData Metadata. - * @param Success Delegate called when successfully joined a match by its ID. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Realtime|Match") - void JoinMatch( - const FString& MatchId, - const TMap& MetaData, - FOnCreateMatch Success, - FOnRtError Error - ); - - /** - * Join a multiplayer match with a matchmaker. - * - * @param Token A matchmaker ticket result object. - * @param Success Delegate called when successfully joined a match using a matchmaker token. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Realtime|Match") - void JoinMatchByToken( - const FString& Token, - FOnCreateMatch Success, - FOnRtError Error - ); - - /** - * Send a state change to a match on the server. - * - * When no presences are supplied the new match state will be sent to all presences. - * - * @param MatchId The Id of the match. - * @param OpCode An operation code for the match state. - * @param Data The new state to send to the match. - * @param Presences The presences in the match to send the state. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Match") // BlueprintCallable since it is shared with Blueprints - void SendMatchData( - const FString& MatchId, - int64 OpCode, - const FString& Data, - const TArray& Presences - ); - - /** - * Leave a match on the server. - * - * @param MatchId The match to leave. - * @param Success Delegate called when successfully left a match on the server. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Realtime|Match") - void LeaveMatch( - const FString& MatchId, - FOnLeaveMatch Success, - FOnRtError Error - ); - - // --- Parties --- // - - /** - * Create a party. - * @param Open Whether or not the party will require join requests to be approved by the party leader. - * @param MaxSize Maximum number of party members. - * @param Success Delegate called when a party is successfully created, returning the Party. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Realtime|Parties") - void CreateParty( - bool Open, - int32 MaxSize, - FOnCreateParty Success, - FOnRtError Error - ); - - /** - * Join a party. - * @param PartyId Party ID. - * @param Success Delegate called when successfully joined a party. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Realtime|Parties") - void JoinParty( - const FString& PartyId, - FOnJoinParty Success, - FOnRtError Error - ); - - /** - * Leave the party. - * @param PartyId Party ID. - * @param Success Delegate called when successfully left a party. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Realtime|Parties") - void LeaveParty( - const FString& PartyId, - FOnLeaveParty Success, - FOnRtError Error - ); - - /** - * Request a list of pending join requests for a party. - * @param PartyId Party ID. - * @param Success Delegate called when a list of pending join requests for a party is successfully fetched. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Realtime|Parties") - void ListPartyJoinRequests( - const FString& PartyId, - FOnListPartyJoinRequests Success, - FOnRtError Error - ); - - /** - * Promote a new party leader. - * @param PartyId Party ID. - * @param PartyMember The presence of an existing party member to promote as the new leader. - * @param Success Delegate called when a party member is successfully promoted as the new leader. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Realtime|Parties") - void PromotePartyMember( - const FString& PartyId, - const FNakamaUserPresence& PartyMember, - FOnPromotePartyMember Success, - FOnRtError Error - ); - - /** - * Cancel a party matchmaking process using a ticket. - * @param PartyId Party ID. - * @param Ticket The ticket to cancel. - * @param Success Delegate called when the party matchmaking process is successfully canceled using a ticket. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Realtime|Parties") - void RemoveMatchMakerParty( - const FString& PartyId, - const FString& Ticket, - FOnRemoveMatchmakerParty Success, - FOnRtError Error - ); - - /** - * Kick a party member, or decline a request to join. - * @param PartyId Party ID to remove/reject from. - * @param Presence The presence to remove or reject. - * @param Success Delegate called when a party member is successfully kicked or a join request is declined. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Realtime|Parties") - void RemovePartyMember( - const FString& PartyId, - const FNakamaUserPresence& Presence, - FOnRemovePartyMember Success, - FOnRtError Error - ); - - /** - * Send data to a party. - * @param PartyId Party ID to send to. - * @param OpCode Op code value. - * @param Data The input data to send from the byte buffer, if any. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime|Parties") // BlueprintCallable since it is shared with Blueprints - void SendPartyData( - const FString& PartyId, - int64 OpCode, - const FString& Data - ); - - /** - * Accept a party member's request to join the party. - * - * @param PartyId The party ID to accept the join request for. - * @param Presence The presence to accept as a party member. - * @param Success Delegate called when a party member's request to join the party is successfully accepted. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Realtime|Parties") - void AcceptPartyMember( - const FString& PartyId, - const FNakamaUserPresence& Presence, - FOnAcceptPartyMember Success, - FOnRtError Error - ); - - /** - * Begin matchmaking as a party. - * @param PartyId Party ID. - * @param Query Filter query used to identify suitable users. - * @param MinCount Minimum total user count to match together. - * @param MaxCount Maximum total user count to match together. - * @param StringProperties String properties. - * @param NumericProperties Numeric properties. - * @param CountMultiple An optional multiple of the matched count that must be satisfied. - * @param IgnoreCountMultiple An optional boolean flag indicating whether the matchmaker should ignore the count multiple during the search. - * @param Success Delegate called when matchmaking as a party has been initiated successfully. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Realtime|Parties") - void AddMatchmakerParty( - const FString& PartyId, - const FString& Query, - int32 MinCount, - int32 MaxCount, - const TMap& StringProperties, - const TMap& NumericProperties, - int32 CountMultiple, - bool IgnoreCountMultiple, - FOnAddMatchmakerParty Success, - FOnRtError Error - ); - - /** - * End a party, kicking all party members and closing it. - * @param PartyId The ID of the party. - * @param Success Delegate called when a party has been successfully closed, and all party members have been kicked. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Realtime|Parties") - void CloseParty( - const FString& PartyId, - FOnCloseParty Success, - FOnRtError Error - ); - - // --- RPC --- // - - /** - * Send an RPC message to the server with the realtime client. - * - * @param FunctionId The ID of the function to execute. - * @param Payload The string content to send to the server. - * @param Success Delegate called when the RPC message has been successfully processed by the server and a response is received. - * @param Error Delegate called if an error occurs, detailing the failure. - */ - UFUNCTION(Category = "Nakama|Realtime|RPC") - void RPC( - const FString& FunctionId, - const FString& Payload, - FOnRtRPC Success, - FOnRtError Error - ); - - // TFunctions - - /** - * Join a chat channel on the server. - * - * @param Target The target channel to join. - * @param ChannelType The type of channel to join. - * @param Persistence True if chat messages should be stored. - * @param Hidden True if the user should be hidden on the channel. - * @param SuccessCallback Callback invoked called when successfully joined the chat channel. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void JoinChat( - const FString& Target, - ENakamaChannelType ChannelType, - TOptional Persistence, - TOptional Hidden, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Leave a chat channel on the server. - * - * @param ChannelId The channel to leave. - * @param SuccessCallback Callback invoked after successfully leaving the chat channel. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void LeaveChat( - const FString& ChannelId, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Send a chat message to a channel on the server. - * - * @param ChannelId The channel to send on. - * @param Content The content of the chat message. Must be a JSON object. - * @param SuccessCallback Callback invoked on successful chat message delivery. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void WriteChatMessage( - const FString& ChannelId, - const FString& Content, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Update a chat message to a channel on the server. - * - * @param ChannelId The ID of the chat channel with the message. - * @param MessageId The ID of the message to update. - * @param Content The content update for the message. Must be a JSON object. - * @param SuccessCallback Callback invoked when the chat message is successfully updated. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void UpdateChatMessage( - const FString& ChannelId, - const FString& MessageId, - const FString& Content, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Remove a chat message from a channel on the server. - * - * @param ChannelId The ID of the chat channel with the message. - * @param MessageId The ID of the message to remove. - * @param SuccessCallback Callback invoked when the chat message is successfully removed. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void RemoveChatMessage( - const FString& ChannelId, - const FString& MessageId, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Create a multiplayer match on the server. - * @param SuccessCallback Callback invoked when a multiplayer match is successfully created on the server. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void CreateMatch( - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Join a multiplayer match by ID. - * - * @param MatchId A match ID. - * @param Metadata Metadata. - * @param SuccessCallback Callback invoked when successfully joined a match by its ID. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void JoinMatch( - const FString& MatchId, - const TMap& Metadata, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Join a multiplayer match with a matchmaker. - * - * @param Token A matchmaker ticket result object. - * @param SuccessCallback Callback invoked when successfully joined a match using a matchmaker token. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void JoinMatchByToken( - const FString& Token, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Leave a match on the server. - * - * @param MatchId The match to leave. - * @param SuccessCallback Delegate called when successfully left a match on the server. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void LeaveMatch( - const FString& MatchId, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Join the matchmaker pool and search for opponents on the server. - * - * @param MinCount The minimum number of players to compete against. - * @param MaxCount The maximum number of players to compete against. - * @param Query A matchmaker query to search for opponents. - * @param StringProperties A set of k/v properties to provide in searches. - * @param NumericProperties A set of k/v numeric properties to provide in searches. - * @param CountMultiple An optional multiple of the matched count that must be satisfied. - * @param SuccessCallback Callback invoked when a matchmaker ticket is successfully acquired. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void AddMatchmaker( - TOptional& MinCount, - TOptional& MaxCount, - const TOptional& Query, - const TMap& StringProperties, - const TMap& NumericProperties, - const TOptional& CountMultiple, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Cancel a party matchmaking process using a ticket. - * @param Ticket The ticket to cancel. - * @param SuccessCallback Callback invoked when successfully removed from the matchmaker using the provided ticket. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void RemoveMatchmaker( - const FString& Ticket, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Follow one or more users for status updates. - * - * @param UserIds The user Ids to follow. - * @param SuccessCallback Callback invoked when the users are successfully followed for status updates. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void FollowUsers( - const TArray& UserIds, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Unfollow status updates for one or more users. - * - * @param UserIds The ids of users to unfollow. - * @param SuccessCallback Callback invoked when status updates for the specified users are successfully unfollowed. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void UnfollowUsers( - const TArray& UserIds, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Update the user's status online. - * - * @param Status The new status of the user. - * @param SuccessCallback Callback invoked when the user's online status is successfully updated. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void UpdateStatus( - const FString& Status, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Send an RPC message to the server with the realtime client. - * - * @param Id The ID of the function to execute. - * @param Payload The string content to send to the server. - * @param SuccessCallback Callback invoked when the RPC message has been successfully processed by the server and a response is received. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void RPC( - const FString& Id, - const TOptional& Payload, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - // Party System - - /** - * Accept a party member's request to join the party. - * - * @param PartyId The party ID to accept the join request for. - * @param Presence The presence to accept as a party member. - * @param SuccessCallback Callback invoked when a party member's request to join the party is successfully accepted. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void AcceptPartyMember( - const FString& PartyId, - const FNakamaUserPresence& Presence, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Begin matchmaking as a party. - * @param PartyId Party ID. - * @param Query Filter query used to identify suitable users. - * @param MinCount Minimum total user count to match together. - * @param MaxCount Maximum total user count to match together. - * @param StringProperties String properties. - * @param NumericProperties Numeric properties. - * @param CountMultiple An optional multiple of the matched count that must be satisfied. - * @param SuccessCallback Callback invoked when matchmaking as a party has been initiated successfully. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void AddMatchmakerParty( - const FString& PartyId, - TOptional& MinCount, - TOptional& MaxCount, - const TOptional& Query, - const TMap& StringProperties, - const TMap& NumericProperties, - const TOptional& CountMultiple, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * End a party, kicking all party members and closing it. - * @param PartyId The ID of the party. - * @param SuccessCallback Callback invoked when a party has been successfully closed, and all party members have been kicked. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void CloseParty( - const FString& PartyId, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Create a party. - * @param bOpen Whether or not the party will require join requests to be approved by the party leader. - * @param MaxSize Maximum number of party members. - * @param SuccessCallback Delegate called when a party is successfully created, returning the Party. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void CreateParty( - bool bOpen, - int32 MaxSize, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Join a party. - * @param PartyId Party ID. - * @param SuccessCallback Callback invoked when successfully joined a party. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void JoinParty( - const FString& PartyId, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Leave the party. - * @param PartyId Party ID. - * @param SuccessCallback Callback invoked when successfully left a party. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void LeaveParty( - const FString& PartyId, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Request a list of pending join requests for a party. - * @param PartyId Party ID. - * @param SuccessCallback Delegate called when a list of pending join requests for a party is successfully fetched. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void ListPartyJoinRequests( - const FString& PartyId, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Promote a new party leader. - * @param PartyId Party ID. - * @param PartyMember The presence of an existing party member to promote as the new leader. - * @param SuccessCallback Callback invoked when a party member is successfully promoted as the new leader. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void PromotePartyMember( - const FString& PartyId, - const FNakamaUserPresence& PartyMember, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Cancel a party matchmaking process using a ticket. - * @param PartyId Party ID. - * @param Ticket The ticket to cancel. - * @param SuccessCallback Callback invoked when the party matchmaking process is successfully canceled using a ticket. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void RemoveMatchmakerParty( - const FString& PartyId, - const FString& Ticket, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - /** - * Kick a party member, or decline a request to join. - * @param PartyId Party ID to remove/reject from. - * @param Presence The presence to remove or reject - * @param SuccessCallback Callback invoked when a party member is successfully kicked or a join request is declined. - * @param ErrorCallback Callback invoked if an error occurs, detailing the failure. - */ - void RemovePartyMember( - const FString& PartyId, - const FNakamaUserPresence& Presence, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - // Helpers/Utilities - - /** - * @return True if connected to server. - */ - UFUNCTION(BlueprintPure, Category = "Nakama|Realtime") - bool IsConnected() const; - - /** - * Get heartbeat interval in milliseconds. - * - * @return heartbeat interval value or opt::nullopt if disabled - */ - UFUNCTION(BlueprintPure, Category = "Nakama|Realtime") - int32 GetHeartbeatIntervalMs() const; - - /** - * Set heartbeat interval in milliseconds. Disconnect event will be - * detected in at most 2 x interval. - * - * Default is 5 seconds. - * - * @param IntervalMs interval in ms send heartbeats in. - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Realtime") - void SetHeartbeatIntervalMs(int32 IntervalMs); - - // Creates a request context and assigns a CID to the outgoing message. - TObjectPtr CreateReqContext(FNakamaRealtimeEnvelope& envelope); - - // Reusable functionality to handle Sending messages with Envelopes (with callbacks) - void SendMessageWithEnvelope(const FString& FieldName, const TSharedPtr& ObjectField, TFunction SuccessCallback, TFunction ErrorCallback); - void SendMessageWithEnvelopeMove(const FString& FieldName, const TSharedPtr& ObjectField, TFunction SuccessCallback, TFunction ErrorCallback); - - // Reusable functionality to handle Sending messages with Envelopes (no callbacks) - void SendDataWithEnvelope(const FString& FieldName, const TSharedPtr& ObjectField); - - // --- Listener Events Section --- // - - // These should be used when binding to the lambdas, but only one can bind to it at once - // use dynamic multicast delegates instead if you wish to bind to it multiple places - void SetConnectCallback(const TFunction& Callback) { OnConnect = Callback; } - void SetConnectionErrorCallback(const TFunction& Callback) { OnConnectionError = Callback; } - void SetDisconnectCallback(const TFunction& Callback) { OnDisconnect = Callback; } - void SetErrorCallback(const TFunction& Callback) { OnError = Callback; } - void SetChannelMessageCallback(const TFunction& Callback) { OnChannelMessage = Callback; } - void SetChannelPresenceCallback(const TFunction& Callback) { OnChannelPresenceEvent = Callback; } - void SetMatchmakerMatchedCallback(const TFunction& Callback) { OnMatchmakerMatched = Callback; } - void SetMatchDataCallback(const TFunction& Callback) { OnMatchData = Callback; } - void SetMatchPresenceCallback(const TFunction& Callback) { OnMatchPresenceEvent = Callback; } - void SetNotificationsCallback(const TFunction& Callback) { OnNotifications = Callback; } - void SetPartyCallback(const TFunction& Callback) { OnParty = Callback; } - void SetPartyCloseCallback(const TFunction& Callback) { OnPartyClose = Callback; } - void SetPartyDataCallback(const TFunction& Callback) { OnPartyData = Callback; } - void SetPartyJoinRequestCallback(const TFunction& Callback) { OnPartyJoinRequest = Callback; } - void SetPartyLeaderCallback(const TFunction& Callback) { OnPartyLeader = Callback; } - void SetPartyMatchmakerTicketCallback(const TFunction& Callback) { OnPartyMatchmakerTicket = Callback; } - void SetPartyPresenceCallback(const TFunction& Callback) { OnPartyPresenceEvent = Callback; } - void SetStatusPresenceCallback(const TFunction& Callback) { OnStatusPresenceEvent = Callback; } - void SetStreamPresenceCallback(const TFunction& Callback) { OnStreamPresenceEvent = Callback; } - void SetStreamDataCallback(const TFunction& Callback) { OnStreamData = Callback; } - -private: - - // The actual Lambdas that will be called within this class - TFunction OnConnect; - TFunction OnConnectionError; - TFunction OnDisconnect; - TFunction OnError; - TFunction OnChannelMessage; - TFunction OnChannelPresenceEvent; - TFunction OnMatchmakerMatched; - TFunction OnMatchPresenceEvent; - TFunction OnMatchData; - TFunction OnNotifications; - TFunction OnStatusPresenceEvent; - TFunction OnStreamPresenceEvent; - TFunction OnStreamData; - TFunction OnParty; - TFunction OnPartyClose; - TFunction OnPartyData; - TFunction OnPartyJoinRequest; - TFunction OnPartyLeader; - TFunction OnPartyMatchmakerTicket; - TFunction OnPartyPresenceEvent; - - // WebSocket Instance - TSharedPtr WebSocket; - - // Params - FString Host; - int32 Port; - bool bUseSSL; - bool bIsCustomWebsocketSet = false; - - UPROPERTY() - bool bShowAsOnline; - - // Heartbeat - UPROPERTY() - FTimerHandle HeartbeatTimerHandle; - - float HeartbeatIntervalMs = 3000.0f; // Adjust this value as needed (3 seconds = 3000 milliseconds) - - bool bHeartbeatFailureReported = false; - double LastHeartbeatTimestamp = 0; - double LastMessageTimestamp = 0; - - bool bLocalDisconnectInitiated = false; - - void CleanupWebSocket(); - - - void SendPing(); - void Heartbeat(); - - // Handling Messages - void HandleReceivedMessage(const FString& Data); - - // Used for "ping" - void SendMessage(const FString& FieldName, const TSharedPtr& Object); - - // Heartbeat and Ticking - float AccumulatedDeltaTime = 0.0f; - - virtual void Tick(float DeltaTime) override; - virtual bool IsTickable() const override; - virtual TStatId GetStatId() const override; - - // Helpers - bool SerializeJsonObject(const TSharedPtr& JsonObject, FString& OutSerializedJson); - - void CancelAllRequests(const ENakamaRtErrorCode & ErrorCode); - void OnTransportError(const FString& Description); - - enum class EConnectionState - { - Disconnected, - Connecting, - Connected, - Disconnecting, - }; - - EConnectionState ConnectionState = EConnectionState::Disconnected; - -protected: - - // CID generator and request contexts - UPROPERTY() - TMap> ReqContexts; - int32 NextCid = 0; - FCriticalSection ReqContextsLock; -}; diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaRealtimeRequestContext.h b/Nakama/Source/NakamaUnreal/Public/NakamaRealtimeRequestContext.h deleted file mode 100644 index 1885d74ae..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaRealtimeRequestContext.h +++ /dev/null @@ -1,85 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaRtError.h" -//#include "NakamaUtils.h" -#include "Serialization/JsonSerializer.h" -#include "UObject/NoExportTypes.h" -#include "NakamaRealtimeRequestContext.generated.h" - -/** - * - */ - -USTRUCT(BlueprintType) -struct FNakamaRealtimeEnvelope -{ - GENERATED_BODY() - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nakama") - FString Payload; - - UPROPERTY(BlueprintReadWrite, Category = "Nakama|Realtime") - int32 CID; - - FNakamaRealtimeEnvelope() : CID(-1) - { - } - - FString EncodeJson(TSharedPtr JsonObject) - { - check(JsonObject.IsValid()); - FString OutputString; - TSharedRef> Writer = TJsonWriterFactory<>::Create(&OutputString); - FJsonSerializer::Serialize(JsonObject.ToSharedRef(), Writer); - return OutputString; - } - -}; - -// Change delegate types -DECLARE_DELEGATE_OneParam(FNakamaRealtimeSuccessCallback, const FNakamaRealtimeEnvelope&); // This basically holds the payload -DECLARE_DELEGATE_OneParam(FNakamaRealtimeSuccessCallbackMove, FNakamaRealtimeEnvelope&&); // This basically holds the payload -DECLARE_DELEGATE_OneParam(FNakamaRealtimeErrorCallback, const FNakamaRtError&); - -UCLASS() -class NAKAMAUNREAL_API UNakamaRealtimeRequestContext : public UObject -{ - GENERATED_BODY() - - -public: - // Note: In Nakama: - // Errors = RtErrorCallback (always) - // Success = std::function - - // The callback to invoke on success - FNakamaRealtimeSuccessCallback SuccessCallback; - - // The move version of callback to invoke on success - FNakamaRealtimeSuccessCallbackMove SuccessCallbackMove; - - // The callback to invoke on error - FNakamaRealtimeErrorCallback ErrorCallback; - - // The CID for the request - int32 CID; - - UNakamaRealtimeRequestContext() : CID(-1) {} -}; diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaRtError.h b/Nakama/Source/NakamaUnreal/Public/NakamaRtError.h deleted file mode 100644 index aabd7876a..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaRtError.h +++ /dev/null @@ -1,130 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaRtError.generated.h" - - -//Error Data -UENUM(BlueprintType) -enum class ENakamaRtErrorCode : uint8 -{ - // Client Side Errors - UNKNOWN = 8 UMETA(DisplayName = "UNKNOWN"), // -100 - CONNECT_ERROR = 9 UMETA(DisplayName = "CONNECT_ERROR"), // -1 - TRANSPORT_ERROR = 10 UMETA(DisplayName = "TRANSPORT_ERROR"), // -2 - DISCONNECTED = 11 UMETA(DisplayName = "DISCONNECTED"), // -3 - UNKNOWN_JSON = 12 UMETA(DisplayName = "UNKNOWN_JSON"), // -4 - - // Server Side Errors - RUNTIME_EXCEPTION = 0 UMETA(DisplayName = "RUNTIME_EXCEPTION"), - UNRECOGNIZED_PAYLOAD = 1 UMETA(DisplayName = "UNRECOGNIZED_PAYLOAD"), - MISSING_PAYLOAD = 2 UMETA(DisplayName = "MISSING_PAYLOAD"), - BAD_INPUT = 3 UMETA(DisplayName = "BAD_INPUT"), - MATCH_NOT_FOUND = 4 UMETA(DisplayName = "MATCH_NOT_FOUND"), - MATCH_JOIN_REJECTED = 5 UMETA(DisplayName = "MATCH_JOIN_REJECTED"), - RUNTIME_FUNCTION_NOT_FOUND = 6 UMETA(DisplayName = "RUNTIME_FUNCTION_NOT_FOUND"), - RUNTIME_FUNCTION_EXCEPTION = 7 UMETA(DisplayName = "RUNTIME_FUNCTION_EXCEPTION"), - - /* - * Base Nakama Enums (Changed since we can't have negatives) - UNKNOWN = -100, - - // client side errors - CONNECT_ERROR = -1, ///< Connect has failed. - TRANSPORT_ERROR = -2, ///< Transport error. - DISCONNECTED = -3, ///< Request cancelled due to transport disconnect - UNKNOWN_JSON = -4, ///< FNakamaRtError was build with a json that does not contain error code - - // server side errors - RUNTIME_EXCEPTION = 0, ///< An unexpected result from the server. - UNRECOGNIZED_PAYLOAD = 1, ///< The server received a message which is not recognised. - MISSING_PAYLOAD = 2, ///< A message was expected but contains no content. - BAD_INPUT = 3, ///< Fields in the message have an invalid format. - MATCH_NOT_FOUND = 4, ///< The match id was not found. - MATCH_JOIN_REJECTED = 5, ///< The match join was rejected. - RUNTIME_FUNCTION_NOT_FOUND = 6, ///< The runtime function does not exist on the server. - RUNTIME_FUNCTION_EXCEPTION = 7 ///< The runtime function executed with an error. - */ - -}; - -UENUM(BlueprintType) -enum class ENakamaDisconnectCode : uint8 -{ - NORMAL_CLOSURE = 0, // 1000 - GOING_AWAY = 1, // 1001 - PROTOCOL_ERROR = 2, // 1002 - UNSUPPORTED_DATA = 3, // 1003 - NO_STATUS_RCVD = 5, // 1005 - ABNORMAL_CLOSURE = 6, // 1006 - INVALID_FRAME_PAYLOAD_DATA = 7, // 1007 - POLICY_VIOLATION = 8, // 1008 - MESSAGE_TOO_BIG = 9, // 1009 - MANDATORY_EXT = 10, // 1010 - INTERNAL_SERVER_ERROR = 11, // 1011 - TLS_HANDSHAKE = 15, // 1015 - - HEARTBEAT_FAILURE = 40, // 4000 - TRANSPORT_ERROR = 41, // 4001 -}; - - -// A logical error which may occur on the server. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaRtError -{ - GENERATED_BODY() - - // Additional error details which may be different for each response. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|RtError") - TMap Context; - - // A message in English to help developers debug the response. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|RtError") - FString Message; - - // The error code - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|RtError") - ENakamaRtErrorCode Code; - - FNakamaRtError(const FString& JsonString); - FNakamaRtError(): Code(ENakamaRtErrorCode::UNKNOWN) { } -}; - -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaDisconnectInfo -{ - GENERATED_BODY() - - // Close Code - https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Info") - ENakamaDisconnectCode Code; - - // Close reason. Optional. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Info") - FString Reason; - - // true if close was initiated by server. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Info") - bool Remote; - - FNakamaDisconnectInfo(): Code(ENakamaDisconnectCode::NORMAL_CLOSURE), Remote(false) { } - - ENakamaDisconnectCode ConvertIntToDisconnectCode(int32 Value); -}; diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaSession.h b/Nakama/Source/NakamaUnreal/Public/NakamaSession.h deleted file mode 100644 index d65165950..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaSession.h +++ /dev/null @@ -1,166 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaUserSession.h" -#include "NakamaSession.generated.h" - -/** - * - */ -UCLASS(Blueprintable, BlueprintType) -class NAKAMAUNREAL_API UNakamaSession : public UObject -{ - GENERATED_BODY() - -public: - - // Blueprint/C++ Exposed Struct with Data - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Sessions") - FNakamaUserSession SessionData; - - static UNakamaSession* SetupSession(const FString& AuthResponse); - - /** - * @return The authentication token used to construct this session. - */ - UFUNCTION(BlueprintPure, Category = "Nakama|Session") - const FString GetAuthToken() const; - - /** - * @return The refresh token used to construct this session. - */ - UFUNCTION(BlueprintPure, Category = "Nakama|Session") - const FString GetRefreshToken() const; - - /** - * @return True if the user account for this session was just created. - */ - UFUNCTION(BlueprintPure, Category = "Nakama|Session") - bool IsCreated() const; - - /** - * @return The username of the user who owns this session. - */ - UFUNCTION(BlueprintPure, Category = "Nakama|Session") - const FString GetUsername() const; - - /** - * @return The ID of the user who owns this session. - */ - UFUNCTION(BlueprintPure, Category = "Nakama|Session") - const FString GetUserId() const; - - /** - * @return The timestamp in milliseconds when this session object was created. - */ - UFUNCTION(BlueprintPure, Category = "Nakama|Session") - const FDateTime GetCreateTime() const; - - /** - * @return The timestamp in milliseconds when this session will expire. - */ - UFUNCTION(BlueprintPure, Category = "Nakama|Session") - const FDateTime GetExpireTime() const; - - /** - * @return The timestamp in milliseconds when the refresh token will expire. - */ - UFUNCTION(BlueprintPure, Category = "Nakama|Session") - const FDateTime GetRefreshExpireTime() const; - - /** - * @return True if the session has expired against the current time. - */ - UFUNCTION(BlueprintPure, Category = "Nakama|Session") - bool IsExpired() const; - - /** - * Check if the session's token has expired against the input time. - * - * @param Time The time to compare against the session. - * Use getUnixTimestampMs() to get current time. - * @return true if the session has expired. - */ - UFUNCTION(BlueprintPure, Category = "Nakama|Session") - bool IsExpiredTime(FDateTime Time) const; - - /** - * Check if the session's token has expired against the input time. - * - * @param now The time to compare against the session. - * Use getUnixTimestampMs() to get current time. - * @return true if the session has expired. - */ - UFUNCTION(BlueprintPure, Category = "Nakama|Session") - bool IsRefreshExpired() const; - - /** - * Check if the session's refresh token has expired against the input time. - * - * @param Time The time to compare against the session. - * Use getUnixTimestampMs() to get current time. - * @return true if the session has expired. - */ - UFUNCTION(BlueprintPure, Category = "Nakama|Session") - bool IsRefreshExpiredTime(FDateTime Time) const; - - /** - * Get session variables. - * - * @return Variables. - */ - UFUNCTION(BlueprintPure, Category = "Nakama|Session") - TMap GetVariables() const; - - /** - * Get session variable value by name. - * - * @param Name Value Key - * @return Variable for this key, or empty string if not found. - */ - UFUNCTION(BlueprintPure, Category = "Nakama|Session") - FString GetVariable(FString Name) const; - - /** - * Restore User Session - * - * @param Token Authentication Token from Session - * @param RefreshToken RefreshToken retrieved from Session - */ - UFUNCTION(BlueprintCallable, Category = "Nakama|Session") - static UNakamaSession* RestoreSession(FString Token, FString RefreshToken); - -private: - - UNakamaSession() {} - - FString _AuthToken; - FString _RefreshToken; - bool _IsCreated; - FString _Username; - FString _UserId; - FDateTime _CreateTime; - FDateTime _ExpireTime; - FDateTime _RefreshExpireTime; - bool _IsExpired; - bool _IsRefreshExpired; - TMap _Variables; - - static bool ParseJwtPayload(const FString& jwt, TSharedPtr& payloadJson); -}; diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaStatus.h b/Nakama/Source/NakamaUnreal/Public/NakamaStatus.h deleted file mode 100644 index bfcde0d75..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaStatus.h +++ /dev/null @@ -1,53 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaPresence.h" -#include "NakamaStatus.generated.h" - -// A snapshot of statuses for some set of users. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaStatus -{ - GENERATED_BODY() - - // User statuses. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User|Presence") - TArray Presences; - - FNakamaStatus(const FString& JsonString); - FNakamaStatus(); -}; - -// A batch of status updates for a given user. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaStatusPresenceEvent -{ - GENERATED_BODY() - - // New statuses for the user. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User|Presence") - TArray Joins; - - // Previous statuses for the user. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User|Presence") - TArray Leaves; - - FNakamaStatusPresenceEvent(const FString& JsonString); - FNakamaStatusPresenceEvent(); -}; \ No newline at end of file diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaStorageObject.h b/Nakama/Source/NakamaUnreal/Public/NakamaStorageObject.h deleted file mode 100644 index 0bb80f38a..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaStorageObject.h +++ /dev/null @@ -1,213 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaStorageTypes.h" -#include "NakamaStorageObject.generated.h" - -// An object within the storage engine. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaStoreObjectData -{ - GENERATED_BODY() - - // The collection which stores the object. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Nakama|Storage") - FString Collection; - - // The key of the object within the collection. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Nakama|Storage") - FString Key; - - // The user owner of the object. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Nakama|Storage") - FString UserId; - - // The value of the object. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Nakama|Storage") - FString Value; - - // The version hash of the object. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Nakama|Storage") - FString Version; - - // The UNIX time when the object was created. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Storage") - FDateTime CreateTime; - - // The UNIX time when the object was last updated. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Storage") - FDateTime UpdateTime; - - // The read access permissions for the object. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Nakama|Storage") //BlueprintReadOnly - ENakamaStoragePermissionRead PermissionRead; //NO_READ - - // The write access permissions for the object. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Nakama|Storage") //BlueprintReadOnly - ENakamaStoragePermissionWrite PermissionWrite; //NO_WRITE - - FNakamaStoreObjectData(const FString& JsonString); - FNakamaStoreObjectData(const TSharedPtr JsonObject); - FNakamaStoreObjectData(); -}; - -// The object to store. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaStoreObjectWrite -{ - GENERATED_BODY() - - // The collection which stores the object. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Nakama|Storage") - FString Collection; - - // The key of the object within the collection. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Nakama|Storage") - FString Key; - - // The value of the object. Must be JSON - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Nakama|Storage") - FString Value; - - // The version hash of the object to check. Possible values are: ["", "*", "#hash#"]. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Nakama|Storage") - FString Version; - - // The read access permissions for the object. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Nakama|Storage") //BlueprintReadOnly - ENakamaStoragePermissionRead PermissionRead; // NO_READ - - // The write access permissions for the object. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Nakama|Storage") //BlueprintReadOnly - ENakamaStoragePermissionWrite PermissionWrite; // NO_WRITE - - FNakamaStoreObjectWrite(const FString& JsonString); - FNakamaStoreObjectWrite(); -}; - - - - - -// Storage objects to get (from reading) -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaReadStorageObjectId -{ - GENERATED_BODY() - - // The collection which stores the object. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Nakama|Storage") - FString Collection; - - // The key of the object within the collection. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Nakama|Storage") - FString Key; - - // The user owner of the object. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Nakama|Storage") - FString UserId; - - FNakamaReadStorageObjectId(const FString& JsonString); - FNakamaReadStorageObjectId(); -}; - - -// Storage objects to delete. -USTRUCT(BlueprintType) // For deleting from storage -struct NAKAMAUNREAL_API FNakamaDeleteStorageObjectId -{ - GENERATED_BODY() - - // The collection which stores the object. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Nakama|Storage") - FString Collection; - - // The key of the object within the collection. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Nakama|Storage") - FString Key; - - // The version hash of the object. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Nakama|Storage") - FString Version; - - FNakamaDeleteStorageObjectId(const FString& JsonString); - FNakamaDeleteStorageObjectId(); -}; - - -// A storage acknowledgement. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaStoreObjectAck -{ - GENERATED_BODY() - - // The collection which stores the object. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Storage") - FString Collection; - - // The key of the object within the collection. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Storage") - FString Key; - - // The version hash of the object. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Storage") - FString Version; - - // The owner of the object. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Storage") - FString UserId; - - FNakamaStoreObjectAck(const FString& JsonString); - FNakamaStoreObjectAck(const TSharedPtr JsonObject); - FNakamaStoreObjectAck(); - -}; - -// Batch of acknowledgements. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaStoreObjectAcks -{ - GENERATED_BODY() - - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Storage") - TArray StorageObjects; - - FNakamaStoreObjectAcks(const FString& JsonString); - FNakamaStoreObjectAcks(); - -}; - -// List of storage objects. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaStorageObjectList -{ - GENERATED_BODY() - - // The list of storage objects. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Storage") - TArray Objects; - - // For the next page results, if any. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Storage") - FString Cursor; - - FNakamaStorageObjectList(const FString& JsonString); - FNakamaStorageObjectList(); - -}; diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaStorageTypes.h b/Nakama/Source/NakamaUnreal/Public/NakamaStorageTypes.h deleted file mode 100644 index d4c0d7435..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaStorageTypes.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaStorageTypes.generated.h" - - -UENUM(BlueprintType) -enum class ENakamaStoragePermissionRead : uint8 -{ - // The object is only readable by server runtime - NO_READ = 0 UMETA(DisplayName = "No Read"), - // Only the user who owns it may access - OWNER_READ = 1 UMETA(DisplayName = "Owner Read"), - // Any user can read the object - PUBLIC_READ = 2 UMETA(DisplayName = "Public Read"), - -}; - -UENUM(BlueprintType) -enum class ENakamaStoragePermissionWrite : uint8 -{ - // The object is only writable by server runtime - NO_WRITE = 0 UMETA(DisplayName = "No Write"), - // Only the user who owns it may write - OWNER_WRITE = 1 UMETA(DisplayName = "Owner Write"), - -}; \ No newline at end of file diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaStreams.h b/Nakama/Source/NakamaUnreal/Public/NakamaStreams.h deleted file mode 100644 index 79a2cbd4d..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaStreams.h +++ /dev/null @@ -1,92 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaPresence.h" -#include "NakamaStreams.generated.h" - -// Represents identifying information for a stream. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaStream -{ - GENERATED_BODY() - - // Subject is the primary identifier, if any. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Streams") - FString Subject; - - // Subcontext is a secondary identifier, if any. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Streams") - FString SubContext; - - // The label is an arbitrary identifying string, if the stream has one. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Streams") - FString Label; - - // Mode identifies the type of stream. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Streams") - int32 Mode; - - FNakamaStream(const FString& JsonString); - FNakamaStream(const TSharedPtr JsonObject); - FNakamaStream(); -}; - -// A data message delivered over a stream. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaStreamData -{ - GENERATED_BODY() - - // The stream this data message relates to. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Streams") - FNakamaStream Stream; - - // The sender, if any. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Streams") - FNakamaUserPresence Sender; - - // Arbitrary contents of the data message. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Streams") - FString Data; - - FNakamaStreamData(const FString& JsonString); - FNakamaStreamData(); -}; - -// A set of joins and leaves on a particular stream. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaStreamPresenceEvent -{ - GENERATED_BODY() - - // The stream this event relates to. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Streams") - FNakamaStream Stream; - - // Presences joining the stream as part of this event, if any. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Streams") - TArray Joins; - - // Presences leaving the stream as part of this event, if any. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Streams") - TArray Leaves; - - FNakamaStreamPresenceEvent(const FString& JsonString); - FNakamaStreamPresenceEvent(); -}; \ No newline at end of file diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaTournament.h b/Nakama/Source/NakamaUnreal/Public/NakamaTournament.h deleted file mode 100644 index 1c5f99cd1..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaTournament.h +++ /dev/null @@ -1,146 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaLeaderboard.h" -#include "NakamaTournament.generated.h" - - -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaTournament -{ - GENERATED_BODY() - - // The ID of the tournament. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Tournaments") - FString Id; - - // The title for the tournament. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Tournaments") - FString Title; - - // The description of the tournament. May be blank. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Tournaments") - FString Description; - - // Additional information stored as a JSON object. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Tournaments") - FString Metadata; - - // The UNIX time when the tournament was created. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Tournaments") - FDateTime CreateTime; - - // The UNIX time when the tournament will start. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Tournaments") - FDateTime StartTime; - - // The UNIX time when the tournament will be stopped. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Tournaments") - FDateTime EndTime; - - // The category of the tournament. e.g. "vip" could be category 1. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Tournaments") - int32 Category; - - // ASC or DESC sort mode of scores in the tournament. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Tournaments") - int32 SortOrder; - - // The current number of players in the tournament. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Tournaments") - int32 Size; - - // The maximum number of players for the tournament. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Tournaments") - int32 MaxSize; - - // The maximum score updates allowed per player for the current tournament. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Tournaments") - int32 MaxNumScore; - - // The UNIX time when the tournament stops being active until next reset. A computed value. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Tournaments") - int32 EndActive; - - // The UNIX time when the tournament is next playable. A computed value. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Tournaments") - int32 NextReset; - - // Duration of the tournament in seconds. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Tournaments") - int32 Duration; - - // The UNIX time when the tournament start being active. A computed value. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Tournaments") - int32 StartActive; - - // True if the tournament is active and can enter. A computed value. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Tournaments") - bool CanEnter; - - FNakamaTournament(const FString& JsonString); - FNakamaTournament(const TSharedPtr JsonObject); - FNakamaTournament(); -}; - -// A set of tournament records which may be part of a tournament records page or a batch of individual records. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaTournamentRecordList -{ - GENERATED_BODY() - - // A list of tournament records. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Tournaments") - TArray Records; - - // A batched set of tournament records belonging to specified owners. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Tournaments") - TArray OwnerRecords; - - // The cursor to send when retrieving the next page, if any. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Tournaments") - FString NextCursor; - - // The cursor to send when retrieving the previous page, if any. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Tournaments") - FString PrevCursor; - - FNakamaTournamentRecordList(const FString& JsonString); - FNakamaTournamentRecordList(); - -}; - -// A list of tournaments. -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaTournamentList -{ - GENERATED_BODY() - - // A list of tournament records. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Tournaments") - TArray Tournaments; - - // The cursor to send when retrieving the next page, if any. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Tournaments") - FString Cursor; - - FNakamaTournamentList(const FString& JsonString); - FNakamaTournamentList(); - -}; \ No newline at end of file diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaUser.h b/Nakama/Source/NakamaUnreal/Public/NakamaUser.h deleted file mode 100644 index 3aa0f4233..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaUser.h +++ /dev/null @@ -1,113 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaUser.generated.h" - -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaUser -{ - GENERATED_BODY() - - // The id of the user's account. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User") - FString Id; - - // The username of the user's account. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User") - FString Username; - - // The display name of the user. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User") - FString DisplayName; - - // A URL for an avatar image. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User") - FString AvatarUrl; - - // The language expected to be a tag which follows the BCP-47 spec. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User") - FString Language; - - // The location set by the user. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User") - FString Location; - - // The timezone set by the user. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User") - FString TimeZone; - - // Additional information stored as a JSON object. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User") - FString MetaData; - - // The Facebook id in the user's account. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User") - FString FacebookId; - - // The Google id in the user's account. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User") - FString GoogleId; - - // The Apple Game Center in of the user's account. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User") - FString GameCenterId; - - // The Apple Sign In ID in the user's account. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User") - FString AppleId; - - // The Steam ID in the user's account. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User") - FString SteamId; - - // The UNIX time when the user was created. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User") - FDateTime CreatedAt; - - // The UNIX time when the user was last updated. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User") - FDateTime updatedAt; - - // Number of related edges to this user (friends). - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User") - int32 EdgeCount; - - // Indicates whether the user is currently online. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User") - bool Online; - - FNakamaUser(const FString& JsonString); - FNakamaUser(const TSharedPtr JsonObject); - FNakamaUser() : CreatedAt(FDateTime::MinValue()), updatedAt(FDateTime::MinValue()), EdgeCount(0), Online(false) { } - -}; - - -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaUserList -{ - GENERATED_BODY() - - // List of Users - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|User") - TArray Users; - - FNakamaUserList(const FString& JsonString); - FNakamaUserList() { } -}; diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaUserSession.h b/Nakama/Source/NakamaUnreal/Public/NakamaUserSession.h deleted file mode 100644 index eb013334f..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaUserSession.h +++ /dev/null @@ -1,71 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "NakamaUserSession.generated.h" - -USTRUCT(BlueprintType) -struct NAKAMAUNREAL_API FNakamaUserSession -{ - GENERATED_BODY() - - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Session") - TMap Variables; - - // The authentication token used to construct this session. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Session") - FString AuthToken; - - // The refresh token used to construct this session. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Session") - FString RefreshToken; - - // The username of the user who owns this session. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Session") - FString Username; - - // The ID of the user who owns this session. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Session") - FString UserId; - - // The timestamp in milliseconds when this session object was created. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Session") - FDateTime CreateTime; - - // The timestamp in milliseconds when this session will expire. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Session") - FDateTime ExpireTime; - - // The timestamp in milliseconds when this session will expire. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Session") - FDateTime RefreshExpireTime; - - // True if the user account for this session was just created. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Session") - bool IsCreated; - - // True if the session has expired against the current time. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Session") - bool IsExpired; - - // True if the session has expired against the current time. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Nakama|Session") - bool IsRefreshExpired; - - FNakamaUserSession(); // Default Constructor -}; \ No newline at end of file diff --git a/Nakama/Source/NakamaUnreal/Public/NakamaUtils.h b/Nakama/Source/NakamaUnreal/Public/NakamaUtils.h deleted file mode 100644 index 17a5d0ea0..000000000 --- a/Nakama/Source/NakamaUnreal/Public/NakamaUtils.h +++ /dev/null @@ -1,275 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" - -#include "NakamaClient.h" -#include "Templates/SharedPointer.h" -#include "NakamaLogger.h" -#include "Misc/Base64.h" -#include "NakamaLoggingMacros.h" - -class FJsonObject; - -class NAKAMAUNREAL_API FNakamaUtils -{ -public: - // Handle Request Methods for REST API - static FString ENakamaRequesMethodToFString(ENakamaRequestMethod Verb) - { - switch (Verb) - { - case ENakamaRequestMethod::GET: - return TEXT("GET"); - case ENakamaRequestMethod::POST: - return TEXT("POST"); - case ENakamaRequestMethod::PUT: - return TEXT("PUT"); - case ENakamaRequestMethod::DEL: - return TEXT("DELETE"); - default: - // Handle unrecognized verb if needed - break; - } - - // Return an empty string for unrecognized verbs - return FString(); - } - - // Bool to String Helper - static FString BoolToString(bool Value) - { - return Value ? TEXT("true") : TEXT("false"); - } - - // Build Query String - static FString BuildQueryString(const TMultiMap& QueryParams) - { - FString QueryString; - - for (const auto& Param : QueryParams) - { - if (!QueryString.IsEmpty()) - { - QueryString += "&"; - } - - // Only specific inputs needs to be encoded - //FString EncodedKey = FGenericPlatformHttp::UrlEncode(Param.Key); - //FString EncodedValue = FGenericPlatformHttp::UrlEncode(Param.Value); - - QueryString += Param.Key + "=" + Param.Value; - } - - return QueryString; - } - - // Extra client checks for lambdas in requests - static bool IsClientActive(const UNakamaClient *Client) - { - return IsValid(Client) && Client->bIsActive == true; - } - - // Extra client checks for lambdas in requests - static bool IsRealtimeClientActive(const UNakamaRealtimeClient* RealtimeClient) - { - return IsValid(RealtimeClient) && RealtimeClient->bIsActive == true; - } - - // Json helpers - static FString EncodeJson(TSharedPtr JsonObject) - { - FString OutputString; - const TSharedRef> Writer = TJsonWriterFactory<>::Create(&OutputString); - FJsonSerializer::Serialize(JsonObject.ToSharedRef(), Writer); - return OutputString; - } - - static bool SerializeJsonObject(const TSharedPtr& JsonObject, FString& OutSerializedJson) - { - if (!JsonObject.IsValid()) - { - return false; - } - - const TSharedRef> JsonWriter = TJsonWriterFactory<>::Create(&OutSerializedJson); - if (!FJsonSerializer::Serialize(JsonObject.ToSharedRef(), JsonWriter)) - { - JsonWriter->Close(); - return false; - } - - JsonWriter->Close(); - - return true; - } - - static TSharedPtr DeserializeJsonObject(const FString& JsonString) { - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - if (!FJsonSerializer::Deserialize(JsonReader, JsonObject)) - { - JsonObject = nullptr; - } - return JsonObject; - } - - static void AddVarsToJson(const TSharedPtr& JsonObject, const TMap& Vars, const FString varsFieldName = TEXT("vars"), const bool addAlways = false) { - - if (addAlways || Vars.Num() > 0) - { - const TSharedPtr VarsJson = MakeShared(); - for (const auto& Var : Vars) - { - if (!Var.Key.IsEmpty() && !Var.Value.IsEmpty()) - { - VarsJson->SetStringField(Var.Key, Var.Value); - } - else - { - NAKAMA_LOG_WARN(TEXT("AddVarsToJson: Empty key or value detected.")); - } - } - JsonObject->SetObjectField(varsFieldName, VarsJson); - } - } - - // Enum as integer string - template - static FString GetEnumValueAsIntString(TEnum EnumValue) - { - const int32 IntValue = static_cast(EnumValue); - return FString::FromInt(IntValue); - } - - // Error handling - static FNakamaError HandleInvalidClient() - { - FNakamaError Error; - Error.Message = FString(TEXT("Client Missing")); - Error.Code = ENakamaErrorCode::InvalidArgument; - - NAKAMA_LOG_ERROR(Error.Message); - - return Error; - } - - static FNakamaError HandleInvalidSession() - { - FNakamaError Error; - Error.Message = FString(TEXT("Session Missing")); - Error.Code = ENakamaErrorCode::InvalidArgument; - - NAKAMA_LOG_ERROR(Error.Message); - - return Error; - } - - static FNakamaError HandleInvalidClientAndSession() - { - FNakamaError Error; - Error.Message = FString(TEXT("Client and Session Missing")); - Error.Code = ENakamaErrorCode::InvalidArgument; - - NAKAMA_LOG_ERROR(Error.Message); - - return Error; - } - - static FNakamaRtError HandleInvalidRealtimeClient() - { - FNakamaRtError Error; - Error.Message = FString(TEXT("Realtime Client Missing")); - Error.Code = ENakamaRtErrorCode::UNKNOWN; // Do bad input? - - NAKAMA_LOG_ERROR(Error.Message); - - return Error; - } - - // Base64 Encode/Decode - static FString Base64Encode(const FString& Source) - { - TArray ByteArray; - FTCHARToUTF8 StringSrc = FTCHARToUTF8(Source.GetCharArray().GetData()); - ByteArray.Append((uint8*)StringSrc.Get(), StringSrc.Length()); - - return FBase64::Encode(ByteArray); - } - - static bool Base64Decode(const FString& Source, FString& Dest) - { - TArray ByteArray; - bool Success = FBase64::Decode(Source, ByteArray); - - FUTF8ToTCHAR StringSrc = FUTF8ToTCHAR((const ANSICHAR*)ByteArray.GetData(), ByteArray.Num()); - Dest.AppendChars(StringSrc.Get(), StringSrc.Length()); - - return Success; - } - - // Working with Optionals (mainly from Blueprints) - - template - static TOptional CreateOptional(const T& value, const T& defaultValue) - { - return value != defaultValue ? TOptional(value) : TOptional(); - } - - // Conversion - - static TMap ConvertFloatMapToDouble(const TMap& FloatMap) - { - TMap DoubleMap; - for (const auto& Pair : FloatMap) - { - DoubleMap.Add(Pair.Key, static_cast(Pair.Value)); - } - return DoubleMap; - } - - // Common functions used by multiple clients - static void ProcessRequestComplete(FHttpRequestPtr Request, const FHttpResponsePtr& Response, bool bSuccess, const TFunction& SuccessCallback, const TFunction& ErrorCallback); - static void ProcessRequestCompleteMove(FHttpRequestPtr Request, const FHttpResponsePtr& Response, bool bSuccess, const TFunction& SuccessCallback, const TFunction& ErrorCallback); - - static void HandleJsonSerializationFailure(TFunction ErrorCallback); - static bool IsSessionValid(const UNakamaSession* Session, TFunction ErrorCallback); - static bool IsResponseSuccessful(int32 ResponseCode); - static FNakamaError CreateRequestFailureError(); - - // Make HTTP request - static TSharedRef MakeRequest( - const FString& URL, - const FString& Content, - ENakamaRequestMethod RequestMethod, - const FString& SessionToken, - float Timeout - ); - - static void SetBasicAuthorizationHeader(TSharedRef HttpRequest, const FString& ServerKey) - { - FString AuthToken = FString::Printf(TEXT("%s:"), *ServerKey); - FTCHARToUTF8 Utf8Token = FTCHARToUTF8(*AuthToken); - FString EncodedAuthToken = FBase64::Encode((const uint8*)Utf8Token.Get(), Utf8Token.Length()); - FString AuthorizationHeader = FString::Printf(TEXT("Basic %s"), *EncodedAuthToken); - - //NAKAMA_LOG_DEBUG(FString::Printf( TEXT("Authorization Header: %s"), *AuthorizationHeader )); - - HttpRequest->SetHeader(TEXT("Authorization"), AuthorizationHeader); - } -}; diff --git a/NakamaBlueprintsDemo/Config/DefaultEditor.ini b/NakamaBlueprintsDemo/Config/DefaultEditor.ini deleted file mode 100644 index e69de29bb..000000000 diff --git a/NakamaBlueprintsDemo/Config/DefaultEngine.ini b/NakamaBlueprintsDemo/Config/DefaultEngine.ini deleted file mode 100644 index 95a4ae74e..000000000 --- a/NakamaBlueprintsDemo/Config/DefaultEngine.ini +++ /dev/null @@ -1,38 +0,0 @@ - - -[/Script/EngineSettings.GameMapsSettings] -GameDefaultMap=/Game/NakamaDemo/Maps/DemoMap.DemoMap -EditorStartupMap=/Game/NakamaDemo/Maps/DemoMap.DemoMap -GameInstanceClass=/Script/Engine.GameInstance - -[/Script/HardwareTargeting.HardwareTargetingSettings] -TargetedHardwareClass=Desktop -AppliedTargetedHardwareClass=Desktop -DefaultGraphicsPerformance=Maximum -AppliedDefaultGraphicsPerformance=Maximum - -[/Script/Engine.Engine] -+ActiveGameNameRedirects=(OldGameName="TP_Blank",NewGameName="/Script/NakamaBlueprintsDemo") -+ActiveGameNameRedirects=(OldGameName="/Script/TP_Blank",NewGameName="/Script/NakamaBlueprintsDemo") -+ActiveClassRedirects=(OldClassName="TP_BlankGameModeBase",NewClassName="NakamaBlueprintsDemoGameModeBase") - -[/Script/Engine.NetworkSettings] -n.VerifyPeer=False - -[/Script/Engine.GarbageCollectionSettings] -gc.MultithreadedDestructionEnabled=True - -[/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings] -bEnablePlugin=True -bAllowNetworkConnection=True -SecurityToken=F8E278C148873F2AC8FEF6AEBFC08E8E -bIncludeInShipping=False -bAllowExternalStartInShipping=False -bCompileAFSProject=False -bUseCompression=False -bLogFiles=False -bReportStats=False -ConnectionType=USBOnly -bUseManualIPAddress=False -ManualIPAddress= - diff --git a/NakamaBlueprintsDemo/Config/DefaultGame.ini b/NakamaBlueprintsDemo/Config/DefaultGame.ini deleted file mode 100644 index 9f6554d41..000000000 --- a/NakamaBlueprintsDemo/Config/DefaultGame.ini +++ /dev/null @@ -1,95 +0,0 @@ - - -[/Script/EngineSettings.GeneralProjectSettings] -ProjectID=1F3E7F814B6C60C48C607BBBE3392E9A - -[/Script/UnrealEd.ProjectPackagingSettings] -Build=IfProjectHasCode -BuildConfiguration=PPBC_Development -BuildTarget= -FullRebuild=False -ForDistribution=False -IncludeDebugFiles=False -BlueprintNativizationMethod=Disabled -bIncludeNativizedAssetsInProjectGeneration=False -bExcludeMonolithicEngineHeadersInNativizedCode=False -UsePakFile=True -bUseIoStore=True -bUseZenStore=False -bMakeBinaryConfig=False -bGenerateChunks=False -bGenerateNoChunks=False -bChunkHardReferencesOnly=False -bForceOneChunkPerFile=False -MaxChunkSize=0 -bBuildHttpChunkInstallData=False -HttpChunkInstallDataDirectory=(Path="") -WriteBackMetadataToAssetRegistry=Disabled -bCompressed=True -PackageCompressionFormat=Oodle -bForceUseProjectCompressionFormatIgnoreHardwareOverride=False -PackageAdditionalCompressionOptions= -PackageCompressionMethod=Kraken -PackageCompressionLevel_DebugDevelopment=4 -PackageCompressionLevel_TestShipping=5 -PackageCompressionLevel_Distribution=7 -PackageCompressionMinBytesSaved=1024 -PackageCompressionMinPercentSaved=5 -bPackageCompressionEnableDDC=False -PackageCompressionMinSizeToConsiderDDC=0 -HttpChunkInstallDataVersion= -IncludePrerequisites=True -IncludeAppLocalPrerequisites=False -bShareMaterialShaderCode=True -bDeterministicShaderCodeOrder=False -bSharedMaterialNativeLibraries=True -ApplocalPrerequisitesDirectory=(Path="") -IncludeCrashReporter=False -InternationalizationPreset=English --CulturesToStage=en -+CulturesToStage=en -LocalizationTargetCatchAllChunkId=0 -bCookAll=False -bCookMapsOnly=False -bSkipEditorContent=False -bSkipMovies=False --IniKeyDenylist=KeyStorePassword --IniKeyDenylist=KeyPassword --IniKeyDenylist=rsa.privateexp --IniKeyDenylist=rsa.modulus --IniKeyDenylist=rsa.publicexp --IniKeyDenylist=aes.key --IniKeyDenylist=SigningPublicExponent --IniKeyDenylist=SigningModulus --IniKeyDenylist=SigningPrivateExponent --IniKeyDenylist=EncryptionKey --IniKeyDenylist=DevCenterUsername --IniKeyDenylist=DevCenterPassword --IniKeyDenylist=IOSTeamID --IniKeyDenylist=SigningCertificate --IniKeyDenylist=MobileProvision --IniKeyDenylist=IniKeyDenylist --IniKeyDenylist=IniSectionDenylist -+IniKeyDenylist=KeyStorePassword -+IniKeyDenylist=KeyPassword -+IniKeyDenylist=rsa.privateexp -+IniKeyDenylist=rsa.modulus -+IniKeyDenylist=rsa.publicexp -+IniKeyDenylist=aes.key -+IniKeyDenylist=SigningPublicExponent -+IniKeyDenylist=SigningModulus -+IniKeyDenylist=SigningPrivateExponent -+IniKeyDenylist=EncryptionKey -+IniKeyDenylist=DevCenterUsername -+IniKeyDenylist=DevCenterPassword -+IniKeyDenylist=IOSTeamID -+IniKeyDenylist=SigningCertificate -+IniKeyDenylist=MobileProvision -+IniKeyDenylist=IniKeyDenylist -+IniKeyDenylist=IniSectionDenylist --IniSectionDenylist=HordeStorageServers --IniSectionDenylist=StorageServers -+IniSectionDenylist=HordeStorageServers -+IniSectionDenylist=StorageServers -+MapsToCook=(FilePath="/Game/NakamaDemo/Maps/DemoMap") - diff --git a/NakamaBlueprintsDemo/Config/DefaultInput.ini b/NakamaBlueprintsDemo/Config/DefaultInput.ini deleted file mode 100644 index 1ff89cc17..000000000 --- a/NakamaBlueprintsDemo/Config/DefaultInput.ini +++ /dev/null @@ -1,86 +0,0 @@ -[/Script/Engine.InputSettings] --AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) --AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) --AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) --AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) --AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) --AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) --AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) -+AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MouseWheelAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Gamepad_LeftTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Gamepad_RightTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Gamepad_Special_Left_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Gamepad_Special_Left_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Vive_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Vive_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Vive_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Vive_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Vive_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Vive_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="OculusTouch_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="OculusTouch_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="OculusTouch_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="OculusTouch_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -bAltEnterTogglesFullscreen=True -bF11TogglesFullscreen=True -bUseMouseForTouch=False -bEnableMouseSmoothing=True -bEnableFOVScaling=True -bCaptureMouseOnLaunch=True -bEnableLegacyInputScales=True -bEnableMotionControls=True -bFilterInputByPlatformUser=False -bEnableInputDeviceSubsystem=True -bShouldFlushPressedKeysOnViewportFocusLost=True -bEnableDynamicComponentInputBinding=True -bAlwaysShowTouchInterface=False -bShowConsoleOnFourFingerTap=True -bEnableGestureRecognizer=False -bUseAutocorrect=False -DefaultViewportMouseCaptureMode=CapturePermanently_IncludingInitialMouseDown -DefaultViewportMouseLockMode=LockOnCapture -FOVScale=0.011110 -DoubleClickTime=0.200000 -DefaultPlayerInputClass=/Script/EnhancedInput.EnhancedPlayerInput -DefaultInputComponentClass=/Script/EnhancedInput.EnhancedInputComponent -DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.DefaultVirtualJoysticks --ConsoleKeys=Tilde -+ConsoleKeys=Tilde - diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Backgrounds/Background.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Backgrounds/Background.uasset deleted file mode 100644 index b84aff52b..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Backgrounds/Background.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Components/Button.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Components/Button.uasset deleted file mode 100644 index 4037ff90b..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Components/Button.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Components/Card.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Components/Card.uasset deleted file mode 100644 index fc6db369d..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Components/Card.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Components/crown.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Components/crown.uasset deleted file mode 100644 index 507f27fec..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Components/crown.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Icons/GitHub.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Icons/GitHub.uasset deleted file mode 100644 index ebd814030..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Icons/GitHub.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Icons/blog.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Icons/blog.uasset deleted file mode 100644 index e16415a9e..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Icons/blog.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Icons/cloud.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Icons/cloud.uasset deleted file mode 100644 index bf8d07a69..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Icons/cloud.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Icons/forums.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Icons/forums.uasset deleted file mode 100644 index b99f9aa92..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Icons/forums.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Icons/google-docs.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Icons/google-docs.uasset deleted file mode 100644 index 5d6198ee6..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Icons/google-docs.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Icons/marketplace.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Icons/marketplace.uasset deleted file mode 100644 index 3081ed51e..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Icons/marketplace.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Icons/settings.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Icons/settings.uasset deleted file mode 100644 index 96a444efa..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Icons/settings.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Icons/support.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Icons/support.uasset deleted file mode 100644 index 5e9d315a7..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Icons/support.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Logos/1.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Logos/1.uasset deleted file mode 100644 index d359881ec..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Logos/1.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Logos/2.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Logos/2.uasset deleted file mode 100644 index 8e39efca9..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Logos/2.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Logos/2_medium.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Logos/2_medium.uasset deleted file mode 100644 index 470dd519c..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Logos/2_medium.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Logos/2_small.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Logos/2_small.uasset deleted file mode 100644 index df1f33269..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Logos/2_small.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Logos/3.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Logos/3.uasset deleted file mode 100644 index 1ca9320be..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/Logos/3.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/dark-logo-dark-text.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/dark-logo-dark-text.uasset deleted file mode 100644 index 92c96b28f..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/dark-logo-dark-text.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/dark-logo-white-text-small.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/dark-logo-white-text-small.uasset deleted file mode 100644 index f12322efd..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/dark-logo-white-text-small.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/dark-logo-white-text.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/dark-logo-white-text.uasset deleted file mode 100644 index 23668fc73..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/dark-logo-white-text.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/dark-logo.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/dark-logo.uasset deleted file mode 100644 index a74e33dda..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/dark-logo.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/dark-text.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/dark-text.uasset deleted file mode 100644 index 87cfcfd52..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/dark-text.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/white-logo-white-text-small.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/white-logo-white-text-small.uasset deleted file mode 100644 index 30c563ae3..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/white-logo-white-text-small.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/white-logo-white-text.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/white-logo-white-text.uasset deleted file mode 100644 index d61369a79..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/white-logo-white-text.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/white-logo.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/white-logo.uasset deleted file mode 100644 index 4a0069027..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/white-logo.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/white-text.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/white-text.uasset deleted file mode 100644 index 90e224caa..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Design/NewLogos/white-text.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-Black.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-Black.uasset deleted file mode 100644 index 1a89e55be..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-Black.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-BlackItalic.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-BlackItalic.uasset deleted file mode 100644 index 0236bb39d..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-BlackItalic.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-Bold.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-Bold.uasset deleted file mode 100644 index 479b51b48..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-Bold.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-BoldItalic.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-BoldItalic.uasset deleted file mode 100644 index 1f14fbd4c..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-BoldItalic.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-ExtraBold.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-ExtraBold.uasset deleted file mode 100644 index 1cb10217f..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-ExtraBold.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-ExtraBoldItalic.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-ExtraBoldItalic.uasset deleted file mode 100644 index 155f839be..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-ExtraBoldItalic.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-ExtraLight.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-ExtraLight.uasset deleted file mode 100644 index 807e3e331..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-ExtraLight.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-ExtraLightItalic.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-ExtraLightItalic.uasset deleted file mode 100644 index 8868cd71a..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-ExtraLightItalic.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-Italic.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-Italic.uasset deleted file mode 100644 index e7c2d29a2..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-Italic.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-Light.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-Light.uasset deleted file mode 100644 index 561b08d9e..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-Light.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-LightItalic.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-LightItalic.uasset deleted file mode 100644 index de5d8c73d..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-LightItalic.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-Medium.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-Medium.uasset deleted file mode 100644 index 117d53393..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-Medium.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-MediumItalic.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-MediumItalic.uasset deleted file mode 100644 index 92b5ea3c8..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-MediumItalic.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-Regular.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-Regular.uasset deleted file mode 100644 index 1f6285d03..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-Regular.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-Regular_Font.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-Regular_Font.uasset deleted file mode 100644 index 2e0a152df..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-Regular_Font.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-SemiBold.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-SemiBold.uasset deleted file mode 100644 index 91b99994e..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-SemiBold.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-SemiBoldItalic.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-SemiBoldItalic.uasset deleted file mode 100644 index beba9093d..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-SemiBoldItalic.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-Thin.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-Thin.uasset deleted file mode 100644 index ba73d912e..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-Thin.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-ThinItalic.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-ThinItalic.uasset deleted file mode 100644 index ebc2ff7ef..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/Montserrat/Montserrat-ThinItalic.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-Black.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-Black.uasset deleted file mode 100644 index 690c55314..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-Black.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-BlackItalic.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-BlackItalic.uasset deleted file mode 100644 index 41653b78f..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-BlackItalic.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-Bold.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-Bold.uasset deleted file mode 100644 index e4f6edb17..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-Bold.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-BoldItalic.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-BoldItalic.uasset deleted file mode 100644 index cb548cd44..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-BoldItalic.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-ExtraLight.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-ExtraLight.uasset deleted file mode 100644 index 376f4b678..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-ExtraLight.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-ExtraLightItalic.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-ExtraLightItalic.uasset deleted file mode 100644 index 85de30103..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-ExtraLightItalic.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-Italic.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-Italic.uasset deleted file mode 100644 index 0f1c2d977..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-Italic.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-Light.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-Light.uasset deleted file mode 100644 index 310cc03c6..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-Light.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-LightItalic.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-LightItalic.uasset deleted file mode 100644 index 08d5aa84d..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-LightItalic.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-Regular.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-Regular.uasset deleted file mode 100644 index 1274c25fb..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-Regular.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-Regular_Font.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-Regular_Font.uasset deleted file mode 100644 index 4503b33d0..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-Regular_Font.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-SemiBold.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-SemiBold.uasset deleted file mode 100644 index da19131b8..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-SemiBold.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-SemiBoldItalic.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-SemiBoldItalic.uasset deleted file mode 100644 index be422cce3..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Assets/Fonts/SourceSansPro/SourceSansPro-SemiBoldItalic.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Blueprints/Core/BPI_Nakama.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Blueprints/Core/BPI_Nakama.uasset deleted file mode 100644 index 85305411b..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Blueprints/Core/BPI_Nakama.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Blueprints/Core/BPL_Nakama.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Blueprints/Core/BPL_Nakama.uasset deleted file mode 100644 index 8097ac5b1..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Blueprints/Core/BPL_Nakama.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Blueprints/Core/GM_NakamaDemo.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Blueprints/Core/GM_NakamaDemo.uasset deleted file mode 100644 index 3cb1a8c5b..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Blueprints/Core/GM_NakamaDemo.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Blueprints/Core/NakamaDemoController.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Blueprints/Core/NakamaDemoController.uasset deleted file mode 100644 index a1922d0b5..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Blueprints/Core/NakamaDemoController.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Blueprints/Enums/ELoginTypes.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Blueprints/Enums/ELoginTypes.uasset deleted file mode 100644 index f03afc9b2..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Blueprints/Enums/ELoginTypes.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Blueprints/Enums/ENotificationSeverity.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Blueprints/Enums/ENotificationSeverity.uasset deleted file mode 100644 index afe3289a2..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Blueprints/Enums/ENotificationSeverity.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Maps/DemoMap.umap b/NakamaBlueprintsDemo/Content/NakamaDemo/Maps/DemoMap.umap deleted file mode 100644 index 3d5e3d0e9..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Maps/DemoMap.umap and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_Button.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_Button.uasset deleted file mode 100644 index 26bb54580..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_Button.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_Button_TextOnly.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_Button_TextOnly.uasset deleted file mode 100644 index bfce4c947..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_Button_TextOnly.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_ChatContent.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_ChatContent.uasset deleted file mode 100644 index 9cb018358..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_ChatContent.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_ChatGroup.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_ChatGroup.uasset deleted file mode 100644 index 081de821f..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_ChatGroup.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_ChatMessage.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_ChatMessage.uasset deleted file mode 100644 index 1a011a50f..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_ChatMessage.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_DockItem.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_DockItem.uasset deleted file mode 100644 index 70b8d5757..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_DockItem.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_DropDown.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_DropDown.uasset deleted file mode 100644 index d4f2963a9..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_DropDown.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_ErrorMessage.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_ErrorMessage.uasset deleted file mode 100644 index 2e664f720..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_ErrorMessage.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_Friend.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_Friend.uasset deleted file mode 100644 index ae6b0f8fd..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_Friend.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_GroupUser.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_GroupUser.uasset deleted file mode 100644 index 6fbc11a54..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_GroupUser.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_InputField.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_InputField.uasset deleted file mode 100644 index 233963b5d..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_InputField.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_LeaderboardRecord.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_LeaderboardRecord.uasset deleted file mode 100644 index b77dfd3ec..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_LeaderboardRecord.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_ListedGroup.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_ListedGroup.uasset deleted file mode 100644 index dac87a9cb..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_ListedGroup.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_Match.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_Match.uasset deleted file mode 100644 index f6d9844a1..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_Match.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_MatchContent.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_MatchContent.uasset deleted file mode 100644 index fb30e8cf7..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_MatchContent.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_MatchData.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_MatchData.uasset deleted file mode 100644 index 85576dc4c..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_MatchData.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_MatchPresence.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_MatchPresence.uasset deleted file mode 100644 index b0c1d6514..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_MatchPresence.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_Matched.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_Matched.uasset deleted file mode 100644 index b9e7686aa..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_Matched.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_MatchmakerTicket.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_MatchmakerTicket.uasset deleted file mode 100644 index f4cc9f603..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_MatchmakerTicket.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_Notification.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_Notification.uasset deleted file mode 100644 index b0c4b8748..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_Notification.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_NotificationTray.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_NotificationTray.uasset deleted file mode 100644 index 401c5058a..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_NotificationTray.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_NumberSelector.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_NumberSelector.uasset deleted file mode 100644 index 05a02b3f2..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_NumberSelector.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_PartiesContent.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_PartiesContent.uasset deleted file mode 100644 index e5f8c65bd..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_PartiesContent.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_Party.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_Party.uasset deleted file mode 100644 index 95c46a8c5..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_Party.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_PartyJoinRequest.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_PartyJoinRequest.uasset deleted file mode 100644 index 06c447cb7..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_PartyJoinRequest.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_PartyUserPresence.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_PartyUserPresence.uasset deleted file mode 100644 index d88012faa..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_PartyUserPresence.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_StorageObject.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_StorageObject.uasset deleted file mode 100644 index 660f366b6..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_StorageObject.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_Tournament.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_Tournament.uasset deleted file mode 100644 index 3476707f0..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_Tournament.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_UpdateGroup.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_UpdateGroup.uasset deleted file mode 100644 index 5e00e36a4..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_UpdateGroup.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_UserGroup.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_UserGroup.uasset deleted file mode 100644 index c06b6914e..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_UserGroup.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_UserPresence.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_UserPresence.uasset deleted file mode 100644 index 54470d75f..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Components/WBP_UserPresence.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Account.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Account.uasset deleted file mode 100644 index 55656fd3d..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Account.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_ChatPanel.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_ChatPanel.uasset deleted file mode 100644 index b654352f2..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_ChatPanel.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Friends.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Friends.uasset deleted file mode 100644 index 5a3070403..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Friends.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Groups.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Groups.uasset deleted file mode 100644 index 3d44a308c..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Groups.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Leaderboards.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Leaderboards.uasset deleted file mode 100644 index 94f7885b8..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Leaderboards.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Matchmaking.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Matchmaking.uasset deleted file mode 100644 index 46280f094..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Matchmaking.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Parties.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Parties.uasset deleted file mode 100644 index 542ab40d2..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Parties.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Realtime.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Realtime.uasset deleted file mode 100644 index ea7182021..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Realtime.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Storage.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Storage.uasset deleted file mode 100644 index a5ba4c04a..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Storage.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Tournaments.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Tournaments.uasset deleted file mode 100644 index 9fe4dffbb..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/Panels/WBP_Tournaments.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/WBP_Login.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/WBP_Login.uasset deleted file mode 100644 index bac48582d..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/WBP_Login.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/WBP_Main.uasset b/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/WBP_Main.uasset deleted file mode 100644 index 68d92492f..000000000 Binary files a/NakamaBlueprintsDemo/Content/NakamaDemo/Widgets/WBP_Main.uasset and /dev/null differ diff --git a/NakamaBlueprintsDemo/NakamaBlueprintsDemo.uproject b/NakamaBlueprintsDemo/NakamaBlueprintsDemo.uproject deleted file mode 100644 index c92ade017..000000000 --- a/NakamaBlueprintsDemo/NakamaBlueprintsDemo.uproject +++ /dev/null @@ -1,19 +0,0 @@ -{ - "FileVersion": 3, - "EngineAssociation": "5.2", - "Category": "", - "Description": "", - "Modules": [ - { - "Name": "NakamaBlueprintsDemo", - "Type": "Runtime", - "LoadingPhase": "Default" - } - ], - "Plugins": [ - { - "Name": "Nakama", - "Enabled": true - } - ] -} \ No newline at end of file diff --git a/NakamaBlueprintsDemo/README.md b/NakamaBlueprintsDemo/README.md deleted file mode 100644 index 9e5111a32..000000000 --- a/NakamaBlueprintsDemo/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# Nakama Demo Project -This is an example Unreal Engine project showcasing the core features of Nakama, made using only the plugins and 100% Blueprints. - -This documentation is NOT Final and subject to change. - -This repository includes the Nakama plugin within the `Plugins` folder. - -It is primarily tested on Unreal Engine 5.0 - -# Getting Started -These are the steps to get started with the project. - -1. Install and run the Nakama server. Follow these [instructions](https://heroiclabs.com/docs/install-docker-quickstart). -2. Install [Unreal Engine](https://www.unrealengine.com) 5.0 or greater -3. Right click the .uproject and select `Generate Visual Studio Project Files` -4. Open the solution in your Unreal Compatible IDE (Visual Studio, Xcode, Rider) and run the project -5. Once the Unreal Editor is open, click Play and start testing the features -6. If you have different server credentials than the default ones, or are running a remote server, open the `NakamaDemoController` and replace the server parameters in the `Create Default Client` and `Setup Realtime Client` which are highlighted in green. - -# Build and Test -Follow these instructions to package the example project: - -https://docs.unrealengine.com/5.0/en-US/packaging-unreal-engine-projects/ - -To package it from the command line on Windows run: - -```& "${env:UnrealEngine}\Engine\Build\BatchFiles\RunUAT.bat" BuildCookRun -Platform=Win64 -Project="${env:NakamaUnreal}\NakamaBlueprintsDemo\NakamaBlueprintsDemo.uproject" -ClientConfig=Development -Cook -Build``` - -On Mac: - -```"$UNREAL_ENGINE/Engine/Build/BatchFiles/RunUAT.sh" BuildCookRun -Platform=Mac -Project="${NAKAMA_UNREAL}/NakamaBlueprintsDemo/NakamaBlueprintsDemo.uproject" -ClientConfig=Development -Cook -Build -Architecture_Mac=arm64``` \ No newline at end of file diff --git a/NakamaBlueprintsDemo/Source/NakamaBlueprintsDemo.Target.cs b/NakamaBlueprintsDemo/Source/NakamaBlueprintsDemo.Target.cs deleted file mode 100644 index d245ced88..000000000 --- a/NakamaBlueprintsDemo/Source/NakamaBlueprintsDemo.Target.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -using UnrealBuildTool; -using System.Collections.Generic; - -public class NakamaBlueprintsDemoTarget : TargetRules -{ - public NakamaBlueprintsDemoTarget( TargetInfo Target) : base(Target) - { - Type = TargetType.Game; - DefaultBuildSettings = BuildSettingsVersion.V2; - ExtraModuleNames.AddRange( new string[] { "NakamaBlueprintsDemo" } ); - } -} diff --git a/NakamaBlueprintsDemo/Source/NakamaBlueprintsDemo/NakamaBlueprintsDemo.Build.cs b/NakamaBlueprintsDemo/Source/NakamaBlueprintsDemo/NakamaBlueprintsDemo.Build.cs deleted file mode 100644 index ef2a9fe3e..000000000 --- a/NakamaBlueprintsDemo/Source/NakamaBlueprintsDemo/NakamaBlueprintsDemo.Build.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -using UnrealBuildTool; - -public class NakamaBlueprintsDemo : ModuleRules -{ - public NakamaBlueprintsDemo(ReadOnlyTargetRules Target) : base(Target) - { - PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - - PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" }); - - PrivateDependencyModuleNames.AddRange(new string[] { "NakamaBlueprints" }); - - } -} diff --git a/NakamaBlueprintsDemo/Source/NakamaBlueprintsDemo/NakamaBlueprintsDemo.cpp b/NakamaBlueprintsDemo/Source/NakamaBlueprintsDemo/NakamaBlueprintsDemo.cpp deleted file mode 100644 index 274b1cb6e..000000000 --- a/NakamaBlueprintsDemo/Source/NakamaBlueprintsDemo/NakamaBlueprintsDemo.cpp +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#include "NakamaBlueprintsDemo.h" -#include "Modules/ModuleManager.h" - -IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, NakamaBlueprintsDemo, "NakamaBlueprintsDemo" ); diff --git a/NakamaBlueprintsDemo/Source/NakamaBlueprintsDemo/NakamaBlueprintsDemo.h b/NakamaBlueprintsDemo/Source/NakamaBlueprintsDemo/NakamaBlueprintsDemo.h deleted file mode 100644 index 677c8e25b..000000000 --- a/NakamaBlueprintsDemo/Source/NakamaBlueprintsDemo/NakamaBlueprintsDemo.h +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" - diff --git a/NakamaBlueprintsDemo/Source/NakamaBlueprintsDemo/NakamaBlueprintsDemoGameModeBase.cpp b/NakamaBlueprintsDemo/Source/NakamaBlueprintsDemo/NakamaBlueprintsDemoGameModeBase.cpp deleted file mode 100644 index f319d15be..000000000 --- a/NakamaBlueprintsDemo/Source/NakamaBlueprintsDemo/NakamaBlueprintsDemoGameModeBase.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - - -#include "NakamaBlueprintsDemoGameModeBase.h" - diff --git a/NakamaBlueprintsDemo/Source/NakamaBlueprintsDemo/NakamaBlueprintsDemoGameModeBase.h b/NakamaBlueprintsDemo/Source/NakamaBlueprintsDemo/NakamaBlueprintsDemoGameModeBase.h deleted file mode 100644 index a4d8f0f8c..000000000 --- a/NakamaBlueprintsDemo/Source/NakamaBlueprintsDemo/NakamaBlueprintsDemoGameModeBase.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "GameFramework/GameModeBase.h" -#include "NakamaBlueprintsDemoGameModeBase.generated.h" - -/** - * - */ -UCLASS() -class NAKAMABLUEPRINTSDEMO_API ANakamaBlueprintsDemoGameModeBase : public AGameModeBase -{ - GENERATED_BODY() - -}; diff --git a/NakamaBlueprintsDemo/Source/NakamaBlueprintsDemoEditor.Target.cs b/NakamaBlueprintsDemo/Source/NakamaBlueprintsDemoEditor.Target.cs deleted file mode 100644 index cc59b8ec2..000000000 --- a/NakamaBlueprintsDemo/Source/NakamaBlueprintsDemoEditor.Target.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -using UnrealBuildTool; -using System.Collections.Generic; - -public class NakamaBlueprintsDemoEditorTarget : TargetRules -{ - public NakamaBlueprintsDemoEditorTarget( TargetInfo Target) : base(Target) - { - Type = TargetType.Editor; - DefaultBuildSettings = BuildSettingsVersion.V2; - ExtraModuleNames.AddRange( new string[] { "NakamaBlueprintsDemo" } ); - } -} diff --git a/NakamaBlueprintsTest/Config/Automation/Presets/NakamaAndSator.json b/NakamaBlueprintsTest/Config/Automation/Presets/NakamaAndSator.json deleted file mode 100644 index 06b2e954a..000000000 --- a/NakamaBlueprintsTest/Config/Automation/Presets/NakamaAndSator.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "id": "NakamaAndSator", - "name": "NakamaAndSatori", - "enabledTests": [ - "Nakama.Base.Authenticate.Device", - "Nakama.Base.Authenticate.Device2", - "Nakama.Base.Authenticate.Email", - "Nakama.Base.Authenticate.Email2", - "Nakama.Base.Errors.InvalidArgument", - "Nakama.Base.Errors.InvalidArgument2", - "Nakama.Base.Friends.ListFriends", - "Nakama.Base.Groups.List", - "Nakama.Base.Internals.UriEncode", - "Nakama.Base.Realtime..FollowUsers", - "Nakama.Base.Realtime.Chat.JoinChat", - "Nakama.Base.Realtime.Chat.JoinChatWriteMessage", - "Nakama.Base.Realtime.Chat.JoinGroupChat", - "Nakama.Base.Realtime.Matches.AddMatchmaker", - "Nakama.Base.Realtime.Matches.AuthoritativeMatch", - "Nakama.Base.Realtime.Matches.Match", - "Nakama.Base.Realtime.Matches.MatchmakerJoinMatch", - "Nakama.Base.Realtime.Notifications.CreateListDelete", - "Nakama.Base.Realtime.Notifications.CreateReceive", - "Nakama.Base.Realtime.Parties.CreateParty", - "Nakama.Base.Realtime.Parties.ListPartyJoinRequests", - "Nakama.Base.Realtime.Parties.PartyMatchmaker", - "Nakama.Base.Realtime.Parties.ReceivedPartyJoinRequests", - "Nakama.Base.Realtime.RPC.RPCWithAuth1", - "Nakama.Base.Realtime.RPC.RPCWithAuth2", - "Nakama.Base.Realtime.RPC.RPCWithAuth4", - "Nakama.Base.Realtime.RPC.RPCWithAuth5", - "Nakama.Base.Realtime.RPC.RPCWithHttpKey", - "Nakama.Base.Realtime.RPC.RPCWithHttpKey2", - "Nakama.Base.Realtime.Tournament", - "Nakama.Base.Sessions.RestoreSession", - "Nakama.Base.Sessions.RestoreSession2", - "Nakama.Base.Storage.InvalidArgument", - "Nakama.Base.Storage.Write", - "Nakama.Base.Storage.WriteCursor", - "Nakama.Base.Users.GetAccount", - "Nakama.Base.Users.GetUsers", - "Project.Functional Tests.FTEST_Nakama.Test_Authenticate_Device", - "Project.Functional Tests.FTEST_Nakama.Test_Authenticate_Device_2", - "Project.Functional Tests.FTEST_Nakama.Test_Authenticate_Email", - "Project.Functional Tests.FTEST_Nakama.Test_Error_InvalidArgument", - "Project.Functional Tests.FTEST_Nakama.Test_Error_InvalidArgument_2", - "Project.Functional Tests.FTEST_Nakama.Test_Friends_ListFriends", - "Project.Functional Tests.FTEST_Nakama.Test_Groups_List", - "Project.Functional Tests.FTEST_Nakama.Test_Sessions_RestoreSession", - "Project.Functional Tests.FTEST_Nakama.Test_Sessions_RestoreSession_2", - "Project.Functional Tests.FTEST_Nakama.Test_Storage_InvalidArgument", - "Project.Functional Tests.FTEST_Nakama.Test_Storage_Write", - "Project.Functional Tests.FTEST_Nakama.Test_Storage_Write_Cursor", - "Project.Functional Tests.FTEST_Nakama.Test_Tournament3", - "Project.Functional Tests.FTEST_Nakama.Test_Users_GetUserAccount", - "Project.Functional Tests.FTEST_Nakama.Test_Users_GetUsers", - "Project.Functional Tests.FTEST_Nakama_Realtime.Tes_Realtime_Notifications_CreateListDelete", - "Project.Functional Tests.FTEST_Nakama_Realtime.Tes_Realtime_Notifications_CreateReceive", - "Project.Functional Tests.FTEST_Nakama_Realtime.Test_Realtime_AddMatchmaker", - "Project.Functional Tests.FTEST_Nakama_Realtime.Test_Realtime_AuthoritativeMatch", - "Project.Functional Tests.FTEST_Nakama_Realtime.Test_Realtime_Chat_JoinChat", - "Project.Functional Tests.FTEST_Nakama_Realtime.Test_Realtime_Chat_JoinChat_WriteMessage", - "Project.Functional Tests.FTEST_Nakama_Realtime.Test_Realtime_Chat_JoinGroupChat", - "Project.Functional Tests.FTEST_Nakama_Realtime.Test_Realtime_Match", - "Project.Functional Tests.FTEST_Nakama_Realtime.Test_Realtime_MatchmakerJoinMatch", - "Project.Functional Tests.FTEST_Nakama_Realtime.Test_Realtime_Parties_CreateJoin", - "Project.Functional Tests.FTEST_Nakama_Realtime.Test_Realtime_Parties_Matchmaker", - "Project.Functional Tests.FTEST_Nakama_Realtime.Test_Realtime_RPC_WithAuth1", - "Project.Functional Tests.FTEST_Nakama_Realtime.Test_Realtime_RPC_WithAuth2", - "Project.Functional Tests.FTEST_Nakama_Realtime.Test_Realtime_RPC_WithAuth4", - "Project.Functional Tests.FTEST_Nakama_Realtime.Test_Realtime_RPC_WithAuth5", - "Project.Functional Tests.FTEST_Nakama_Realtime.Test_Realtime_RPC_WithHttpKey", - "Project.Functional Tests.FTEST_Nakama_Realtime.Test_Realtime_RPC_WithHttpKey2", - "Project.Functional Tests.FTEST_Nakama_Realtime.Test_Realtime_Tournament", - "Project.Functional Tests.FTEST_Satori.Test_Authenticate", - "Project.Functional Tests.FTEST_Satori.Test_PostEvent" - ] -} \ No newline at end of file diff --git a/NakamaBlueprintsTest/Config/DefaultEditor.ini b/NakamaBlueprintsTest/Config/DefaultEditor.ini deleted file mode 100644 index e69de29bb..000000000 diff --git a/NakamaBlueprintsTest/Config/DefaultEngine.ini b/NakamaBlueprintsTest/Config/DefaultEngine.ini deleted file mode 100644 index 4b1152413..000000000 --- a/NakamaBlueprintsTest/Config/DefaultEngine.ini +++ /dev/null @@ -1,73 +0,0 @@ - - -[/Script/EngineSettings.GameMapsSettings] -GameDefaultMap=/Game/FTEST_Nakama.FTEST_Nakama -EditorStartupMap=/Game/FTEST_Nakama.FTEST_Nakama - -[/Script/WindowsTargetPlatform.WindowsTargetSettings] -DefaultGraphicsRHI=DefaultGraphicsRHI_DX12 --D3D12TargetedShaderFormats=PCD3D_SM5 -+D3D12TargetedShaderFormats=PCD3D_SM6 --D3D11TargetedShaderFormats=PCD3D_SM5 -+D3D11TargetedShaderFormats=PCD3D_SM5 -Compiler=Default -AudioSampleRate=48000 -AudioCallbackBufferFrameSize=1024 -AudioNumBuffersToEnqueue=1 -AudioMaxChannels=0 -AudioNumSourceWorkers=4 -SpatializationPlugin= -SourceDataOverridePlugin= -ReverbPlugin= -OcclusionPlugin= -CompressionOverrides=(bOverrideCompressionTimes=False,DurationThreshold=5.000000,MaxNumRandomBranches=0,SoundCueQualityIndex=0) -CacheSizeKB=65536 -MaxChunkSizeOverrideKB=0 -bResampleForDevice=False -MaxSampleRate=48000.000000 -HighSampleRate=32000.000000 -MedSampleRate=24000.000000 -LowSampleRate=12000.000000 -MinSampleRate=8000.000000 -CompressionQualityModifier=1.000000 -AutoStreamingThreshold=0.000000 -SoundCueCookQualityIndex=-1 - - -[/Script/HardwareTargeting.HardwareTargetingSettings] -TargetedHardwareClass=Desktop -AppliedTargetedHardwareClass=Desktop -DefaultGraphicsPerformance=Maximum -AppliedDefaultGraphicsPerformance=Maximum - -[/Script/Engine.RendererSettings] -r.GenerateMeshDistanceFields=True -r.DynamicGlobalIlluminationMethod=1 -r.ReflectionMethod=1 -r.Shadow.Virtual.Enable=1 -r.DefaultFeature.AutoExposure.ExtendDefaultLuminanceRange=True - -[/Script/WorldPartitionEditor.WorldPartitionEditorSettings] -CommandletClass=Class'/Script/UnrealEd.WorldPartitionConvertCommandlet' - -[/Script/Engine.UserInterfaceSettings] -bAuthorizeAutomaticWidgetVariableCreation=False - -[/Script/Engine.Engine] -+ActiveGameNameRedirects=(OldGameName="TP_BlankBP",NewGameName="/Script/NakamaBlueprintsTest") -+ActiveGameNameRedirects=(OldGameName="/Script/TP_BlankBP",NewGameName="/Script/NakamaBlueprintsTest") - -[/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings] -bEnablePlugin=True -bAllowNetworkConnection=True -SecurityToken=8A39C8BF4DF34CC560C1F38D15E567B9 -bIncludeInShipping=False -bAllowExternalStartInShipping=False -bCompileAFSProject=False -bUseCompression=False -bLogFiles=False -bReportStats=False -ConnectionType=USBOnly -bUseManualIPAddress=False -ManualIPAddress= - diff --git a/NakamaBlueprintsTest/Config/DefaultGame.ini b/NakamaBlueprintsTest/Config/DefaultGame.ini deleted file mode 100644 index 947d75fea..000000000 --- a/NakamaBlueprintsTest/Config/DefaultGame.ini +++ /dev/null @@ -1,4 +0,0 @@ - - -[/Script/EngineSettings.GeneralProjectSettings] -ProjectID=7892922444584D0E4DDF4F820184F440 diff --git a/NakamaBlueprintsTest/Config/DefaultInput.ini b/NakamaBlueprintsTest/Config/DefaultInput.ini deleted file mode 100644 index 4cc36056b..000000000 --- a/NakamaBlueprintsTest/Config/DefaultInput.ini +++ /dev/null @@ -1,85 +0,0 @@ -[/Script/Engine.InputSettings] --AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) --AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) --AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) --AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) --AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) --AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) --AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) -+AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MouseWheelAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Gamepad_LeftTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Gamepad_RightTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Gamepad_Special_Left_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Gamepad_Special_Left_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Vive_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Vive_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Vive_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Vive_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Vive_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Vive_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="OculusTouch_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="OculusTouch_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="OculusTouch_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="OculusTouch_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -bAltEnterTogglesFullscreen=True -bF11TogglesFullscreen=True -bUseMouseForTouch=False -bEnableMouseSmoothing=True -bEnableFOVScaling=True -bCaptureMouseOnLaunch=True -bEnableLegacyInputScales=True -bEnableMotionControls=True -bFilterInputByPlatformUser=False -bShouldFlushPressedKeysOnViewportFocusLost=True -bAlwaysShowTouchInterface=False -bShowConsoleOnFourFingerTap=True -bEnableGestureRecognizer=False -bUseAutocorrect=False -DefaultViewportMouseCaptureMode=CapturePermanently_IncludingInitialMouseDown -DefaultViewportMouseLockMode=LockOnCapture -FOVScale=0.011110 -DoubleClickTime=0.200000 -DefaultPlayerInputClass=/Script/EnhancedInput.EnhancedPlayerInput -DefaultInputComponentClass=/Script/EnhancedInput.EnhancedInputComponent -DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.DefaultVirtualJoysticks --ConsoleKeys=Tilde -+ConsoleKeys=Tilde -+ConsoleKeys=Caret - diff --git a/NakamaBlueprintsTest/Content/FTEST_Nakama.umap b/NakamaBlueprintsTest/Content/FTEST_Nakama.umap deleted file mode 100644 index c0d067fb6..000000000 Binary files a/NakamaBlueprintsTest/Content/FTEST_Nakama.umap and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/FTEST_Nakama_Realtime.umap b/NakamaBlueprintsTest/Content/FTEST_Nakama_Realtime.umap deleted file mode 100644 index 3d536256a..000000000 Binary files a/NakamaBlueprintsTest/Content/FTEST_Nakama_Realtime.umap and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/FTEST_Satori.umap b/NakamaBlueprintsTest/Content/FTEST_Satori.umap deleted file mode 100644 index 1834ba73d..000000000 Binary files a/NakamaBlueprintsTest/Content/FTEST_Satori.umap and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Satori_Tests/Authenticate/Test_Authenticate.uasset b/NakamaBlueprintsTest/Content/Satori_Tests/Authenticate/Test_Authenticate.uasset deleted file mode 100644 index 2684d56f2..000000000 Binary files a/NakamaBlueprintsTest/Content/Satori_Tests/Authenticate/Test_Authenticate.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Satori_Tests/Base/Satori_Test_Base.uasset b/NakamaBlueprintsTest/Content/Satori_Tests/Base/Satori_Test_Base.uasset deleted file mode 100644 index c965ba3c0..000000000 Binary files a/NakamaBlueprintsTest/Content/Satori_Tests/Base/Satori_Test_Base.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Satori_Tests/Event/Test_PostEvent.uasset b/NakamaBlueprintsTest/Content/Satori_Tests/Event/Test_PostEvent.uasset deleted file mode 100644 index 51daed03e..000000000 Binary files a/NakamaBlueprintsTest/Content/Satori_Tests/Event/Test_PostEvent.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Satori_Tests/Test_Authenticate.uasset b/NakamaBlueprintsTest/Content/Satori_Tests/Test_Authenticate.uasset deleted file mode 100644 index a32060111..000000000 Binary files a/NakamaBlueprintsTest/Content/Satori_Tests/Test_Authenticate.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Authenticate/Test_Authenticate_Device.uasset b/NakamaBlueprintsTest/Content/Tests/Authenticate/Test_Authenticate_Device.uasset deleted file mode 100644 index 6fcfa6173..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Authenticate/Test_Authenticate_Device.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Authenticate/Test_Authenticate_Device_2.uasset b/NakamaBlueprintsTest/Content/Tests/Authenticate/Test_Authenticate_Device_2.uasset deleted file mode 100644 index 392acb6f0..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Authenticate/Test_Authenticate_Device_2.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Authenticate/Test_Authenticate_Email.uasset b/NakamaBlueprintsTest/Content/Tests/Authenticate/Test_Authenticate_Email.uasset deleted file mode 100644 index d484e2701..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Authenticate/Test_Authenticate_Email.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Base/Nakama_Test_Base.uasset b/NakamaBlueprintsTest/Content/Tests/Base/Nakama_Test_Base.uasset deleted file mode 100644 index c26f65eb4..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Base/Nakama_Test_Base.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Errors/Test_Error_InvalidArgument.uasset b/NakamaBlueprintsTest/Content/Tests/Errors/Test_Error_InvalidArgument.uasset deleted file mode 100644 index 808596b16..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Errors/Test_Error_InvalidArgument.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Errors/Test_Error_InvalidArgument_2.uasset b/NakamaBlueprintsTest/Content/Tests/Errors/Test_Error_InvalidArgument_2.uasset deleted file mode 100644 index ec879f157..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Errors/Test_Error_InvalidArgument_2.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Errors/Test_Error_NotFound.uasset b/NakamaBlueprintsTest/Content/Tests/Errors/Test_Error_NotFound.uasset deleted file mode 100644 index cc0cc3e65..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Errors/Test_Error_NotFound.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Errors/Test_Error_Unauthenticated.uasset b/NakamaBlueprintsTest/Content/Tests/Errors/Test_Error_Unauthenticated.uasset deleted file mode 100644 index e74ca8a01..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Errors/Test_Error_Unauthenticated.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Friends/Test_Friends_ListFriends.uasset b/NakamaBlueprintsTest/Content/Tests/Friends/Test_Friends_ListFriends.uasset deleted file mode 100644 index 68eaac731..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Friends/Test_Friends_ListFriends.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Groups/Test_Groups_List.uasset b/NakamaBlueprintsTest/Content/Tests/Groups/Test_Groups_List.uasset deleted file mode 100644 index 6406b53fa..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Groups/Test_Groups_List.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Matches/Test_Matches_ListMatches.uasset b/NakamaBlueprintsTest/Content/Tests/Matches/Test_Matches_ListMatches.uasset deleted file mode 100644 index 4094ce08e..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Matches/Test_Matches_ListMatches.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Realtime/Chat/Test_Realtime_Chat_JoinChat.uasset b/NakamaBlueprintsTest/Content/Tests/Realtime/Chat/Test_Realtime_Chat_JoinChat.uasset deleted file mode 100644 index ddd3e57ea..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Realtime/Chat/Test_Realtime_Chat_JoinChat.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Realtime/Chat/Test_Realtime_Chat_JoinChat_WriteMessage.uasset b/NakamaBlueprintsTest/Content/Tests/Realtime/Chat/Test_Realtime_Chat_JoinChat_WriteMessage.uasset deleted file mode 100644 index 7deaa664b..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Realtime/Chat/Test_Realtime_Chat_JoinChat_WriteMessage.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Realtime/Chat/Test_Realtime_Chat_JoinGroupChat.uasset b/NakamaBlueprintsTest/Content/Tests/Realtime/Chat/Test_Realtime_Chat_JoinGroupChat.uasset deleted file mode 100644 index e3101f4a9..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Realtime/Chat/Test_Realtime_Chat_JoinGroupChat.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Realtime/Matches/Test_Realtime_AddMatchmaker.uasset b/NakamaBlueprintsTest/Content/Tests/Realtime/Matches/Test_Realtime_AddMatchmaker.uasset deleted file mode 100644 index 5b22359f2..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Realtime/Matches/Test_Realtime_AddMatchmaker.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Realtime/Matches/Test_Realtime_AuthoritativeMatch.uasset b/NakamaBlueprintsTest/Content/Tests/Realtime/Matches/Test_Realtime_AuthoritativeMatch.uasset deleted file mode 100644 index 34687288c..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Realtime/Matches/Test_Realtime_AuthoritativeMatch.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Realtime/Matches/Test_Realtime_Match.uasset b/NakamaBlueprintsTest/Content/Tests/Realtime/Matches/Test_Realtime_Match.uasset deleted file mode 100644 index a697ad4b7..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Realtime/Matches/Test_Realtime_Match.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Realtime/Matches/Test_Realtime_MatchmakerJoinMatch.uasset b/NakamaBlueprintsTest/Content/Tests/Realtime/Matches/Test_Realtime_MatchmakerJoinMatch.uasset deleted file mode 100644 index aefafc930..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Realtime/Matches/Test_Realtime_MatchmakerJoinMatch.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Realtime/Notifications/Tes_Realtime_Notifications_CreateListDelete.uasset b/NakamaBlueprintsTest/Content/Tests/Realtime/Notifications/Tes_Realtime_Notifications_CreateListDelete.uasset deleted file mode 100644 index f22848234..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Realtime/Notifications/Tes_Realtime_Notifications_CreateListDelete.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Realtime/Notifications/Tes_Realtime_Notifications_CreateReceive.uasset b/NakamaBlueprintsTest/Content/Tests/Realtime/Notifications/Tes_Realtime_Notifications_CreateReceive.uasset deleted file mode 100644 index 663c2a204..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Realtime/Notifications/Tes_Realtime_Notifications_CreateReceive.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Realtime/Parties/Test_Realtime_Parties_Create.uasset b/NakamaBlueprintsTest/Content/Tests/Realtime/Parties/Test_Realtime_Parties_Create.uasset deleted file mode 100644 index 1ce5c1634..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Realtime/Parties/Test_Realtime_Parties_Create.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Realtime/Parties/Test_Realtime_Parties_CreateJoin.uasset b/NakamaBlueprintsTest/Content/Tests/Realtime/Parties/Test_Realtime_Parties_CreateJoin.uasset deleted file mode 100644 index 4d4151fbd..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Realtime/Parties/Test_Realtime_Parties_CreateJoin.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Realtime/Parties/Test_Realtime_Parties_Matchmaker.uasset b/NakamaBlueprintsTest/Content/Tests/Realtime/Parties/Test_Realtime_Parties_Matchmaker.uasset deleted file mode 100644 index bf901a6e5..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Realtime/Parties/Test_Realtime_Parties_Matchmaker.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Realtime/RPC/Test_Realtime_RPC_WithAuth1.uasset b/NakamaBlueprintsTest/Content/Tests/Realtime/RPC/Test_Realtime_RPC_WithAuth1.uasset deleted file mode 100644 index 309ded93d..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Realtime/RPC/Test_Realtime_RPC_WithAuth1.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Realtime/RPC/Test_Realtime_RPC_WithAuth2.uasset b/NakamaBlueprintsTest/Content/Tests/Realtime/RPC/Test_Realtime_RPC_WithAuth2.uasset deleted file mode 100644 index a65cce27e..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Realtime/RPC/Test_Realtime_RPC_WithAuth2.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Realtime/RPC/Test_Realtime_RPC_WithAuth4.uasset b/NakamaBlueprintsTest/Content/Tests/Realtime/RPC/Test_Realtime_RPC_WithAuth4.uasset deleted file mode 100644 index f86e7f7f7..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Realtime/RPC/Test_Realtime_RPC_WithAuth4.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Realtime/RPC/Test_Realtime_RPC_WithAuth5.uasset b/NakamaBlueprintsTest/Content/Tests/Realtime/RPC/Test_Realtime_RPC_WithAuth5.uasset deleted file mode 100644 index 9782add27..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Realtime/RPC/Test_Realtime_RPC_WithAuth5.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Realtime/RPC/Test_Realtime_RPC_WithHttpKey.uasset b/NakamaBlueprintsTest/Content/Tests/Realtime/RPC/Test_Realtime_RPC_WithHttpKey.uasset deleted file mode 100644 index 70f9fa82e..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Realtime/RPC/Test_Realtime_RPC_WithHttpKey.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Realtime/RPC/Test_Realtime_RPC_WithHttpKey2.uasset b/NakamaBlueprintsTest/Content/Tests/Realtime/RPC/Test_Realtime_RPC_WithHttpKey2.uasset deleted file mode 100644 index 196bf0604..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Realtime/RPC/Test_Realtime_RPC_WithHttpKey2.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Realtime/Test_Realtime_Tournament.uasset b/NakamaBlueprintsTest/Content/Tests/Realtime/Test_Realtime_Tournament.uasset deleted file mode 100644 index c9b935094..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Realtime/Test_Realtime_Tournament.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Realtime/Test_Tournament.uasset b/NakamaBlueprintsTest/Content/Tests/Realtime/Test_Tournament.uasset deleted file mode 100644 index 73b120d2b..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Realtime/Test_Tournament.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Sessions/Test_Sessions_RestoreSession.uasset b/NakamaBlueprintsTest/Content/Tests/Sessions/Test_Sessions_RestoreSession.uasset deleted file mode 100644 index d056af115..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Sessions/Test_Sessions_RestoreSession.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Sessions/Test_Sessions_RestoreSession_2.uasset b/NakamaBlueprintsTest/Content/Tests/Sessions/Test_Sessions_RestoreSession_2.uasset deleted file mode 100644 index c061f50fa..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Sessions/Test_Sessions_RestoreSession_2.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Storage/Test_Storage_InvalidArgument.uasset b/NakamaBlueprintsTest/Content/Tests/Storage/Test_Storage_InvalidArgument.uasset deleted file mode 100644 index f65d378d8..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Storage/Test_Storage_InvalidArgument.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Storage/Test_Storage_Write.uasset b/NakamaBlueprintsTest/Content/Tests/Storage/Test_Storage_Write.uasset deleted file mode 100644 index 75f5b6f9b..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Storage/Test_Storage_Write.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Storage/Test_Storage_Write_Cursor.uasset b/NakamaBlueprintsTest/Content/Tests/Storage/Test_Storage_Write_Cursor.uasset deleted file mode 100644 index 2b2d9c410..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Storage/Test_Storage_Write_Cursor.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Users/Test_Users_GetAccount.uasset b/NakamaBlueprintsTest/Content/Tests/Users/Test_Users_GetAccount.uasset deleted file mode 100644 index c5f44f8d2..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Users/Test_Users_GetAccount.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Users/Test_Users_GetUserAccount.uasset b/NakamaBlueprintsTest/Content/Tests/Users/Test_Users_GetUserAccount.uasset deleted file mode 100644 index 9fbfb9e6e..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Users/Test_Users_GetUserAccount.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/Content/Tests/Users/Test_Users_GetUsers.uasset b/NakamaBlueprintsTest/Content/Tests/Users/Test_Users_GetUsers.uasset deleted file mode 100644 index e06afe99e..000000000 Binary files a/NakamaBlueprintsTest/Content/Tests/Users/Test_Users_GetUsers.uasset and /dev/null differ diff --git a/NakamaBlueprintsTest/NakamaBlueprintsTest.uproject b/NakamaBlueprintsTest/NakamaBlueprintsTest.uproject deleted file mode 100644 index 3d0f50969..000000000 --- a/NakamaBlueprintsTest/NakamaBlueprintsTest.uproject +++ /dev/null @@ -1,37 +0,0 @@ -{ - "FileVersion": 3, - "EngineAssociation": "{2C8C59F9-4278-49EA-779F-E1A43E90E045}", - "Category": "", - "Description": "", - "Modules": [ - { - "Name": "NakamaBlueprintsTest", - "Type": "Runtime", - "LoadingPhase": "Default", - "AdditionalDependencies": [ - "Engine" - ] - } - ], - "Plugins": [ - { - "Name": "ModelingToolsEditorMode", - "Enabled": true, - "TargetAllowList": [ - "Editor" - ] - }, - { - "Name": "FunctionalTestingEditor", - "Enabled": true - }, - { - "Name": "Nakama", - "Enabled": true - }, - { - "Name": "Satori", - "Enabled": true - } - ] -} \ No newline at end of file diff --git a/NakamaBlueprintsTest/README.md b/NakamaBlueprintsTest/README.md deleted file mode 100644 index c655181c9..000000000 --- a/NakamaBlueprintsTest/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# Nakama Blueprints Tests - -**Nakama Blueprints Tests** is a dedicated project aimed at validating the blueprint logic using Unreal's Automated Testing systems. - -- 🎮 **Version Compatibility:** Primarily tested on Unreal Engine 5.2. -- 📁 **Dependencies:** The Nakama plugin is bundled within the `Plugins` directory. - -## 🚀 Getting Started - -Follow these steps to get the project up and running: - -1. **Nakama Server Setup:** - - Install and boot up the Nakama server. - - Detailed [instructions available here](https://heroiclabs.com/docs/install-docker-quickstart). - -2. **Unreal Engine Installation:** - - Make sure to have [Unreal Engine](https://www.unrealengine.com) version 5.0 or later. - -3. **Project Initialization:** - - Right-click the `.uproject` file and select `Generate Visual Studio Project Files`. - -4. **IDE Configuration:** - - Open the generated solution in your preferred Unreal-compatible IDE (e.g., Visual Studio, Xcode, Rider). - - Launch the project. - -5. **Server Credential Configuration:** - - If using non-default server credentials or a remote server, navigate to `Content/Tests/Base` and open `Nakama_Test_Base`. - - Adjust the server parameters in the `CreateClient` function. - -**Note: You might also be able to open the project directly after adding the Nakama Plugin into the project** - -## 🧪 Running the Tests - -To execute the Nakama tests: - -1. In the Unreal Editor, head to `Tools` -> `Test Automation`. -2. Select your testing device (e.g., YOURNAME_123). -3. Under `Project` -> `FunctionalTests`, you'll find all the `Nakama Tests`. -4. Select the desired tests (or select all) and press `Start Tests`. diff --git a/NakamaBlueprintsTest/Source/NakamaBlueprintsTest.Target.cs b/NakamaBlueprintsTest/Source/NakamaBlueprintsTest.Target.cs deleted file mode 100644 index e4c035b68..000000000 --- a/NakamaBlueprintsTest/Source/NakamaBlueprintsTest.Target.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -using UnrealBuildTool; -using System.Collections.Generic; - -public class NakamaBlueprintsTestTarget : TargetRules -{ - public NakamaBlueprintsTestTarget(TargetInfo Target) : base(Target) - { - Type = TargetType.Game; - DefaultBuildSettings = BuildSettingsVersion.V5; - IncludeOrderVersion = EngineIncludeOrderVersion.Latest; - - //bIWYU = true; - bEnforceIWYU = true; - bUseUnityBuild = false; - bForceUnityBuild = false; - bUsePCHFiles = false; - - ExtraModuleNames.AddRange( new string[] { "NakamaBlueprintsTest" } ); - } -} diff --git a/NakamaBlueprintsTest/Source/NakamaBlueprintsTest/NakamaBlueprintsTest.Build.cs b/NakamaBlueprintsTest/Source/NakamaBlueprintsTest/NakamaBlueprintsTest.Build.cs deleted file mode 100644 index 526f2fe8a..000000000 --- a/NakamaBlueprintsTest/Source/NakamaBlueprintsTest/NakamaBlueprintsTest.Build.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -using UnrealBuildTool; - -public class NakamaBlueprintsTest : ModuleRules -{ - public NakamaBlueprintsTest(ReadOnlyTargetRules Target) : base(Target) - { - PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - - PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "Json", "JsonUtilities" }); - - PrivateDependencyModuleNames.AddRange(new string[] { "NakamaUnreal" }); - } -} diff --git a/NakamaBlueprintsTest/Source/NakamaBlueprintsTest/NakamaBlueprintsTest.cpp b/NakamaBlueprintsTest/Source/NakamaBlueprintsTest/NakamaBlueprintsTest.cpp deleted file mode 100644 index f19bb9421..000000000 --- a/NakamaBlueprintsTest/Source/NakamaBlueprintsTest/NakamaBlueprintsTest.cpp +++ /dev/null @@ -1,6 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#include "NakamaBlueprintsTest.h" -#include "Modules/ModuleManager.h" - -IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, NakamaBlueprintsTest, "NakamaBlueprintsTest" ); diff --git a/NakamaBlueprintsTest/Source/NakamaBlueprintsTest/NakamaBlueprintsTest.h b/NakamaBlueprintsTest/Source/NakamaBlueprintsTest/NakamaBlueprintsTest.h deleted file mode 100644 index 90aad9e7e..000000000 --- a/NakamaBlueprintsTest/Source/NakamaBlueprintsTest/NakamaBlueprintsTest.h +++ /dev/null @@ -1,6 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#pragma once - -#include "CoreMinimal.h" - diff --git a/NakamaBlueprintsTest/Source/NakamaBlueprintsTest/Private/NakamaTestFunctionLibrary.cpp b/NakamaBlueprintsTest/Source/NakamaBlueprintsTest/Private/NakamaTestFunctionLibrary.cpp deleted file mode 100644 index a95c19cb4..000000000 --- a/NakamaBlueprintsTest/Source/NakamaBlueprintsTest/Private/NakamaTestFunctionLibrary.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - - -#include "NakamaTestFunctionLibrary.h" -#include "Serialization/JsonSerializer.h" -#include "Dom/JsonObject.h" - -FString UNakamaTestFunctionLibrary::GetMatchIdFromJsonString(const FString& JsonString) -{ - TSharedPtr JsonObject; - TSharedRef> JsonReader = TJsonReaderFactory::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - FString MatchId; - if (JsonObject->TryGetStringField(TEXT("match_id"), MatchId)) - { - return MatchId; - } - } - - // Return an empty string if the parsing fails or if the "match_id" field is not present - return FString(); -} - -FString UNakamaTestFunctionLibrary::GenerateTournamentJSONString(bool bAuthoritative, const FString& SortOrder, - const FString& Operator, int32 Duration, const FString& ResetSchedule, const FString& Title, - const FString& Description, int32 Category, int64 StartTime, int64 EndTime, int32 MaxSize, int32 MaxNumScore, - bool bJoinRequired) -{ - TSharedPtr JsonObject = MakeShareable(new FJsonObject); - - JsonObject->SetBoolField("authoritative", bAuthoritative); - JsonObject->SetStringField("sort_order", SortOrder); - JsonObject->SetStringField("operator", Operator); - JsonObject->SetNumberField("duration", Duration); - JsonObject->SetStringField("reset_schedule", ResetSchedule); - JsonObject->SetStringField("title", Title); - JsonObject->SetStringField("description", Description); - JsonObject->SetNumberField("category", Category); - JsonObject->SetNumberField("start_time", StartTime); - JsonObject->SetNumberField("end_time", EndTime); - JsonObject->SetNumberField("max_size", MaxSize); - JsonObject->SetNumberField("max_num_score", MaxNumScore); - JsonObject->SetBoolField("join_required", bJoinRequired); - - FString JsonString; - TSharedRef> JsonWriter = TJsonWriterFactory::Create(&JsonString); - FJsonSerializer::Serialize(JsonObject.ToSharedRef(), JsonWriter); - - return JsonString; -} - -int64 UNakamaTestFunctionLibrary::GetCurrentUnixTimestampInSeconds() -{ - return FDateTime::Now().ToUnixTimestamp(); -} - -FString UNakamaTestFunctionLibrary::GetTournamentIdFromJsonString(const FString& JsonString) -{ - TSharedPtr JsonObject; - TSharedRef> JsonReader = TJsonReaderFactory::Create(JsonString); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - FString TournamentId; - if (JsonObject->TryGetStringField("tournament_id", TournamentId)) - { - return TournamentId; - } - } - - // Return an empty string if the parsing fails or if the "tournament_id" field is not present - return FString(); -} diff --git a/NakamaBlueprintsTest/Source/NakamaBlueprintsTest/Public/NakamaTestFunctionLibrary.h b/NakamaBlueprintsTest/Source/NakamaBlueprintsTest/Public/NakamaTestFunctionLibrary.h deleted file mode 100644 index b87d30479..000000000 --- a/NakamaBlueprintsTest/Source/NakamaBlueprintsTest/Public/NakamaTestFunctionLibrary.h +++ /dev/null @@ -1,45 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#pragma once - -#include "CoreMinimal.h" -#include "Kismet/BlueprintFunctionLibrary.h" -#include "NakamaTestFunctionLibrary.generated.h" - -/** - * - */ -UCLASS() -class NAKAMABLUEPRINTSTEST_API UNakamaTestFunctionLibrary : public UBlueprintFunctionLibrary -{ - GENERATED_BODY() - -public: - - UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Nakama|Test") - static FString GetMatchIdFromJsonString(const FString& JsonString); - - UFUNCTION(BlueprintCallable, BlueprintPure, Category = "JSON Generation", meta = (DisplayName = "Generate Tournament JSON String")) - static FString GenerateTournamentJSONString( - bool bAuthoritative, - const FString& SortOrder, - const FString& Operator, - int32 Duration, - const FString& ResetSchedule, - const FString& Title, - const FString& Description, - int32 Category, - int64 StartTime, - int64 EndTime, - int32 MaxSize, - int32 MaxNumScore, - bool bJoinRequired - ); - - UFUNCTION(BlueprintPure, Category = "Nakama|Test|Utilities", meta = (DisplayName = "Get Current Unix Timestamp In Seconds")) - static int64 GetCurrentUnixTimestampInSeconds(); - - UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Nakama|Test") - static FString GetTournamentIdFromJsonString(const FString& JsonString); - -}; diff --git a/NakamaBlueprintsTest/Source/NakamaBlueprintsTestEditor.Target.cs b/NakamaBlueprintsTest/Source/NakamaBlueprintsTestEditor.Target.cs deleted file mode 100644 index 52e507f70..000000000 --- a/NakamaBlueprintsTest/Source/NakamaBlueprintsTestEditor.Target.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -using UnrealBuildTool; -using System.Collections.Generic; - -public class NakamaBlueprintsTestEditorTarget : TargetRules -{ - public NakamaBlueprintsTestEditorTarget(TargetInfo Target) : base(Target) - { - Type = TargetType.Editor; - DefaultBuildSettings = BuildSettingsVersion.V5; - IncludeOrderVersion = EngineIncludeOrderVersion.Latest; - - //bIWYU = true; - bEnforceIWYU = true; - bUseUnityBuild = false; - bForceUnityBuild = false; - bUsePCHFiles = false; - - ExtraModuleNames.AddRange( new string[] { "NakamaBlueprintsTest" } ); - } -} diff --git a/NakamaTest/Config/DefaultEditor.ini b/NakamaTest/Config/DefaultEditor.ini deleted file mode 100644 index e69de29bb..000000000 diff --git a/NakamaTest/Config/DefaultEngine.ini b/NakamaTest/Config/DefaultEngine.ini deleted file mode 100644 index 23ea44f6c..000000000 --- a/NakamaTest/Config/DefaultEngine.ini +++ /dev/null @@ -1,41 +0,0 @@ - - -[/Script/EngineSettings.GameMapsSettings] -GameInstanceClass=/Script/Engine.GameInstance -EditorStartupMap=/Game/Maps/EmptyMap.EmptyMap -GameDefaultMap=/Game/Maps/EmptyMap.EmptyMap - -[/Script/HardwareTargeting.HardwareTargetingSettings] -TargetedHardwareClass=Desktop -AppliedTargetedHardwareClass=Desktop -DefaultGraphicsPerformance=Maximum -AppliedDefaultGraphicsPerformance=Maximum - -[/Script/Engine.Engine] -+ActiveGameNameRedirects=(OldGameName="TP_Blank",NewGameName="/Script/NakamaTest") -+ActiveGameNameRedirects=(OldGameName="/Script/TP_Blank",NewGameName="/Script/NakamaTest") -+ActiveClassRedirects=(OldClassName="TP_BlankGameModeBase",NewClassName="NakamaTestGameModeBase") - -[/Script/Engine.NetworkSettings] -n.VerifyPeer=False - -[/Script/Engine.GarbageCollectionSettings] -gc.MultithreadedDestructionEnabled=True - -[SystemSettings] -r.ShaderCompiler.JobCacheDDC=1 - -[/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings] -bEnablePlugin=True -bAllowNetworkConnection=True -SecurityToken=C81F065EE841AFC328FDC5A13EB634DC -bIncludeInShipping=False -bAllowExternalStartInShipping=False -bCompileAFSProject=False -bUseCompression=False -bLogFiles=False -bReportStats=False -ConnectionType=USBOnly -bUseManualIPAddress=False -ManualIPAddress= - diff --git a/NakamaTest/Config/DefaultGame.ini b/NakamaTest/Config/DefaultGame.ini deleted file mode 100644 index ca512d5f7..000000000 --- a/NakamaTest/Config/DefaultGame.ini +++ /dev/null @@ -1,95 +0,0 @@ - - -[/Script/EngineSettings.GeneralProjectSettings] -ProjectID=1F3E7F814B6C60C48C607BBBE3392E9A - -[/Script/UnrealEd.ProjectPackagingSettings] -Build=IfProjectHasCode -BuildConfiguration=PPBC_Development -BuildTarget= -FullRebuild=False -ForDistribution=False -IncludeDebugFiles=False -BlueprintNativizationMethod=Disabled -bIncludeNativizedAssetsInProjectGeneration=False -bExcludeMonolithicEngineHeadersInNativizedCode=False -UsePakFile=True -bUseIoStore=True -bUseZenStore=False -bMakeBinaryConfig=False -bGenerateChunks=False -bGenerateNoChunks=False -bChunkHardReferencesOnly=False -bForceOneChunkPerFile=False -MaxChunkSize=0 -bBuildHttpChunkInstallData=False -HttpChunkInstallDataDirectory=(Path="") -WriteBackMetadataToAssetRegistry=Disabled -bCompressed=True -PackageCompressionFormat=Oodle -bForceUseProjectCompressionFormatIgnoreHardwareOverride=False -PackageAdditionalCompressionOptions= -PackageCompressionMethod=Kraken -PackageCompressionLevel_DebugDevelopment=4 -PackageCompressionLevel_TestShipping=5 -PackageCompressionLevel_Distribution=7 -PackageCompressionMinBytesSaved=1024 -PackageCompressionMinPercentSaved=5 -bPackageCompressionEnableDDC=False -PackageCompressionMinSizeToConsiderDDC=0 -HttpChunkInstallDataVersion= -IncludePrerequisites=True -IncludeAppLocalPrerequisites=False -bShareMaterialShaderCode=True -bDeterministicShaderCodeOrder=False -bSharedMaterialNativeLibraries=True -ApplocalPrerequisitesDirectory=(Path="") -IncludeCrashReporter=False -InternationalizationPreset=English --CulturesToStage=en -+CulturesToStage=en -LocalizationTargetCatchAllChunkId=0 -bCookAll=True -bCookMapsOnly=False -bSkipEditorContent=False -bSkipMovies=False --IniKeyDenylist=KeyStorePassword --IniKeyDenylist=KeyPassword --IniKeyDenylist=rsa.privateexp --IniKeyDenylist=rsa.modulus --IniKeyDenylist=rsa.publicexp --IniKeyDenylist=aes.key --IniKeyDenylist=SigningPublicExponent --IniKeyDenylist=SigningModulus --IniKeyDenylist=SigningPrivateExponent --IniKeyDenylist=EncryptionKey --IniKeyDenylist=DevCenterUsername --IniKeyDenylist=DevCenterPassword --IniKeyDenylist=IOSTeamID --IniKeyDenylist=SigningCertificate --IniKeyDenylist=MobileProvision --IniKeyDenylist=IniKeyDenylist --IniKeyDenylist=IniSectionDenylist -+IniKeyDenylist=KeyStorePassword -+IniKeyDenylist=KeyPassword -+IniKeyDenylist=rsa.privateexp -+IniKeyDenylist=rsa.modulus -+IniKeyDenylist=rsa.publicexp -+IniKeyDenylist=aes.key -+IniKeyDenylist=SigningPublicExponent -+IniKeyDenylist=SigningModulus -+IniKeyDenylist=SigningPrivateExponent -+IniKeyDenylist=EncryptionKey -+IniKeyDenylist=DevCenterUsername -+IniKeyDenylist=DevCenterPassword -+IniKeyDenylist=IOSTeamID -+IniKeyDenylist=SigningCertificate -+IniKeyDenylist=MobileProvision -+IniKeyDenylist=IniKeyDenylist -+IniKeyDenylist=IniSectionDenylist --IniSectionDenylist=HordeStorageServers --IniSectionDenylist=StorageServers -+IniSectionDenylist=HordeStorageServers -+IniSectionDenylist=StorageServers -+MapsToCook=(FilePath="/Game/Maps/EmptyMap") - diff --git a/NakamaTest/Config/DefaultInput.ini b/NakamaTest/Config/DefaultInput.ini deleted file mode 100644 index 1ff89cc17..000000000 --- a/NakamaTest/Config/DefaultInput.ini +++ /dev/null @@ -1,86 +0,0 @@ -[/Script/Engine.InputSettings] --AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) --AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) --AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) --AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) --AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) --AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) --AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) -+AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MouseWheelAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Gamepad_LeftTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Gamepad_RightTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Gamepad_Special_Left_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Gamepad_Special_Left_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Vive_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Vive_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Vive_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Vive_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Vive_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="Vive_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="OculusTouch_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="OculusTouch_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="OculusTouch_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="OculusTouch_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -+AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) -bAltEnterTogglesFullscreen=True -bF11TogglesFullscreen=True -bUseMouseForTouch=False -bEnableMouseSmoothing=True -bEnableFOVScaling=True -bCaptureMouseOnLaunch=True -bEnableLegacyInputScales=True -bEnableMotionControls=True -bFilterInputByPlatformUser=False -bEnableInputDeviceSubsystem=True -bShouldFlushPressedKeysOnViewportFocusLost=True -bEnableDynamicComponentInputBinding=True -bAlwaysShowTouchInterface=False -bShowConsoleOnFourFingerTap=True -bEnableGestureRecognizer=False -bUseAutocorrect=False -DefaultViewportMouseCaptureMode=CapturePermanently_IncludingInitialMouseDown -DefaultViewportMouseLockMode=LockOnCapture -FOVScale=0.011110 -DoubleClickTime=0.200000 -DefaultPlayerInputClass=/Script/EnhancedInput.EnhancedPlayerInput -DefaultInputComponentClass=/Script/EnhancedInput.EnhancedInputComponent -DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.DefaultVirtualJoysticks --ConsoleKeys=Tilde -+ConsoleKeys=Tilde - diff --git a/NakamaTest/Content/Maps/EmptyMap.umap b/NakamaTest/Content/Maps/EmptyMap.umap deleted file mode 100644 index 121906a6c..000000000 Binary files a/NakamaTest/Content/Maps/EmptyMap.umap and /dev/null differ diff --git a/NakamaTest/NakamaTest.uproject b/NakamaTest/NakamaTest.uproject deleted file mode 100644 index 97ec35541..000000000 --- a/NakamaTest/NakamaTest.uproject +++ /dev/null @@ -1,35 +0,0 @@ -{ - "FileVersion": 3, - "EngineAssociation": "{EDE4B98B-4F6E-CD0F-7410-B3B99E43CB35}", - "Category": "", - "Description": "", - "Modules": [ - { - "Name": "NakamaTest", - "Type": "Runtime", - "LoadingPhase": "Default" - } - ], - "Plugins": [ - { - "Name": "Nakama", - "Enabled": true - }, - { - "Name": "AutomationDriverTests", - "Enabled": true - }, - { - "Name": "EditorTests", - "Enabled": true - }, - { - "Name": "FunctionalTestingEditor", - "Enabled": true - }, - { - "Name": "RuntimeTests", - "Enabled": true - } - ] -} \ No newline at end of file diff --git a/NakamaTest/README.md b/NakamaTest/README.md deleted file mode 100644 index f77b1b882..000000000 --- a/NakamaTest/README.md +++ /dev/null @@ -1,61 +0,0 @@ -# Nakama Test Project -This is a test Unreal Engine project testing the core features of the Nakama Unreal Engine plugin. - -# Build and Test -Build the Nakama plugin and place it in the Plugins folder. - -Follow these instructions to package the example project: - -https://docs.unrealengine.com/4.27/en-US/Basics/Projects/Packaging/ - -Follow these instructions to package the test project: - -https://docs.unrealengine.com/4.27/en-US/Basics/Projects/Packaging/ - -### Windows - -To build the test, run: - -`$UNREAL_ENGINE/Engine/Build/BatchFiles/RunUAT.sh BuildCookRun -targetconfig=Debug -project="$NAKAMA_UNREAL/NakamaTest/NakamaTest.uproject" -noP4 -installed -utf8output -build -cook -stage -package -verbose -stdout -nohostplatform -useshellexecute` - -To run the test, run: - -```bash -./NakamaTest/Saved/StagedBuilds/Windows/NakamaTest.exe -nullrhi -verbose -ExecCmds="Automation RunTests NakamaTest.Core" -``` - -There appears to be a problem with logging to stdout on Windows, so you'll need to obtain the test logs from `NakamaTest\Saved\StagedBuilds\Windows\NakamaTest\Saved\Logs\NakamaTest.log`. - -### Mac - -To build the test, run: - -`$UNREAL_ENGINE/Engine/Build/BatchFiles/RunUAT.sh BuildCookRun -project="$NAKAMA_UNREAL/NakamaTest/NakamaTest.uproject" -targetConfig=Debug -noP4 -platform=Mac -Architecture_Mac=arm64 -targetconfig=Debug -installed -unrealexe=UnrealEditor -utf8output -build -cook -stage -package -verbose` - -To run the test, run: - -`./NakamaTest/Binaries/Mac/NakamaTest.app/Contents/MacOS/NakamaTest -nullrhi -stdout -forcelogflush -ExecCmds="Automation RunTests NakamaTest.Core"` - -You can pass `List` instead to the `Automation` command to view all tests. It will include engine tests. - -You can also pass `-LogCmds=" verbose"` where `` is one of those defined by `DEFINE_LOG_CATEGORY`. - -### Linux - -To build the test, run: - -`$UNREAL_ENGINE/Engine/Build/BatchFiles/RunUAT.sh BuildCookRun -project="$NAKAMA_UNREAL/NakamaTest/NakamaTest.uproject" -clientconfig=Test -noP4 -platform=Linux -targetconfig=Debug -installed -utf8output -build -cook -stage -package -verbose` - -To run the test: - -`NakamaTest/Binaries/Linux/NakamaTest-Linux-Test -nullrhi -stdout -forcelogflush -ExecCmds="Automation RunTests NakamaTest.Core"` - -To debug with LLDB on Mac: - -`lldb ${NAKAMA_UNREAL}/NakamaTest/Binaries/Mac/NakamaTest-Mac-Test.app/Contents/MacOS/NakamaTest-Mac-Test` - -Set the startup args inside the lldb shell: - -`settings set -- target.run-args -nullrhi -stdout -forcelogflush -ExecCmds="Automation RunTests NakamaTest.Core"` - -Then call `run`. diff --git a/NakamaTest/Source/NakamaTest.Target.cs b/NakamaTest/Source/NakamaTest.Target.cs deleted file mode 100644 index 52777b3b1..000000000 --- a/NakamaTest/Source/NakamaTest.Target.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -using UnrealBuildTool; -using System.Collections.Generic; - -public class NakamaTestTarget : TargetRules -{ - public NakamaTestTarget( TargetInfo Target) : base(Target) - { - Type = TargetType.Game; - DefaultBuildSettings = BuildSettingsVersion.Latest; - ExtraModuleNames.AddRange( new string[] { "NakamaTest" } ); - bUseLoggingInShipping = true; - } -} diff --git a/NakamaTest/Source/NakamaTest/NakamaTest.Build.cs b/NakamaTest/Source/NakamaTest/NakamaTest.Build.cs deleted file mode 100644 index d70e2549e..000000000 --- a/NakamaTest/Source/NakamaTest/NakamaTest.Build.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2023 The Nakama Authors. - -using System; -using System.IO; -using UnrealBuildTool; - -public class NakamaTest : ModuleRules -{ - public NakamaTest(ReadOnlyTargetRules Target) : base(Target) - { - PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - PrivateDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "WebSockets" }); - } -} diff --git a/NakamaTest/Source/NakamaTest/NakamaTest.cpp b/NakamaTest/Source/NakamaTest/NakamaTest.cpp deleted file mode 100644 index bacdcdea5..000000000 --- a/NakamaTest/Source/NakamaTest/NakamaTest.cpp +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2023 The Nakama Authors. - -#include "NakamaTest.h" -#include "CoreMinimal.h" - -IMPLEMENT_PRIMARY_GAME_MODULE(FDefaultGameModuleImpl, NakamaTest, "NakamaTest"); diff --git a/NakamaTest/Source/NakamaTest/NakamaTest.h b/NakamaTest/Source/NakamaTest/NakamaTest.h deleted file mode 100644 index 824215b3d..000000000 --- a/NakamaTest/Source/NakamaTest/NakamaTest.h +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2023 The Nakama Authors. - -#pragma once - -#include "CoreMinimal.h" - -class NakamaTest : public IModuleInterface -{ -public: - - /** IModuleInterface implementation */ - virtual void StartupModule() override; - virtual void ShutdownModule() override; -}; diff --git a/NakamaTest/Source/NakamaTestEditor.Target.cs b/NakamaTest/Source/NakamaTestEditor.Target.cs deleted file mode 100644 index ec4cbf04e..000000000 --- a/NakamaTest/Source/NakamaTestEditor.Target.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2023 The Nakama Authors. - -using UnrealBuildTool; -using System.Collections.Generic; - -public class NakamaTestEditorTarget : TargetRules -{ - public NakamaTestEditorTarget( TargetInfo Target) : base(Target) - { - Type = TargetType.Editor; - DefaultBuildSettings = BuildSettingsVersion.Latest; - ExtraModuleNames.AddRange( new string[] { "NakamaTest" } ); - } -} \ No newline at end of file diff --git a/README.md b/README.md index d1eda9935..b713ff472 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,16 @@ - Nakama Unreal +Nakama Unreal ============= -[Nakama](https://github.com/heroiclabs/nakama) is an open-source server designed to power modern games and apps. Features include user accounts, chat, social, matchmaker, realtime multiplayer, and much [more](https://heroiclabs.com). +[Nakama](https://github.com/heroiclabs/nakama) is an open-source server designed to power modern games and apps. Features include user accounts, chat, social, matchmaker, realtime multiplayer, and much [more](https://heroiclabs.com/nakama). -This client implements the full API and socket options with the server. It's written in C++ with minimal dependencies to support Unreal 4 and 5. +This client implements the full Nakama API and socket options with the server, as well as the [Satori](https://heroiclabs.com/docs/satori/concepts/introduction/index.html) API. +It's written in C++ with minimal dependencies to support Unreal 4 and 5. -If you experience any issues with the client, it can be useful to enable debug logs and [open an issue](https://github.com/heroiclabs/nakama-cpp/issues). +If you experience any issues with the client, it can be useful to enable debug logs and [open an issue](https://github.com/heroiclabs/nakama-unreal/issues). -Full documentation is online - https://heroiclabs.com/docs +The plugin documentation including Getting Started and Code Examples is online at [https://heroiclabs.com/docs/unreal-client-guide/](https://heroiclabs.com/docs/unreal-client-guide/). + +For all other docs, check out [https://heroiclabs.com/docs](https://heroiclabs.com/docs). # General Information @@ -15,20 +18,18 @@ This plugin can also be used for programmers who like C++ or Blueprints. All var The plugin is divided into three modules which can be pulled in depending on your needs. -- `NakamaUnreal` The recommended C++-based module for using Nakama in UnrealEngine. This integrates with Unreal's native types and UObjects. +- `Nakama` High-level Nakama API: The recommended C++-based module for using Nakama in UnrealEngine. +- `NakamaApi` Low-level Nakama API: Required for the `Nakama` module, but not recommended for direct use. - `NakamaBlueprints` For users who would prefer to use Blueprints in their project. - -Clients are referred in this documentation as **Realtime Client** and **Default Client** in which the realtime client is the socket and the default client is using REST API to communicate with Nakama. - # Getting Started -You'll need to setup the server and database before you can connect with the client. The simplest way is to use Docker but have a look at the [server documentation](https://github.com/heroiclabs/nakama#getting-started) for other options. +You'll need to set up the server and database before you can connect with the client. The simplest way is to use Docker but have a look at the [server documentation](https://github.com/heroiclabs/nakama#getting-started) for other options. To get started using Nakama in Unreal you will need the following: 1. Install and run the servers. Follow these [instructions](https://heroiclabs.com/docs/nakama/getting-started/install/docker/). -2. A copy of the core [Nakama Unreal](https://github.com/heroiclabs/nakama-unreal) plugin +2. A copy of the [Nakama Client](https://github.com/heroiclabs/nakama-unreal/releases) plugin 3. [Unreal Engine](https://www.unrealengine.com) 4.25 or greater or 5. 4. A compiler for the platform you are developing on, such as [Visual Studio](https://www.visualstudio.com/vs/community/) on Windows or [XCode](https://developer.apple.com/xcode/download/) on OSX. @@ -36,321 +37,129 @@ Also, please ensure your Unreal project is a C++ project. If it is Blueprint on ## Installing the Plugin -To use Nakama in your Unreal project, you'll need to download our plugin from the Unreal Marketplace or copy the nakama-unreal files you downloaded into the appropriate place. To do the latter: +To use Nakama in your Unreal project, you'll need to copy the Nakama Client files you downloaded into the appropriate place: 1. Open your Unreal project folder (for example, `C:\\MyUnrealProject\\`) in Explorer or Finder. 2. If one does not already exist, create a `Plugins` folder here. -3. Copy the `Nakama` folder from [nakama-unreal](https://github.com/heroiclabs/nakama-unreal/releases) and put it in the `Plugins` folder - -**Optionally:** you can put the plugins inside your Unreal Engine plugin folder (for example, `C:\Program Files\Epic Games\UE_4.26\Engine\Plugins\Marketplace`) to use the plugin on multiple projects. - -At this point, you are done. Restart Unreal. After it compiles things, open Edit->Plugins and scroll to the bottom. If all went well, you should see HeroicLabs.Nakama listed as a plugin. - - **Clients** - -You have to decide where you want to create and keep record of these clients. - - **Sessions** +3. Copy the `Nakama` folder from the root of the downloaded release zip and put it in the `Plugins` folder. - Sessions are portable UObjects that contain a session pointer and a struct with the actual data in the session, like the tokens, user data, expiry information an so on. There are also some utility functions provided with the Session objects, like getting a specific variable or restoring the session. +**Optionally:** you can put the plugin inside your Unreal Engine plugin folder (for example, `C:\Program Files\Epic Games\UE_4.26\Engine\Plugins\`) to use the plugin across multiple projects. - **Tick System** - -Normally you would have to handle ticking on a C++ basis, luckily this is done automatically under the hood in this plugin after you have created the client. When you create the Client you can define a Tick Interval, by default this is set to 0, which means it will tick every frame, if you want it to tick every 50ms you have to set it as 0.05, to make it tick every second this number would be 1. - -# Getting Started with NakamaUnreal (C++ with Unreal Types) - -Below is a simple example of setting up a default client, authenticating, setting up a realtime client and joining a chat room. In the example we will put everything in a empty AActor class that is placed in the level. - -Remember to add NakamaUnreal to your Private Dependencies under your project Build.cs file. For example: +Remember to add the modules to the dependencies under your project's Build.cs file. For example: ```cs - -PrivateDependencyModuleNames.AddRange(new string[] { "NakamaUnreal" }); - -``` - -Starting with the headers public variables, we are using a blank actor that will be placed in the scene in this example. Unreal Engine uses a reflection system that provides metadata about its classes and allows for advanced features like Blueprint/C++ communication, serialization, and more. -When working with Nakama objects, or any UObject-derived class, it's crucial to mark them using the Unreal reflection system. This is done using macros such as UFUNCTION(), and UPROPERTY(). - -```cpp -UPROPERTY() -UNakamaClient* NakamaClient; - -UPROPERTY() -UNakamaRealtimeClient* NakamaRealtimeClient; - -UPROPERTY() -UNakamaSession* UserSession; - -UFUNCTION() -void OnAuthenticationSuccess(UNakamaSession* LoginData); - -UFUNCTION() -void OnAuthenticationError(const FNakamaError& Error); - -UFUNCTION() -void OnRealtimeClientConnectSuccess(); - -UFUNCTION() -void OnRealtimeClientConnectError(const FNakamaRtError& ErrorData); - -// Initialize client and authenticate here -virtual void BeginPlay() override; +PublicDependencyModuleNames.AddRange(new string[] { "Nakama", "NakamaApi", "NakamaBlueprints" }); ``` -For instance, if you want a Nakama object to be available for manipulation within the Blueprint Editor, you'd mark it with UPROPERTY(). +At this point, you are done. Restart Unreal. After it compiles everything, open Edit->Plugins, then search for "Nakama". If all went well, you should see the Nakama plugin, make sure to enable it (this may prompt an editor restart). -```cpp -UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Nakama") -UNakamaClient* NakamaClientInstance; -``` +# Example Usage -By using the BlueprintReadWrite specifier, the NakamaClientInstance variable becomes both readable and writable in Blueprints. +There's a variety of ways to authenticate with the server. Authentication can create a user if they don't already exist with those credentials. It's also easy to authenticate with a social profile from Google Play Games, Facebook, Game Center, etc. -Then inside the Begin Play we setup default client, authenticate and bind delegates. +1. Create the Nakama Client Config using your desired connection credentials: ```cpp -// Called when the game starts or when spawned -void AMyActor::BeginPlay() -{ - Super::BeginPlay(); - - // Default Client Parameters - FString ServerKey = TEXT("defaultkey"); - FString Host = TEXT("127.0.0.1"); - int32 Port = 7350; - bool bUseSSL = false; - bool bEnableDebug = true; - - // Setup Default Client - NakamaClient = UNakamaClient::CreateDefaultClient(ServerKey, Host, Port, bUseSSL, bEnableDebug); - - // Authentication Parameters - FString Email = TEXT("debug@mail.com"); - FString Password = TEXT("verysecretpassword"); - FString Username = TEXT("debug-user"); - TMap Variables; - - // Setup Delegates of same type and bind them to local functions - FOnAuthUpdate AuthenticationSuccessDelegate; - AuthenticationSuccessDelegate.AddDynamic(this, &AMyActor::OnAuthenticationSuccess); +const FString ServerKey = TEXT("defaultkey"); +const FString Host = TEXT("127.0.0.1"); +constexpr int32 Port = 7350; +constexpr bool bUseSSL = false; - FOnError AuthenticationErrorDelegate; - AuthenticationErrorDelegate.AddDynamic(this, &AMyActor::OnAuthenticationError); - - NakamaClient->AuthenticateEmail(Email, Password, Username, true, Variables, AuthenticationSuccessDelegate, AuthenticationErrorDelegate); -} +const FNakamaClientConfig ClientConfig = FNakamaClientConfig{ServerKey, Host, Port, bUseSSL}; ``` -Then the response of the authentication callbacks +2. Authenticate using the previously created Nakama Client Config along with the device's Id, desired username, and whether to create a new account if this user doesn't exist: ```cpp -void AMyActor::OnAuthenticationSuccess(UNakamaSession* LoginData) -{ - if(GEngine) GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Green, FString::Printf(TEXT("Authenticated As %s"), *LoginData->SessionData.Username)); - - UserSession = LoginData; - - // Setup Delegates of same type and bind them to local functions - FOnRealtimeClientConnected ConnectionSuccessDelegate; - ConnectionSuccessDelegate.AddDynamic(this, &AMyActor::OnRealtimeClientConnectSuccess); - - FOnRealtimeClientConnectionError ConnectionErrorDelegate; - ConnectionErrorDelegate.AddDynamic(this, &AMyActor::OnRealtimeClientConnectError); - - // This is our realtime client (socket) ready to use - NakamaRealtimeClient = NakamaClient->SetupRealtimeClient(); - - // Remember to Connect - bool bCreateStatus = true; - NakamaRealtimeClient->Connect(UserSession, bCreateStatus, ConnectionSuccessDelegate, ConnectionErrorDelegate); - -} +FNakamaSession Session; -void AMyActor::OnAuthenticationError(const FNakamaError& Error) -{ - if(GEngine) GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, FString::Printf(TEXT("Failed to Authenticate: %s"), *Error.Message)); -} -``` - -And finally the overridden realtime client setup callback, you can now use the realtime client. - -```cpp -void AMyActor::OnRealtimeClientConnectSuccess() -{ - if(GEngine) GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Green, FString(TEXT("Socket Setup Success!"))); +const FString DeviceId = TEXT(""); +const FString Username = TEXT("NewUser"); +constexpr bool bCreate = true; - // Example of Joining Chat without callbacks - NakamaRealtimeClient->JoinChat("Heroes", ENakamaChannelType::ROOM, true, false, {}, {}); +Nakama::AuthenticateDevice(ClientConfig, bCreate, Username, DeviceId).Next( + [&Session](const FNakamaSessionResult& AuthenticateResult) + { + if (AuthenticateResult.bIsError) + { + UE_LOG(LogTemp, Error, TEXT("Failed to log in: %s"), *AuthenticateResult.Error.Message); + return; + } -} + Session = AuthenticateResult.Value; -void AMyActor::OnRealtimeClientConnectError(const FNakamaRtError& ErrorData) -{ - if(GEngine) GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Red, FString(TEXT("Socket Setup Failed!"))); -} + UE_LOG(LogTemp, Log, TEXT("Log in successful: %s"), *AuthenticateResult.Value.Username); + }); ``` -If you setup everything correctly, create a blueprint version of this actor and place it in the level you will see on-screen messages saying you authenticated, your username and then socket connected message. - -# Delegates and Lambdas in Nakama Unreal - -The latest Nakama Unreal release offers the flexibility to use either `Dynamic Multicast Delegates` or `Lambdas (TFunctions)` for handling functions and events. Here's a brief comparison and guidelines on how to use them: - -## Dynamic Multicast Delegates: -- **Binding**: Bound using `AddDynamic`. -- **Usage**: Suited for scenarios where multiple bindings are needed. - -## Lambdas (TFunctions): -- **Binding**: Defined in-line and don't require an external function. -- **Usage**: Convenient for one-off or temporary handlers. Note that a lambda can only be bound to one place at a time. - -### How to Choose? -Provide your preferred callback type (either a `delegate` or a `lambda`) into the `Success` and `Error` parameters of the relevant Nakama function. - ---- - -## Example: Using Lambdas - -Here's a demonstration of using `lambdas` as an alternative to `delegates`: +3. Once the user is authenticated, you can start making API requests by passing in both the Nakama Client Config, and the session that was received from authentication: ```cpp -// Define success callback with a lambda -auto successCallback = [&](UNakamaSession* session) -{ - UE_LOG(LogTemp, Warning, TEXT("Session Token: %s"), *Session->GetAuthToken()); - UE_LOG(LogTemp, Warning, TEXT("Username: %s"), *Session->GetUsername()); -}; - -// Define error callback with a lambda -auto errorCallback = [&](const FNakamaError& Error) +Nakama::GetAccount(ClientConfig, Session).Next([](const FNakamaAccountResult& AccountResult) { - UE_LOG(LogTemp, Warning, TEXT("Error Code: %d"), Error.Code); -}; - -// Execute the AuthenticateEmail function with lambdas -Client->AuthenticateEmail(TEXT("debug@mail.com"), TEXT("verysecretpassword"), TEXT("debug-user"), true, {}, successCallback, errorCallback); -``` - - - -# Working with Realtime Events - -Upon initializing your Realtime Client, it's essential to establish event listeners for critical in-game events, ranging from channel messages and notifications to party interactions. Nakama Unreal provides flexibility by allowing both lambdas and delegates for this purpose. - -```cpp -// Start by creating a Realtime Client: -UNakamaRealtimeClient* Socket = NakamaClient->SetupRealtimeClient(); - -// When using delegates, you need to declare functions that match the delegate's signature: -Socket->ChannelMessageReceived.AddDynamic(this, &ANakamaActor::OnChannelMessageReceived); -Socket->NotificationReceived.AddDynamic(this, &ANakamaActor::OnNotificationReceived); - -// Lambdas offer a concise way to define event handlers directly in-line: -// Note: A lambda can be bound only once. -Socket->SetChannelMessageCallback( [](const FNakamaChannelMessage& ChannelMessage) -{ - UE_LOG(LogTemp, Warning, TEXT("Channel Message: %s"), *ChannelMessage.Content); + if (AccountResult.bIsError) + { + UE_LOG(LogTemp, Error, TEXT("An error occurred: %d:%s"), AccountResult.Error.Code, + *AccountResult.Error.Message); + } }); - -Socket->SetNotificationsCallback( [](const FNakamaNotificationList& NotificationList) -{ - UE_LOG(LogTemp, Warning, TEXT("Notifications: %d"), NotificationList.Notifications.Num()); - for (auto& Notification : NotificationList.Notifications) - { - UE_LOG(LogTemp, Warning, TEXT("Notification: %s"), *Notification.Content); - } -}); - -// Establish a connection to start receiving events. -// Optional success and error callbacks (either lambdas or delegates) can be provided: -Socket->Connect(Session, true); -``` -Function implementations might look something like this: - - -```cpp -void ANakamaActor::OnChannelMessageReceived(const FNakamaChannelMessage& ChannelMessage) -{ - UE_LOG(LogTemp, Warning, TEXT("Channel Message: %s"), *ChannelMessage.Content); -} - -void ANakamaActor::OnNotificationReceived(const FNakamaNotificationList& Notifications) -{ - for (auto NotificationData : Notifications.Notifications) - { - UE_LOG(LogTemp, Warning, TEXT("Notification: %s"), *NotificationData.Content); - } -} ``` # Getting Started with `NakamaBlueprints` In this section you will learn how to manually create and manage Nakama clients that are provided by this plugin, entirely in blueprints. -It is up to the you where to create and store references to the clients, this could be done in any actor, component, character, gamemode etc. A good place to put the clients is in the Player Controller or the Game Instance. - +It is up to the you where to create and store references to the clients, this could be done in any actor, component, character, gamemode etc. A good place to put the clients is in the Game Instance. -Start by adding the **Create Default Client** node which is a part fo the Nakama Library. +Start by adding the Make Nakama Client Config node which is a part fo the Nakama Library. ![image createclient-1](./images/Client-1.png) -It is good practice to promote the clients to variables so you can access them other places in your blueprint graphs. - +It is good practice to promote the clients to variables so you can access them in other places in your blueprint graphs. ![image createclient-2](./images/Client-2.png) -You are now ready to authenticate, using one of the many provided Nakama authentication types, in this example we will be authenticating with email and password, normally you would setup a Widget Blueprint and pass the input from the UI into the authentication node, and authenticate by pressing a login button. +You are now ready to authenticate using one of the many provided Nakama authentication types, in this example we will be authenticating with email and password. Normally you would setup a Widget Blueprint and pass the input from the UI into the authentication node, and then authenticate by pressing a log-in button. ![image createclient-3](./images/Client-3.png) - -As you can see, this returns a session object that will be passed into other functions, make sure to promote the session object for later use. With this plugin you may have multiple sessions per unreal instance, it is up to you to keep record and decide how you want to use the sessions. The variables pin also needs to be connected, but you can leave the string map empty if you don't want to use custom variables. +As you can see, this returns a session object that will be passed into other functions, make sure to promote the session object for later use. With this plugin you may have multiple sessions per unreal instance, it is up to you to keep record and decide how you want to use the sessions. ![image createclient-4](./images/Client-4.png) +## Setting up the Real-time Listeners and Binding to Events -After you have created a default client you'll be able to setup one or more realtime clients (sockets) that interact with the server. - -Drag out from the **NakamaClient** that you created earlier, and call the **Setup Realtime Client** function. - - -![image createclient-4](./images/Client-5.png) - -Remember to provide the user session from the successful authentication earlier, then bind custom events to the success and error callbacks. The Realtime Client will be returned from this node, and is ready to be used to communicate with the Nakama server. You can now use features such as Chat and Matchmaker. - -![image createclient-5](./images/Client-6.png) - +After you have authenticated and have received an auth token, you'll be able to set up a socket connection with the server if you wish to use real-time features. -# Setting up Listeners and Binding to Events - -After creating your Realtime Client you are ready to bind to its' events +First, create the Nakama Rt Connection object and store the handle for use later. ![image binding-1](./images/Bindings-1.png) -After setting up your specific listeners you are ready to bind to the callbacks. +Using the Rt Handle, bind to the `ConnectCompleted` and `Closed` events to handle the connection lifecycle. Then you can call **Connect** to establish the connection by passing in the params, including the auth token from the session that you stored earlier. In this screenshot, we have split the Params struct pin to keep the nodes tidy. ![image binding-2](./images/Bindings-2.png) -Create a custom event and give it a meaningful name. +Using the Rt Handle, you are also able to bind more events to receive updates from various real-time systems. These can be bound right before connecting to make sure that the callbacks are ready immediately upon connecting the socket. ![image binding-3](./images/Bindings-3.png) - In the example below, we setup a listener for notifications, then we bind to the event, loop over notifications and print them as debug strings on the screen. +For this example, we'll be using the `Notifications` callback. Create a custom event and give it a meaningful name. - ![image binding-4](./images/Bindings-4.png) +![image binding-4](./images/Bindings-4.png) + + In the example below, we setup a listener for notifications, then we bind to the event, loop over notifications and print them as debug strings on the screen. -In the next example we listen to matchmaker matched event then bind to it and handle the response by joining a match with the returned token which then returns a Match including the match id, presences, label and so on. + ![image binding-5](./images/Bindings-5.png) -![image binding-5](./images/Bindings-5.png) +In the next example we listen to matchmaker matched event then bind to it and handle the response by joining a match with the returned token or match id, which then returns a Match including the match id, presences, label and so on. +![image binding-6](./images/Bindings-6.png) -# Session Management +## Session Management -As described earlier, when you authenticate with Nakama you'll receive a Session Object which you should store somewhere easily accessible in your Blueprints, since many of the nodes in this plugin require a session object as input to function. +As described earlier, when you authenticate with Nakama you'll receive a Session struct which you should store somewhere easily accessible in your Blueprints, since many of the nodes in this plugin require a session as an input to function. -The session object contains the actual session reference and also a structure with the data readable in blueprints. Drag out from the session and get the session data. +The session struct contains data readable in blueprints. Drag out from the session and break the struct. ![image sessions-1](./images/Sessions-1.png) @@ -358,184 +167,193 @@ There are also some additional session management methods like restoring the ses ![image sessions-2](./images/Sessions-2.png) +![image sessions-3](./images/Sessions-3.png) It is recommended to store the auth token from the session and check at startup if it has expired. If the token has expired you must reauthenticate. The expiry time of the token can be changed as a setting in the server. +## Requests -# Requests - -The clients include lots of builtin APIs for various features of the game server. These can be accessed with the async methods, which returns Success and Error callbacks. They can also call custom logic as RPC functions on the server. All requests are sent with a session object which authorizes the clients. +The client includes a lot of built-in APIs for various features of the game server. These can be accessed with the async methods, which returns Success and Error callbacks. They can also call custom logic as RPC functions on the server. All requests are sent with a session struct which authorizes the clients. ![image requests](./images/Requests.png) -The RPC node can be used to run specific functionality on the server, the payload should be in JSON format. +The RpcFunc node can be used to run specific functionality on the server, the payload should be in JSON format. ![image rpc](./images/RPC.png) -Moving forward, you should be ready to use all functionality of Nakama to power your awesome Unreal Engine built game or app, done entirely in Blueprints. Please refer to the official documentation at https://heroiclabs.com/docs even though some of the documentation is described in C++ the same core functionality applies to the Blueprint implementation. - - -# Cursors +## Cursors Cursors are used to add paging functionality to certain nodes, like friends list and leaderboard records. When there is more data to be retrieved, a cursor string will be returned in the Success callback. You can store this cursor as a string and use it later, like when a person clicks a "more" button or use it immediately to fetch more data. Look at the example below. ![image cursors](./images/Cursors.png) -# Logging -By default, logging is disabled. However, when creating a Client, you have the option to `Enable Debug`, allowing logs to be written using the debug log category. You can also manually control logging. +# Satori -**Enabling Logging from Blueprints:** -![image logging](./images/Logging.png) +Satori is a liveops server for games that powers actionable analytics, A/B testing, and remote configuration. Use the Satori Unreal Client to communicate with Satori from within your Unreal game. -**Enabling Logging from C++** +Use the Satori Unreal Client with either Blueprints or C++. -To enable logging through C++, include the following header file: +Full documentation is online - https://heroiclabs.com/docs/satori/client-libraries/unreal -```cpp -#include "NakamaLogger.h" -``` -Subsequently, to toggle logging, use: +## Installing the Plugin -```cpp -UNakamaLogger::EnableLogging(true); -``` -To set the log level, use: +To use Satori in your Unreal project, you'll need to copy the Satori Client files you downloaded into the appropriate place: +1. Open your Unreal project folder (for example, `C:\\MyUnrealProject\\`) in Explorer or Finder. +2. If one does not already exist, create a `Plugins` folder here. +3. Copy the `Satori` folder from the root of the downloaded release zip and put it in the `Plugins` folder. -```cpp -UNakamaLogger::SetLogLevel(ENakamaLogLevel::Debug); -``` +**Optionally:** you can put the plugin inside your Unreal Engine plugin folder (for example, `C:\Program Files\Epic Games\UE_4.26\Engine\Plugins\`) to use the plugin across multiple projects. -Log categories are as follows: +Remember to add the modules to the dependencies under your project's Build.cs file. For example: -- `Debug` writes all logs. +```cs +PublicDependencyModuleNames.AddRange(new string[] { "Satori", "SatoriApi", "SatoriBlueprints" }); +``` -- `Info` writes logs with `Info`, `Warn`, `Error` and `Fatal` logging level. +At this point, you are done. Restart Unreal. After it compiles everything, open Edit->Plugins, then search for "Satori". If all went well, you should see the Satori plugin, make sure to enable it (this may prompt an editor restart). -- `Warn` writes logs with `Warn`, `Error` and `Fatal` logging level. +# Example Usage -- `Error` writes logs with `Error` and `Fatal` logging level. +1. Create the Satori Client Config using your desired connection credentials: -- `Fatal` writes only logs with `Fatal` logging level. +```cpp +const FString ServerKey = TEXT("apiKey"); +const FString Host = TEXT("127.0.0.1"); +constexpr int32 Port = 7450; +constexpr bool bUseSSL = false -# Running Tests -This repository includes a test-suite to test the various features of Nakama for Unreal, tests can be run in the Editor, from Command-Line and there is eve a `BlueprintsTest` project with separate documentation if you would like to run the same tests in Blueprints. +const FSatoriClientConfig ClientConfig = FSatoriClientConfig{ServerKey, Host, Port, bUseSSL}; +``` -## Using the Editor -- Create a blank C++ Project -- Add `Nakama` plugin to `Plugins` directory within the project -- Build and open the project in the Unreal Editor -- Be sure to enable the `Functional Testing Editor` Plugin in Unreal under `Edit -> Plugins` then restart the editor -- Navigate to `Tool -> TestAutomation` -- Select your device on the left hand side, navigate to the Automation tab then choose which Nakama Tests you would like to run and click `Start Tests` -- Logs will be provided with the result of the tests +2. Authenticate using the previously created Satori Client Config along with a unique ID for the user: -![image testing](./images/Testing-1.png) +```cpp +FSatoriSession Session; -## Using Command-Line: +const FString Id = FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); +constexpr bool bNoSession = false; +const TMap Default = {}; +const TMap Custom = {}; -The tests can be run in both packaged and using the command-line version of the Unreal Editor. +Satori::Authenticate(ClientConfig, Id, bNoSession, Default, Custom).Next( + [&Session](const FSatoriSessionResult& SessionResult) + { + if (SessionResult.bIsError) + { + UE_LOG(LogTemp, Error, TEXT("Error authenticating: {%d} %s"), SessionResult.Error.Code, + *SessionResult.Error.Message); + return; + } -For all Command-Line based tests start by doing these steps: -- Create a blank C++ Unreal Engine Project with your desired editor version, this documentation focuses on Unreal Engine 5.0+ -- Place the `Nakama` plugin in the `Plugins` directory within the project -- Enable the `Nakama` Plugin for the project -- You can either build the project like normal, or use the build commands below + Session = SessionResult.Value; -**Windows - Editor:** + UE_LOG(LogTemp, Log, TEXT("Session has token: %s"), *Session.Token); + UE_LOG(LogTemp, Log, TEXT("Session has refresh token: %s"), *Session.RefreshToken); + }); -To build the test, run: -```bash -"\Engine\Build\BatchFiles\Build.bat" Editor Win64 Development -Project="\.uproject" -``` -To run the test, run: -```bash -"\Engine\Binaries\Win64\UnrealEditor-Cmd.exe" "\.uproject" -ExecCmds="Automation RunTests " -log -NullRHI -verbose -unattended -ReportOutputPath="" ``` -If you want to run all tests replace `` with `Nakama.Base`, if you specify `ReportOutputPath` you will receive an overview json logfile, logs will be stored within the `Saved/Logs` directory. +3. Once the user is authenticated, you can start making API requests by passing in both the Satori Client Config, and the session that was received from authentication: -**Windows - Packaged:** +```cpp +Satori::ListProperties(ClientConfig, Session).Next( + [](const FSatoriPropertiesResult& ListPropertiesResult) + { + if (ListPropertiesResult.bIsError) + { + UE_LOG(LogTemp, Error, TEXT("Error listing properties: {%d} %s"), + ListPropertiesResult.Error.Code, + *ListPropertiesResult.Error.Message); + return; + } + + for (TTuple Property : ListPropertiesResult.Value.Default) + { + UE_LOG(LogTemp, Log, TEXT("Default Property found: %s with value %s"), + *Property.Key, *Property.Value); + } + + for (TTuple Property : ListPropertiesResult.Value.Custom) + { + UE_LOG(LogTemp, Log, TEXT("Custom Property found: %s with value %s"), *Property.Key, + *Property.Value); + } + + for (TTuple Property : ListPropertiesResult.Value.Computed) + { + UE_LOG(LogTemp, Log, TEXT("Computed Property found: %s with value %s"), + *Property.Key, *Property.Value); + } + }); -To build the test, run: -```bash -"/Engine/Build/BatchFiles/RunUAT.sh" BuildCookRun -targetconfig=Debug -project="\.uproject" -noP4 -installed -utf8output -build -cook -stage -package -verbose -stdout -nohostplatform -useshellexecute -``` -To run the test, run: -```bash -.//Saved/StagedBuilds/Windows/.exe -nullrhi -verbose -ExecCmds="Automation RunTests Nakama.Base" -log ``` -**Mac - Packaged:** +# Development -To build the test, run: -```bash -"/Engine/Build/BatchFiles/RunUAT.sh" BuildCookRun -project="\.uproject" -targetConfig=Debug -noP4 -platform=Mac -Architecture_Mac=arm64 -targetconfig=Debug -installed -unrealexe=UnrealEditor -utf8output -build -cook -stage -package -verbose -``` - -To run the test, run: -```bash -.//Binaries/Mac/.app/Contents/MacOS/ -nullrhi -stdout -forcelogflush -ExecCmds="Automation RunTests Nakama.Base" -log -``` +## Task Automation -**Linux - Packaged:** +This project uses [Taskfile](https://taskfile.dev/) to automate common development tasks. Taskfile provides a consistent interface for building, testing, and deploying the project across different platforms and environments. -To build the test, run: -```bash -"/Engine/Build/BatchFiles/RunUAT.sh" BuildCookRun -project="\.uproject" -clientconfig=Test -noP4 -platform=Linux -targetconfig=Debug -installed -utf8output -build -cook -stage -package -verbose -``` -To run the test, run: -```bash -.//Binaries/Linux/ -nullrhi -stdout -forcelogflush -ExecCmds="Automation RunTests Nakama.Base" -log -``` +### Why Taskfile? -**Passing Parameters** +- **Consistency**: GitHub Actions and local development use the same tasks, ensuring what works locally will work in CI/CD +- **Easy Verification**: You can run the exact same commands locally that run in CI/CD pipelines +- **Cross-Platform**: Works consistently on macOS, Linux, and Windows +- **Simple Syntax**: YAML-based configuration that's easy to read and maintain +- **Heroic Standard**: Consistent across Heroic products -Parameters such as hostname, port and server key can be passed as command-line arguments, here is an example: +### Installation -```bash --hostname="127.0.0.1" -port=7350 -serverkey="defaultkey" -serverhttpkey="defaulthttpkey" -timeout=45 -useSSL +#### macOS +```shell +brew install go-task ``` -# Additional Information +Or using the install script: +```shell +sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin +``` -Some of the features of this plugin depend on JSON, such as sending chat messages and storing data using storage objects. It is therefore recommended that if you use purely blueprints that you find a plugin that can construct and parse Json strings such as [VaRest](https://github.com/ufna/VaRest). +#### Windows +```powershell +# Using Chocolatey +choco install go-task -When you are developing within the editor, you can run multiple unreal instances using PIE (Play In Editor) and are able to authenticate using separate accounts, which is very useful when testing functionalities that require multiple players, such as chat, realtime multiplayer, matchmaking and so on. +# Using Scoop +scoop install task -In the installation part of this documentation we add C++ to our project, this is only to be able to compile the plugin, you can still use only Blueprints. +# Using Winget +winget install Task.Task +``` +#### Linux +```shell +# Using Snap +snap install task --classic -# Example Project +# Using install script +sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin +``` -Provided with this plugin is an [example project](https://github.com/heroiclabs/nakama-unreal/tree/master/NakamaBlueprintsDemo) which is developed in pure Blueprints that showcases almost all of the Nakama core features. +## Available Tasks +Run `task --list` to see all available tasks. -![image example-1](./images/ExampleProject-1.png) +### Testing -![image example-2](./images/ExampleProject-2.png) +The client provides a test suite that comes as an Unreal Project that can be found under `IntegrationTests/`. +To build and run the test project in headless mode, you can issue this command: -![image example-3](./images/ExampleProject-3.png) +```powershell +task test +``` ## Contribute -The development roadmap is managed as GitHub issues and pull requests are welcome. If you're interested to enhance the code please open an issue to discuss the changes or drop in and discuss it in the [community forum](https://forum.heroiclabs.com). - -## Source Builds - -The Nakama Unreal SDK is implemented as a native Unreal Engine source module. - -To use the SDK from source in your own project: -1. Copy the `Nakama` folder into your project's `Plugins` directory. -2. Add `NakamaUnreal` to your project's `PublicDependencyModuleNames` in `YourProject.Build.cs`. - -The SDK will be comiped along with your project source code. - -### Nakama Unreal Client guide - -You can find Nakama Unreal Client guide [here](https://heroiclabs.com/docs/unreal-client-guide/). +The development roadmap is managed as GitHub issues and pull requests are welcome. If you're interested in enhancing the code please open an issue to discuss the changes or drop in and discuss it in the [community forum](https://forum.heroiclabs.com). ## License -This project is licensed under the [Apache-2 License](https://github.com/heroiclabs/nakama-dotnet/blob/master/LICENSE). +This project is licensed under the [Apache-2 License](https://github.com/heroiclabs/nakama-unreal/blob/master/LICENSE). diff --git a/Satori/Satori.uplugin b/Satori/Satori.uplugin index 20f4798fe..7f6ce89d2 100644 --- a/Satori/Satori.uplugin +++ b/Satori/Satori.uplugin @@ -1,7 +1,7 @@ { - "FileVersion": 1, - "Version": 4, - "VersionName": "2.11.1", + "FileVersion": 3, + "Version": 3000, + "VersionName": "3.0.0", "FriendlyName": "Satori client", "Description": "A UE4 and UE5 client for the Satori server.", "Category": "HeroicLabs.Satori", @@ -16,7 +16,19 @@ "Installed": false, "Modules": [ { - "Name": "SatoriUnreal", + "Name": "SatoriApi", + "Type": "Runtime", + "LoadingPhase": "Default", + "WhitelistPlatforms": [ + "Win64", + "Linux", + "IOS", + "Mac", + "Android" + ] + }, + { + "Name": "Satori", "Type": "Runtime", "LoadingPhase": "Default", "WhitelistPlatforms": [ diff --git a/Satori/Source/Satori/Private/Satori.cpp b/Satori/Source/Satori/Private/Satori.cpp new file mode 100644 index 000000000..f12e69f89 --- /dev/null +++ b/Satori/Source/Satori/Private/Satori.cpp @@ -0,0 +1,2707 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + +#include "Satori.h" +#include "Containers/Ticker.h" + +bool Satori::IsTransientError(const FSatoriError& Error) +{ + switch (Error.Code) + { + case 0: // Connection failed (TCP refused, DNS timeout, etc.) + case 4: // DEADLINE_EXCEEDED + case 13: // INTERNAL + case 14: // UNAVAILABLE + return true; + default: + return false; + } +} + +float Satori::CalculateBackoff(int32 Attempt, const FSatoriRetryConfig& Config) +{ + const float ExponentialDelay = FMath::Pow(2.0f, static_cast(Attempt - 1)) * Config.BaseDelayMs; + const float Jitter = FMath::FRand() * ExponentialDelay; + return Jitter / 1000.0f; +} +namespace +{ + +/** Optionally refresh the session before calling the RPC. */ +void MaybeRefreshThenCall( + const TSharedRef& SessionState, + const FSatoriClientConfig& ClientConfig, + const FSatoriRetryConfig& RetryConfig, + const TSharedRef>& CancellationToken, + const TSharedRef>& OnError, + TFunction OnReady +) +{ + if (!RetryConfig.bAutoRefreshSession + || SessionState->RefreshToken.IsEmpty() + || !SessionState->IsExpired(RetryConfig.AutoRefreshBufferSeconds)) + { + OnReady(); + return; + } + + if (SessionState->IsRefreshExpired()) + { + (*OnError)(FSatoriError(TEXT("Refresh token has expired"), 16)); + return; + } + + auto OnSessionRefreshed = RetryConfig.OnSessionRefreshed; + auto OnSessionRefreshedOwner = RetryConfig.OnSessionRefreshedOwner; + SatoriApi::AuthenticateRefresh( + ClientConfig, + SessionState->RefreshToken, + [SessionState, OnSessionRefreshed, OnSessionRefreshedOwner, OnReady = MoveTemp(OnReady)](const FSatoriSession& RefreshedSession) mutable + { + SessionState->Update(RefreshedSession.Token, RefreshedSession.RefreshToken); + if (OnSessionRefreshed && (OnSessionRefreshedOwner.IsExplicitlyNull() || OnSessionRefreshedOwner.IsValid())) + { + OnSessionRefreshed(*SessionState); + } + OnReady(); + }, + [OnError](const FSatoriError& Error) + { + (*OnError)(FSatoriError(FString::Printf(TEXT("Session refresh failed: %s"), *Error.Message), Error.Code)); + }, + RetryConfig.Timeout, + CancellationToken); +} + +} // anonymous namespace +TSatoriFuture Satori::Authenticate( + const FSatoriClientConfig& ClientConfig, + const FString& Id, + bool NoSession, + const TMap& Default, + const TMap& Custom, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriSessionResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Id + , NoSession + , Default + , Custom + ]() + { + + SatoriApi::Authenticate( + ClientConfig, + Id, + NoSession, + Default, + Custom, + [FutureState, DoRequest, OnError](const FSatoriSession& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriSessionResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::AuthenticateLogout( + const FSatoriClientConfig& ClientConfig, + const FString& Token, + const FString& RefreshToken, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Token + , RefreshToken + ]() + { + + SatoriApi::AuthenticateLogout( + ClientConfig, + Token, + RefreshToken, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult{ FSatoriVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::AuthenticateRefresh( + const FSatoriClientConfig& ClientConfig, + const FString& RefreshToken, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriSessionResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , RefreshToken + ]() + { + + SatoriApi::AuthenticateRefresh( + ClientConfig, + RefreshToken, + [FutureState, DoRequest, OnError](const FSatoriSession& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriSessionResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::DeleteIdentity( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + ]() + { + + SatoriApi::DeleteIdentity( + ClientConfig, + HttpKey, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult{ FSatoriVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::DeleteIdentity( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + ]() + { + + SatoriApi::DeleteIdentity( + ClientConfig, + *SessionState, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult{ FSatoriVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::Event( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& Events, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Events + ]() + { + + SatoriApi::Event( + ClientConfig, + HttpKey, + Events, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult{ FSatoriVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::Event( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const TArray& Events, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Events + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Events + ]() + { + + SatoriApi::Event( + ClientConfig, + *SessionState, + Events, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult{ FSatoriVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::ServerEvent( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& Events, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Events + ]() + { + + SatoriApi::ServerEvent( + ClientConfig, + HttpKey, + Events, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult{ FSatoriVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::ServerEvent( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const TArray& Events, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Events + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Events + ]() + { + + SatoriApi::ServerEvent( + ClientConfig, + *SessionState, + Events, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult{ FSatoriVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::GetExperiments( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& Names, + const TArray& Labels, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriExperimentListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Names + , Labels + ]() + { + + SatoriApi::GetExperiments( + ClientConfig, + HttpKey, + Names, + Labels, + [FutureState, DoRequest, OnError](const FSatoriExperimentList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriExperimentListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::GetExperiments( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const TArray& Names, + const TArray& Labels, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriExperimentListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Names + , Labels + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Names + , Labels + ]() + { + + SatoriApi::GetExperiments( + ClientConfig, + *SessionState, + Names, + Labels, + [FutureState, DoRequest, OnError](const FSatoriExperimentList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriExperimentListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::GetFlagOverrides( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& Names, + const TArray& Labels, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriFlagOverrideListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Names + , Labels + ]() + { + + SatoriApi::GetFlagOverrides( + ClientConfig, + HttpKey, + Names, + Labels, + [FutureState, DoRequest, OnError](const FSatoriFlagOverrideList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriFlagOverrideListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::GetFlagOverrides( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const TArray& Names, + const TArray& Labels, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriFlagOverrideListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Names + , Labels + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Names + , Labels + ]() + { + + SatoriApi::GetFlagOverrides( + ClientConfig, + *SessionState, + Names, + Labels, + [FutureState, DoRequest, OnError](const FSatoriFlagOverrideList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriFlagOverrideListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::GetFlags( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& Names, + const TArray& Labels, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriFlagListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Names + , Labels + ]() + { + + SatoriApi::GetFlags( + ClientConfig, + HttpKey, + Names, + Labels, + [FutureState, DoRequest, OnError](const FSatoriFlagList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriFlagListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::GetFlags( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const TArray& Names, + const TArray& Labels, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriFlagListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Names + , Labels + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Names + , Labels + ]() + { + + SatoriApi::GetFlags( + ClientConfig, + *SessionState, + Names, + Labels, + [FutureState, DoRequest, OnError](const FSatoriFlagList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriFlagListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::GetLiveEvents( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& Names, + const TArray& Labels, + int32 PastRunCount, + int32 FutureRunCount, + int64 StartTimeSec, + int64 EndTimeSec, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriLiveEventListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Names + , Labels + , PastRunCount + , FutureRunCount + , StartTimeSec + , EndTimeSec + ]() + { + + SatoriApi::GetLiveEvents( + ClientConfig, + HttpKey, + Names, + Labels, + PastRunCount, + FutureRunCount, + StartTimeSec, + EndTimeSec, + [FutureState, DoRequest, OnError](const FSatoriLiveEventList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriLiveEventListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::GetLiveEvents( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const TArray& Names, + const TArray& Labels, + int32 PastRunCount, + int32 FutureRunCount, + int64 StartTimeSec, + int64 EndTimeSec, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriLiveEventListResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Names + , Labels + , PastRunCount + , FutureRunCount + , StartTimeSec + , EndTimeSec + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Names + , Labels + , PastRunCount + , FutureRunCount + , StartTimeSec + , EndTimeSec + ]() + { + + SatoriApi::GetLiveEvents( + ClientConfig, + *SessionState, + Names, + Labels, + PastRunCount, + FutureRunCount, + StartTimeSec, + EndTimeSec, + [FutureState, DoRequest, OnError](const FSatoriLiveEventList& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriLiveEventListResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::JoinLiveEvent( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Id, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Id + ]() + { + + SatoriApi::JoinLiveEvent( + ClientConfig, + HttpKey, + Id, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult{ FSatoriVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::JoinLiveEvent( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const FString& Id, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Id + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Id + ]() + { + + SatoriApi::JoinLiveEvent( + ClientConfig, + *SessionState, + Id, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult{ FSatoriVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::Healthcheck( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + ]() + { + + SatoriApi::Healthcheck( + ClientConfig, + HttpKey, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult{ FSatoriVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::Healthcheck( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + ]() + { + + SatoriApi::Healthcheck( + ClientConfig, + *SessionState, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult{ FSatoriVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::Identify( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Id, + const TMap& Default, + const TMap& Custom, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriSessionResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Id + , Default + , Custom + ]() + { + + SatoriApi::Identify( + ClientConfig, + HttpKey, + Id, + Default, + Custom, + [FutureState, DoRequest, OnError](const FSatoriSession& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriSessionResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::Identify( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const FString& Id, + const TMap& Default, + const TMap& Custom, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriSessionResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Id + , Default + , Custom + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Id + , Default + , Custom + ]() + { + + SatoriApi::Identify( + ClientConfig, + *SessionState, + Id, + Default, + Custom, + [FutureState, DoRequest, OnError](const FSatoriSession& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriSessionResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::ListProperties( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriPropertiesResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + ]() + { + + SatoriApi::ListProperties( + ClientConfig, + HttpKey, + [FutureState, DoRequest, OnError](const FSatoriProperties& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriPropertiesResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::ListProperties( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriPropertiesResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + ]() + { + + SatoriApi::ListProperties( + ClientConfig, + *SessionState, + [FutureState, DoRequest, OnError](const FSatoriProperties& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriPropertiesResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::Readycheck( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + ]() + { + + SatoriApi::Readycheck( + ClientConfig, + HttpKey, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult{ FSatoriVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::Readycheck( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + ]() + { + + SatoriApi::Readycheck( + ClientConfig, + *SessionState, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult{ FSatoriVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::UpdateProperties( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + FSatoriOptionalBool Recompute, + const TMap& Default, + const TMap& Custom, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Recompute + , Default + , Custom + ]() + { + + SatoriApi::UpdateProperties( + ClientConfig, + HttpKey, + Recompute, + Default, + Custom, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult{ FSatoriVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::UpdateProperties( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + FSatoriOptionalBool Recompute, + const TMap& Default, + const TMap& Custom, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Recompute + , Default + , Custom + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Recompute + , Default + , Custom + ]() + { + + SatoriApi::UpdateProperties( + ClientConfig, + *SessionState, + Recompute, + Default, + Custom, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult{ FSatoriVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::GetMessageList( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + int32 Limit, + bool Forward, + const FString& Cursor, + const TArray& MessageIds, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriGetMessageListResponseResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Limit + , Forward + , Cursor + , MessageIds + ]() + { + + SatoriApi::GetMessageList( + ClientConfig, + HttpKey, + Limit, + Forward, + Cursor, + MessageIds, + [FutureState, DoRequest, OnError](const FSatoriGetMessageListResponse& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriGetMessageListResponseResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::GetMessageList( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + int32 Limit, + bool Forward, + const FString& Cursor, + const TArray& MessageIds, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriGetMessageListResponseResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Limit + , Forward + , Cursor + , MessageIds + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Limit + , Forward + , Cursor + , MessageIds + ]() + { + + SatoriApi::GetMessageList( + ClientConfig, + *SessionState, + Limit, + Forward, + Cursor, + MessageIds, + [FutureState, DoRequest, OnError](const FSatoriGetMessageListResponse& Result) + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriGetMessageListResponseResult{ Result, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::UpdateMessage( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Id, + int64 ReadTime, + int64 ConsumeTime, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Id + , ReadTime + , ConsumeTime + ]() + { + + SatoriApi::UpdateMessage( + ClientConfig, + HttpKey, + Id, + ReadTime, + ConsumeTime, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult{ FSatoriVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::UpdateMessage( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const FString& Id, + int64 ReadTime, + int64 ConsumeTime, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Id + , ReadTime + , ConsumeTime + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Id + , ReadTime + , ConsumeTime + ]() + { + + SatoriApi::UpdateMessage( + ClientConfig, + *SessionState, + Id, + ReadTime, + ConsumeTime, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult{ FSatoriVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::DeleteMessage( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Id, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , HttpKey + , Id + ]() + { + + SatoriApi::DeleteMessage( + ClientConfig, + HttpKey, + Id, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult{ FSatoriVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} +TSatoriFuture Satori::DeleteMessage( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const FString& Id, + const FSatoriRetryConfig& RetryConfig, + TSharedRef> CancellationToken +) +{ + auto FutureState = MakeShared::FState>(); + auto SessionState = MakeShared(Session); + auto RetryCount = MakeShared(0); + auto DoRequest = MakeShared>(); + + auto OnError = MakeShared>(); + *OnError = [FutureState, RetryCount, DoRequest, OnError, RetryConfig](const FSatoriError& Error) + { + if (Satori::IsTransientError(Error) && *RetryCount < RetryConfig.MaxRetries) + { + (*RetryCount)++; + float Delay = Satori::CalculateBackoff(*RetryCount, RetryConfig); + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([DoRequest](float) -> bool { (*DoRequest)(); return false; }), + Delay); + } + else + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult { {}, Error, true }); + } + }; + + *DoRequest = [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Id + ]() + { + MaybeRefreshThenCall( + SessionState + , ClientConfig + , RetryConfig + , CancellationToken + , OnError + , [ + FutureState + , SessionState + , DoRequest + , OnError + , ClientConfig + , RetryConfig + , CancellationToken + , Session + , Id + ]() + { + + SatoriApi::DeleteMessage( + ClientConfig, + *SessionState, + Id, + [FutureState, DoRequest, OnError]() + { + *DoRequest = nullptr; + *OnError = nullptr; + FutureState->Resolve(FSatoriVoidResult{ FSatoriVoid{}, {}, false }); + }, + *OnError, + RetryConfig.Timeout, + CancellationToken + ); + } + ); + }; + + (*DoRequest)(); + return TSatoriFuture(FutureState); +} diff --git a/Satori/Source/Satori/Private/SatoriModule.cpp b/Satori/Source/Satori/Private/SatoriModule.cpp new file mode 100644 index 000000000..9cceb3782 --- /dev/null +++ b/Satori/Source/Satori/Private/SatoriModule.cpp @@ -0,0 +1,30 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SatoriModule.h" +#include "SatoriApi.h" + +void FSatoriModule::StartupModule() +{ + UE_LOG(LogSatori, Log, TEXT("Satori module starting")); +} + +void FSatoriModule::ShutdownModule() +{ + UE_LOG(LogSatori, Log, TEXT("Satori module shutting down")); +} + +IMPLEMENT_MODULE(FSatoriModule, Satori) diff --git a/Satori/Source/Satori/Public/Satori.h b/Satori/Source/Satori/Public/Satori.h new file mode 100644 index 000000000..93c3ac1c2 --- /dev/null +++ b/Satori/Source/Satori/Public/Satori.h @@ -0,0 +1,789 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + +#pragma once + +#include "CoreMinimal.h" +#include "SatoriApi.h" +#include "AsyncFuture.h" + +/** Tag type used as the value type for RPCs that return no data. */ +struct SATORI_API FSatoriVoid {}; +struct SATORI_API FSatoriVoidResult +{ + using ValueType = FSatoriVoid; + FSatoriVoid Value{}; + FSatoriError Error; + bool bIsError = true; +}; +struct SATORI_API FSatoriSessionResult +{ + using ValueType = FSatoriSession; + FSatoriSession Value {}; + FSatoriError Error; + bool bIsError = true; +}; +struct SATORI_API FSatoriExperimentListResult +{ + using ValueType = FSatoriExperimentList; + FSatoriExperimentList Value {}; + FSatoriError Error; + bool bIsError = true; +}; +struct SATORI_API FSatoriFlagOverrideListResult +{ + using ValueType = FSatoriFlagOverrideList; + FSatoriFlagOverrideList Value {}; + FSatoriError Error; + bool bIsError = true; +}; +struct SATORI_API FSatoriFlagListResult +{ + using ValueType = FSatoriFlagList; + FSatoriFlagList Value {}; + FSatoriError Error; + bool bIsError = true; +}; +struct SATORI_API FSatoriLiveEventListResult +{ + using ValueType = FSatoriLiveEventList; + FSatoriLiveEventList Value {}; + FSatoriError Error; + bool bIsError = true; +}; +struct SATORI_API FSatoriPropertiesResult +{ + using ValueType = FSatoriProperties; + FSatoriProperties Value {}; + FSatoriError Error; + bool bIsError = true; +}; +struct SATORI_API FSatoriGetMessageListResponseResult +{ + using ValueType = FSatoriGetMessageListResponse; + FSatoriGetMessageListResponse Value {}; + FSatoriError Error; + bool bIsError = true; +}; + +/** + * Satori-flavoured alias for TAsyncFuture. + * All three .Next() overloads are inherited from TAsyncFuture and dispatch + * user callbacks to the game thread, making it safe to touch UObject*, + * fire delegates, or update UI from any .Next() callback. + */ +template +using TSatoriFuture = TAsyncFuture; + +/** Type trait for TSatoriFuture (delegates to TIsTAsyncFuture). */ +template using TIsTSatoriFuture = TIsTAsyncFuture; + +/** Retry configuration for transient error handling + session auto-refresh. */ +struct SATORI_API FSatoriRetryConfig +{ + /** Maximum number of retry attempts (0 = no retries). */ + int32 MaxRetries = 4; + + /** Base delay in milliseconds before first retry. Actual delay uses exponential backoff with full jitter. */ + int32 BaseDelayMs = 500; + + /** Whether to automatically refresh expired sessions before making API calls. */ + bool bAutoRefreshSession = true; + + /** How many seconds before expiry a session is considered "about to expire" and gets refreshed. */ + int64 AutoRefreshBufferSeconds = 300; + + /** HTTP request timeout in seconds. */ + float Timeout = 10.0f; + + /** + * Called after a session is automatically refreshed, so callers can persist the updated session. + * + * If the callback captures a UObject pointer, set OnSessionRefreshedOwner to that object. + * The callback will be skipped if the owner has been garbage-collected, preventing + * dangling-pointer crashes during in-flight retries. + */ + TFunction OnSessionRefreshed; + + /** Optional weak owner for OnSessionRefreshed. When set, the callback is skipped if the owner is stale. */ + TWeakObjectPtr OnSessionRefreshedOwner; +}; + +/** + * High-level Satori API: free functions with retry logic + session auto-refresh. + * + * Transient errors (UNAVAILABLE, INTERNAL, DEADLINE_EXCEEDED) are + * automatically retried with exponential backoff and full jitter. + */ +namespace Satori +{ + /** Returns true if the error code is considered transient (eligible for retry). */ + SATORI_API bool IsTransientError(const FSatoriError& Error); + + /** Compute backoff delay in seconds for a given attempt using exponential backoff with full jitter. */ + SATORI_API float CalculateBackoff(int32 Attempt, const FSatoriRetryConfig& Config); + + /** + * Authenticate against the server. + * + * @param Config The client configuration. + * @param Id Identity ID. Must be between eight and 128 characters (inclusive). + * @param NoSession Optional no_session modifies the request to only create/update + * @param Default Optional default properties to update with this call. + * @param Custom Optional custom properties to update with this call. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture Authenticate( + const FSatoriClientConfig& ClientConfig, + const FString& Id, + bool NoSession, + const TMap& Default, + const TMap& Custom, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Log out a session, invalidate a refresh token, or log out all sessions/refresh tokens for a user. + * + * @param Config The client configuration. + * @param Token Session token to log out. + * @param RefreshToken Refresh token to invalidate. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture AuthenticateLogout( + const FSatoriClientConfig& ClientConfig, + const FString& Token, + const FString& RefreshToken, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Refresh a user's session using a refresh token retrieved from a previous authentication request. + * + * @param Config The client configuration. + * @param RefreshToken Refresh token. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture AuthenticateRefresh( + const FSatoriClientConfig& ClientConfig, + const FString& RefreshToken, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Delete the caller's identity and associated data. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture DeleteIdentity( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Delete the caller's identity and associated data. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture DeleteIdentity( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Publish an event for this session. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Events Some number of events produced by a client. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture Event( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& Events, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Publish an event for this session. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Events Some number of events produced by a client. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture Event( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const TArray& Events, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Publish server events for multiple distinct identities. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Events Some number of events produced by a client. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture ServerEvent( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& Events, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Publish server events for multiple distinct identities. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Events Some number of events produced by a client. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture ServerEvent( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const TArray& Events, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Get or list all available experiments for this identity. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Names Experiment names; if empty string, all experiments are returned based on the remaining filters. + * @param Labels Label names that must be defined for each Experiment; if empty string, all experiments are returned based on the remaining filters. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture GetExperiments( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& Names, + const TArray& Labels, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Get or list all available experiments for this identity. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Names Experiment names; if empty string, all experiments are returned based on the remaining filters. + * @param Labels Label names that must be defined for each Experiment; if empty string, all experiments are returned based on the remaining filters. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture GetExperiments( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const TArray& Names, + const TArray& Labels, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List all available flags and their value overrides for this identity. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Names Flag names; if empty string, all flags are returned based on the remaining filters. + * @param Labels Label names that must be defined for each Flag; if empty string, all flags are returned based on the remaining filters. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture GetFlagOverrides( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& Names, + const TArray& Labels, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List all available flags and their value overrides for this identity. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Names Flag names; if empty string, all flags are returned based on the remaining filters. + * @param Labels Label names that must be defined for each Flag; if empty string, all flags are returned based on the remaining filters. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture GetFlagOverrides( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const TArray& Names, + const TArray& Labels, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List all available flags for this identity. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Names Flag names; if empty string, all flags are returned based on the remaining filters. + * @param Labels Label names that must be defined for each Flag; if empty string, all flags are returned based on the remaining filters. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture GetFlags( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& Names, + const TArray& Labels, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List all available flags for this identity. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Names Flag names; if empty string, all flags are returned based on the remaining filters. + * @param Labels Label names that must be defined for each Flag; if empty string, all flags are returned based on the remaining filters. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture GetFlags( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const TArray& Names, + const TArray& Labels, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List available live events. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Names Live event names; if empty string, all live events are returned based on the remaining filters. + * @param Labels Label names that must be defined for each Live Event; if empty string, all live events are returned based on the remaining filters. + * @param PastRunCount The maximum number of past event runs to return for each live event. + * @param FutureRunCount The maximum number of future event runs to return for each live event. + * @param StartTimeSec Start time of the time window filter to apply. + * @param EndTimeSec End time of the time window filter to apply. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture GetLiveEvents( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const TArray& Names, + const TArray& Labels, + int32 PastRunCount, + int32 FutureRunCount, + int64 StartTimeSec, + int64 EndTimeSec, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List available live events. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Names Live event names; if empty string, all live events are returned based on the remaining filters. + * @param Labels Label names that must be defined for each Live Event; if empty string, all live events are returned based on the remaining filters. + * @param PastRunCount The maximum number of past event runs to return for each live event. + * @param FutureRunCount The maximum number of future event runs to return for each live event. + * @param StartTimeSec Start time of the time window filter to apply. + * @param EndTimeSec End time of the time window filter to apply. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture GetLiveEvents( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const TArray& Names, + const TArray& Labels, + int32 PastRunCount, + int32 FutureRunCount, + int64 StartTimeSec, + int64 EndTimeSec, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Join an 'explicit join' live event. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Id Live event id to join. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture JoinLiveEvent( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Id, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Join an 'explicit join' live event. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Id Live event id to join. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture JoinLiveEvent( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const FString& Id, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * A healthcheck which load balancers can use to check the service. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture Healthcheck( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * A healthcheck which load balancers can use to check the service. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture Healthcheck( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Enrich/replace the current session with new identifier. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Id Identity ID to enrich the current session and return a new session. Old session will no longer be usable. + * @param Default Optional default properties to update with this call. + * @param Custom Optional custom properties to update with this call. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture Identify( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Id, + const TMap& Default, + const TMap& Custom, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Enrich/replace the current session with new identifier. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Id Identity ID to enrich the current session and return a new session. Old session will no longer be usable. + * @param Default Optional default properties to update with this call. + * @param Custom Optional custom properties to update with this call. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture Identify( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const FString& Id, + const TMap& Default, + const TMap& Custom, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List properties associated with this identity. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture ListProperties( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * List properties associated with this identity. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture ListProperties( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * A readycheck which load balancers can use to check the service. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture Readycheck( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * A readycheck which load balancers can use to check the service. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture Readycheck( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Update identity properties. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Recompute Informs the server to recompute the audience membership of the identity. + * @param Default Event default properties. + * @param Custom Event custom properties. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture UpdateProperties( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + FSatoriOptionalBool Recompute, + const TMap& Default, + const TMap& Custom, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Update identity properties. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Recompute Informs the server to recompute the audience membership of the identity. + * @param Default Event default properties. + * @param Custom Event custom properties. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture UpdateProperties( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + FSatoriOptionalBool Recompute, + const TMap& Default, + const TMap& Custom, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Get the list of messages for the identity. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Limit Max number of messages to return. Between 1 and 100. + * @param Forward True if listing should be older messages to newer, false if reverse. + * @param Cursor A pagination cursor, if any. + * @param MessageIds A list of message identifiers to be returned. If these are given as inputs, the pagination will be disabled. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture GetMessageList( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + int32 Limit, + bool Forward, + const FString& Cursor, + const TArray& MessageIds, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Get the list of messages for the identity. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Limit Max number of messages to return. Between 1 and 100. + * @param Forward True if listing should be older messages to newer, false if reverse. + * @param Cursor A pagination cursor, if any. + * @param MessageIds A list of message identifiers to be returned. If these are given as inputs, the pagination will be disabled. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture GetMessageList( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + int32 Limit, + bool Forward, + const FString& Cursor, + const TArray& MessageIds, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Updates a message for an identity. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Id The identifier of the messages. + * @param ReadTime The time the message was read at the client. + * @param ConsumeTime The time the message was consumed by the identity. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture UpdateMessage( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Id, + int64 ReadTime, + int64 ConsumeTime, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Updates a message for an identity. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Id The identifier of the messages. + * @param ReadTime The time the message was read at the client. + * @param ConsumeTime The time the message was consumed by the identity. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture UpdateMessage( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const FString& Id, + int64 ReadTime, + int64 ConsumeTime, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Deletes a message for an identity. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Id The identifier of the message. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture DeleteMessage( + const FSatoriClientConfig& ClientConfig, + const FString& HttpKey, + const FString& Id, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /** + * Deletes a message for an identity. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Id The identifier of the message. + * @param RetryConfig Retry configuration. + * @param CancellationToken Set to true to cancel the in-flight request. + */ + SATORI_API TSatoriFuture DeleteMessage( + const FSatoriClientConfig& ClientConfig, + const FSatoriSession& Session, + const FString& Id, + const FSatoriRetryConfig& RetryConfig = {}, + TSharedRef> CancellationToken = MakeShared>(false) + ); +} diff --git a/Nakama/Source/NakamaTests/Public/NakamaTests.h b/Satori/Source/Satori/Public/SatoriModule.h similarity index 76% rename from Nakama/Source/NakamaTests/Public/NakamaTests.h rename to Satori/Source/Satori/Public/SatoriModule.h index 630ede68a..e5c461e63 100644 --- a/Nakama/Source/NakamaTests/Public/NakamaTests.h +++ b/Satori/Source/Satori/Public/SatoriModule.h @@ -1,5 +1,5 @@ /* -* Copyright 2025 The Nakama Authors + * Copyright 2026 The Nakama Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,18 +16,11 @@ #pragma once -#include "CoreMinimal.h" #include "Modules/ModuleManager.h" -DECLARE_LOG_CATEGORY_EXTERN(LogNakamaTests, Log, All); - - -class FNakamaTestsModule : public IModuleInterface +class FSatoriModule : public IModuleInterface { public: - - /** IModuleInterface implementation */ virtual void StartupModule() override; virtual void ShutdownModule() override; - -}; \ No newline at end of file +}; diff --git a/Satori/Source/SatoriUnreal/SatoriUnreal.Build.cs b/Satori/Source/Satori/Satori.Build.cs similarity index 89% rename from Satori/Source/SatoriUnreal/SatoriUnreal.Build.cs rename to Satori/Source/Satori/Satori.Build.cs index 39ea75915..6adac7c41 100644 --- a/Satori/Source/SatoriUnreal/SatoriUnreal.Build.cs +++ b/Satori/Source/Satori/Satori.Build.cs @@ -17,9 +17,9 @@ using UnrealBuildTool; using System.IO; -public class SatoriUnreal : ModuleRules +public class Satori : ModuleRules { - public SatoriUnreal(ReadOnlyTargetRules Target) : base(Target) + public Satori(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; @@ -37,11 +37,10 @@ public SatoriUnreal(ReadOnlyTargetRules Target) : base(Target) ); - // NakamaUnreal no longer depends on NakamaCore PublicDependencyModuleNames.AddRange( new string[] { - "Core", "HTTP", "WebSockets", "JsonUtilities" + "Core", "SatoriApi", "HTTP", "JsonUtilities" // ... add other public dependencies that you statically link with here ... } ); diff --git a/Satori/Source/SatoriApi/Private/SatoriApi.cpp b/Satori/Source/SatoriApi/Private/SatoriApi.cpp new file mode 100644 index 000000000..5681dcf6c --- /dev/null +++ b/Satori/Source/SatoriApi/Private/SatoriApi.cpp @@ -0,0 +1,2011 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + +#include "SatoriApi.h" +#include "GenericPlatform/GenericPlatformHttp.h" +#include "SatoriHttpHelper.h" + +DEFINE_LOG_CATEGORY(LogSatori); + +using namespace SatoriHttpInternal; + + + +SATORIAPI_API void SatoriApi::Authenticate ( + const FSatoriClientConfig& Config, + const FString& Id, + bool NoSession, + const TMap& Default, + const TMap& Custom, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/authenticate"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Id.IsEmpty() == false) + { + Body->SetStringField(TEXT("id"), Id); + } + + { + Body->SetBoolField(TEXT("no_session"), NoSession); + } + if (Default.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Default) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("default"), MapObj); + } + if (Custom.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Custom) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("custom"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ESatoriRequestAuth::Basic, + TEXT(""), + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FSatoriSession Result = FSatoriSession::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::AuthenticateLogout ( + const FSatoriClientConfig& Config, + const FString& Token, + const FString& RefreshToken, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/authenticate/logout"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Token.IsEmpty() == false) + { + Body->SetStringField(TEXT("token"), Token); + } + if (RefreshToken.IsEmpty() == false) + { + Body->SetStringField(TEXT("refresh_token"), RefreshToken); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ESatoriRequestAuth::Bearer, + Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::AuthenticateRefresh ( + const FSatoriClientConfig& Config, + const FString& RefreshToken, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/authenticate/refresh"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (RefreshToken.IsEmpty() == false) + { + Body->SetStringField(TEXT("refresh_token"), RefreshToken); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ESatoriRequestAuth::Basic, + TEXT(""), + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FSatoriSession Result = FSatoriSession::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::DeleteIdentity ( + const FSatoriClientConfig& Config, + const FString& HttpKey, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/identity"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("DELETE"), + Body, + ESatoriRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::DeleteIdentity ( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/identity"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("DELETE"), + Body, + ESatoriRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::Event ( + const FSatoriClientConfig& Config, + const FString& HttpKey, + const TArray& Events, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/event"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Events.Num() > 0) + { + TArray> Array; + for (const FSatoriEvent& Item : Events) + { + Array.Add(MakeShared(Item.ToJson())); + } + Body->SetArrayField(TEXT("events"), Array); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ESatoriRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::Event ( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + const TArray& Events, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/event"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Events.Num() > 0) + { + TArray> Array; + for (const FSatoriEvent& Item : Events) + { + Array.Add(MakeShared(Item.ToJson())); + } + Body->SetArrayField(TEXT("events"), Array); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ESatoriRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::ServerEvent ( + const FSatoriClientConfig& Config, + const FString& HttpKey, + const TArray& Events, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/server-event"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Events.Num() > 0) + { + TArray> Array; + for (const FSatoriEvent& Item : Events) + { + Array.Add(MakeShared(Item.ToJson())); + } + Body->SetArrayField(TEXT("events"), Array); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ESatoriRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::ServerEvent ( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + const TArray& Events, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/server-event"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Events.Num() > 0) + { + TArray> Array; + for (const FSatoriEvent& Item : Events) + { + Array.Add(MakeShared(Item.ToJson())); + } + Body->SetArrayField(TEXT("events"), Array); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ESatoriRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::GetExperiments ( + const FSatoriClientConfig& Config, + const FString& HttpKey, + const TArray& Names, + const TArray& Labels, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/experiment"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : Names) + { + QueryParams.Add({TEXT("names"), FString::Printf(TEXT("%s"), *(Item))}); + } + for (const FString& Item : Labels) + { + QueryParams.Add({TEXT("labels"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ESatoriRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FSatoriExperimentList Result = FSatoriExperimentList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::GetExperiments ( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + const TArray& Names, + const TArray& Labels, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/experiment"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : Names) + { + QueryParams.Add({TEXT("names"), FString::Printf(TEXT("%s"), *(Item))}); + } + for (const FString& Item : Labels) + { + QueryParams.Add({TEXT("labels"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ESatoriRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FSatoriExperimentList Result = FSatoriExperimentList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::GetFlagOverrides ( + const FSatoriClientConfig& Config, + const FString& HttpKey, + const TArray& Names, + const TArray& Labels, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/flag/override"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : Names) + { + QueryParams.Add({TEXT("names"), FString::Printf(TEXT("%s"), *(Item))}); + } + for (const FString& Item : Labels) + { + QueryParams.Add({TEXT("labels"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ESatoriRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FSatoriFlagOverrideList Result = FSatoriFlagOverrideList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::GetFlagOverrides ( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + const TArray& Names, + const TArray& Labels, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/flag/override"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : Names) + { + QueryParams.Add({TEXT("names"), FString::Printf(TEXT("%s"), *(Item))}); + } + for (const FString& Item : Labels) + { + QueryParams.Add({TEXT("labels"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ESatoriRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FSatoriFlagOverrideList Result = FSatoriFlagOverrideList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::GetFlags ( + const FSatoriClientConfig& Config, + const FString& HttpKey, + const TArray& Names, + const TArray& Labels, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/flag"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : Names) + { + QueryParams.Add({TEXT("names"), FString::Printf(TEXT("%s"), *(Item))}); + } + for (const FString& Item : Labels) + { + QueryParams.Add({TEXT("labels"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ESatoriRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FSatoriFlagList Result = FSatoriFlagList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::GetFlags ( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + const TArray& Names, + const TArray& Labels, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/flag"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : Names) + { + QueryParams.Add({TEXT("names"), FString::Printf(TEXT("%s"), *(Item))}); + } + for (const FString& Item : Labels) + { + QueryParams.Add({TEXT("labels"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ESatoriRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FSatoriFlagList Result = FSatoriFlagList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::GetLiveEvents ( + const FSatoriClientConfig& Config, + const FString& HttpKey, + const TArray& Names, + const TArray& Labels, + int32 PastRunCount, + int32 FutureRunCount, + int64 StartTimeSec, + int64 EndTimeSec, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/live-event"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : Names) + { + QueryParams.Add({TEXT("names"), FString::Printf(TEXT("%s"), *(Item))}); + } + for (const FString& Item : Labels) + { + QueryParams.Add({TEXT("labels"), FString::Printf(TEXT("%s"), *(Item))}); + } + + { + QueryParams.Add({TEXT("past_run_count"), FString::Printf(TEXT("%d"), (PastRunCount))}); + } + + { + QueryParams.Add({TEXT("future_run_count"), FString::Printf(TEXT("%d"), (FutureRunCount))}); + } + + { + QueryParams.Add({TEXT("start_time_sec"), FString::Printf(TEXT("%lld"), (StartTimeSec))}); + } + + { + QueryParams.Add({TEXT("end_time_sec"), FString::Printf(TEXT("%lld"), (EndTimeSec))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ESatoriRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FSatoriLiveEventList Result = FSatoriLiveEventList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::GetLiveEvents ( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + const TArray& Names, + const TArray& Labels, + int32 PastRunCount, + int32 FutureRunCount, + int64 StartTimeSec, + int64 EndTimeSec, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/live-event"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + for (const FString& Item : Names) + { + QueryParams.Add({TEXT("names"), FString::Printf(TEXT("%s"), *(Item))}); + } + for (const FString& Item : Labels) + { + QueryParams.Add({TEXT("labels"), FString::Printf(TEXT("%s"), *(Item))}); + } + + { + QueryParams.Add({TEXT("past_run_count"), FString::Printf(TEXT("%d"), (PastRunCount))}); + } + + { + QueryParams.Add({TEXT("future_run_count"), FString::Printf(TEXT("%d"), (FutureRunCount))}); + } + + { + QueryParams.Add({TEXT("start_time_sec"), FString::Printf(TEXT("%lld"), (StartTimeSec))}); + } + + { + QueryParams.Add({TEXT("end_time_sec"), FString::Printf(TEXT("%lld"), (EndTimeSec))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ESatoriRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FSatoriLiveEventList Result = FSatoriLiveEventList::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::JoinLiveEvent ( + const FSatoriClientConfig& Config, + const FString& HttpKey, + const FString& Id, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/live-event/{id}/participation"); + // + // Fill Path Params + const FString Encoded_Id = FGenericPlatformHttp::UrlEncode(Id); + Endpoint = Endpoint.Replace(TEXT("{id}"), *Encoded_Id); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ESatoriRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::JoinLiveEvent ( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + const FString& Id, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/live-event/{id}/participation"); + // + // Fill Path Params + const FString Encoded_Id = FGenericPlatformHttp::UrlEncode(Id); + Endpoint = Endpoint.Replace(TEXT("{id}"), *Encoded_Id); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("POST"), + Body, + ESatoriRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::Healthcheck ( + const FSatoriClientConfig& Config, + const FString& HttpKey, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/healthcheck"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ESatoriRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::Healthcheck ( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/healthcheck"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ESatoriRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::Identify ( + const FSatoriClientConfig& Config, + const FString& HttpKey, + const FString& Id, + const TMap& Default, + const TMap& Custom, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/identify"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Id.IsEmpty() == false) + { + Body->SetStringField(TEXT("id"), Id); + } + if (Default.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Default) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("default"), MapObj); + } + if (Custom.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Custom) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("custom"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("PUT"), + Body, + ESatoriRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FSatoriSession Result = FSatoriSession::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::Identify ( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + const FString& Id, + const TMap& Default, + const TMap& Custom, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/identify"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Id.IsEmpty() == false) + { + Body->SetStringField(TEXT("id"), Id); + } + if (Default.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Default) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("default"), MapObj); + } + if (Custom.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Custom) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("custom"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("PUT"), + Body, + ESatoriRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FSatoriSession Result = FSatoriSession::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::ListProperties ( + const FSatoriClientConfig& Config, + const FString& HttpKey, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/properties"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ESatoriRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FSatoriProperties Result = FSatoriProperties::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::ListProperties ( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/properties"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ESatoriRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FSatoriProperties Result = FSatoriProperties::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::Readycheck ( + const FSatoriClientConfig& Config, + const FString& HttpKey, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/readycheck"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ESatoriRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::Readycheck ( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/readycheck"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ESatoriRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::UpdateProperties ( + const FSatoriClientConfig& Config, + const FString& HttpKey, + FSatoriOptionalBool Recompute, + const TMap& Default, + const TMap& Custom, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/properties"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Recompute.IsEmpty() == false) + { + Body->SetBoolField(TEXT("recompute"), Recompute.GetValue()); + } + if (Default.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Default) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("default"), MapObj); + } + if (Custom.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Custom) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("custom"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("PUT"), + Body, + ESatoriRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::UpdateProperties ( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + FSatoriOptionalBool Recompute, + const TMap& Default, + const TMap& Custom, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/properties"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + if (Recompute.IsEmpty() == false) + { + Body->SetBoolField(TEXT("recompute"), Recompute.GetValue()); + } + if (Default.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Default) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("default"), MapObj); + } + if (Custom.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Custom) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Body->SetObjectField(TEXT("custom"), MapObj); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("PUT"), + Body, + ESatoriRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::GetMessageList ( + const FSatoriClientConfig& Config, + const FString& HttpKey, + int32 Limit, + bool Forward, + const FString& Cursor, + const TArray& MessageIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/message"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit))}); + } + + { + QueryParams.Add({TEXT("forward"), FString::Printf(TEXT("%s"), *LexToString(Forward))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + for (const FString& Item : MessageIds) + { + QueryParams.Add({TEXT("message_ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ESatoriRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FSatoriGetMessageListResponse Result = FSatoriGetMessageListResponse::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::GetMessageList ( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + int32 Limit, + bool Forward, + const FString& Cursor, + const TArray& MessageIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/message"); + // + // Fill Path Params + + // + // Fill Query Params + TArray> QueryParams; + + { + QueryParams.Add({TEXT("limit"), FString::Printf(TEXT("%d"), (Limit))}); + } + + { + QueryParams.Add({TEXT("forward"), FString::Printf(TEXT("%s"), *LexToString(Forward))}); + } + if (Cursor.IsEmpty() == false) + { + QueryParams.Add({TEXT("cursor"), FString::Printf(TEXT("%s"), *(Cursor))}); + } + for (const FString& Item : MessageIds) + { + QueryParams.Add({TEXT("message_ids"), FString::Printf(TEXT("%s"), *(Item))}); + } + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("GET"), + Body, + ESatoriRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + FSatoriGetMessageListResponse Result = FSatoriGetMessageListResponse::FromJson(Json); + OnSuccess(Result); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::UpdateMessage ( + const FSatoriClientConfig& Config, + const FString& HttpKey, + const FString& Id, + int64 ReadTime, + int64 ConsumeTime, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/message/{id}"); + // + // Fill Path Params + const FString Encoded_Id = FGenericPlatformHttp::UrlEncode(Id); + Endpoint = Endpoint.Replace(TEXT("{id}"), *Encoded_Id); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + + { + Body->SetNumberField(TEXT("read_time"), ReadTime); + } + + { + Body->SetNumberField(TEXT("consume_time"), ConsumeTime); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("PUT"), + Body, + ESatoriRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::UpdateMessage ( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + const FString& Id, + int64 ReadTime, + int64 ConsumeTime, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/message/{id}"); + // + // Fill Path Params + const FString Encoded_Id = FGenericPlatformHttp::UrlEncode(Id); + Endpoint = Endpoint.Replace(TEXT("{id}"), *Encoded_Id); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + Body = MakeShared(); + + { + Body->SetNumberField(TEXT("read_time"), ReadTime); + } + + { + Body->SetNumberField(TEXT("consume_time"), ConsumeTime); + } + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("PUT"), + Body, + ESatoriRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::DeleteMessage ( + const FSatoriClientConfig& Config, + const FString& HttpKey, + const FString& Id, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/message/{id}"); + // + // Fill Path Params + const FString Encoded_Id = FGenericPlatformHttp::UrlEncode(Id); + Endpoint = Endpoint.Replace(TEXT("{id}"), *Encoded_Id); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("DELETE"), + Body, + ESatoriRequestAuth::HttpKey, + HttpKey, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + + +SATORIAPI_API void SatoriApi::DeleteMessage ( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + const FString& Id, + TFunction OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken +) +{ + FString Endpoint = TEXT("/v1/message/{id}"); + // + // Fill Path Params + const FString Encoded_Id = FGenericPlatformHttp::UrlEncode(Id); + Endpoint = Endpoint.Replace(TEXT("{id}"), *Encoded_Id); + + // + // Fill Query Params + TArray> QueryParams; + + // + // Fill Body Params + TSharedPtr Body; + + // + // Make the request + MakeRequest( + Config, + Endpoint, + QueryParams, + TEXT("DELETE"), + Body, + ESatoriRequestAuth::Bearer, + Session.Token, + [OnSuccess](TSharedPtr Json) + { + if (OnSuccess) + { + + OnSuccess(); + + } + }, + OnError, Timeout, CancellationToken); +} + + diff --git a/Satori/Source/SatoriApi/Private/SatoriClientConfig.cpp b/Satori/Source/SatoriApi/Private/SatoriClientConfig.cpp new file mode 100644 index 000000000..cfc1879d6 --- /dev/null +++ b/Satori/Source/SatoriApi/Private/SatoriClientConfig.cpp @@ -0,0 +1,23 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SatoriClientConfig.h" + +FString FSatoriClientConfig::GetBaseUrl() const noexcept +{ + const FString Scheme = bUseSSL ? TEXT("https") : TEXT("http"); + return FString::Printf(TEXT("%s://%s:%d"), *Scheme, *Host, Port); +} diff --git a/Satori/Source/SatoriApi/Private/SatoriModule.cpp b/Satori/Source/SatoriApi/Private/SatoriModule.cpp new file mode 100644 index 000000000..924422df9 --- /dev/null +++ b/Satori/Source/SatoriApi/Private/SatoriModule.cpp @@ -0,0 +1,30 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SatoriApiModule.h" +#include "SatoriApi.h" + +void FSatoriApiModule::StartupModule() +{ + UE_LOG(LogSatori, Log, TEXT("SatoriApi module starting")); +} + +void FSatoriApiModule::ShutdownModule() +{ + UE_LOG(LogSatori, Log, TEXT("SatoriApi module shutting down")); +} + +IMPLEMENT_MODULE(FSatoriApiModule, SatoriApi) diff --git a/Satori/Source/SatoriApi/Private/SatoriSession.cpp b/Satori/Source/SatoriApi/Private/SatoriSession.cpp new file mode 100644 index 000000000..8c52e67c6 --- /dev/null +++ b/Satori/Source/SatoriApi/Private/SatoriSession.cpp @@ -0,0 +1,170 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SatoriSession.h" +#include "Misc/Base64.h" +#include "Dom/JsonObject.h" +#include "Serialization/JsonReader.h" +#include "Serialization/JsonSerializer.h" + +bool FSatoriSession::ParseJwtPayload(const FString& Jwt, TSharedPtr& Out) noexcept +{ + TArray Parts; + Jwt.ParseIntoArray(Parts, TEXT(".")); + if (Parts.Num() < 2) + { + return false; + } + + // Base64url -> standard Base64 + FString Payload = Parts[1]; + Payload.ReplaceInline(TEXT("-"), TEXT("+")); + Payload.ReplaceInline(TEXT("_"), TEXT("/")); + + // Pad to multiple of 4 + while (Payload.Len() % 4 != 0) + { + Payload += TEXT("="); + } + + TArray DecodedBytes; + if (!FBase64::Decode(Payload, DecodedBytes)) + { + return false; + } + + const FUTF8ToTCHAR Converter(reinterpret_cast(DecodedBytes.GetData()), DecodedBytes.Num()); + FString JsonString(Converter.Length(), Converter.Get()); + + TSharedRef> Reader = TJsonReaderFactory<>::Create(JsonString); + return FJsonSerializer::Deserialize(Reader, Out) && Out.IsValid(); +} + +void FSatoriSession::ParseTokens() noexcept +{ + UserId.Empty(); + Username.Empty(); + TokenExpiresAt = 0; + TokenIssuedAt = 0; + RefreshTokenExpiresAt = 0; + Vars.Empty(); + + // Parse auth token + TSharedPtr TokenPayload; + if (!Token.IsEmpty() && ParseJwtPayload(Token, TokenPayload)) + { + if (TokenPayload->HasField(TEXT("uid"))) + { + UserId = TokenPayload->GetStringField(TEXT("uid")); + } + if (TokenPayload->HasField(TEXT("usn"))) + { + Username = TokenPayload->GetStringField(TEXT("usn")); + } + if (TokenPayload->HasField(TEXT("exp"))) + { + TokenExpiresAt = static_cast(TokenPayload->GetNumberField(TEXT("exp"))); + } + if (TokenPayload->HasField(TEXT("iat"))) + { + TokenIssuedAt = static_cast(TokenPayload->GetNumberField(TEXT("iat"))); + } + if (TokenPayload->HasField(TEXT("vrs"))) + { + const TSharedPtr* VrsObj; + if (TokenPayload->TryGetObjectField(TEXT("vrs"), VrsObj)) + { + for (const auto& Pair : (*VrsObj)->Values) + { + Vars.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + } + + // Parse refresh token + TSharedPtr RefreshPayload; + if (!RefreshToken.IsEmpty() && ParseJwtPayload(RefreshToken, RefreshPayload)) + { + if (RefreshPayload->HasField(TEXT("exp"))) + { + RefreshTokenExpiresAt = static_cast(RefreshPayload->GetNumberField(TEXT("exp"))); + } + } +} + +bool FSatoriSession::IsExpired(int64 BufferSeconds) const noexcept +{ + if (TokenExpiresAt == 0) + { + return true; + } + return (TokenExpiresAt - BufferSeconds) <= FDateTime::UtcNow().ToUnixTimestamp(); +} + +bool FSatoriSession::IsRefreshExpired(int64 BufferSeconds) const noexcept +{ + if (RefreshTokenExpiresAt == 0) + { + return true; + } + return (RefreshTokenExpiresAt - BufferSeconds) <= FDateTime::UtcNow().ToUnixTimestamp(); +} + +void FSatoriSession::Update(const FString& NewToken, const FString& NewRefreshToken) noexcept +{ + Token = NewToken; + RefreshToken = NewRefreshToken; + ParseTokens(); +} + +FSatoriSession FSatoriSession::FromJson(const TSharedPtr& Json) noexcept +{ + FSatoriSession Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("created"))) + { + Result.Created = Json->GetBoolField(TEXT("created")); + } + if (Json->HasField(TEXT("token"))) + { + Result.Token = Json->GetStringField(TEXT("token")); + } + if (Json->HasField(TEXT("refresh_token"))) + { + Result.RefreshToken = Json->GetStringField(TEXT("refresh_token")); + } + Result.ParseTokens(); + return Result; +} + +TSharedPtr FSatoriSession::ToJson() const noexcept +{ + TSharedPtr Json = MakeShared(); + Json->SetBoolField(TEXT("created"), Created); + if (!Token.IsEmpty()) + { + Json->SetStringField(TEXT("token"), Token); + } + if (!RefreshToken.IsEmpty()) + { + Json->SetStringField(TEXT("refresh_token"), RefreshToken); + } + return Json; +} diff --git a/Satori/Source/SatoriApi/Private/SatoriTypes.cpp b/Satori/Source/SatoriApi/Private/SatoriTypes.cpp new file mode 100644 index 000000000..18e1fe512 --- /dev/null +++ b/Satori/Source/SatoriApi/Private/SatoriTypes.cpp @@ -0,0 +1,1659 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + +#include "SatoriTypes.h" + +FSatoriAuthenticateLogoutRequest FSatoriAuthenticateLogoutRequest::FromJson(const TSharedPtr& Json) +{ + FSatoriAuthenticateLogoutRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("token"))) + { + Result.Token = Json->GetStringField(TEXT("token")); + } + if (Json->HasField(TEXT("refresh_token"))) + { + Result.RefreshToken = Json->GetStringField(TEXT("refresh_token")); + } + return Result; +} + +TSharedPtr FSatoriAuthenticateLogoutRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Token.IsEmpty() == false) + { + Json->SetStringField(TEXT("token"), Token); + } + if (RefreshToken.IsEmpty() == false) + { + Json->SetStringField(TEXT("refresh_token"), RefreshToken); + } + + return Json; +} + +FSatoriAuthenticateRefreshRequest FSatoriAuthenticateRefreshRequest::FromJson(const TSharedPtr& Json) +{ + FSatoriAuthenticateRefreshRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("refresh_token"))) + { + Result.RefreshToken = Json->GetStringField(TEXT("refresh_token")); + } + return Result; +} + +TSharedPtr FSatoriAuthenticateRefreshRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (RefreshToken.IsEmpty() == false) + { + Json->SetStringField(TEXT("refresh_token"), RefreshToken); + } + + return Json; +} + +FSatoriAuthenticateRequest FSatoriAuthenticateRequest::FromJson(const TSharedPtr& Json) +{ + FSatoriAuthenticateRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("id"))) + { + Result.Id = Json->GetStringField(TEXT("id")); + } + if (Json->HasField(TEXT("no_session"))) + { + Result.NoSession = Json->GetBoolField(TEXT("no_session")); + } + if (Json->HasField(TEXT("default"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("default"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.Default.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + if (Json->HasField(TEXT("custom"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("custom"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.Custom.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + return Result; +} + +TSharedPtr FSatoriAuthenticateRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Id.IsEmpty() == false) + { + Json->SetStringField(TEXT("id"), Id); + } + + { + Json->SetBoolField(TEXT("no_session"), NoSession); + } + if (Default.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Default) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("default"), MapObj); + } + if (Custom.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Custom) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("custom"), MapObj); + } + + return Json; +} + +FSatoriDeleteMessageRequest FSatoriDeleteMessageRequest::FromJson(const TSharedPtr& Json) +{ + FSatoriDeleteMessageRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("id"))) + { + Result.Id = Json->GetStringField(TEXT("id")); + } + return Result; +} + +TSharedPtr FSatoriDeleteMessageRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Id.IsEmpty() == false) + { + Json->SetStringField(TEXT("id"), Id); + } + + return Json; +} + +FSatoriEvent FSatoriEvent::FromJson(const TSharedPtr& Json) +{ + FSatoriEvent Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("name"))) + { + Result.Name = Json->GetStringField(TEXT("name")); + } + if (Json->HasField(TEXT("id"))) + { + Result.Id = Json->GetStringField(TEXT("id")); + } + if (Json->HasField(TEXT("value"))) + { + Result.Value = Json->GetStringField(TEXT("value")); + } + if (Json->HasField(TEXT("timestamp"))) + { + FDateTime::ParseIso8601(*Json->GetStringField(TEXT("timestamp")), Result.Timestamp); + } + if (Json->HasField(TEXT("identity_id"))) + { + Result.IdentityId = Json->GetStringField(TEXT("identity_id")); + } + if (Json->HasField(TEXT("session_id"))) + { + Result.SessionId = Json->GetStringField(TEXT("session_id")); + } + if (Json->HasField(TEXT("session_issued_at"))) + { + Result.SessionIssuedAt = Json->GetNumberField(TEXT("session_issued_at")); + } + if (Json->HasField(TEXT("session_expires_at"))) + { + Result.SessionExpiresAt = Json->GetNumberField(TEXT("session_expires_at")); + } + if (Json->HasField(TEXT("metadata"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("metadata"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.Metadata.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + return Result; +} + +TSharedPtr FSatoriEvent::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Name.IsEmpty() == false) + { + Json->SetStringField(TEXT("name"), Name); + } + if (Id.IsEmpty() == false) + { + Json->SetStringField(TEXT("id"), Id); + } + if (Value.IsEmpty() == false) + { + Json->SetStringField(TEXT("value"), Value); + } + + { + Json->SetStringField(TEXT("timestamp"), Timestamp.ToIso8601()); + } + if (IdentityId.IsEmpty() == false) + { + Json->SetStringField(TEXT("identity_id"), IdentityId); + } + if (SessionId.IsEmpty() == false) + { + Json->SetStringField(TEXT("session_id"), SessionId); + } + + { + Json->SetNumberField(TEXT("session_issued_at"), SessionIssuedAt); + } + + { + Json->SetNumberField(TEXT("session_expires_at"), SessionExpiresAt); + } + if (Metadata.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Metadata) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("metadata"), MapObj); + } + + return Json; +} + +FSatoriEventRequest FSatoriEventRequest::FromJson(const TSharedPtr& Json) +{ + FSatoriEventRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("events"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("events"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Events.Add(FSatoriEvent::FromJson(*ItemObj)); + } + } + } + } + return Result; +} + +TSharedPtr FSatoriEventRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Events.Num() > 0) + { + TArray> Array; + for (const auto& Item : Events) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("events"), Array); + } + + return Json; +} + +FSatoriExperiment FSatoriExperiment::FromJson(const TSharedPtr& Json) +{ + FSatoriExperiment Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("name"))) + { + Result.Name = Json->GetStringField(TEXT("name")); + } + if (Json->HasField(TEXT("value"))) + { + Result.Value = Json->GetStringField(TEXT("value")); + } + if (Json->HasField(TEXT("labels"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("labels"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.Labels.Add((Item->AsString())); + } + } + } + if (Json->HasField(TEXT("phase_name"))) + { + Result.PhaseName = Json->GetStringField(TEXT("phase_name")); + } + if (Json->HasField(TEXT("phase_variant_name"))) + { + Result.PhaseVariantName = Json->GetStringField(TEXT("phase_variant_name")); + } + return Result; +} + +TSharedPtr FSatoriExperiment::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Name.IsEmpty() == false) + { + Json->SetStringField(TEXT("name"), Name); + } + if (Value.IsEmpty() == false) + { + Json->SetStringField(TEXT("value"), Value); + } + if (Labels.Num() > 0) + { + TArray> Array; + for (const auto& Item : Labels) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("labels"), Array); + } + if (PhaseName.IsEmpty() == false) + { + Json->SetStringField(TEXT("phase_name"), PhaseName); + } + if (PhaseVariantName.IsEmpty() == false) + { + Json->SetStringField(TEXT("phase_variant_name"), PhaseVariantName); + } + + return Json; +} + +FSatoriExperimentList FSatoriExperimentList::FromJson(const TSharedPtr& Json) +{ + FSatoriExperimentList Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("experiments"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("experiments"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Experiments.Add(FSatoriExperiment::FromJson(*ItemObj)); + } + } + } + } + return Result; +} + +TSharedPtr FSatoriExperimentList::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Experiments.Num() > 0) + { + TArray> Array; + for (const auto& Item : Experiments) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("experiments"), Array); + } + + return Json; +} + +FSatoriValueChangeReason FSatoriValueChangeReason::FromJson(const TSharedPtr& Json) +{ + FSatoriValueChangeReason Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("type"))) + { + Result.Type = static_cast(Json->GetIntegerField(TEXT("type"))); + } + if (Json->HasField(TEXT("name"))) + { + Result.Name = Json->GetStringField(TEXT("name")); + } + if (Json->HasField(TEXT("variant_name"))) + { + Result.VariantName = Json->GetStringField(TEXT("variant_name")); + } + return Result; +} + +TSharedPtr FSatoriValueChangeReason::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetNumberField(TEXT("type"), static_cast(Type)); + } + if (Name.IsEmpty() == false) + { + Json->SetStringField(TEXT("name"), Name); + } + if (VariantName.IsEmpty() == false) + { + Json->SetStringField(TEXT("variant_name"), VariantName); + } + + return Json; +} + +FSatoriFlag FSatoriFlag::FromJson(const TSharedPtr& Json) +{ + FSatoriFlag Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("name"))) + { + Result.Name = Json->GetStringField(TEXT("name")); + } + if (Json->HasField(TEXT("value"))) + { + Result.Value = Json->GetStringField(TEXT("value")); + } + if (Json->HasField(TEXT("condition_changed"))) + { + Result.ConditionChanged = Json->GetBoolField(TEXT("condition_changed")); + } + if (Json->HasField(TEXT("change_reason"))) + { + const TSharedPtr* NestedObj; + if (Json->TryGetObjectField(TEXT("change_reason"), NestedObj)) + { + Result.ChangeReason = (FSatoriValueChangeReason::FromJson(*NestedObj)); + } + } + if (Json->HasField(TEXT("labels"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("labels"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.Labels.Add((Item->AsString())); + } + } + } + return Result; +} + +TSharedPtr FSatoriFlag::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Name.IsEmpty() == false) + { + Json->SetStringField(TEXT("name"), Name); + } + if (Value.IsEmpty() == false) + { + Json->SetStringField(TEXT("value"), Value); + } + + { + Json->SetBoolField(TEXT("condition_changed"), ConditionChanged); + } + + { + Json->SetObjectField(TEXT("change_reason"), ChangeReason.ToJson()); + } + if (Labels.Num() > 0) + { + TArray> Array; + for (const auto& Item : Labels) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("labels"), Array); + } + + return Json; +} + +FSatoriFlagList FSatoriFlagList::FromJson(const TSharedPtr& Json) +{ + FSatoriFlagList Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("flags"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("flags"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Flags.Add(FSatoriFlag::FromJson(*ItemObj)); + } + } + } + } + return Result; +} + +TSharedPtr FSatoriFlagList::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Flags.Num() > 0) + { + TArray> Array; + for (const auto& Item : Flags) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("flags"), Array); + } + + return Json; +} + +FSatoriFlagOverrideValue FSatoriFlagOverrideValue::FromJson(const TSharedPtr& Json) +{ + FSatoriFlagOverrideValue Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("type"))) + { + Result.Type = static_cast(Json->GetIntegerField(TEXT("type"))); + } + if (Json->HasField(TEXT("name"))) + { + Result.Name = Json->GetStringField(TEXT("name")); + } + if (Json->HasField(TEXT("variant_name"))) + { + Result.VariantName = Json->GetStringField(TEXT("variant_name")); + } + if (Json->HasField(TEXT("value"))) + { + Result.Value = Json->GetStringField(TEXT("value")); + } + if (Json->HasField(TEXT("create_time_sec"))) + { + Result.CreateTimeSec = Json->GetNumberField(TEXT("create_time_sec")); + } + return Result; +} + +TSharedPtr FSatoriFlagOverrideValue::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetNumberField(TEXT("type"), static_cast(Type)); + } + if (Name.IsEmpty() == false) + { + Json->SetStringField(TEXT("name"), Name); + } + if (VariantName.IsEmpty() == false) + { + Json->SetStringField(TEXT("variant_name"), VariantName); + } + if (Value.IsEmpty() == false) + { + Json->SetStringField(TEXT("value"), Value); + } + + { + Json->SetNumberField(TEXT("create_time_sec"), CreateTimeSec); + } + + return Json; +} + +FSatoriFlagOverride FSatoriFlagOverride::FromJson(const TSharedPtr& Json) +{ + FSatoriFlagOverride Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("flag_name"))) + { + Result.FlagName = Json->GetStringField(TEXT("flag_name")); + } + if (Json->HasField(TEXT("overrides"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("overrides"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Overrides.Add(FSatoriFlagOverrideValue::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("labels"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("labels"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.Labels.Add((Item->AsString())); + } + } + } + return Result; +} + +TSharedPtr FSatoriFlagOverride::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (FlagName.IsEmpty() == false) + { + Json->SetStringField(TEXT("flag_name"), FlagName); + } + if (Overrides.Num() > 0) + { + TArray> Array; + for (const auto& Item : Overrides) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("overrides"), Array); + } + if (Labels.Num() > 0) + { + TArray> Array; + for (const auto& Item : Labels) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("labels"), Array); + } + + return Json; +} + +FSatoriFlagOverrideList FSatoriFlagOverrideList::FromJson(const TSharedPtr& Json) +{ + FSatoriFlagOverrideList Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("flags"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("flags"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Flags.Add(FSatoriFlagOverride::FromJson(*ItemObj)); + } + } + } + } + return Result; +} + +TSharedPtr FSatoriFlagOverrideList::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Flags.Num() > 0) + { + TArray> Array; + for (const auto& Item : Flags) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("flags"), Array); + } + + return Json; +} + +FSatoriGetExperimentsRequest FSatoriGetExperimentsRequest::FromJson(const TSharedPtr& Json) +{ + FSatoriGetExperimentsRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("names"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("names"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.Names.Add((Item->AsString())); + } + } + } + if (Json->HasField(TEXT("labels"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("labels"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.Labels.Add((Item->AsString())); + } + } + } + return Result; +} + +TSharedPtr FSatoriGetExperimentsRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Names.Num() > 0) + { + TArray> Array; + for (const auto& Item : Names) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("names"), Array); + } + if (Labels.Num() > 0) + { + TArray> Array; + for (const auto& Item : Labels) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("labels"), Array); + } + + return Json; +} + +FSatoriGetFlagsRequest FSatoriGetFlagsRequest::FromJson(const TSharedPtr& Json) +{ + FSatoriGetFlagsRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("names"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("names"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.Names.Add((Item->AsString())); + } + } + } + if (Json->HasField(TEXT("labels"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("labels"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.Labels.Add((Item->AsString())); + } + } + } + return Result; +} + +TSharedPtr FSatoriGetFlagsRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Names.Num() > 0) + { + TArray> Array; + for (const auto& Item : Names) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("names"), Array); + } + if (Labels.Num() > 0) + { + TArray> Array; + for (const auto& Item : Labels) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("labels"), Array); + } + + return Json; +} + +FSatoriGetLiveEventsRequest FSatoriGetLiveEventsRequest::FromJson(const TSharedPtr& Json) +{ + FSatoriGetLiveEventsRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("names"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("names"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.Names.Add((Item->AsString())); + } + } + } + if (Json->HasField(TEXT("labels"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("labels"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.Labels.Add((Item->AsString())); + } + } + } + if (Json->HasField(TEXT("past_run_count"))) + { + Result.PastRunCount = Json->GetNumberField(TEXT("past_run_count")); + } + if (Json->HasField(TEXT("future_run_count"))) + { + Result.FutureRunCount = Json->GetNumberField(TEXT("future_run_count")); + } + if (Json->HasField(TEXT("start_time_sec"))) + { + Result.StartTimeSec = Json->GetNumberField(TEXT("start_time_sec")); + } + if (Json->HasField(TEXT("end_time_sec"))) + { + Result.EndTimeSec = Json->GetNumberField(TEXT("end_time_sec")); + } + return Result; +} + +TSharedPtr FSatoriGetLiveEventsRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Names.Num() > 0) + { + TArray> Array; + for (const auto& Item : Names) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("names"), Array); + } + if (Labels.Num() > 0) + { + TArray> Array; + for (const auto& Item : Labels) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("labels"), Array); + } + + { + Json->SetNumberField(TEXT("past_run_count"), PastRunCount); + } + + { + Json->SetNumberField(TEXT("future_run_count"), FutureRunCount); + } + + { + Json->SetNumberField(TEXT("start_time_sec"), StartTimeSec); + } + + { + Json->SetNumberField(TEXT("end_time_sec"), EndTimeSec); + } + + return Json; +} + +FSatoriMessage FSatoriMessage::FromJson(const TSharedPtr& Json) +{ + FSatoriMessage Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("schedule_id"))) + { + Result.ScheduleId = Json->GetStringField(TEXT("schedule_id")); + } + if (Json->HasField(TEXT("send_time"))) + { + Result.SendTime = Json->GetNumberField(TEXT("send_time")); + } + if (Json->HasField(TEXT("create_time"))) + { + Result.CreateTime = Json->GetNumberField(TEXT("create_time")); + } + if (Json->HasField(TEXT("update_time"))) + { + Result.UpdateTime = Json->GetNumberField(TEXT("update_time")); + } + if (Json->HasField(TEXT("read_time"))) + { + Result.ReadTime = Json->GetNumberField(TEXT("read_time")); + } + if (Json->HasField(TEXT("consume_time"))) + { + Result.ConsumeTime = Json->GetNumberField(TEXT("consume_time")); + } + if (Json->HasField(TEXT("text"))) + { + Result.Text = Json->GetStringField(TEXT("text")); + } + if (Json->HasField(TEXT("id"))) + { + Result.Id = Json->GetStringField(TEXT("id")); + } + if (Json->HasField(TEXT("title"))) + { + Result.Title = Json->GetStringField(TEXT("title")); + } + if (Json->HasField(TEXT("image_url"))) + { + Result.ImageUrl = Json->GetStringField(TEXT("image_url")); + } + if (Json->HasField(TEXT("metadata"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("metadata"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.Metadata.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + return Result; +} + +TSharedPtr FSatoriMessage::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (ScheduleId.IsEmpty() == false) + { + Json->SetStringField(TEXT("schedule_id"), ScheduleId); + } + + { + Json->SetNumberField(TEXT("send_time"), SendTime); + } + + { + Json->SetNumberField(TEXT("create_time"), CreateTime); + } + + { + Json->SetNumberField(TEXT("update_time"), UpdateTime); + } + + { + Json->SetNumberField(TEXT("read_time"), ReadTime); + } + + { + Json->SetNumberField(TEXT("consume_time"), ConsumeTime); + } + if (Text.IsEmpty() == false) + { + Json->SetStringField(TEXT("text"), Text); + } + if (Id.IsEmpty() == false) + { + Json->SetStringField(TEXT("id"), Id); + } + if (Title.IsEmpty() == false) + { + Json->SetStringField(TEXT("title"), Title); + } + if (ImageUrl.IsEmpty() == false) + { + Json->SetStringField(TEXT("image_url"), ImageUrl); + } + if (Metadata.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Metadata) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("metadata"), MapObj); + } + + return Json; +} + +FSatoriGetMessageListRequest FSatoriGetMessageListRequest::FromJson(const TSharedPtr& Json) +{ + FSatoriGetMessageListRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("limit"))) + { + Result.Limit = Json->GetNumberField(TEXT("limit")); + } + if (Json->HasField(TEXT("forward"))) + { + Result.Forward = Json->GetBoolField(TEXT("forward")); + } + if (Json->HasField(TEXT("cursor"))) + { + Result.Cursor = Json->GetStringField(TEXT("cursor")); + } + if (Json->HasField(TEXT("message_ids"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("message_ids"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.MessageIds.Add((Item->AsString())); + } + } + } + return Result; +} + +TSharedPtr FSatoriGetMessageListRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + + { + Json->SetNumberField(TEXT("limit"), Limit); + } + + { + Json->SetBoolField(TEXT("forward"), Forward); + } + if (Cursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cursor"), Cursor); + } + if (MessageIds.Num() > 0) + { + TArray> Array; + for (const auto& Item : MessageIds) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("message_ids"), Array); + } + + return Json; +} + +FSatoriGetMessageListResponse FSatoriGetMessageListResponse::FromJson(const TSharedPtr& Json) +{ + FSatoriGetMessageListResponse Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("messages"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("messages"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.Messages.Add(FSatoriMessage::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("next_cursor"))) + { + Result.NextCursor = Json->GetStringField(TEXT("next_cursor")); + } + if (Json->HasField(TEXT("prev_cursor"))) + { + Result.PrevCursor = Json->GetStringField(TEXT("prev_cursor")); + } + if (Json->HasField(TEXT("cacheable_cursor"))) + { + Result.CacheableCursor = Json->GetStringField(TEXT("cacheable_cursor")); + } + return Result; +} + +TSharedPtr FSatoriGetMessageListResponse::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Messages.Num() > 0) + { + TArray> Array; + for (const auto& Item : Messages) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("messages"), Array); + } + if (NextCursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("next_cursor"), NextCursor); + } + if (PrevCursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("prev_cursor"), PrevCursor); + } + if (CacheableCursor.IsEmpty() == false) + { + Json->SetStringField(TEXT("cacheable_cursor"), CacheableCursor); + } + + return Json; +} + +FSatoriIdentifyRequest FSatoriIdentifyRequest::FromJson(const TSharedPtr& Json) +{ + FSatoriIdentifyRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("id"))) + { + Result.Id = Json->GetStringField(TEXT("id")); + } + if (Json->HasField(TEXT("default"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("default"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.Default.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + if (Json->HasField(TEXT("custom"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("custom"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.Custom.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + return Result; +} + +TSharedPtr FSatoriIdentifyRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Id.IsEmpty() == false) + { + Json->SetStringField(TEXT("id"), Id); + } + if (Default.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Default) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("default"), MapObj); + } + if (Custom.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Custom) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("custom"), MapObj); + } + + return Json; +} + +FSatoriJoinLiveEventRequest FSatoriJoinLiveEventRequest::FromJson(const TSharedPtr& Json) +{ + FSatoriJoinLiveEventRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("id"))) + { + Result.Id = Json->GetStringField(TEXT("id")); + } + return Result; +} + +TSharedPtr FSatoriJoinLiveEventRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Id.IsEmpty() == false) + { + Json->SetStringField(TEXT("id"), Id); + } + + return Json; +} + +FSatoriLiveEvent FSatoriLiveEvent::FromJson(const TSharedPtr& Json) +{ + FSatoriLiveEvent Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("name"))) + { + Result.Name = Json->GetStringField(TEXT("name")); + } + if (Json->HasField(TEXT("description"))) + { + Result.Description = Json->GetStringField(TEXT("description")); + } + if (Json->HasField(TEXT("value"))) + { + Result.Value = Json->GetStringField(TEXT("value")); + } + if (Json->HasField(TEXT("active_start_time_sec"))) + { + Result.ActiveStartTimeSec = Json->GetNumberField(TEXT("active_start_time_sec")); + } + if (Json->HasField(TEXT("active_end_time_sec"))) + { + Result.ActiveEndTimeSec = Json->GetNumberField(TEXT("active_end_time_sec")); + } + if (Json->HasField(TEXT("id"))) + { + Result.Id = Json->GetStringField(TEXT("id")); + } + if (Json->HasField(TEXT("start_time_sec"))) + { + Result.StartTimeSec = Json->GetNumberField(TEXT("start_time_sec")); + } + if (Json->HasField(TEXT("end_time_sec"))) + { + Result.EndTimeSec = Json->GetNumberField(TEXT("end_time_sec")); + } + if (Json->HasField(TEXT("duration_sec"))) + { + Result.DurationSec = Json->GetNumberField(TEXT("duration_sec")); + } + if (Json->HasField(TEXT("reset_cron"))) + { + Result.ResetCron = Json->GetStringField(TEXT("reset_cron")); + } + if (Json->HasField(TEXT("status"))) + { + Result.Status = static_cast(Json->GetIntegerField(TEXT("status"))); + } + if (Json->HasField(TEXT("labels"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("labels"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + Result.Labels.Add((Item->AsString())); + } + } + } + return Result; +} + +TSharedPtr FSatoriLiveEvent::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Name.IsEmpty() == false) + { + Json->SetStringField(TEXT("name"), Name); + } + if (Description.IsEmpty() == false) + { + Json->SetStringField(TEXT("description"), Description); + } + if (Value.IsEmpty() == false) + { + Json->SetStringField(TEXT("value"), Value); + } + + { + Json->SetNumberField(TEXT("active_start_time_sec"), ActiveStartTimeSec); + } + + { + Json->SetNumberField(TEXT("active_end_time_sec"), ActiveEndTimeSec); + } + if (Id.IsEmpty() == false) + { + Json->SetStringField(TEXT("id"), Id); + } + + { + Json->SetNumberField(TEXT("start_time_sec"), StartTimeSec); + } + + { + Json->SetNumberField(TEXT("end_time_sec"), EndTimeSec); + } + + { + Json->SetNumberField(TEXT("duration_sec"), DurationSec); + } + if (ResetCron.IsEmpty() == false) + { + Json->SetStringField(TEXT("reset_cron"), ResetCron); + } + + { + Json->SetNumberField(TEXT("status"), static_cast(Status)); + } + if (Labels.Num() > 0) + { + TArray> Array; + for (const auto& Item : Labels) + { + Array.Add(MakeShared(Item)); + } + Json->SetArrayField(TEXT("labels"), Array); + } + + return Json; +} + +FSatoriLiveEventList FSatoriLiveEventList::FromJson(const TSharedPtr& Json) +{ + FSatoriLiveEventList Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("live_events"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("live_events"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.LiveEvents.Add(FSatoriLiveEvent::FromJson(*ItemObj)); + } + } + } + } + if (Json->HasField(TEXT("explicit_join_live_events"))) + { + const TArray>* ArrayPtr; + if (Json->TryGetArrayField(TEXT("explicit_join_live_events"), ArrayPtr)) + { + for (const auto& Item : *ArrayPtr) + { + const TSharedPtr* ItemObj = nullptr; + if (Item->TryGetObject(ItemObj) && ItemObj) + { + Result.ExplicitJoinLiveEvents.Add(FSatoriLiveEvent::FromJson(*ItemObj)); + } + } + } + } + return Result; +} + +TSharedPtr FSatoriLiveEventList::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (LiveEvents.Num() > 0) + { + TArray> Array; + for (const auto& Item : LiveEvents) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("live_events"), Array); + } + if (ExplicitJoinLiveEvents.Num() > 0) + { + TArray> Array; + for (const auto& Item : ExplicitJoinLiveEvents) + { + Array.Add(MakeShared(Item.ToJson())); + } + Json->SetArrayField(TEXT("explicit_join_live_events"), Array); + } + + return Json; +} + +FSatoriProperties FSatoriProperties::FromJson(const TSharedPtr& Json) +{ + FSatoriProperties Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("default"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("default"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.Default.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + if (Json->HasField(TEXT("computed"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("computed"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.Computed.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + if (Json->HasField(TEXT("custom"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("custom"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.Custom.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + return Result; +} + +TSharedPtr FSatoriProperties::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Default.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Default) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("default"), MapObj); + } + if (Computed.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Computed) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("computed"), MapObj); + } + if (Custom.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Custom) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("custom"), MapObj); + } + + return Json; +} + +FSatoriUpdatePropertiesRequest FSatoriUpdatePropertiesRequest::FromJson(const TSharedPtr& Json) +{ + FSatoriUpdatePropertiesRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("recompute"))) + { + Result.Recompute = Json->GetBoolField(TEXT("recompute")); + } + if (Json->HasField(TEXT("default"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("default"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.Default.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + if (Json->HasField(TEXT("custom"))) + { + const TSharedPtr* MapObj; + if (Json->TryGetObjectField(TEXT("custom"), MapObj)) + { + for (const auto& Pair : (*MapObj)->Values) + { + Result.Custom.Add(Pair.Key, Pair.Value->AsString()); + } + } + } + return Result; +} + +TSharedPtr FSatoriUpdatePropertiesRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Recompute.IsEmpty() == false) + { + Json->SetBoolField(TEXT("recompute"), Recompute.GetValue()); + } + if (Default.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Default) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("default"), MapObj); + } + if (Custom.Num() > 0) + { + TSharedPtr MapObj = MakeShared(); + for (const auto& Pair : Custom) + { + MapObj->SetStringField(Pair.Key, Pair.Value); + } + Json->SetObjectField(TEXT("custom"), MapObj); + } + + return Json; +} + +FSatoriUpdateMessageRequest FSatoriUpdateMessageRequest::FromJson(const TSharedPtr& Json) +{ + FSatoriUpdateMessageRequest Result; + if (!Json.IsValid()) + { + return Result; + } + if (Json->HasField(TEXT("id"))) + { + Result.Id = Json->GetStringField(TEXT("id")); + } + if (Json->HasField(TEXT("read_time"))) + { + Result.ReadTime = Json->GetNumberField(TEXT("read_time")); + } + if (Json->HasField(TEXT("consume_time"))) + { + Result.ConsumeTime = Json->GetNumberField(TEXT("consume_time")); + } + return Result; +} + +TSharedPtr FSatoriUpdateMessageRequest::ToJson() const +{ + TSharedPtr Json = MakeShared(); + if (Id.IsEmpty() == false) + { + Json->SetStringField(TEXT("id"), Id); + } + + { + Json->SetNumberField(TEXT("read_time"), ReadTime); + } + + { + Json->SetNumberField(TEXT("consume_time"), ConsumeTime); + } + + return Json; +} + diff --git a/Satori/Source/SatoriApi/Public/AsyncFuture.h b/Satori/Source/SatoriApi/Public/AsyncFuture.h new file mode 100644 index 000000000..2e7976f96 --- /dev/null +++ b/Satori/Source/SatoriApi/Public/AsyncFuture.h @@ -0,0 +1,198 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "CoreMinimal.h" +#include "Tasks/Task.h" +#include "Async/TaskGraphInterfaces.h" +#include + +// Forward declaration +template struct TAsyncFuture; + +/** Type trait: is T a TAsyncFuture? */ +template struct TIsTAsyncFuture : std::false_type {}; +template struct TIsTAsyncFuture> : std::true_type {}; + +/** + * Chainable, game-thread-safe future for async operations. + * + * Parameterized by a concrete result type (e.g. FNakamaSessionResult or + * FSatoriSessionResult). Every user-visible callback is dispatched to the + * game thread via AsyncTask(ENamedThreads::GameThread, ...) so that callers + * can safely touch UObject*, fire delegates, or update UI without additional + * marshalling. + * + * Three .Next() overloads are provided: + * + * 1. Chaining with auto-propagation: + * Next(callback(const ValueType&) -> TAsyncFuture) + * Available only when ResultT defines ::ValueType. + * On error the error is forwarded to the outer future and the callback is + * skipped entirely (error propagation runs on the background thread because + * no user code is involved). + * + * 2. Chaining without auto-propagation: + * Next(callback(ResultT) -> TAsyncFuture) + * Available for all ResultT, including types without ::ValueType (e.g. + * WebSocket envelope types). The callback receives the full result and is + * responsible for inspecting bIsError and deciding what to return. + * + * 3. Terminal: + * Next(callback(ResultT) -> void) + * Called unconditionally when the future resolves. The caller inspects + * the result for errors inside the callback. + */ +template +struct TAsyncFuture +{ + using WrappedResultType = ResultT; + + struct FState + { + ResultT Result{}; + UE::Tasks::FTaskEvent Event{ UE_SOURCE_LOCATION }; + void Resolve(ResultT&& InResult) + { + Result = MoveTemp(InResult); + Event.Trigger(); + } + }; + + TSharedPtr State; + + TAsyncFuture() = default; + explicit TAsyncFuture(TSharedPtr InState) noexcept + : State(MoveTemp(InState)) {} + TAsyncFuture(TAsyncFuture&& Other) noexcept = default; + TAsyncFuture& operator=(TAsyncFuture&& Other) noexcept = default; + TAsyncFuture(const TAsyncFuture&) = delete; + TAsyncFuture& operator=(const TAsyncFuture&) = delete; + + /** + * Overload 1 — Chaining with auto-propagation. + * callback(const ValueType&) -> TAsyncFuture + * Only enabled when ResultT has ::ValueType. + * On error, propagates to OtherResult without calling the callback. + */ + template, const VT&>, + std::enable_if_t::value, int> = 0> + Ret Next(Func&& Callback) && noexcept + { + using InnerResultT = typename Ret::WrappedResultType; + auto OuterState = MakeShared::FState>(); + auto CapturedState = State; + UE::Tasks::Launch(UE_SOURCE_LOCATION, + [Cb = Forward(Callback), CapturedState, OuterState]() mutable + { + if (CapturedState->Result.bIsError) + { + // Error propagation: no user code involved, safe on any thread. + OuterState->Resolve(InnerResultT{{}, CapturedState->Result.Error, true}); + return; + } + // User callback may touch UObject* or fire delegates — dispatch to game thread. + AsyncTask(ENamedThreads::GameThread, + [Cb = MoveTemp(Cb), CapturedState, OuterState]() mutable + { + Ret Inner = Cb(CapturedState->Result.Value); + auto InnerState = Inner.State; + UE::Tasks::Launch(UE_SOURCE_LOCATION, + [InnerState, OuterState]() + { + OuterState->Resolve(MoveTemp(InnerState->Result)); + }, + InnerState->Event); + }); + }, + CapturedState->Event); + State.Reset(); + return TAsyncFuture(OuterState); + } + + /** + * Overload 2 — Chaining, user handles errors. + * callback(ResultT) -> TAsyncFuture + * Available for all ResultT, including WebSocket types without ::ValueType. + * The callback receives the full result and is responsible for error handling. + */ + template, ResultT>, + std::enable_if_t::value, int> = 0> + Ret Next(Func&& Callback) && noexcept + { + using InnerResultT = typename Ret::WrappedResultType; + auto OuterState = MakeShared::FState>(); + auto CapturedState = State; + UE::Tasks::Launch(UE_SOURCE_LOCATION, + [Cb = Forward(Callback), CapturedState, OuterState]() mutable + { + // User callback may touch UObject* or fire delegates — dispatch to game thread. + AsyncTask(ENamedThreads::GameThread, + [Cb = MoveTemp(Cb), CapturedState, OuterState]() mutable + { + Ret Inner = Cb(MoveTemp(CapturedState->Result)); + auto InnerState = Inner.State; + UE::Tasks::Launch(UE_SOURCE_LOCATION, + [InnerState, OuterState]() + { + OuterState->Resolve(MoveTemp(InnerState->Result)); + }, + InnerState->Event); + }); + }, + CapturedState->Event); + State.Reset(); + return TAsyncFuture(OuterState); + } + + /** Overload 3 — Terminal. callback(ResultT) -> void */ + template, ResultT>, + std::enable_if_t::value, int> = 0> + void Next(Func&& Callback) && noexcept + { + auto CapturedState = State; + UE::Tasks::Launch(UE_SOURCE_LOCATION, + [Cb = Forward(Callback), CapturedState]() mutable + { + // User callback may touch UObject* or fire delegates — dispatch to game thread. + AsyncTask(ENamedThreads::GameThread, + [Cb = MoveTemp(Cb), CapturedState]() mutable + { + Cb(MoveTemp(CapturedState->Result)); + }); + }, + CapturedState->Event); + State.Reset(); + } +}; + +/** + * Create a pre-resolved TAsyncFuture. + * Useful for returning an immediate result (e.g. a local error) from a + * chaining callback without going through the network stack. + */ +template +TAsyncFuture MakeCompletedAsyncFuture(ResultT Value) +{ + auto State = MakeShared::FState>(); + State->Resolve(MoveTemp(Value)); + return TAsyncFuture(State); +} diff --git a/Satori/Source/SatoriApi/Public/SatoriApi.h b/Satori/Source/SatoriApi/Public/SatoriApi.h new file mode 100644 index 000000000..0179aea88 --- /dev/null +++ b/Satori/Source/SatoriApi/Public/SatoriApi.h @@ -0,0 +1,825 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + +#pragma once + +#include +#include "CoreMinimal.h" +#include "Templates/Function.h" +#include "Templates/SharedPointer.h" +#include "Dom/JsonObject.h" +#include "SatoriError.h" +#include "SatoriClientConfig.h" +#include "SatoriSession.h" +#include "SatoriTypes.h" + +SATORIAPI_API DECLARE_LOG_CATEGORY_EXTERN(LogSatori, Log, All); + +namespace SatoriApi +{ + + /* + * Authenticate against the server. + * + * @param Config The client configuration. + * @param Id Identity ID. Must be between eight and 128 characters (inclusive). + * @param NoSession Optional no_session modifies the request to only create/update + * @param Default Optional default properties to update with this call. + * @param Custom Optional custom properties to update with this call. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void Authenticate( + const FSatoriClientConfig& Config, + const FString& Id, + bool NoSession, + const TMap& Default, + const TMap& Custom, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Log out a session, invalidate a refresh token, or log out all sessions/refresh tokens for a user. + * + * @param Config The client configuration. + * @param Token Session token to log out. + * @param RefreshToken Refresh token to invalidate. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void AuthenticateLogout( + const FSatoriClientConfig& Config, + const FString& Token, + const FString& RefreshToken, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Refresh a user's session using a refresh token retrieved from a previous authentication request. + * + * @param Config The client configuration. + * @param RefreshToken Refresh token. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void AuthenticateRefresh( + const FSatoriClientConfig& Config, + const FString& RefreshToken, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Delete the caller's identity and associated data. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void DeleteIdentity( + const FSatoriClientConfig& Config, + const FString& HttpKey, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Delete the caller's identity and associated data. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void DeleteIdentity( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Publish an event for this session. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Events Some number of events produced by a client. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void Event( + const FSatoriClientConfig& Config, + const FString& HttpKey, + const TArray& Events, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Publish an event for this session. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Events Some number of events produced by a client. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void Event( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + const TArray& Events, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Publish server events for multiple distinct identities. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Events Some number of events produced by a client. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void ServerEvent( + const FSatoriClientConfig& Config, + const FString& HttpKey, + const TArray& Events, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Publish server events for multiple distinct identities. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Events Some number of events produced by a client. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void ServerEvent( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + const TArray& Events, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Get or list all available experiments for this identity. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Names Experiment names; if empty string, all experiments are returned based on the remaining filters. + * @param Labels Label names that must be defined for each Experiment; if empty string, all experiments are returned based on the remaining filters. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void GetExperiments( + const FSatoriClientConfig& Config, + const FString& HttpKey, + const TArray& Names, + const TArray& Labels, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Get or list all available experiments for this identity. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Names Experiment names; if empty string, all experiments are returned based on the remaining filters. + * @param Labels Label names that must be defined for each Experiment; if empty string, all experiments are returned based on the remaining filters. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void GetExperiments( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + const TArray& Names, + const TArray& Labels, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List all available flags and their value overrides for this identity. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Names Flag names; if empty string, all flags are returned based on the remaining filters. + * @param Labels Label names that must be defined for each Flag; if empty string, all flags are returned based on the remaining filters. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void GetFlagOverrides( + const FSatoriClientConfig& Config, + const FString& HttpKey, + const TArray& Names, + const TArray& Labels, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List all available flags and their value overrides for this identity. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Names Flag names; if empty string, all flags are returned based on the remaining filters. + * @param Labels Label names that must be defined for each Flag; if empty string, all flags are returned based on the remaining filters. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void GetFlagOverrides( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + const TArray& Names, + const TArray& Labels, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List all available flags for this identity. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Names Flag names; if empty string, all flags are returned based on the remaining filters. + * @param Labels Label names that must be defined for each Flag; if empty string, all flags are returned based on the remaining filters. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void GetFlags( + const FSatoriClientConfig& Config, + const FString& HttpKey, + const TArray& Names, + const TArray& Labels, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List all available flags for this identity. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Names Flag names; if empty string, all flags are returned based on the remaining filters. + * @param Labels Label names that must be defined for each Flag; if empty string, all flags are returned based on the remaining filters. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void GetFlags( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + const TArray& Names, + const TArray& Labels, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List available live events. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Names Live event names; if empty string, all live events are returned based on the remaining filters. + * @param Labels Label names that must be defined for each Live Event; if empty string, all live events are returned based on the remaining filters. + * @param PastRunCount The maximum number of past event runs to return for each live event. + * @param FutureRunCount The maximum number of future event runs to return for each live event. + * @param StartTimeSec Start time of the time window filter to apply. + * @param EndTimeSec End time of the time window filter to apply. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void GetLiveEvents( + const FSatoriClientConfig& Config, + const FString& HttpKey, + const TArray& Names, + const TArray& Labels, + int32 PastRunCount, + int32 FutureRunCount, + int64 StartTimeSec, + int64 EndTimeSec, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List available live events. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Names Live event names; if empty string, all live events are returned based on the remaining filters. + * @param Labels Label names that must be defined for each Live Event; if empty string, all live events are returned based on the remaining filters. + * @param PastRunCount The maximum number of past event runs to return for each live event. + * @param FutureRunCount The maximum number of future event runs to return for each live event. + * @param StartTimeSec Start time of the time window filter to apply. + * @param EndTimeSec End time of the time window filter to apply. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void GetLiveEvents( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + const TArray& Names, + const TArray& Labels, + int32 PastRunCount, + int32 FutureRunCount, + int64 StartTimeSec, + int64 EndTimeSec, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Join an 'explicit join' live event. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Id Live event id to join. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void JoinLiveEvent( + const FSatoriClientConfig& Config, + const FString& HttpKey, + const FString& Id, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Join an 'explicit join' live event. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Id Live event id to join. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void JoinLiveEvent( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + const FString& Id, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * A healthcheck which load balancers can use to check the service. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void Healthcheck( + const FSatoriClientConfig& Config, + const FString& HttpKey, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * A healthcheck which load balancers can use to check the service. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void Healthcheck( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Enrich/replace the current session with new identifier. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Id Identity ID to enrich the current session and return a new session. Old session will no longer be usable. + * @param Default Optional default properties to update with this call. + * @param Custom Optional custom properties to update with this call. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void Identify( + const FSatoriClientConfig& Config, + const FString& HttpKey, + const FString& Id, + const TMap& Default, + const TMap& Custom, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Enrich/replace the current session with new identifier. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Id Identity ID to enrich the current session and return a new session. Old session will no longer be usable. + * @param Default Optional default properties to update with this call. + * @param Custom Optional custom properties to update with this call. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void Identify( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + const FString& Id, + const TMap& Default, + const TMap& Custom, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List properties associated with this identity. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void ListProperties( + const FSatoriClientConfig& Config, + const FString& HttpKey, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * List properties associated with this identity. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void ListProperties( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * A readycheck which load balancers can use to check the service. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void Readycheck( + const FSatoriClientConfig& Config, + const FString& HttpKey, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * A readycheck which load balancers can use to check the service. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void Readycheck( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Update identity properties. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Recompute Informs the server to recompute the audience membership of the identity. + * @param Default Event default properties. + * @param Custom Event custom properties. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void UpdateProperties( + const FSatoriClientConfig& Config, + const FString& HttpKey, + FSatoriOptionalBool Recompute, + const TMap& Default, + const TMap& Custom, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Update identity properties. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Recompute Informs the server to recompute the audience membership of the identity. + * @param Default Event default properties. + * @param Custom Event custom properties. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void UpdateProperties( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + FSatoriOptionalBool Recompute, + const TMap& Default, + const TMap& Custom, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Get the list of messages for the identity. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Limit Max number of messages to return. Between 1 and 100. + * @param Forward True if listing should be older messages to newer, false if reverse. + * @param Cursor A pagination cursor, if any. + * @param MessageIds A list of message identifiers to be returned. If these are given as inputs, the pagination will be disabled. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void GetMessageList( + const FSatoriClientConfig& Config, + const FString& HttpKey, + int32 Limit, + bool Forward, + const FString& Cursor, + const TArray& MessageIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Get the list of messages for the identity. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Limit Max number of messages to return. Between 1 and 100. + * @param Forward True if listing should be older messages to newer, false if reverse. + * @param Cursor A pagination cursor, if any. + * @param MessageIds A list of message identifiers to be returned. If these are given as inputs, the pagination will be disabled. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void GetMessageList( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + int32 Limit, + bool Forward, + const FString& Cursor, + const TArray& MessageIds, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Updates a message for an identity. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Id The identifier of the messages. + * @param ReadTime The time the message was read at the client. + * @param ConsumeTime The time the message was consumed by the identity. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void UpdateMessage( + const FSatoriClientConfig& Config, + const FString& HttpKey, + const FString& Id, + int64 ReadTime, + int64 ConsumeTime, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Updates a message for an identity. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Id The identifier of the messages. + * @param ReadTime The time the message was read at the client. + * @param ConsumeTime The time the message was consumed by the identity. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void UpdateMessage( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + const FString& Id, + int64 ReadTime, + int64 ConsumeTime, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Deletes a message for an identity. + * + * @param Config The client configuration. + * @param HttpKey HttpKey for server-to-server communication + * @param Id The identifier of the message. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void DeleteMessage( + const FSatoriClientConfig& Config, + const FString& HttpKey, + const FString& Id, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + + /* + * Deletes a message for an identity. + * + * @param Config The client configuration. + * @param Session The session of the user. + * @param Id The identifier of the message. + * @param OnSuccess Called when the operation succeeds. + * @param OnError Called when the operation fails. + * @param Timeout Request timeout in seconds. + * @param CancellationToken Set to true to cancel the in-flight request. + **/ + SATORIAPI_API void DeleteMessage( + const FSatoriClientConfig& Config, + const FSatoriSession& Session, + const FString& Id, + TFunction OnSuccess, + TFunction OnError, + float Timeout = 10.0f, + TSharedRef> CancellationToken = MakeShared>(false) + ); + +} diff --git a/Satori/Source/SatoriBlueprints/Public/SatoriBlueprints.h b/Satori/Source/SatoriApi/Public/SatoriApiModule.h similarity index 76% rename from Satori/Source/SatoriBlueprints/Public/SatoriBlueprints.h rename to Satori/Source/SatoriApi/Public/SatoriApiModule.h index 9ff6aed95..cf5b321a8 100644 --- a/Satori/Source/SatoriBlueprints/Public/SatoriBlueprints.h +++ b/Satori/Source/SatoriApi/Public/SatoriApiModule.h @@ -1,5 +1,5 @@ /* -* Copyright 2025 The Nakama Authors + * Copyright 2026 The Nakama Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,18 +16,11 @@ #pragma once -#include "CoreMinimal.h" #include "Modules/ModuleManager.h" -DECLARE_LOG_CATEGORY_EXTERN(LogSatoriBlueprints, Log, All); - - -class FSatoriBlueprintsModule : public IModuleInterface +class FSatoriApiModule : public IModuleInterface { public: - - /** IModuleInterface implementation */ virtual void StartupModule() override; virtual void ShutdownModule() override; - }; diff --git a/Satori/Source/SatoriApi/Public/SatoriClientConfig.h b/Satori/Source/SatoriApi/Public/SatoriClientConfig.h new file mode 100644 index 000000000..ea7b85c8a --- /dev/null +++ b/Satori/Source/SatoriApi/Public/SatoriClientConfig.h @@ -0,0 +1,56 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "CoreMinimal.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "SatoriClientConfig.generated.h" + +/** Low-level API client configuration. */ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriClientConfig +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Satori") + FString ServerKey; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Satori") + FString Host; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Satori") + int32 Port = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Satori") + bool bUseSSL = false; + + FString GetBaseUrl() const noexcept; +}; + +UCLASS() +class SATORIAPI_API USatoriClientConfigFunctions : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + + UFUNCTION(BlueprintPure, Category = "Satori|ClientConfig") + static FString GetBaseUrl(const FSatoriClientConfig& Config) + { + return Config.GetBaseUrl(); + } +}; diff --git a/Satori/Source/SatoriApi/Public/SatoriError.h b/Satori/Source/SatoriApi/Public/SatoriError.h new file mode 100644 index 000000000..5c42e8994 --- /dev/null +++ b/Satori/Source/SatoriApi/Public/SatoriError.h @@ -0,0 +1,54 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "SatoriError.generated.h" + +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriError +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString Message; + + UPROPERTY(BlueprintReadWrite, Category = "Satori") + int32 Code = 0; + +}; + +/** gRPC status codes returned by Satori in FSatoriError::Code. */ +namespace SatoriErrorCode +{ + constexpr int32 OK = 0; + constexpr int32 Cancelled = 1; + constexpr int32 Unknown = 2; + constexpr int32 InvalidArgument = 3; + constexpr int32 DeadlineExceeded = 4; + constexpr int32 NotFound = 5; + constexpr int32 AlreadyExists = 6; + constexpr int32 PermissionDenied = 7; + constexpr int32 ResourceExhausted = 8; + constexpr int32 FailedPrecondition = 9; + constexpr int32 Aborted = 10; + constexpr int32 OutOfRange = 11; + constexpr int32 Unimplemented = 12; + constexpr int32 Internal = 13; + constexpr int32 Unavailable = 14; + constexpr int32 DataLoss = 15; + constexpr int32 Unauthenticated = 16; +} diff --git a/Satori/Source/SatoriApi/Public/SatoriHttpHelper.h b/Satori/Source/SatoriApi/Public/SatoriHttpHelper.h new file mode 100644 index 000000000..10b2d25d2 --- /dev/null +++ b/Satori/Source/SatoriApi/Public/SatoriHttpHelper.h @@ -0,0 +1,254 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "CoreMinimal.h" +#include "Dom/JsonObject.h" +#include "HttpModule.h" +#include "Interfaces/IHttpRequest.h" +#include "Interfaces/IHttpResponse.h" +#include "Misc/Base64.h" +#include "Misc/DateTime.h" +#include "Serialization/JsonReader.h" +#include "Serialization/JsonSerializer.h" +#include "Serialization/JsonWriter.h" +#include "Serialization/MemoryWriter.h" +#include "Templates/Function.h" +#include "Templates/SharedPointer.h" +#include "SatoriError.h" +#include "SatoriClientConfig.h" +#include "SatoriApi.h" + +/** + * Internal HTTP helpers for SatoriApi. + * All functions are typed against FSatoriClientConfig, ESatoriRequestAuth, and FSatoriError + * so SatoriApi does not depend on Nakama modules. + * + * Include this header in generated .cpp files only — not in public API headers. + */ +namespace SatoriHttpInternal +{ + +inline FString SerializeJsonToString(const TSharedPtr& Json) +{ + TArray JsonBytes; + JsonBytes.Reserve(1024); + FMemoryWriter Archive(JsonBytes); + TSharedRef>> Writer = + TJsonWriterFactory>::Create(&Archive); + FJsonSerializer::Serialize(Json.ToSharedRef(), Writer); + Writer->Close(); + FUTF8ToTCHAR Converter(reinterpret_cast(JsonBytes.GetData()), JsonBytes.Num()); + return FString(Converter.Length(), Converter.Get()); +} + +inline FString SerializeJsonEscaped(const TSharedPtr& Json) +{ + TArray JsonBytes; + JsonBytes.Reserve(2048); + FMemoryWriter Archive(JsonBytes); + TSharedRef>> Writer = + TJsonWriterFactory>::Create(&Archive); + FJsonSerializer::Serialize(Json.ToSharedRef(), Writer); + Writer->Close(); + FUTF8ToTCHAR Converter(reinterpret_cast(JsonBytes.GetData()), JsonBytes.Num()); + FString Condensed(Converter.Length(), Converter.Get()); + FString Escaped = Condensed.Replace(TEXT("\\"), TEXT("\\\\")).Replace(TEXT("\""), TEXT("\\\"")); + return TEXT("\"") + Escaped + TEXT("\""); +} + +inline void DoHttpRequest( + const FSatoriClientConfig& Config, + const FString& Endpoint, + const FString& Method, + const FString& BodyString, + ESatoriRequestAuth AuthType, + const FString& TokenString, + TFunction)> OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken) noexcept +{ + const FString Url = Config.GetBaseUrl() + Endpoint; + + TSharedRef Request = FHttpModule::Get().CreateRequest(); + Request->SetURL(Url); + Request->SetVerb(Method); + Request->SetHeader(TEXT("Content-Type"), TEXT("application/json")); + Request->SetHeader(TEXT("Accept"), TEXT("application/json")); + + switch (AuthType) + { + case ESatoriRequestAuth::Basic: + { + const FString Auth = FString::Printf(TEXT("%s:"), *Config.ServerKey); + Request->SetHeader(TEXT("Authorization"), FString::Printf(TEXT("Basic %s"), *FBase64::Encode(Auth))); + } + break; + case ESatoriRequestAuth::Bearer: + if (!TokenString.IsEmpty()) + { + Request->SetHeader(TEXT("Authorization"), FString::Printf(TEXT("Bearer %s"), *TokenString)); + } + break; + case ESatoriRequestAuth::HttpKey: + if (!TokenString.IsEmpty()) + { + const FString Auth = FString::Printf(TEXT("%s:"), *TokenString); + Request->SetHeader(TEXT("Authorization"), FString::Printf(TEXT("Basic %s"), *FBase64::Encode(Auth))); + } + break; + case ESatoriRequestAuth::None: + default: + break; + } + + if (!BodyString.IsEmpty() && Method != TEXT("GET")) + { + Request->SetContentAsString(BodyString); + } + + Request->SetTimeout(Timeout); + + Request->OnProcessRequestComplete().BindLambda( + [OnSuccess, OnError, CancellationToken](FHttpRequestPtr Req, FHttpResponsePtr Res, bool bSuccess) + { + if (CancellationToken->Load()) + { + if (OnError) + { + OnError(FSatoriError{TEXT("Request cancelled"), -1}); + } + return; + } + + if (!bSuccess || !Res.IsValid()) + { + if (OnError) + { + OnError(FSatoriError{TEXT("Connection failed"), 0}); + } + return; + } + + const int32 Code = Res->GetResponseCode(); + const FString Content = Res->GetContentAsString(); + + if (Code < 200 || Code >= 300) + { + FString ErrorMsg = FString::Printf(TEXT("HTTP %d"), Code); + int32 ErrorCode = Code; + TSharedPtr Json; + if (FJsonSerializer::Deserialize(TJsonReaderFactory<>::Create(Content), Json) && Json.IsValid()) + { + if (Json->HasField(TEXT("message"))) + { + ErrorMsg = Json->GetStringField(TEXT("message")); + } + if (Json->HasField(TEXT("code"))) + { + ErrorCode = static_cast(Json->GetNumberField(TEXT("code"))); + } + } + if (OnError) + { + OnError(FSatoriError{ErrorMsg, ErrorCode}); + } + return; + } + + TSharedPtr Json; + if (!Content.IsEmpty()) + { + if (!FJsonSerializer::Deserialize(TJsonReaderFactory<>::Create(Content), Json) || !Json.IsValid()) + { + if (OnError) + { + OnError(FSatoriError{TEXT("Invalid JSON response"), 500}); + } + return; + } + } + + if (OnSuccess) + { + OnSuccess(Json); + } + }); + + Request->ProcessRequest(); +} + +inline FString BuildQueryString(const TArray>& QueryParams) +{ + if (QueryParams.Num() == 0) return FString{}; + TArray Parts; + Parts.Reserve(QueryParams.Num()); + for (const auto& P : QueryParams) + { + Parts.Add(P.Key + TEXT("=") + FGenericPlatformHttp::UrlEncode(P.Value)); + } + return TEXT("?") + FString::Join(Parts, TEXT("&")); +} + +inline void SendRequest( + const FSatoriClientConfig& Config, + const FString& Endpoint, + const TArray>& QueryParams, + const FString& Method, + const FString& BodyString, + ESatoriRequestAuth AuthType, + const FString& BearerToken, + TFunction)> OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken) noexcept +{ + if (CancellationToken->Load()) + { + if (OnError) + { + OnError(FSatoriError{TEXT("Request cancelled"), -1}); + } + return; + } + + DoHttpRequest(Config, Endpoint + BuildQueryString(QueryParams), Method, BodyString, AuthType, BearerToken, OnSuccess, OnError, Timeout, CancellationToken); +} + +inline void MakeRequest( + const FSatoriClientConfig& Config, + const FString& Endpoint, + const TArray>& QueryParams, + const FString& Method, + const TSharedPtr& Body, + ESatoriRequestAuth AuthType, + const FString& BearerToken, + TFunction)> OnSuccess, + TFunction OnError, + float Timeout, + TSharedRef> CancellationToken) noexcept +{ + FString BodyString; + if (Body.IsValid() && Method != TEXT("GET")) + { + BodyString = SerializeJsonToString(Body); + } + SendRequest(Config, Endpoint, QueryParams, Method, BodyString, AuthType, BearerToken, OnSuccess, OnError, Timeout, CancellationToken); +} + +} // namespace SatoriHttpInternal diff --git a/Satori/Source/SatoriApi/Public/SatoriOptionals.h b/Satori/Source/SatoriApi/Public/SatoriOptionals.h new file mode 100644 index 000000000..24d34f634 --- /dev/null +++ b/Satori/Source/SatoriApi/Public/SatoriOptionals.h @@ -0,0 +1,119 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "CoreMinimal.h" +#include "SatoriOptionals.generated.h" + +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriOptionalBool +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Satori|Optional") + bool bIsSet = false; + + UPROPERTY(BlueprintReadWrite, Category = "Satori|Optional") + bool Value = false; + + FSatoriOptionalBool() = default; + FSatoriOptionalBool(bool InValue) : bIsSet(true), Value(InValue) {} + + bool IsSet() const { return bIsSet; } + bool IsEmpty() const { return !bIsSet; } + bool GetValue() const { return Value; } +}; + +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriOptionalInt32 +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Satori|Optional") + bool bIsSet = false; + + UPROPERTY(BlueprintReadWrite, Category = "Satori|Optional") + int32 Value = 0; + + FSatoriOptionalInt32() = default; + FSatoriOptionalInt32(int32 InValue) : bIsSet(true), Value(InValue) {} + FSatoriOptionalInt32(double InValue) : bIsSet(true), Value(static_cast(InValue)) {} + + bool IsSet() const { return bIsSet; } + bool IsEmpty() const { return !bIsSet; } + int32 GetValue() const { return Value; } +}; + +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriOptionalInt64 +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Satori|Optional") + bool bIsSet = false; + + UPROPERTY(BlueprintReadWrite, Category = "Satori|Optional") + int64 Value = 0; + + FSatoriOptionalInt64() = default; + FSatoriOptionalInt64(int32 InValue) : bIsSet(true), Value(static_cast(InValue)) {} + FSatoriOptionalInt64(int64 InValue) : bIsSet(true), Value(InValue) {} + FSatoriOptionalInt64(double InValue) : bIsSet(true), Value(static_cast(InValue)) {} + + bool IsSet() const { return bIsSet; } + bool IsEmpty() const { return !bIsSet; } + int64 GetValue() const { return Value; } +}; + +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriOptionalFloat +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Satori|Optional") + bool bIsSet = false; + + UPROPERTY(BlueprintReadWrite, Category = "Satori|Optional") + float Value = 0.f; + + FSatoriOptionalFloat() = default; + FSatoriOptionalFloat(float InValue) : bIsSet(true), Value(InValue) {} + FSatoriOptionalFloat(double InValue) : bIsSet(true), Value(static_cast(InValue)) {} + + bool IsSet() const { return bIsSet; } + bool IsEmpty() const { return !bIsSet; } + float GetValue() const { return Value; } +}; + +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriOptionalDouble +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, Category = "Satori|Optional") + bool bIsSet = false; + + UPROPERTY(BlueprintReadWrite, Category = "Satori|Optional") + double Value = 0.0; + + FSatoriOptionalDouble() = default; + FSatoriOptionalDouble(double InValue) : bIsSet(true), Value(InValue) {} + + bool IsSet() const { return bIsSet; } + bool IsEmpty() const { return !bIsSet; } + double GetValue() const { return Value; } +}; diff --git a/Satori/Source/SatoriApi/Public/SatoriSession.h b/Satori/Source/SatoriApi/Public/SatoriSession.h new file mode 100644 index 000000000..743817a9d --- /dev/null +++ b/Satori/Source/SatoriApi/Public/SatoriSession.h @@ -0,0 +1,109 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "CoreMinimal.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "SatoriSession.generated.h" + +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriSession +{ + GENERATED_BODY() + + /** Session variables from the auth token JWT. */ + UPROPERTY(BlueprintReadOnly, Category = "Satori") + TMap Vars; + + /** Authentication credentials. */ + UPROPERTY(BlueprintReadOnly, Category = "Satori") + FString Token; + + /** Refresh token that can be used for session token renewal. */ + UPROPERTY(BlueprintReadOnly, Category = "Satori") + FString RefreshToken; + + /** User ID parsed from the auth token JWT. */ + UPROPERTY(BlueprintReadOnly, Category = "Satori") + FString UserId; + + /** Username parsed from the auth token JWT. */ + UPROPERTY(BlueprintReadOnly, Category = "Satori") + FString Username; + + /** Auth token expiry (Unix timestamp) parsed from JWT. */ + UPROPERTY(BlueprintReadOnly, Category = "Satori") + int64 TokenExpiresAt = 0; + + /** Auth token issued-at (Unix timestamp) parsed from JWT. */ + UPROPERTY(BlueprintReadOnly, Category = "Satori") + int64 TokenIssuedAt = 0; + + /** Refresh token expiry (Unix timestamp) parsed from JWT. */ + UPROPERTY(BlueprintReadOnly, Category = "Satori") + int64 RefreshTokenExpiresAt = 0; + + /** True if the corresponding account was just created, false otherwise. */ + UPROPERTY(BlueprintReadOnly, Category = "Satori") + bool Created = false; + + /** True if the auth token expires within BufferSeconds from now. */ + bool IsExpired(int64 BufferSeconds = 0) const noexcept; + + /** True if the refresh token has expired (no buffer). */ + bool IsRefreshExpired(int64 BufferSeconds = 0) const noexcept; + + /** Replace tokens and re-parse JWT claims. */ + void Update(const FString& NewToken, const FString& NewRefreshToken) noexcept; + + static FSatoriSession FromJson(const TSharedPtr& Json) noexcept; + TSharedPtr ToJson() const noexcept; + +private: + static bool ParseJwtPayload(const FString& Jwt, TSharedPtr& Out) noexcept; + void ParseTokens() noexcept; +}; + +UCLASS() +class SATORIAPI_API USatoriSessionFunctions : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + + /** True if the auth token expires within BufferSeconds from now. */ + UFUNCTION(BlueprintPure, Category = "Satori|Session") + static bool IsExpired(const FSatoriSession& Session, int64 BufferSeconds = 0) + { + return Session.IsExpired(BufferSeconds); + } + + /** True if the refresh token has expired (no buffer). */ + UFUNCTION(BlueprintPure, Category = "Satori|Session") + static bool IsRefreshExpired(const FSatoriSession& Session, int64 BufferSeconds = 0) + { + return Session.IsRefreshExpired(BufferSeconds); + } + + /** Replace tokens and re-parse JWT claims. */ + UFUNCTION(BlueprintCallable, Category = "Satori|Session") + static void UpdateSession(UPARAM(ref) FSatoriSession& Session, const FString& NewToken, const FString& NewRefreshToken) + { + Session.Update(NewToken, NewRefreshToken); + } +}; + diff --git a/Satori/Source/SatoriApi/Public/SatoriTypes.h b/Satori/Source/SatoriApi/Public/SatoriTypes.h new file mode 100644 index 000000000..11b206b56 --- /dev/null +++ b/Satori/Source/SatoriApi/Public/SatoriTypes.h @@ -0,0 +1,785 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + +#pragma once + +#include "CoreMinimal.h" +#include "SatoriOptionals.h" +#include "SatoriTypes.generated.h" + +enum class ESatoriRequestAuth : uint8 +{ + None, + Basic, + Bearer, + HttpKey +}; + +/* +* The type of configuration that overrides a flag value. +*/ +UENUM(BlueprintType) +enum class ESatoriValueChangeReasonType : uint8 +{ + VCR_UNKNOWN = 0 // + , VCR_FLAG_VARIANT = 1 // + , VCR_LIVE_EVENT = 2 // + , VCR_EXPERIMENT = 3 // +}; + +/* +* The type of configuration that overrides a flag value. +*/ +UENUM(BlueprintType) +enum class ESatoriFlagOverrideType : uint8 +{ + FOT_FLAG = 0 // + , FOT_FLAG_VARIANT = 1 // + , FOT_LIVE_EVENT_FLAG = 2 // + , FOT_LIVE_EVENT_FLAG_VARIANT = 3 // + , FOT_EXPERIMENT_PHASE_VARIANT_FLAG = 4 // +}; + +/* +* The status variants of a live event. +*/ +UENUM(BlueprintType) +enum class ESatoriLiveEventStatus : uint8 +{ + LES_UNKNOWN = 0 // + , LES_ACTIVE = 1 // + , LES_UPCOMING = 2 // + , LES_TERMINATED = 3 // +}; + +/** +* Log out a session, invalidate a refresh token, or log out all sessions/refresh tokens for a user. +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriAuthenticateLogoutRequest +{ + GENERATED_BODY() + + /** Session token to log out. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString Token; + + /** Refresh token to invalidate. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString RefreshToken; + + static FSatoriAuthenticateLogoutRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Authenticate against the server with a refresh token. +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriAuthenticateRefreshRequest +{ + GENERATED_BODY() + + /** Refresh token. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString RefreshToken; + + static FSatoriAuthenticateRefreshRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Authentication request +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriAuthenticateRequest +{ + GENERATED_BODY() + + /** Identity ID. Must be between eight and 128 characters (inclusive). */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString Id; + + /** Optional no_session modifies the request to only create/update */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + bool NoSession = false; + + /** Optional default properties to update with this call. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TMap Default; + + /** Optional custom properties to update with this call. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TMap Custom; + + static FSatoriAuthenticateRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* The request to delete a scheduled message. +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriDeleteMessageRequest +{ + GENERATED_BODY() + + /** The identifier of the message. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString Id; + + static FSatoriDeleteMessageRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A single event. Usually, but not necessarily, part of a batch. +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriEvent +{ + GENERATED_BODY() + + /** Event name. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString Name; + + /** Optional event ID assigned by the client, used to de-duplicate in retransmission scenarios. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString Id; + + /** Optional value. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString Value; + + /** The time when the event was triggered on the producer side. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FDateTime Timestamp; + + /** The identity id associated with the event. Ignored if the event is published as part of a session. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString IdentityId; + + /** The session id associated with the event. Ignored if the event is published as part of a session. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString SessionId; + + /** The session issued at associated with the event. Ignored if the event is published as part of a session. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + int64 SessionIssuedAt = 0; + + /** The session expires at associated with the event. Ignored if the event is published as part of a session. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + int64 SessionExpiresAt = 0; + + /** Event metadata, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TMap Metadata; + + static FSatoriEvent FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Publish an event to the server +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriEventRequest +{ + GENERATED_BODY() + + /** Some number of events produced by a client. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TArray Events; + + static FSatoriEventRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* An experiment that this user is partaking. +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriExperiment +{ + GENERATED_BODY() + + /** Experiment name */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString Name; + + /** Value associated with this Experiment. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString Value; + + /** The labels associated with this experiment. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TArray Labels; + + /** Experiment Phase name */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString PhaseName; + + /** Experiment Phase Variant name */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString PhaseVariantName; + + static FSatoriExperiment FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* All experiments that this identity is involved with. +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriExperimentList +{ + GENERATED_BODY() + + /** All experiments for this identity. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TArray Experiments; + + static FSatoriExperimentList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* The origin of change on a flag value. +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriValueChangeReason +{ + GENERATED_BODY() + + /** The type of the configuration that declared the override. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + ESatoriValueChangeReasonType Type = static_cast(0); + + /** The name of the configuration that overrides the flag value. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString Name; + + /** The variant name of the configuration that overrides the flag value. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString VariantName; + + static FSatoriValueChangeReason FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Feature flag available to the identity. +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriFlag +{ + GENERATED_BODY() + + /** Flag name */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString Name; + + /** Value associated with this flag. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString Value; + + /** Whether the value for this flag has conditionally changed from the default state. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + bool ConditionChanged = false; + + /** The origin of change on the flag value returned. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FSatoriValueChangeReason ChangeReason; + + /** The labels associated with this flag. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TArray Labels; + + static FSatoriFlag FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* All flags available to the identity +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriFlagList +{ + GENERATED_BODY() + + /** All flags */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TArray Flags; + + static FSatoriFlagList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* The details of a flag value override. +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriFlagOverrideValue +{ + GENERATED_BODY() + + /** The type of the configuration that declared the override. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + ESatoriFlagOverrideType Type = static_cast(0); + + /** The name of the configuration that overrides the flag value. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString Name; + + /** The variant name of the configuration that overrides the flag value. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString VariantName; + + /** The value of the configuration that overrides the flag. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString Value; + + /** The create time of the configuration that overrides the flag. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + int64 CreateTimeSec = 0; + + static FSatoriFlagOverrideValue FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Feature flag available to the identity. +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriFlagOverride +{ + GENERATED_BODY() + + /** Flag name */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString FlagName; + + /** The list of configuration that affect the value of the flag. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TArray Overrides; + + /** The labels associated with this flag. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TArray Labels; + + static FSatoriFlagOverride FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* All flags available to the identity and their value overrides +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriFlagOverrideList +{ + GENERATED_BODY() + + /** All flags */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TArray Flags; + + static FSatoriFlagOverrideList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Request to get all experiments data. +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriGetExperimentsRequest +{ + GENERATED_BODY() + + /** Experiment names; if empty string, all experiments are returned based on the remaining filters. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TArray Names; + + /** Label names that must be defined for each Experiment; if empty string, all experiments are returned based on the remaining filters. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TArray Labels; + + static FSatoriGetExperimentsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Request to get all flags data. +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriGetFlagsRequest +{ + GENERATED_BODY() + + /** Flag names; if empty string, all flags are returned based on the remaining filters. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TArray Names; + + /** Label names that must be defined for each Flag; if empty string, all flags are returned based on the remaining filters. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TArray Labels; + + static FSatoriGetFlagsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Request to get all live events. +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriGetLiveEventsRequest +{ + GENERATED_BODY() + + /** Live event names; if empty string, all live events are returned based on the remaining filters. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TArray Names; + + /** Label names that must be defined for each Live Event; if empty string, all live events are returned based on the remaining filters. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TArray Labels; + + /** The maximum number of past event runs to return for each live event. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + int32 PastRunCount = 0; + + /** The maximum number of future event runs to return for each live event. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + int32 FutureRunCount = 0; + + /** Start time of the time window filter to apply. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + int64 StartTimeSec = 0; + + /** End time of the time window filter to apply. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + int64 EndTimeSec = 0; + + static FSatoriGetLiveEventsRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A scheduled message. +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriMessage +{ + GENERATED_BODY() + + /** The identifier of the schedule. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString ScheduleId; + + /** The send time for the message. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + int64 SendTime = 0; + + /** The time the message was created. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + int64 CreateTime = 0; + + /** The time the message was updated. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + int64 UpdateTime = 0; + + /** The time the message was read by the client. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + int64 ReadTime = 0; + + /** The time the message was consumed by the identity. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + int64 ConsumeTime = 0; + + /** The message's text. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString Text; + + /** The message's unique identifier. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString Id; + + /** The message's title. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString Title; + + /** The message's image url. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString ImageUrl; + + /** A key-value pairs of metadata. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TMap Metadata; + + static FSatoriMessage FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriGetMessageListRequest +{ + GENERATED_BODY() + + /** Max number of messages to return. Between 1 and 100. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + int32 Limit = 0; + + /** True if listing should be older messages to newer, false if reverse. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + bool Forward = false; + + /** A pagination cursor, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString Cursor; + + /** A list of message identifiers to be returned. If these are given as inputs, the pagination will be disabled. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TArray MessageIds; + + static FSatoriGetMessageListRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A response containing all the messages for an identity. +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriGetMessageListResponse +{ + GENERATED_BODY() + + /** The list of messages. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TArray Messages; + + /** The cursor to send when retrieving the next page, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString NextCursor; + + /** The cursor to send when retrieving the previous page, if any. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString PrevCursor; + + /** Cacheable cursor to list newer messages. Durable and designed to be stored, unlike next/prev cursors. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString CacheableCursor; + + static FSatoriGetMessageListResponse FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Enrich/replace the current session with a new ID. +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriIdentifyRequest +{ + GENERATED_BODY() + + /** Identity ID to enrich the current session and return a new session. Old session will no longer be usable. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString Id; + + /** Optional default properties to update with this call. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TMap Default; + + /** Optional custom properties to update with this call. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TMap Custom; + + static FSatoriIdentifyRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Request to join a 'explicit join' live event. +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriJoinLiveEventRequest +{ + GENERATED_BODY() + + /** Live event id to join. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString Id; + + static FSatoriJoinLiveEventRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* A single live event. +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriLiveEvent +{ + GENERATED_BODY() + + /** Name. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString Name; + + /** Description. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString Description; + + /** Event value. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString Value; + + /** Start time of current event run. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + int64 ActiveStartTimeSec = 0; + + /** End time of current event run. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + int64 ActiveEndTimeSec = 0; + + /** The live event identifier. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString Id; + + /** Start time. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + int64 StartTimeSec = 0; + + /** End time, 0 if it repeats forever. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + int64 EndTimeSec = 0; + + /** Duration in seconds. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + int64 DurationSec = 0; + + /** Reset CRON schedule, if configured. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString ResetCron; + + /** The status of this live event run. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + ESatoriLiveEventStatus Status = static_cast(0); + + /** The labels associated with this live event. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TArray Labels; + + static FSatoriLiveEvent FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* List of Live events. +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriLiveEventList +{ + GENERATED_BODY() + + /** Live events. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TArray LiveEvents; + + /** Live events that require explicit join. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TArray ExplicitJoinLiveEvents; + + static FSatoriLiveEventList FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Properties associated with an identity. +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriProperties +{ + GENERATED_BODY() + + /** Event default properties. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TMap Default; + + /** Event computed properties. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TMap Computed; + + /** Event custom properties. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TMap Custom; + + static FSatoriProperties FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* Update Properties associated with this identity. +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriUpdatePropertiesRequest +{ + GENERATED_BODY() + + /** Informs the server to recompute the audience membership of the identity. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FSatoriOptionalBool Recompute; + + /** Event default properties. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TMap Default; + + /** Event custom properties. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + TMap Custom; + + static FSatoriUpdatePropertiesRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + +/** +* The request to update the status of a message. +*/ +USTRUCT(BlueprintType) +struct SATORIAPI_API FSatoriUpdateMessageRequest +{ + GENERATED_BODY() + + /** The identifier of the messages. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + FString Id; + + /** The time the message was read at the client. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + int64 ReadTime = 0; + + /** The time the message was consumed by the identity. */ + UPROPERTY(BlueprintReadWrite, Category = "Satori") + int64 ConsumeTime = 0; + + static FSatoriUpdateMessageRequest FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; + diff --git a/Nakama/Source/NakamaTests/NakamaTests.Build.cs b/Satori/Source/SatoriApi/SatoriApi.Build.cs similarity index 73% rename from Nakama/Source/NakamaTests/NakamaTests.Build.cs rename to Satori/Source/SatoriApi/SatoriApi.Build.cs index c996f8679..a2bfb160f 100644 --- a/Nakama/Source/NakamaTests/NakamaTests.Build.cs +++ b/Satori/Source/SatoriApi/SatoriApi.Build.cs @@ -15,10 +15,11 @@ */ using UnrealBuildTool; +using System.IO; -public class NakamaTests : ModuleRules +public class SatoriApi : ModuleRules { - public NakamaTests(ReadOnlyTargetRules Target) : base(Target) + public SatoriApi(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; @@ -39,10 +40,7 @@ public NakamaTests(ReadOnlyTargetRules Target) : base(Target) PublicDependencyModuleNames.AddRange( new string[] { - "HTTP", - "Core", - "NakamaUnreal", - "FunctionalTesting" + "Core", "HTTP", "JsonUtilities" // ... add other public dependencies that you statically link with here ... } ); @@ -51,15 +49,15 @@ public NakamaTests(ReadOnlyTargetRules Target) : base(Target) PrivateDependencyModuleNames.AddRange( new string[] { - "CoreUObject", + "CoreUObject", "Engine", "Slate", "SlateCore", "Engine", "JsonUtilities", "Json", - - // ... private dependencies that you statically link with here ... + "HTTP" + // ... add private dependencies that you statically link with here ... } ); @@ -70,5 +68,10 @@ public NakamaTests(ReadOnlyTargetRules Target) : base(Target) // ... add any modules that your module loads dynamically here ... } ); - } + + PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Public")); + PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "Private")); + + PrivateIncludePaths.Add(Path.Combine(ModuleDirectory, "Public")); + } } diff --git a/Satori/Source/SatoriBlueprints/Private/SatoriBlueprint.cpp b/Satori/Source/SatoriBlueprints/Private/SatoriBlueprint.cpp deleted file mode 100644 index e45849634..000000000 --- a/Satori/Source/SatoriBlueprints/Private/SatoriBlueprint.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "SatoriBlueprints.h" -#include "Modules/ModuleManager.h" - -#define LOCTEXT_NAMESPACE "FSatoriBlueprintsModule" - -DEFINE_LOG_CATEGORY(LogSatoriBlueprints); - - -void FSatoriBlueprintsModule::StartupModule() -{ - // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module - - //NLogger::init(std::make_shared(), NLogLevel::Debug); - -} - -void FSatoriBlueprintsModule::ShutdownModule() -{ - // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, - // we call this function before unloading the module. -} - -#undef LOCTEXT_NAMESPACE - -IMPLEMENT_MODULE(FSatoriBlueprintsModule, SatoriBlueprints) diff --git a/Satori/Source/SatoriBlueprints/Private/SatoriBlueprintsModule.cpp b/Satori/Source/SatoriBlueprints/Private/SatoriBlueprintsModule.cpp new file mode 100644 index 000000000..dbc88e8da --- /dev/null +++ b/Satori/Source/SatoriBlueprints/Private/SatoriBlueprintsModule.cpp @@ -0,0 +1,3 @@ +#include "SatoriBlueprintsModule.h" + +IMPLEMENT_MODULE(FSatoriBlueprintsModule, SatoriBlueprints) diff --git a/Satori/Source/SatoriBlueprints/Private/SatoriClientBlueprintLibrary.cpp b/Satori/Source/SatoriBlueprints/Private/SatoriClientBlueprintLibrary.cpp new file mode 100644 index 000000000..ee478549d --- /dev/null +++ b/Satori/Source/SatoriBlueprints/Private/SatoriClientBlueprintLibrary.cpp @@ -0,0 +1,935 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + +#include "SatoriClientBlueprintLibrary.h" + +USatoriClientAuthenticate* USatoriClientAuthenticate::Authenticate( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FString& Id + , bool NoSession + , const TMap& Default + , const TMap& Custom +) +{ + USatoriClientAuthenticate* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredId = Id; + Action->StoredNoSession = NoSession; + Action->StoredDefault = Default; + Action->StoredCustom = Custom; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void USatoriClientAuthenticate::Activate() +{ + static const TCHAR* TraceScope_Authenticate = TEXT("SatoriBP_Authenticate"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_Authenticate); + + TWeakObjectPtr WeakThis(this); + + SatoriApi::Authenticate( + StoredClientConfig, + StoredId, + StoredNoSession, + StoredDefault, + StoredCustom, + [WeakThis](const FSatoriSession& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FSatoriError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +USatoriClientAuthenticateLogout* USatoriClientAuthenticateLogout::AuthenticateLogout( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FString& Token + , const FString& RefreshToken +) +{ + USatoriClientAuthenticateLogout* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredToken = Token; + Action->StoredRefreshToken = RefreshToken; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void USatoriClientAuthenticateLogout::Activate() +{ + static const TCHAR* TraceScope_AuthenticateLogout = TEXT("SatoriBP_AuthenticateLogout"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_AuthenticateLogout); + + TWeakObjectPtr WeakThis(this); + + SatoriApi::AuthenticateLogout( + StoredClientConfig, + StoredToken, + StoredRefreshToken, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FSatoriError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +USatoriClientAuthenticateRefresh* USatoriClientAuthenticateRefresh::AuthenticateRefresh( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FString& RefreshToken +) +{ + USatoriClientAuthenticateRefresh* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredRefreshToken = RefreshToken; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void USatoriClientAuthenticateRefresh::Activate() +{ + static const TCHAR* TraceScope_AuthenticateRefresh = TEXT("SatoriBP_AuthenticateRefresh"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_AuthenticateRefresh); + + TWeakObjectPtr WeakThis(this); + + SatoriApi::AuthenticateRefresh( + StoredClientConfig, + StoredRefreshToken, + [WeakThis](const FSatoriSession& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FSatoriError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +USatoriClientDeleteIdentity* USatoriClientDeleteIdentity::DeleteIdentity( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session +) +{ + USatoriClientDeleteIdentity* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void USatoriClientDeleteIdentity::Activate() +{ + static const TCHAR* TraceScope_DeleteIdentity = TEXT("SatoriBP_DeleteIdentity"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_DeleteIdentity); + + TWeakObjectPtr WeakThis(this); + + SatoriApi::DeleteIdentity( + StoredClientConfig, + StoredSession, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FSatoriError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +USatoriClientEvent* USatoriClientEvent::Event( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + , const TArray& Events +) +{ + USatoriClientEvent* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredEvents = Events; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void USatoriClientEvent::Activate() +{ + static const TCHAR* TraceScope_Event = TEXT("SatoriBP_Event"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_Event); + + TWeakObjectPtr WeakThis(this); + + SatoriApi::Event( + StoredClientConfig, + StoredSession, + StoredEvents, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FSatoriError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +USatoriClientServerEvent* USatoriClientServerEvent::ServerEvent( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + , const TArray& Events +) +{ + USatoriClientServerEvent* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredEvents = Events; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void USatoriClientServerEvent::Activate() +{ + static const TCHAR* TraceScope_ServerEvent = TEXT("SatoriBP_ServerEvent"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ServerEvent); + + TWeakObjectPtr WeakThis(this); + + SatoriApi::ServerEvent( + StoredClientConfig, + StoredSession, + StoredEvents, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FSatoriError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +USatoriClientGetExperiments* USatoriClientGetExperiments::GetExperiments( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + , const TArray& Names + , const TArray& Labels +) +{ + USatoriClientGetExperiments* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredNames = Names; + Action->StoredLabels = Labels; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void USatoriClientGetExperiments::Activate() +{ + static const TCHAR* TraceScope_GetExperiments = TEXT("SatoriBP_GetExperiments"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_GetExperiments); + + TWeakObjectPtr WeakThis(this); + + SatoriApi::GetExperiments( + StoredClientConfig, + StoredSession, + StoredNames, + StoredLabels, + [WeakThis](const FSatoriExperimentList& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FSatoriError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +USatoriClientGetFlagOverrides* USatoriClientGetFlagOverrides::GetFlagOverrides( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + , const TArray& Names + , const TArray& Labels +) +{ + USatoriClientGetFlagOverrides* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredNames = Names; + Action->StoredLabels = Labels; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void USatoriClientGetFlagOverrides::Activate() +{ + static const TCHAR* TraceScope_GetFlagOverrides = TEXT("SatoriBP_GetFlagOverrides"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_GetFlagOverrides); + + TWeakObjectPtr WeakThis(this); + + SatoriApi::GetFlagOverrides( + StoredClientConfig, + StoredSession, + StoredNames, + StoredLabels, + [WeakThis](const FSatoriFlagOverrideList& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FSatoriError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +USatoriClientGetFlags* USatoriClientGetFlags::GetFlags( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + , const TArray& Names + , const TArray& Labels +) +{ + USatoriClientGetFlags* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredNames = Names; + Action->StoredLabels = Labels; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void USatoriClientGetFlags::Activate() +{ + static const TCHAR* TraceScope_GetFlags = TEXT("SatoriBP_GetFlags"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_GetFlags); + + TWeakObjectPtr WeakThis(this); + + SatoriApi::GetFlags( + StoredClientConfig, + StoredSession, + StoredNames, + StoredLabels, + [WeakThis](const FSatoriFlagList& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FSatoriError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +USatoriClientGetLiveEvents* USatoriClientGetLiveEvents::GetLiveEvents( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + , const TArray& Names + , const TArray& Labels + , int32 PastRunCount + , int32 FutureRunCount + , int64 StartTimeSec + , int64 EndTimeSec +) +{ + USatoriClientGetLiveEvents* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredNames = Names; + Action->StoredLabels = Labels; + Action->StoredPastRunCount = PastRunCount; + Action->StoredFutureRunCount = FutureRunCount; + Action->StoredStartTimeSec = StartTimeSec; + Action->StoredEndTimeSec = EndTimeSec; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void USatoriClientGetLiveEvents::Activate() +{ + static const TCHAR* TraceScope_GetLiveEvents = TEXT("SatoriBP_GetLiveEvents"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_GetLiveEvents); + + TWeakObjectPtr WeakThis(this); + + SatoriApi::GetLiveEvents( + StoredClientConfig, + StoredSession, + StoredNames, + StoredLabels, + StoredPastRunCount, + StoredFutureRunCount, + StoredStartTimeSec, + StoredEndTimeSec, + [WeakThis](const FSatoriLiveEventList& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FSatoriError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +USatoriClientJoinLiveEvent* USatoriClientJoinLiveEvent::JoinLiveEvent( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + , const FString& Id +) +{ + USatoriClientJoinLiveEvent* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredId = Id; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void USatoriClientJoinLiveEvent::Activate() +{ + static const TCHAR* TraceScope_JoinLiveEvent = TEXT("SatoriBP_JoinLiveEvent"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_JoinLiveEvent); + + TWeakObjectPtr WeakThis(this); + + SatoriApi::JoinLiveEvent( + StoredClientConfig, + StoredSession, + StoredId, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FSatoriError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +USatoriClientHealthcheck* USatoriClientHealthcheck::Healthcheck( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session +) +{ + USatoriClientHealthcheck* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void USatoriClientHealthcheck::Activate() +{ + static const TCHAR* TraceScope_Healthcheck = TEXT("SatoriBP_Healthcheck"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_Healthcheck); + + TWeakObjectPtr WeakThis(this); + + SatoriApi::Healthcheck( + StoredClientConfig, + StoredSession, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FSatoriError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +USatoriClientIdentify* USatoriClientIdentify::Identify( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + , const FString& Id + , const TMap& Default + , const TMap& Custom +) +{ + USatoriClientIdentify* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredId = Id; + Action->StoredDefault = Default; + Action->StoredCustom = Custom; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void USatoriClientIdentify::Activate() +{ + static const TCHAR* TraceScope_Identify = TEXT("SatoriBP_Identify"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_Identify); + + TWeakObjectPtr WeakThis(this); + + SatoriApi::Identify( + StoredClientConfig, + StoredSession, + StoredId, + StoredDefault, + StoredCustom, + [WeakThis](const FSatoriSession& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FSatoriError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +USatoriClientListProperties* USatoriClientListProperties::ListProperties( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session +) +{ + USatoriClientListProperties* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void USatoriClientListProperties::Activate() +{ + static const TCHAR* TraceScope_ListProperties = TEXT("SatoriBP_ListProperties"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_ListProperties); + + TWeakObjectPtr WeakThis(this); + + SatoriApi::ListProperties( + StoredClientConfig, + StoredSession, + [WeakThis](const FSatoriProperties& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FSatoriError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +USatoriClientReadycheck* USatoriClientReadycheck::Readycheck( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session +) +{ + USatoriClientReadycheck* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void USatoriClientReadycheck::Activate() +{ + static const TCHAR* TraceScope_Readycheck = TEXT("SatoriBP_Readycheck"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_Readycheck); + + TWeakObjectPtr WeakThis(this); + + SatoriApi::Readycheck( + StoredClientConfig, + StoredSession, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FSatoriError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +USatoriClientUpdateProperties* USatoriClientUpdateProperties::UpdateProperties( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + , FSatoriOptionalBool Recompute + , const TMap& Default + , const TMap& Custom +) +{ + USatoriClientUpdateProperties* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredRecompute = Recompute; + Action->StoredDefault = Default; + Action->StoredCustom = Custom; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void USatoriClientUpdateProperties::Activate() +{ + static const TCHAR* TraceScope_UpdateProperties = TEXT("SatoriBP_UpdateProperties"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_UpdateProperties); + + TWeakObjectPtr WeakThis(this); + + SatoriApi::UpdateProperties( + StoredClientConfig, + StoredSession, + StoredRecompute, + StoredDefault, + StoredCustom, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FSatoriError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +USatoriClientGetMessageList* USatoriClientGetMessageList::GetMessageList( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + , int32 Limit + , bool Forward + , const FString& Cursor + , const TArray& MessageIds +) +{ + USatoriClientGetMessageList* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredLimit = Limit; + Action->StoredForward = Forward; + Action->StoredCursor = Cursor; + Action->StoredMessageIds = MessageIds; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void USatoriClientGetMessageList::Activate() +{ + static const TCHAR* TraceScope_GetMessageList = TEXT("SatoriBP_GetMessageList"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_GetMessageList); + + TWeakObjectPtr WeakThis(this); + + SatoriApi::GetMessageList( + StoredClientConfig, + StoredSession, + StoredLimit, + StoredForward, + StoredCursor, + StoredMessageIds, + [WeakThis](const FSatoriGetMessageListResponse& Result) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}, Result); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FSatoriError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error, {}); + Self->SetReadyToDestroy(); + } + } + ); +} + +USatoriClientUpdateMessage* USatoriClientUpdateMessage::UpdateMessage( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + , const FString& Id + , int64 ReadTime + , int64 ConsumeTime +) +{ + USatoriClientUpdateMessage* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredId = Id; + Action->StoredReadTime = ReadTime; + Action->StoredConsumeTime = ConsumeTime; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void USatoriClientUpdateMessage::Activate() +{ + static const TCHAR* TraceScope_UpdateMessage = TEXT("SatoriBP_UpdateMessage"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_UpdateMessage); + + TWeakObjectPtr WeakThis(this); + + SatoriApi::UpdateMessage( + StoredClientConfig, + StoredSession, + StoredId, + StoredReadTime, + StoredConsumeTime, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FSatoriError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} + +USatoriClientDeleteMessage* USatoriClientDeleteMessage::DeleteMessage( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + , const FString& Id +) +{ + USatoriClientDeleteMessage* Action = NewObject(GetTransientPackage()); + Action->StoredClientConfig = ClientConfig; + Action->StoredSession = Session; + Action->StoredId = Id; + + Action->RegisterWithGameInstance(WorldContextObject); + return Action; +} + +void USatoriClientDeleteMessage::Activate() +{ + static const TCHAR* TraceScope_DeleteMessage = TEXT("SatoriBP_DeleteMessage"); + TRACE_CPUPROFILER_EVENT_SCOPE_STR(TraceScope_DeleteMessage); + + TWeakObjectPtr WeakThis(this); + + SatoriApi::DeleteMessage( + StoredClientConfig, + StoredSession, + StoredId, + [WeakThis]() + { + if (auto* Self = WeakThis.Get()) + { + Self->OnSuccess.Broadcast({}); + Self->SetReadyToDestroy(); + } + }, + [WeakThis](const FSatoriError& Error) + { + if (auto* Self = WeakThis.Get()) + { + Self->OnError.Broadcast(Error); + Self->SetReadyToDestroy(); + } + } + ); +} diff --git a/Satori/Source/SatoriBlueprints/Private/SatoriClientRequests.cpp b/Satori/Source/SatoriBlueprints/Private/SatoriClientRequests.cpp deleted file mode 100644 index acad505ea..000000000 --- a/Satori/Source/SatoriBlueprints/Private/SatoriClientRequests.cpp +++ /dev/null @@ -1,992 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "SatoriClientRequests.h" -#include "SatoriUtils.h" - -USatoriClientAuthenticate* USatoriClientAuthenticate::Authenticate( - USatoriClient* Client, - const FString& ID, - const TMap& DefaultProperties, - const TMap& CustomProperties, - const bool bNoSession) -{ - USatoriClientAuthenticate* Node = NewObject(); - Node->SatoriClient = Client; - Node->ID = ID; - Node->DefaultProperties = DefaultProperties; - Node->CustomProperties = CustomProperties; - Node->bNoSession = bNoSession; - - return Node; -} - -void USatoriClientAuthenticate::Activate() -{ - if (!SatoriClient) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClient(); - OnError.Broadcast({}, Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](USatoriSession* session) - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(session, {}); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FSatoriError& error) - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast({}, error); - SetReadyToDestroy(); - }; - - SatoriClient->Authenticate(ID, DefaultProperties, CustomProperties, bNoSession, successCallback, errorCallback); -} - -USatoriClientAuthenticateRefresh* USatoriClientAuthenticateRefresh::AuthenticateRefresh(USatoriClient* Client, USatoriSession* Session) -{ - USatoriClientAuthenticateRefresh* Node = NewObject(); - Node->SatoriClient = Client; - Node->UserSession = Session; - - return Node; -} - -void USatoriClientAuthenticateRefresh::Activate() -{ - // Check validity of client and session - if (!SatoriClient && !UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClientAndSession(); - OnError.Broadcast({}, Error); - SetReadyToDestroy(); - return; - } - - if (!SatoriClient) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClient(); - OnError.Broadcast({}, Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidSession(); - OnError.Broadcast({}, Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](USatoriSession* session) - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(session, {}); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FSatoriError& error) - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast({}, error); - SetReadyToDestroy(); - }; - - SatoriClient->AuthenticateRefresh(UserSession, successCallback, errorCallback); -} - -USatoriClientAuthenticateLogout* USatoriClientAuthenticateLogout::AuthenticateLogout(USatoriClient* Client, USatoriSession* Session) -{ - USatoriClientAuthenticateLogout* Node = NewObject(); - Node->SatoriClient = Client; - Node->UserSession = Session; - - return Node; -} - -void USatoriClientAuthenticateLogout::Activate() -{ - // Check validity of client and session - if (!SatoriClient && !UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!SatoriClient) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FSatoriError& error) - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - SatoriClient->AuthenticateLogout(UserSession, successCallback, errorCallback); -} - -USatoriClientIdentify* USatoriClientIdentify::Identify( - USatoriClient* Client, - USatoriSession* Session, - const FString& ID, - const TMap& DefaultProperties, - const TMap& CustomProperties) -{ - USatoriClientIdentify* Node = NewObject(); - Node->SatoriClient = Client; - Node->UserSession = Session; - Node->ID = ID; - Node->DefaultProperties = DefaultProperties; - Node->CustomProperties = CustomProperties; - - return Node; -} - -void USatoriClientIdentify::Activate() -{ - // Check validity of client and session - if (!SatoriClient && !UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClientAndSession(); - OnError.Broadcast({}, Error); - SetReadyToDestroy(); - return; - } - - if (!SatoriClient) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClient(); - OnError.Broadcast({}, Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidSession(); - OnError.Broadcast({}, Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](USatoriSession* session) - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(session, {}); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FSatoriError& error) - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast({}, error); - SetReadyToDestroy(); - }; - - SatoriClient->Identify(UserSession, ID, DefaultProperties, CustomProperties, successCallback, errorCallback); -} - -USatoriClientListIdentityProperties* USatoriClientListIdentityProperties::ListIdentityProperties(USatoriClient* Client, USatoriSession* Session) -{ - USatoriClientListIdentityProperties* Node = NewObject(); - Node->SatoriClient = Client; - Node->UserSession = Session; - - return Node; -} - -void USatoriClientListIdentityProperties::Activate() -{ - // Check validity of client and session - if (!SatoriClient && !UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClientAndSession(); - OnError.Broadcast({}, Error); - SetReadyToDestroy(); - return; - } - - if (!SatoriClient) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClient(); - OnError.Broadcast({}, Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidSession(); - OnError.Broadcast({}, Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FSatoriProperties& Properties) - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(Properties, {}); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FSatoriError& error) - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast({}, error); - SetReadyToDestroy(); - }; - - SatoriClient->ListIdentityProperties(UserSession, successCallback, errorCallback); -} - -USatoriClientUpdateProperties* USatoriClientUpdateProperties::UpdateProperties( - USatoriClient* Client, - USatoriSession* Session, - const TMap& DefaultProperties, - const TMap& CustomProperties, - const bool bRecompute) -{ - USatoriClientUpdateProperties* Node = NewObject(); - Node->SatoriClient = Client; - Node->UserSession = Session; - Node->DefaultProperties = DefaultProperties; - Node->CustomProperties = CustomProperties; - Node->bRecompute = bRecompute; - - return Node; -} - -void USatoriClientUpdateProperties::Activate() -{ - // Check validity of client and session - if (!SatoriClient && !UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!SatoriClient) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FSatoriError& error) - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - SatoriClient->UpdateProperties(UserSession, DefaultProperties, CustomProperties, bRecompute, successCallback, errorCallback); -} - -USatoriClientDeleteIdentity* USatoriClientDeleteIdentity::DeleteIdentity(USatoriClient* Client, USatoriSession* Session) -{ - USatoriClientDeleteIdentity* Node = NewObject(); - Node->SatoriClient = Client; - Node->UserSession = Session; - - return Node; -} - -void USatoriClientDeleteIdentity::Activate() -{ - // Check validity of client and session - if (!SatoriClient && !UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!SatoriClient) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FSatoriError& error) - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - SatoriClient->DeleteIdentity(UserSession, successCallback, errorCallback); -} - -USatoriClientPostEvent* USatoriClientPostEvent::PostEvent(USatoriClient* Client, USatoriSession* Session, const TArray& Events) -{ - USatoriClientPostEvent* Node = NewObject(); - Node->SatoriClient = Client; - Node->UserSession = Session; - Node->Events = Events; - - return Node; -} - -void USatoriClientPostEvent::Activate() -{ - // Check validity of client and session - if (!SatoriClient && !UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!SatoriClient) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FSatoriError& error) - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - SatoriClient->PostEvent(UserSession, Events, successCallback, errorCallback); -} - -USatoriClientGetExperiments* USatoriClientGetExperiments::GetExperiments(USatoriClient* Client, USatoriSession* Session, const TArray& Names) -{ - USatoriClientGetExperiments* Node = NewObject(); - Node->SatoriClient = Client; - Node->UserSession = Session; - Node->Names = Names; - - return Node; -} - -void USatoriClientGetExperiments::Activate() -{ - // Check validity of client and session - if (!SatoriClient && !UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClientAndSession(); - OnError.Broadcast({}, Error); - SetReadyToDestroy(); - return; - } - - if (!SatoriClient) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClient(); - OnError.Broadcast({}, Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidSession(); - OnError.Broadcast({}, Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FSatoriExperimentList& Experiments) - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(Experiments, {}); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FSatoriError& error) - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast({}, error); - SetReadyToDestroy(); - }; - - SatoriClient->GetExperiments(UserSession, Names, successCallback, errorCallback); -} - -USatoriClientGetFlags* USatoriClientGetFlags::GetFlags(USatoriClient* Client, USatoriSession* Session, const TArray& Names) -{ - USatoriClientGetFlags* Node = NewObject(); - Node->SatoriClient = Client; - Node->UserSession = Session; - Node->Names = Names; - - return Node; -} - -void USatoriClientGetFlags::Activate() -{ - // Check validity of client and session - if (!SatoriClient && !UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClientAndSession(); - OnError.Broadcast({}, Error); - SetReadyToDestroy(); - return; - } - - if (!SatoriClient) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClient(); - OnError.Broadcast({}, Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidSession(); - OnError.Broadcast({}, Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FSatoriFlagList& Flags) - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(Flags, {}); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FSatoriError& error) - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast({}, error); - SetReadyToDestroy(); - }; - - SatoriClient->GetFlags(UserSession, Names, successCallback, errorCallback); -} - -USatoriClientGetFlagOverrides* USatoriClientGetFlagOverrides::GetFlagOverrides(USatoriClient* Client, USatoriSession* Session, const TArray& Names) -{ - USatoriClientGetFlagOverrides* Node = NewObject(); - Node->SatoriClient = Client; - Node->UserSession = Session; - Node->Names = Names; - - return Node; -} - -void USatoriClientGetFlagOverrides::Activate() -{ - // Check validity of client and session - if (!SatoriClient && !UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClientAndSession(); - OnError.Broadcast({}, Error); - SetReadyToDestroy(); - return; - } - - if (!SatoriClient) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClient(); - OnError.Broadcast({}, Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidSession(); - OnError.Broadcast({}, Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FSatoriFlagOverrideList& FlagOverrides) - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(FlagOverrides, {}); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FSatoriError& error) - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast({}, error); - SetReadyToDestroy(); - }; - - SatoriClient->GetFlagOverrides(UserSession, Names, successCallback, errorCallback); -} - -USatoriClientGetLiveEvents* USatoriClientGetLiveEvents::GetLiveEvents(USatoriClient* Client, USatoriSession* Session, const TArray& LiveEventNames) -{ - USatoriClientGetLiveEvents* Node = NewObject(); - Node->SatoriClient = Client; - Node->UserSession = Session; - Node->LiveEventNames = LiveEventNames; - - return Node; -} - -void USatoriClientGetLiveEvents::Activate() -{ - // Check validity of client and session - if (!SatoriClient && !UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClientAndSession(); - OnError.Broadcast({}, Error); - SetReadyToDestroy(); - return; - } - - if (!SatoriClient) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClient(); - OnError.Broadcast({}, Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidSession(); - OnError.Broadcast({}, Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FSatoriLiveEventList& LiveEvents) - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(LiveEvents, {}); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FSatoriError& error) - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast({}, error); - SetReadyToDestroy(); - }; - - SatoriClient->GetLiveEvents(UserSession, LiveEventNames, successCallback, errorCallback); -} - -USatoriClientGetMessages* USatoriClientGetMessages::GetMessages( - USatoriClient* Client, - USatoriSession* Session, - int32 Limit, - bool Forward, - const FString& Cursor) -{ - USatoriClientGetMessages* Node = NewObject(); - Node->SatoriClient = Client; - Node->UserSession = Session; - Node->Limit = Limit; - Node->Forward = Forward; - Node->Cursor = Cursor; - - return Node; -} - -void USatoriClientGetMessages::Activate() -{ - // Check validity of client and session - if (!SatoriClient && !UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClientAndSession(); - OnError.Broadcast({}, Error); - SetReadyToDestroy(); - return; - } - - if (!SatoriClient) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClient(); - OnError.Broadcast({}, Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidSession(); - OnError.Broadcast({}, Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this](const FSatoriMessageList& Messages) - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast(Messages, {}); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FSatoriError& error) - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast({}, error); - SetReadyToDestroy(); - }; - - SatoriClient->GetMessages(UserSession, Limit, Forward, Cursor, successCallback, errorCallback); -} - -USatoriClientUpdateMessage* USatoriClientUpdateMessage::UpdateMessage( - USatoriClient* Client, - USatoriSession* Session, - const FString& MessageId, - const FDateTime ReadTime, - const FDateTime ConsumeTime) -{ - USatoriClientUpdateMessage* Node = NewObject(); - Node->SatoriClient = Client; - Node->UserSession = Session; - Node->MessageId = MessageId; - Node->ReadTime = ReadTime; - Node->ConsumeTime = ConsumeTime; - - return Node; -} - -void USatoriClientUpdateMessage::Activate() -{ - // Check validity of client and session - if (!SatoriClient && !UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!SatoriClient) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FSatoriError& error) - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - SatoriClient->UpdateMessage(UserSession, MessageId, ReadTime, ConsumeTime, successCallback, errorCallback); -} - -USatoriClientDeleteMessage* USatoriClientDeleteMessage::DeleteMessage(USatoriClient* Client, USatoriSession* Session, const FString& MessageId) -{ - USatoriClientDeleteMessage* Node = NewObject(); - Node->SatoriClient = Client; - Node->UserSession = Session; - Node->MessageId = MessageId; - - return Node; -} - -void USatoriClientDeleteMessage::Activate() -{ - // Check validity of client and session - if (!SatoriClient && !UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClientAndSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!SatoriClient) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidClient(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - if (!UserSession) - { - const FSatoriError Error = FSatoriUtils::HandleInvalidSession(); - OnError.Broadcast(Error); - SetReadyToDestroy(); - return; - } - - auto successCallback = [this]() - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnSuccess.Broadcast({}); - SetReadyToDestroy(); - }; - - auto errorCallback = [this](const FSatoriError& error) - { - if (!USatoriClient::IsClientActive(SatoriClient)) - { - SetReadyToDestroy(); - return; - } - - OnError.Broadcast(error); - SetReadyToDestroy(); - }; - - SatoriClient->DeleteMessage(UserSession, MessageId, successCallback, errorCallback); -} diff --git a/Nakama/Source/NakamaUnreal/Private/NakamaRealtimeRequestContext.cpp b/Satori/Source/SatoriBlueprints/Public/SatoriBlueprintsModule.h similarity index 79% rename from Nakama/Source/NakamaUnreal/Private/NakamaRealtimeRequestContext.cpp rename to Satori/Source/SatoriBlueprints/Public/SatoriBlueprintsModule.h index f363ba628..15050a00a 100644 --- a/Nakama/Source/NakamaUnreal/Private/NakamaRealtimeRequestContext.cpp +++ b/Satori/Source/SatoriBlueprints/Public/SatoriBlueprintsModule.h @@ -1,5 +1,5 @@ /* -* Copyright 2025 The Nakama Authors + * Copyright 2026 The Nakama Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,5 +14,10 @@ * limitations under the License. */ -#include "NakamaRealtimeRequestContext.h" +#pragma once +#include "Modules/ModuleManager.h" + +class FSatoriBlueprintsModule : public IModuleInterface +{ +}; diff --git a/Satori/Source/SatoriBlueprints/Public/SatoriClientBlueprintLibrary.h b/Satori/Source/SatoriBlueprints/Public/SatoriClientBlueprintLibrary.h new file mode 100644 index 000000000..f9b0823ed --- /dev/null +++ b/Satori/Source/SatoriBlueprints/Public/SatoriClientBlueprintLibrary.h @@ -0,0 +1,816 @@ +/* + * Copyright 2026 The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + +#pragma once + +#include "CoreMinimal.h" +#include "Kismet/BlueprintAsyncActionBase.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "SatoriError.h" +#include "SatoriTypes.h" +#include "SatoriApi.h" + +#include "SatoriClientBlueprintLibrary.generated.h" + +// ============================================================================ +// Dynamic delegates for async actions +// ============================================================================ + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSatoriEmptyResponse, const FSatoriError&, Error); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSatoriAuthenticate, const FSatoriError&, Error, const FSatoriSession&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSatoriAuthenticateRefresh, const FSatoriError&, Error, const FSatoriSession&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSatoriGetExperiments, const FSatoriError&, Error, const FSatoriExperimentList&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSatoriGetFlagOverrides, const FSatoriError&, Error, const FSatoriFlagOverrideList&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSatoriGetFlags, const FSatoriError&, Error, const FSatoriFlagList&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSatoriGetLiveEvents, const FSatoriError&, Error, const FSatoriLiveEventList&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSatoriIdentify, const FSatoriError&, Error, const FSatoriSession&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSatoriListProperties, const FSatoriError&, Error, const FSatoriProperties&, Result); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSatoriGetMessageList, const FSatoriError&, Error, const FSatoriGetMessageListResponse&, Result); + +// ============================================================================ +// Async Action Classes (one per RPC) +// ============================================================================ + +/** +* Authenticate against the server. +*/ +UCLASS(Transient) +class SATORIBLUEPRINTS_API USatoriClientAuthenticate : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnSatoriAuthenticate OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnSatoriAuthenticate OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "Authenticate", AutoCreateRefTerm = "Default,Custom") + , Category = "Satori|Client" + ) + static USatoriClientAuthenticate* Authenticate( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FString& Id + , bool NoSession + , const TMap& Default + , const TMap& Custom + ); + + virtual void Activate() override; + +private: + FSatoriClientConfig StoredClientConfig; + FString StoredId; + bool StoredNoSession; + TMap StoredDefault; + TMap StoredCustom; +}; + +/** +* Log out a session, invalidate a refresh token, or log out all sessions/refresh tokens for a user. +*/ +UCLASS(Transient) +class SATORIBLUEPRINTS_API USatoriClientAuthenticateLogout : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnSatoriEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnSatoriEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "AuthenticateLogout") + , Category = "Satori|Client" + ) + static USatoriClientAuthenticateLogout* AuthenticateLogout( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FString& Token + , const FString& RefreshToken + ); + + virtual void Activate() override; + +private: + FSatoriClientConfig StoredClientConfig; + FString StoredToken; + FString StoredRefreshToken; +}; + +/** +* Refresh a user's session using a refresh token retrieved from a previous authentication request. +*/ +UCLASS(Transient) +class SATORIBLUEPRINTS_API USatoriClientAuthenticateRefresh : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnSatoriAuthenticateRefresh OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnSatoriAuthenticateRefresh OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "AuthenticateRefresh") + , Category = "Satori|Client" + ) + static USatoriClientAuthenticateRefresh* AuthenticateRefresh( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FString& RefreshToken + ); + + virtual void Activate() override; + +private: + FSatoriClientConfig StoredClientConfig; + FString StoredRefreshToken; +}; + +/** +* Delete the caller's identity and associated data. +*/ +UCLASS(Transient) +class SATORIBLUEPRINTS_API USatoriClientDeleteIdentity : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnSatoriEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnSatoriEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "DeleteIdentity") + , Category = "Satori|Client" + ) + static USatoriClientDeleteIdentity* DeleteIdentity( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + ); + + virtual void Activate() override; + +private: + FSatoriClientConfig StoredClientConfig; + FSatoriSession StoredSession; +}; + +/** +* Publish an event for this session. +*/ +UCLASS(Transient) +class SATORIBLUEPRINTS_API USatoriClientEvent : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnSatoriEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnSatoriEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "Event") + , Category = "Satori|Client" + ) + static USatoriClientEvent* Event( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + , const TArray& Events + ); + + virtual void Activate() override; + +private: + FSatoriClientConfig StoredClientConfig; + FSatoriSession StoredSession; + TArray StoredEvents; +}; + +/** +* Publish server events for multiple distinct identities. +*/ +UCLASS(Transient) +class SATORIBLUEPRINTS_API USatoriClientServerEvent : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnSatoriEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnSatoriEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ServerEvent") + , Category = "Satori|Client" + ) + static USatoriClientServerEvent* ServerEvent( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + , const TArray& Events + ); + + virtual void Activate() override; + +private: + FSatoriClientConfig StoredClientConfig; + FSatoriSession StoredSession; + TArray StoredEvents; +}; + +/** +* Get or list all available experiments for this identity. +*/ +UCLASS(Transient) +class SATORIBLUEPRINTS_API USatoriClientGetExperiments : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnSatoriGetExperiments OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnSatoriGetExperiments OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "GetExperiments") + , Category = "Satori|Client" + ) + static USatoriClientGetExperiments* GetExperiments( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + , const TArray& Names + , const TArray& Labels + ); + + virtual void Activate() override; + +private: + FSatoriClientConfig StoredClientConfig; + FSatoriSession StoredSession; + TArray StoredNames; + TArray StoredLabels; +}; + +/** +* List all available flags and their value overrides for this identity. +*/ +UCLASS(Transient) +class SATORIBLUEPRINTS_API USatoriClientGetFlagOverrides : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnSatoriGetFlagOverrides OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnSatoriGetFlagOverrides OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "GetFlagOverrides") + , Category = "Satori|Client" + ) + static USatoriClientGetFlagOverrides* GetFlagOverrides( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + , const TArray& Names + , const TArray& Labels + ); + + virtual void Activate() override; + +private: + FSatoriClientConfig StoredClientConfig; + FSatoriSession StoredSession; + TArray StoredNames; + TArray StoredLabels; +}; + +/** +* List all available flags for this identity. +*/ +UCLASS(Transient) +class SATORIBLUEPRINTS_API USatoriClientGetFlags : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnSatoriGetFlags OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnSatoriGetFlags OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "GetFlags") + , Category = "Satori|Client" + ) + static USatoriClientGetFlags* GetFlags( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + , const TArray& Names + , const TArray& Labels + ); + + virtual void Activate() override; + +private: + FSatoriClientConfig StoredClientConfig; + FSatoriSession StoredSession; + TArray StoredNames; + TArray StoredLabels; +}; + +/** +* List available live events. +*/ +UCLASS(Transient) +class SATORIBLUEPRINTS_API USatoriClientGetLiveEvents : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnSatoriGetLiveEvents OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnSatoriGetLiveEvents OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "GetLiveEvents") + , Category = "Satori|Client" + ) + static USatoriClientGetLiveEvents* GetLiveEvents( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + , const TArray& Names + , const TArray& Labels + , int32 PastRunCount + , int32 FutureRunCount + , int64 StartTimeSec + , int64 EndTimeSec + ); + + virtual void Activate() override; + +private: + FSatoriClientConfig StoredClientConfig; + FSatoriSession StoredSession; + TArray StoredNames; + TArray StoredLabels; + int32 StoredPastRunCount; + int32 StoredFutureRunCount; + int64 StoredStartTimeSec; + int64 StoredEndTimeSec; +}; + +/** +* Join an 'explicit join' live event. +*/ +UCLASS(Transient) +class SATORIBLUEPRINTS_API USatoriClientJoinLiveEvent : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnSatoriEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnSatoriEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "JoinLiveEvent") + , Category = "Satori|Client" + ) + static USatoriClientJoinLiveEvent* JoinLiveEvent( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + , const FString& Id + ); + + virtual void Activate() override; + +private: + FSatoriClientConfig StoredClientConfig; + FSatoriSession StoredSession; + FString StoredId; +}; + +/** +* A healthcheck which load balancers can use to check the service. +*/ +UCLASS(Transient) +class SATORIBLUEPRINTS_API USatoriClientHealthcheck : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnSatoriEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnSatoriEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "Healthcheck") + , Category = "Satori|Client" + ) + static USatoriClientHealthcheck* Healthcheck( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + ); + + virtual void Activate() override; + +private: + FSatoriClientConfig StoredClientConfig; + FSatoriSession StoredSession; +}; + +/** +* Enrich/replace the current session with new identifier. +*/ +UCLASS(Transient) +class SATORIBLUEPRINTS_API USatoriClientIdentify : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnSatoriIdentify OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnSatoriIdentify OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "Identify", AutoCreateRefTerm = "Default,Custom") + , Category = "Satori|Client" + ) + static USatoriClientIdentify* Identify( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + , const FString& Id + , const TMap& Default + , const TMap& Custom + ); + + virtual void Activate() override; + +private: + FSatoriClientConfig StoredClientConfig; + FSatoriSession StoredSession; + FString StoredId; + TMap StoredDefault; + TMap StoredCustom; +}; + +/** +* List properties associated with this identity. +*/ +UCLASS(Transient) +class SATORIBLUEPRINTS_API USatoriClientListProperties : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnSatoriListProperties OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnSatoriListProperties OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "ListProperties") + , Category = "Satori|Client" + ) + static USatoriClientListProperties* ListProperties( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + ); + + virtual void Activate() override; + +private: + FSatoriClientConfig StoredClientConfig; + FSatoriSession StoredSession; +}; + +/** +* A readycheck which load balancers can use to check the service. +*/ +UCLASS(Transient) +class SATORIBLUEPRINTS_API USatoriClientReadycheck : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnSatoriEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnSatoriEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "Readycheck") + , Category = "Satori|Client" + ) + static USatoriClientReadycheck* Readycheck( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + ); + + virtual void Activate() override; + +private: + FSatoriClientConfig StoredClientConfig; + FSatoriSession StoredSession; +}; + +/** +* Update identity properties. +*/ +UCLASS(Transient) +class SATORIBLUEPRINTS_API USatoriClientUpdateProperties : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnSatoriEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnSatoriEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "UpdateProperties", AutoCreateRefTerm = "Default,Custom") + , Category = "Satori|Client" + ) + static USatoriClientUpdateProperties* UpdateProperties( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + , FSatoriOptionalBool Recompute + , const TMap& Default + , const TMap& Custom + ); + + virtual void Activate() override; + +private: + FSatoriClientConfig StoredClientConfig; + FSatoriSession StoredSession; + FSatoriOptionalBool StoredRecompute; + TMap StoredDefault; + TMap StoredCustom; +}; + +/** +* Get the list of messages for the identity. +*/ +UCLASS(Transient) +class SATORIBLUEPRINTS_API USatoriClientGetMessageList : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnSatoriGetMessageList OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnSatoriGetMessageList OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "GetMessageList") + , Category = "Satori|Client" + ) + static USatoriClientGetMessageList* GetMessageList( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + , int32 Limit + , bool Forward + , const FString& Cursor + , const TArray& MessageIds + ); + + virtual void Activate() override; + +private: + FSatoriClientConfig StoredClientConfig; + FSatoriSession StoredSession; + int32 StoredLimit; + bool StoredForward; + FString StoredCursor; + TArray StoredMessageIds; +}; + +/** +* Updates a message for an identity. +*/ +UCLASS(Transient) +class SATORIBLUEPRINTS_API USatoriClientUpdateMessage : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnSatoriEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnSatoriEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "UpdateMessage") + , Category = "Satori|Client" + ) + static USatoriClientUpdateMessage* UpdateMessage( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + , const FString& Id + , int64 ReadTime + , int64 ConsumeTime + ); + + virtual void Activate() override; + +private: + FSatoriClientConfig StoredClientConfig; + FSatoriSession StoredSession; + FString StoredId; + int64 StoredReadTime; + int64 StoredConsumeTime; +}; + +/** +* Deletes a message for an identity. +*/ +UCLASS(Transient) +class SATORIBLUEPRINTS_API USatoriClientDeleteMessage : public UBlueprintAsyncActionBase +{ + GENERATED_BODY() + +public: + + UPROPERTY(BlueprintAssignable) + FOnSatoriEmptyResponse OnSuccess; + + UPROPERTY(BlueprintAssignable) + FOnSatoriEmptyResponse OnError; + + UFUNCTION( + BlueprintCallable + , meta = ( + BlueprintInternalUseOnly = "true" + , WorldContext = "WorldContextObject" + , DisplayName = "DeleteMessage") + , Category = "Satori|Client" + ) + static USatoriClientDeleteMessage* DeleteMessage( + UObject* WorldContextObject + , FSatoriClientConfig ClientConfig + , const FSatoriSession& Session + , const FString& Id + ); + + virtual void Activate() override; + +private: + FSatoriClientConfig StoredClientConfig; + FSatoriSession StoredSession; + FString StoredId; +}; diff --git a/Satori/Source/SatoriBlueprints/Public/SatoriClientRequests.h b/Satori/Source/SatoriBlueprints/Public/SatoriClientRequests.h deleted file mode 100644 index 4f9552eb0..000000000 --- a/Satori/Source/SatoriBlueprints/Public/SatoriClientRequests.h +++ /dev/null @@ -1,670 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "SatoriClient.h" -#include "Kismet/BlueprintAsyncActionBase.h" -#include "SatoriError.h" - -#include "SatoriClientRequests.generated.h" - -// Delegates -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSatoriAuthenticate, USatoriSession*, Session, FSatoriError, Error); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSatoriAuthenticateRefresh, USatoriSession*, Session, FSatoriError, Error); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSatoriAuthenticateLogout, FSatoriError, Error); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSatoriIdentify, USatoriSession*, Session, FSatoriError, Error); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSatoriListIdentityProperties, FSatoriProperties, Properties, FSatoriError, Error); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSatoriUpdateProperties, FSatoriError, Error); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSatoriDeleteIdentity, FSatoriError, Error); - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSatoriPostEvent, FSatoriError, Error); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSatoriGetExperiments, FSatoriExperimentList, Experiments, FSatoriError, Error); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSatoriGetFlags, FSatoriFlagList, Flags, FSatoriError, Error); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSatoriGetFlagOverrides, FSatoriFlagOverrideList, FlagOverrides, FSatoriError, Error); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSatoriGetLiveEvents, FSatoriLiveEventList, LiveEvents, FSatoriError, Error); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSatoriGetMessages, FSatoriMessageList, Messages, FSatoriError, Error); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSatoriUpdateMessage, FSatoriError, Error); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSatoriDeleteMessage, FSatoriError, Error); - - - -// --- Authentication --- // - - -/** - * Authenticate Custom - */ - -UCLASS() -class SATORIBLUEPRINTS_API USatoriClientAuthenticate : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr SatoriClient; - - UPROPERTY(BlueprintAssignable) - FOnSatoriAuthenticate OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnSatoriAuthenticate OnError; - - /** - * Authenticate to get a satori session. - * @param ID Must be between eight and 128 characters (inclusive). Must be an alphanumeric string with only underscores and hyphens allowed. - * @param DefaultProperties Optional default properties to update with this call. If not set, properties are left as they are on the server. - * @param CustomProperties Optional custom properties to update with this call. If not set, properties are left as they are on the server. - * @param bNoSession Modifies the request to only create/update an identity without creating a new session. If set to 'true' the response won't include a token and a refresh token. - * @param Success Delegate called upon successful authentication, providing the user session. - * @param Error Delegate called on authentication failure with error details. - */ - UFUNCTION(BlueprintCallable, Category = "Satori|Authentication", meta = (BlueprintInternalUseOnly = "true", AutoCreateRefTerm = "DefaultProperties,CustomProperties")) - static USatoriClientAuthenticate* Authenticate( - USatoriClient *Client, - const FString& ID, - const TMap& DefaultProperties, - const TMap& CustomProperties, - const bool bNoSession); - - virtual void Activate() override; - -private: - - FString ID; - TMap DefaultProperties; - TMap CustomProperties; - bool bNoSession; -}; - - -/** - * Refresh authentication - */ - -UCLASS() -class SATORIBLUEPRINTS_API USatoriClientAuthenticateRefresh : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr SatoriClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnSatoriAuthenticateRefresh OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnSatoriAuthenticateRefresh OnError; - - /** - * Post one or more events. - * - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Satori|Authentication", meta = (BlueprintInternalUseOnly = "true")) - static USatoriClientAuthenticateRefresh* AuthenticateRefresh(USatoriClient* Client, USatoriSession* Session); - - virtual void Activate() override; -}; - - -/** - * Logout - */ - -UCLASS() -class SATORIBLUEPRINTS_API USatoriClientAuthenticateLogout : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr SatoriClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnSatoriAuthenticateLogout OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnSatoriAuthenticateLogout OnError; - - /** - * Post one or more events. - * - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Satori|Authentication", meta = (BlueprintInternalUseOnly = "true")) - static USatoriClientAuthenticateLogout* AuthenticateLogout(USatoriClient* Client, USatoriSession* Session); - - virtual void Activate() override; -}; - - -/** - * Identify - */ - -UCLASS() -class SATORIBLUEPRINTS_API USatoriClientIdentify : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr SatoriClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnSatoriIdentify OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnSatoriIdentify OnError; - - /** - * Post one or more events. - * - * @param Events The ids of the users to add or invite as friends. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Satori|Authentication", meta = (BlueprintInternalUseOnly = "true", AutoCreateRefTerm = "DefaultProperties,CustomProperties")) - static USatoriClientIdentify* Identify( - USatoriClient* Client, - USatoriSession* Session, - const FString& ID, - const TMap& DefaultProperties, - const TMap& CustomProperties); - - virtual void Activate() override; - -private: - FString ID; - TMap DefaultProperties; - TMap CustomProperties; -}; - - -/** - * List identity properties - */ - -UCLASS() -class SATORIBLUEPRINTS_API USatoriClientListIdentityProperties : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr SatoriClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnSatoriListIdentityProperties OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnSatoriListIdentityProperties OnError; - - /** - * Post one or more events. - * - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Satori|Authentication", meta = (BlueprintInternalUseOnly = "true")) - static USatoriClientListIdentityProperties* ListIdentityProperties(USatoriClient* Client, USatoriSession* Session); - - virtual void Activate() override; -}; - - -/** - * Update properties - */ - -UCLASS() -class SATORIBLUEPRINTS_API USatoriClientUpdateProperties : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr SatoriClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnSatoriUpdateProperties OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnSatoriUpdateProperties OnError; - - /** - * Post one or more events. - * - * @param Events The ids of the users to add or invite as friends. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Satori|Authentication", meta = (BlueprintInternalUseOnly = "true", AutoCreateRefTerm = "DefaultProperties,CustomProperties")) - static USatoriClientUpdateProperties* UpdateProperties( - USatoriClient* Client, - USatoriSession* Session, - const TMap& DefaultProperties, - const TMap& CustomProperties, - const bool bRecompute); - - virtual void Activate() override; - -private: - TMap DefaultProperties; - TMap CustomProperties; - bool bRecompute; -}; - - -/** - * Delete identity - */ - -UCLASS() -class SATORIBLUEPRINTS_API USatoriClientDeleteIdentity : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr SatoriClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnSatoriDeleteIdentity OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnSatoriDeleteIdentity OnError; - - /** - * Post one or more events. - * - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Satori|Authentication", meta = (BlueprintInternalUseOnly = "true")) - static USatoriClientDeleteIdentity* DeleteIdentity(USatoriClient* Client, USatoriSession* Session); - - virtual void Activate() override; -}; - - - -// --- Interface --- // - - -/** - * Send event - */ - -UCLASS() -class SATORIBLUEPRINTS_API USatoriClientPostEvent : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr SatoriClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnSatoriPostEvent OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnSatoriPostEvent OnError; - - /** - * Post one or more events. - * - * @param Events The events to publish. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Satori|Events", meta = (BlueprintInternalUseOnly = "true")) - static USatoriClientPostEvent* PostEvent(USatoriClient* Client, USatoriSession* Session, const TArray& Events); - - virtual void Activate() override; - -private: - TArray Events; -}; - - -/** - * Get experiments - */ - -UCLASS() -class SATORIBLUEPRINTS_API USatoriClientGetExperiments : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr SatoriClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnSatoriGetExperiments OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnSatoriGetExperiments OnError; - - /** - * Post one or more events. - * - * @param Names The ids of the experiments to query. Leave empty or pass an empty array to get them all. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Satori|Experiments", meta = (BlueprintInternalUseOnly = "true", AutoCreateRefTerm = "Names")) - static USatoriClientGetExperiments* GetExperiments(USatoriClient* Client, USatoriSession* Session, const TArray& Names); - - virtual void Activate() override; - -private: - TArray Names; -}; - - -/** - * Get flags - */ - -UCLASS() -class SATORIBLUEPRINTS_API USatoriClientGetFlags : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr SatoriClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnSatoriGetFlags OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnSatoriGetFlags OnError; - - /** - * Post one or more events. - * - * @param Names The ids of the flags to query. Leave empty or pass an empty array to get them all. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Satori|Flags", meta = (BlueprintInternalUseOnly = "true", AutoCreateRefTerm = "Names")) - static USatoriClientGetFlags* GetFlags(USatoriClient* Client, USatoriSession* Session, const TArray& Names); - - virtual void Activate() override; - -private: - TArray Names; -}; - - -/** - * Get flags - */ - -UCLASS() -class SATORIBLUEPRINTS_API USatoriClientGetFlagOverrides : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr SatoriClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnSatoriGetFlagOverrides OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnSatoriGetFlagOverrides OnError; - - /** - * Post one or more events. - * - * @param Names The ids of the flags to query overrides for. Leave empty or pass an empty array to get them all. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Satori|Flags", meta = (BlueprintInternalUseOnly = "true", AutoCreateRefTerm = "Names")) - static USatoriClientGetFlagOverrides* GetFlagOverrides(USatoriClient* Client, USatoriSession* Session, const TArray& Names); - - virtual void Activate() override; - -private: - TArray Names; -}; - - -/** - * Get live events - */ - -UCLASS() -class SATORIBLUEPRINTS_API USatoriClientGetLiveEvents : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr SatoriClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnSatoriGetLiveEvents OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnSatoriGetLiveEvents OnError; - - /** - * Post one or more events. - * - * @param Events The ids of the users to add or invite as friends. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Satori|LiveEvents", meta = (BlueprintInternalUseOnly = "true", AutoCreateRefTerm = "LiveEventNames")) - static USatoriClientGetLiveEvents* GetLiveEvents(USatoriClient* Client, USatoriSession* Session, const TArray& LiveEventNames); - - virtual void Activate() override; - -private: - TArray LiveEventNames; -}; - - -/** - * Get messages - */ - -UCLASS() -class SATORIBLUEPRINTS_API USatoriClientGetMessages : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr SatoriClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnSatoriGetMessages OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnSatoriGetMessages OnError; - - /** - * Post one or more events. - * - * @param Events The ids of the users to add or invite as friends. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Satori|Messages", meta = (BlueprintInternalUseOnly = "true")) - static USatoriClientGetMessages* GetMessages( - USatoriClient* Client, - USatoriSession* Session, - int32 Limit, - bool Forward, - const FString& Cursor); - - virtual void Activate() override; - -private: - int32 Limit; - bool Forward; - FString Cursor; -}; - - -/** - * Update message - */ - -UCLASS() -class SATORIBLUEPRINTS_API USatoriClientUpdateMessage : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr SatoriClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnSatoriUpdateMessage OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnSatoriUpdateMessage OnError; - - /** - * Post one or more events. - * - * @param Events The ids of the users to add or invite as friends. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Satori|Messages", meta = (BlueprintInternalUseOnly = "true")) - static USatoriClientUpdateMessage* UpdateMessage( - USatoriClient* Client, - USatoriSession* Session, - const FString& MessageId, - const FDateTime ReadTime, - const FDateTime ConsumeTime); - - virtual void Activate() override; - -private: - FString MessageId; - FDateTime ReadTime; - FDateTime ConsumeTime; -}; - - -/** - * Delete message - */ - -UCLASS() -class SATORIBLUEPRINTS_API USatoriClientDeleteMessage : public UBlueprintAsyncActionBase -{ - GENERATED_BODY() - -public: - - UPROPERTY() - TObjectPtr SatoriClient; - - UPROPERTY() - TObjectPtr UserSession; - - UPROPERTY(BlueprintAssignable) - FOnSatoriDeleteMessage OnSuccess; - - UPROPERTY(BlueprintAssignable) - FOnSatoriDeleteMessage OnError; - - /** - * Post one or more events. - * - * @param Events The ids of the users to add or invite as friends. - * @param Session The session of the user. - * @param Client The Client to use. - */ - UFUNCTION(BlueprintCallable, Category = "Satori|Messages", meta = (BlueprintInternalUseOnly = "true")) - static USatoriClientDeleteMessage* DeleteMessage(USatoriClient* Client, USatoriSession* Session, const FString& MessageId); - - virtual void Activate() override; - -private: - FString MessageId; -}; diff --git a/Satori/Source/SatoriBlueprints/SatoriBlueprints.Build.cs b/Satori/Source/SatoriBlueprints/SatoriBlueprints.Build.cs index 81f22b97f..0d5f0ef0b 100644 --- a/Satori/Source/SatoriBlueprints/SatoriBlueprints.Build.cs +++ b/Satori/Source/SatoriBlueprints/SatoriBlueprints.Build.cs @@ -40,7 +40,7 @@ public SatoriBlueprints(ReadOnlyTargetRules Target) : base(Target) new string[] { "Core", - "SatoriUnreal" + "SatoriApi" // ... add other public dependencies that you statically link with here ... } ); diff --git a/Satori/Source/SatoriUnreal/Private/SatoriClient.cpp b/Satori/Source/SatoriUnreal/Private/SatoriClient.cpp deleted file mode 100644 index 6c79ab32a..000000000 --- a/Satori/Source/SatoriUnreal/Private/SatoriClient.cpp +++ /dev/null @@ -1,2137 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "SatoriClient.h" -#include "SatoriUtils.h" -#include "GenericPlatform/GenericPlatformHttp.h" -#include "Interfaces/IHttpResponse.h" - -void USatoriClient::InitializeClient( - const FString& InHostname, - int32 InPort, - const FString& InServerKey, - bool bInUseSSL, - bool EnableDebug) -{ - Hostname = InHostname; - Port = InPort; - ServerKey = InServerKey; - bUseSSL = bInUseSSL; - bEnableDebug = EnableDebug; -} - -void USatoriClient::InitializeSystem( - const FString& InServerKey, - const FString& Host, - int32 InPort, - bool UseSSL, - bool EnableDebug) -{ - InitializeClient(Host, InPort, InServerKey, UseSSL, EnableDebug); - bIsActive = true; -} - -void USatoriClient::Disconnect() -{ - if (IsValidLowLevel()) - { - CancelAllRequests(); - } -} - -void USatoriClient::CancelAllRequests() -{ - if (!IsValidLowLevel()) - { - return; - } - - // Lock the mutex to protect access to ActiveRequests - FScopeLock Lock(&ActiveRequestsMutex); - - // Iterate over the active requests and cancel each one - for (const FHttpRequestPtr& Request : ActiveRequests) - { - Request->OnProcessRequestComplete().Unbind(); - Request->CancelRequest(); - } - - // Clear the ActiveRequests array - ActiveRequests.Empty(); -} - -void USatoriClient::Destroy() -{ - bIsActive = false; - ConditionalBeginDestroy(); -} - -void USatoriClient::SetTimeout(float InTimeout) -{ - Timeout = InTimeout; -} - -float USatoriClient::GetTimeout() -{ - return Timeout; -} - -void USatoriClient::BeginDestroy() -{ - UObject::BeginDestroy(); - bIsActive = false; -} - -USatoriClient* USatoriClient::CreateDefaultClient( - const FString& ServerKey, - const FString& Host, - int32 Port, - bool UseSSL, - bool EnableDebug) -{ - USatoriClient* NewClient = NewObject(); - NewClient->InitializeSystem(ServerKey, Host, Port, UseSSL, EnableDebug); - - if (EnableDebug) - { - USatoriLogger::EnableLogging(true); - USatoriLogger::SetLogLevel(ESatoriLogLevel::Debug); - } - - return NewClient; -} - -void USatoriClient::Authenticate( - const FString& ID, - const TMap& DefaultProperties, - const TMap& CustomProperties, - const bool bNoSession, - FOnSatoriAuthUpdate Success, - FOnSatoriError Error) -{ - auto successCallback = [this, Success](USatoriSession* session) - { - if (!IsClientActive(this)) - return; - - Success.Broadcast(session); - }; - - auto errorCallback = [this, Error](const FSatoriError& error) - { - if (!IsClientActive(this)) - return; - - Error.Broadcast(error); - }; - - // A custom identifier must contain alphanumeric - // characters with dashesand be between 6 and 128 bytes. - - Authenticate(ID, DefaultProperties, CustomProperties, bNoSession, successCallback, errorCallback); -} - -void USatoriClient::Authenticate( - const FString& ID, - const TMap& DefaultProperties, - const TMap& CustomProperties, - const bool bNoSession, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v1/authenticate"); - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - - ContentJson->SetStringField(TEXT("id"), ID); - if (bNoSession) - { - ContentJson->SetBoolField(TEXT("no_session"), bNoSession); - } - - if(!DefaultProperties.IsEmpty()) - { - TSharedPtr DefaultPropertiesJson = MakeShared(); - for (const TPair& Pair : DefaultProperties) - { - DefaultPropertiesJson->SetStringField(Pair.Key, Pair.Value); - } - ContentJson->SetObjectField(TEXT("default"), DefaultPropertiesJson); - } - - if (!CustomProperties.IsEmpty()) - { - TSharedPtr CustomPropertiesJson = MakeShared(); - for (const TPair& Pair : CustomProperties) - { - CustomPropertiesJson->SetStringField(Pair.Key, Pair.Value); - } - ContentJson->SetObjectField(TEXT("custom"), CustomPropertiesJson); - } - - // Serialize the request content - FString Content; - if (!FSatoriUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FSatoriUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ESatoriRequestMethod::POST, TMultiMap(), ""); - - // Set the basic authorization header - FSatoriUtils::SetBasicAuthorizationHeader(HttpRequest, ServerKey); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FSatoriUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(USatoriSession::SetupSession(ResponseBody)); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FSatoriError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FSatoriError RequestError = FSatoriUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void USatoriClient::AuthenticateRefresh( - USatoriSession* Session, - FOnSatoriAuthUpdate Success, - FOnSatoriError Error) -{ - auto successCallback = [this, Success](USatoriSession* UserSession) - { - if (!IsClientActive(this)) - { - return; - } - - Success.Broadcast(UserSession); - }; - - auto errorCallback = [this, Error](const FSatoriError& error) - { - if (!IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - AuthenticateRefresh(Session, successCallback, errorCallback); -} - -void USatoriClient::AuthenticateRefresh( - USatoriSession* Session, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v1/authenticate/refresh"); - - // Verify the session - if (!FSatoriUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the request content - TSharedPtr ContentJson = MakeShareable(new FJsonObject); - ContentJson->SetStringField(TEXT("refresh_token"), Session->GetRefreshToken()); - - // Serialize the request content - FString Content; - if (!FSatoriUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FSatoriUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ESatoriRequestMethod::POST, TMultiMap(), ""); - - // Set the basic authorization header - FSatoriUtils::SetBasicAuthorizationHeader(HttpRequest, ServerKey); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FSatoriUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(USatoriSession::SetupSession(ResponseBody)); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FSatoriError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FSatoriError RequestError = FSatoriUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void USatoriClient::AuthenticateLogout( - USatoriSession* Session, - FOnAuthLogoutSent Success, - FOnSatoriError Error) -{ - auto successCallback = [this, Success]() - { - if (!IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FSatoriError& error) - { - if (!IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - AuthenticateLogout(Session, successCallback, errorCallback); -} - -void USatoriClient::AuthenticateLogout( - USatoriSession* Session, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v1/authenticate/logout"); - - // Verify the session - if (!FSatoriUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the request content - TSharedPtr ContentJson = MakeShareable(new FJsonObject); - ContentJson->SetStringField(TEXT("token"), Session->GetAuthToken()); - ContentJson->SetStringField(TEXT("refresh_token"), Session->GetRefreshToken()); - - // Serialize the request content - FString Content; - if (!FSatoriUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FSatoriUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ESatoriRequestMethod::POST, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FSatoriUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FSatoriError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FSatoriError RequestError = FSatoriUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void USatoriClient::Identify( - USatoriSession* Session, - const FString& ID, - const TMap& defaultProperties, - const TMap& customProperties, - FOnSatoriAuthUpdate Success, - FOnSatoriError Error) -{ - auto successCallback = [this, Success](USatoriSession* UserSession) - { - if (!IsClientActive(this)) - { - return; - } - - Success.Broadcast(UserSession); - }; - - auto errorCallback = [this, Error](const FSatoriError& error) - { - if (!IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - Identify(Session, ID, defaultProperties, customProperties, successCallback, errorCallback); -} - -void USatoriClient::Identify( - USatoriSession* Session, - const FString& ID, - const TMap& DefaultProperties, - const TMap& CustomProperties, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v1/identify"); - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - - ContentJson->SetStringField(TEXT("id"), ID); - - if(!DefaultProperties.IsEmpty()) - { - TSharedPtr DefaultPropertiesJson = MakeShared(); - for (const TPair& Pair : DefaultProperties) - { - DefaultPropertiesJson->SetStringField(Pair.Key, Pair.Value); - } - ContentJson->SetObjectField(TEXT("default"), DefaultPropertiesJson); - } - - if (!CustomProperties.IsEmpty()) - { - TSharedPtr CustomPropertiesJson = MakeShared(); - for (const TPair& Pair : CustomProperties) - { - CustomPropertiesJson->SetStringField(Pair.Key, Pair.Value); - } - ContentJson->SetObjectField(TEXT("custom"), CustomPropertiesJson); - } - - // Serialize the request content - FString Content; - if (!FSatoriUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FSatoriUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ESatoriRequestMethod::PUT, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FSatoriUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(USatoriSession::SetupSession(ResponseBody)); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FSatoriError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FSatoriError RequestError = FSatoriUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void USatoriClient::ListIdentityProperties( - USatoriSession* Session, - FOnGetProperties Success, - FOnSatoriError Error) -{ - auto successCallback = [this, Success](const FSatoriProperties& Properties) - { - if (!IsClientActive(this)) - { - return; - } - - Success.Broadcast(Properties); - }; - - auto errorCallback = [this, Error](const FSatoriError& error) - { - if (!IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - ListIdentityProperties(Session, successCallback, errorCallback); -} - -void USatoriClient::ListIdentityProperties( - USatoriSession* Session, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v1/properties"); - - // Verify the session - if (!FSatoriUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ESatoriRequestMethod::GET, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FSatoriUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FSatoriProperties Properties = FSatoriProperties(ResponseBody); - SuccessCallback(Properties); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FSatoriError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FSatoriError RequestError = FSatoriUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void USatoriClient::UpdateProperties( - USatoriSession* Session, - const TMap& DefaultProperties, - const TMap& CustomProperties, - const bool bRecompute, - FOnUpdatePropertiesSent Success, - FOnSatoriError Error) -{ - auto successCallback = [this, Success]() - { - if (!IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FSatoriError& error) - { - if (!IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - UpdateProperties(Session, DefaultProperties, CustomProperties, bRecompute, successCallback, errorCallback); -} - -void USatoriClient::UpdateProperties( - USatoriSession* Session, - const TMap& DefaultProperties, - const TMap& CustomProperties, - const bool bRecompute, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v1/properties"); - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - - if (!DefaultProperties.IsEmpty()) - { - TSharedPtr DefaultPropertiesJson = MakeShared(); - for (const TPair& Pair : DefaultProperties) - { - DefaultPropertiesJson->SetStringField(Pair.Key, Pair.Value); - } - ContentJson->SetObjectField(TEXT("default"), DefaultPropertiesJson); - } - - if (!CustomProperties.IsEmpty()) - { - TSharedPtr CustomPropertiesJson = MakeShared(); - for (const TPair& Pair : CustomProperties) - { - CustomPropertiesJson->SetStringField(Pair.Key, Pair.Value); - } - ContentJson->SetObjectField(TEXT("custom"), CustomPropertiesJson); - } - - ContentJson->SetBoolField(TEXT("recompute"), bRecompute); - - // Serialize the request content - FString Content; - if (!FSatoriUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FSatoriUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ESatoriRequestMethod::PUT, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FSatoriUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FSatoriError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FSatoriError RequestError = FSatoriUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void USatoriClient::DeleteIdentity( - USatoriSession* Session, - FOnDeleteIdentitySent Success, - FOnSatoriError Error) -{ - auto successCallback = [this, Success]() - { - if (!IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FSatoriError& error) - { - if (!IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - DeleteIdentity(Session, successCallback, errorCallback); -} - -void USatoriClient::DeleteIdentity( - USatoriSession* Session, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v1/identity"); - - // Verify the session - if (!FSatoriUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ESatoriRequestMethod::DEL, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FSatoriUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FSatoriError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FSatoriError RequestError = FSatoriUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void USatoriClient::PostServerEvent( - const TArray& Events, - FOnPostServerEventSent Success, - FOnSatoriError Error) -{ - auto successCallback = [this, Success]() - { - if (!IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FSatoriError& error) - { - if (!IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - PostServerEvent(Events, successCallback, errorCallback); -} - -void USatoriClient::PostServerEvent( - const TArray& Events, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v1/server-event"); - - // Setup the request content - TSharedPtr ContentJson = MakeShareable(new FJsonObject); - - // Setup the request body - TArray> EventsJson; - for (const FSatoriEvent& Event : Events) - { - TSharedPtr EventJson = MakeShareable(new FJsonObject()); - EventJson->SetStringField(TEXT("name"), Event.Name); - if (!Event.ID.IsEmpty()) - { - EventJson->SetStringField(TEXT("id"), Event.ID); - } - FSatoriUtils::AddVarsToJson(EventJson, Event.Metadata, TEXT("metadata")); - if (!Event.Value.IsEmpty()) - { - EventJson->SetStringField(TEXT("value"), Event.Value); - } - // google rpc requires RFC 3339, let's just hope Unreal's ISO 8601 keeps being compliant with it. - EventJson->SetStringField(TEXT("timestamp"), Event.Timestamp.ToIso8601()); - - if (!Event.IdentityId.IsEmpty()) - { - EventJson->SetStringField(TEXT("identity_id"), Event.IdentityId); - } - if (!Event.SessionId.IsEmpty()) - { - EventJson->SetStringField(TEXT("session_id"), Event.SessionId); - } - if (Event.SessionIssuedAt > 0) - { - EventJson->SetNumberField(TEXT("session_issued_at"), Event.SessionIssuedAt); - } - if (Event.SessionExpiresAt > 0) - { - EventJson->SetNumberField(TEXT("session_expires_at"), Event.SessionExpiresAt); - } - - EventsJson.Add(MakeShareable(new FJsonValueObject(EventJson))); - } - - ContentJson->SetArrayField(TEXT("events"), EventsJson); - - // Serialize the request content - FString Content; - if (!FSatoriUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FSatoriUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest( - Endpoint, Content, ESatoriRequestMethod::POST, TMultiMap(), ""); - - FSatoriUtils::SetBasicAuthorizationHeader(HttpRequest, ServerKey); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) - { - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FSatoriUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FSatoriError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FSatoriError RequestError = FSatoriUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void USatoriClient::PostEvent( - USatoriSession* Session, - const TArray& Events, - FOnPostEventSent Success, - FOnSatoriError Error) -{ - auto successCallback = [this, Success]() - { - if (!IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FSatoriError& error) - { - if (!IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - PostEvent(Session, Events, successCallback, errorCallback); -} - -void USatoriClient::PostEvent( - USatoriSession* Session, - const TArray& Events, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v1/event"); - - // Verify the session - if (!FSatoriUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the request content - TSharedPtr ContentJson = MakeShareable(new FJsonObject); - - // Setup the request body - TArray> EventsJson; - for (const FSatoriEvent& Event : Events) - { - TSharedPtr EventJson = MakeShareable(new FJsonObject()); - EventJson->SetStringField(TEXT("name"), Event.Name); - if (!Event.ID.IsEmpty()) - { - EventJson->SetStringField(TEXT("id"), Event.ID); - } - FSatoriUtils::AddVarsToJson(EventJson, Event.Metadata, TEXT("metadata")); - if (!Event.Value.IsEmpty()) - { - EventJson->SetStringField(TEXT("value"), Event.Value); - } - // google rpc requires RFC 3339, let's just hope Unreal's ISO 8601 keeps being compliant with it. - EventJson->SetStringField(TEXT("timestamp"), Event.Timestamp.ToIso8601()); - EventsJson.Add(MakeShareable(new FJsonValueObject(EventJson))); - } - - ContentJson->SetArrayField(TEXT("events"), EventsJson); - - // Serialize the request content - FString Content; - if (!FSatoriUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FSatoriUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ESatoriRequestMethod::POST, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FSatoriUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FSatoriError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FSatoriError RequestError = FSatoriUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void USatoriClient::GetExperiments( - USatoriSession* Session, - const TArray& Names, - FOnGetExperiments Success, - FOnSatoriError Error) -{ - auto successCallback = [this, Success](const FSatoriExperimentList& Experiments) - { - if (!IsClientActive(this)) - { - return; - } - - Success.Broadcast(Experiments); - }; - - auto errorCallback = [this, Error](const FSatoriError& error) - { - if (!IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - GetExperiments(Session, Names, successCallback, errorCallback); -} - -void USatoriClient::GetExperiments( - USatoriSession* Session, - const TArray& Names, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v1/experiment"); - - // Verify the session - if (!FSatoriUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - for (const FString& Name : Names) - { - QueryParams.Add(TEXT("names"), Name); - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ESatoriRequestMethod::GET, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FSatoriUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FSatoriExperimentList Experiments = FSatoriExperimentList(ResponseBody); - SuccessCallback(Experiments); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FSatoriError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FSatoriError RequestError = FSatoriUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void USatoriClient::GetFlags( - USatoriSession* Session, - const TArray& Names, - FOnGetFlags Success, - FOnSatoriError Error) -{ - auto successCallback = [this, Success](const FSatoriFlagList& Flags) - { - if (!IsClientActive(this)) - { - return; - } - - Success.Broadcast(Flags); - }; - - auto errorCallback = [this, Error](const FSatoriError& error) - { - if (!IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - GetFlags(Session, Names, successCallback, errorCallback); -} - -void USatoriClient::GetFlags( - USatoriSession* Session, - const TArray& Names, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v1/flag"); - - // Verify the session - if (!FSatoriUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - for (const FString& Name : Names) - { - QueryParams.Add(TEXT("names"), Name); - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ESatoriRequestMethod::GET, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FSatoriUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FSatoriFlagList Flags = FSatoriFlagList(ResponseBody); - SuccessCallback(Flags); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FSatoriError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FSatoriError RequestError = FSatoriUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void USatoriClient::GetFlagOverrides( - USatoriSession* Session, - const TArray& Names, - FOnGetFlagOverrides Success, - FOnSatoriError Error) -{ - auto successCallback = [this, Success](const FSatoriFlagOverrideList& Flags) - { - if (!IsClientActive(this)) - { - return; - } - - Success.Broadcast(Flags); - }; - - auto errorCallback = [this, Error](const FSatoriError& error) - { - if (!IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - GetFlagOverrides(Session, Names, successCallback, errorCallback); -} - -void USatoriClient::GetFlagOverrides( - USatoriSession* Session, - const TArray& Names, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v1/flag/override"); - - // Verify the session - if (!FSatoriUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - for (const FString& Name : Names) - { - QueryParams.Add(TEXT("names"), Name); - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ESatoriRequestMethod::GET, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FSatoriUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FSatoriFlagOverrideList FlagOverrides = FSatoriFlagOverrideList(ResponseBody); - SuccessCallback(FlagOverrides); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FSatoriError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FSatoriError RequestError = FSatoriUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void USatoriClient::GetLiveEvents( - USatoriSession* Session, - const TArray& LiveEventNames, - FOnGetLiveEvents Success, - FOnSatoriError Error) -{ - auto successCallback = [this, Success](const FSatoriLiveEventList& LiveEvents) - { - if (!IsClientActive(this)) - { - return; - } - - Success.Broadcast(LiveEvents); - }; - - auto errorCallback = [this, Error](const FSatoriError& error) - { - if (!IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - GetLiveEvents(Session, LiveEventNames, successCallback, errorCallback); -} - -void USatoriClient::GetLiveEvents( - USatoriSession* Session, - const TArray& LiveEventNames, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v1/live-event"); - - // Verify the session - if (!FSatoriUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - for (const FString& Name : LiveEventNames) - { - QueryParams.Add(TEXT("names"), Name); - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ESatoriRequestMethod::GET, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FSatoriUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FSatoriLiveEventList LiveEvents = FSatoriLiveEventList(ResponseBody); - SuccessCallback(LiveEvents); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FSatoriError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FSatoriError RequestError = FSatoriUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void USatoriClient::GetMessages( - USatoriSession* Session, - int32 Limit, - bool Forward, - const FString& Cursor, - FOnGetMessages Success, - FOnSatoriError Error) -{ - auto successCallback = [this, Success](const FSatoriMessageList& Messages) - { - if (!IsClientActive(this)) - { - return; - } - - Success.Broadcast(Messages); - }; - - auto errorCallback = [this, Error](const FSatoriError& error) - { - if (!IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - GetMessages(Session, Limit, Forward, Cursor, successCallback, errorCallback); -} - -void USatoriClient::GetMessages( - USatoriSession* Session, - int32 Limit, - bool Forward, - const FString& Cursor, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v1/message"); - - // Verify the session - if (!FSatoriUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Setup the query parameters - TMultiMap QueryParams; - QueryParams.Add(TEXT("limit"), FString::FromInt(Limit)); - QueryParams.Add(TEXT("forward"), FSatoriUtils::BoolToString(Forward)); - if (!Cursor.IsEmpty()) { QueryParams.Add(TEXT("cursor"), FGenericPlatformHttp::UrlEncode(Cursor)); } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ESatoriRequestMethod::GET, QueryParams, Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FSatoriUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - const FSatoriMessageList Messages = FSatoriMessageList(ResponseBody); - SuccessCallback(Messages); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FSatoriError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FSatoriError RequestError = FSatoriUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void USatoriClient::UpdateMessage( - USatoriSession* Session, - const FString& MessageId, - const FDateTime ReadTime, - const FDateTime ConsumeTime, - FOnUpdateMessageSent Success, - FOnSatoriError Error) -{ - auto successCallback = [this, Success]() - { - if (!IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FSatoriError& error) - { - if (!IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - UpdateMessage(Session, MessageId, ReadTime, ConsumeTime, successCallback, errorCallback); -} - -void USatoriClient::UpdateMessage( - USatoriSession* Session, - const FString& MessageId, - const FDateTime ReadTime, - const FDateTime ConsumeTime, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v1/message/") + FGenericPlatformHttp::UrlEncode(MessageId); - - // Setup the request content - const TSharedPtr ContentJson = MakeShared(); - - ContentJson->SetNumberField(TEXT("read_time"), ReadTime.ToUnixTimestamp()); - ContentJson->SetNumberField(TEXT("consume_time"), ConsumeTime.ToUnixTimestamp()); - - // Serialize the request content - FString Content; - if (!FSatoriUtils::SerializeJsonObject(ContentJson, Content)) - { - // Handle JSON serialization failure - FSatoriUtils::HandleJsonSerializationFailure(ErrorCallback); - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, Content, ESatoriRequestMethod::PUT, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FSatoriUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FSatoriError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FSatoriError RequestError = FSatoriUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -void USatoriClient::DeleteMessage( - USatoriSession* Session, - const FString& MessageId, - FOnDeleteMessageSent Success, - FOnSatoriError Error) -{ - auto successCallback = [this, Success]() - { - if (!IsClientActive(this)) - { - return; - } - - Success.Broadcast(); - }; - - auto errorCallback = [this, Error](const FSatoriError& error) - { - if (!IsClientActive(this)) - { - return; - } - - Error.Broadcast(error); - }; - - DeleteMessage(Session, MessageId, successCallback, errorCallback); -} - -void USatoriClient::DeleteMessage( - USatoriSession* Session, - const FString& MessageId, - TFunction SuccessCallback, - TFunction ErrorCallback) -{ - // Setup the endpoint - const FString Endpoint = TEXT("/v1/message/") + FGenericPlatformHttp::UrlEncode(MessageId); - - // Verify the session - if (!FSatoriUtils::IsSessionValid(Session, ErrorCallback)) - { - return; - } - - // Make the request - const auto HttpRequest = MakeRequest(Endpoint, TEXT(""), ESatoriRequestMethod::DEL, TMultiMap(), Session->GetAuthToken()); - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - // Add the HttpRequest to ActiveRequests - ActiveRequests.Add(HttpRequest); - - // Bind the response callback and handle the response - HttpRequest->OnProcessRequestComplete().BindLambda([SuccessCallback, ErrorCallback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) { - - if (!IsValidLowLevel()) - { - return; - } - - // Lock the ActiveRequests mutex to protect concurrent access - FScopeLock Lock(&ActiveRequestsMutex); - - if (ActiveRequests.Contains(Request)) - { - if (bSuccess && Response.IsValid()) - { - const FString ResponseBody = Response->GetContentAsString(); - - // Check if Request was successful - if (FSatoriUtils::IsResponseSuccessful(Response->GetResponseCode())) - { - // Check for Success Callback - if (SuccessCallback) - { - SuccessCallback(); - } - } - else - { - // Check for Error Callback - if (ErrorCallback) - { - const FSatoriError Error(ResponseBody); - ErrorCallback(Error); - } - } - } - else - { - // Handle Invalid Response - if (ErrorCallback) - { - const FSatoriError RequestError = FSatoriUtils::CreateRequestFailureError(); - ErrorCallback(RequestError); - } - } - - // Remove the HttpRequest from ActiveRequests - ActiveRequests.Remove(Request); - } - }); - - // Process the request - HttpRequest->ProcessRequest(); -} - -bool USatoriClient::IsClientActive(const USatoriClient* Client) -{ - return IsValid(Client) && Client->bIsActive == true; -} - -FString USatoriClient::ConstructURL(const FString& Endpoint) -{ - FString Protocol = bUseSSL ? TEXT("https") : TEXT("http"); - FString URL = FString::Printf(TEXT("%s://%s:%d%s"), *Protocol, *Hostname, Port, *Endpoint); - - return URL; -} - -TSharedRef USatoriClient::MakeRequest( - const FString& Endpoint, - const FString& Content, - ESatoriRequestMethod RequestMethod, - const TMultiMap& QueryParams, - const FString& SessionToken) -{ - // Append query parameters to the endpoint - FString ModifiedEndpoint = Endpoint; - if (QueryParams.Num() > 0) - { - FString QueryString = FSatoriUtils::BuildQueryString(QueryParams); - ModifiedEndpoint += "?" + QueryString; - } - - // Construct the URL - FString URL = ConstructURL(ModifiedEndpoint); - - return FSatoriUtils::MakeRequest(URL, Content, RequestMethod, SessionToken, Timeout); -} diff --git a/Satori/Source/SatoriUnreal/Private/SatoriError.cpp b/Satori/Source/SatoriUnreal/Private/SatoriError.cpp deleted file mode 100644 index b539462e5..000000000 --- a/Satori/Source/SatoriUnreal/Private/SatoriError.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "SatoriError.h" -#include "SatoriUtils.h" - -ESatoriErrorCode FSatoriError::ConvertSatoriErrorCode(int32 CodeValue) -{ - switch (CodeValue) - { - case 0: - return ESatoriErrorCode::Ok; - case 1: - return ESatoriErrorCode::Cancelled; - case 2: - return ESatoriErrorCode::Unknown; - case 3: - return ESatoriErrorCode::InvalidArgument; - case 4: - return ESatoriErrorCode::DeadlineExceeded; - case 5: - return ESatoriErrorCode::NotFound; - case 6: - return ESatoriErrorCode::AlreadyExists; - case 7: - return ESatoriErrorCode::PermissionDenied; - case 8: - return ESatoriErrorCode::ResourceExhausted; - case 9: - return ESatoriErrorCode::FailedPrecondition; - case 10: - return ESatoriErrorCode::Aborted; - case 11: - return ESatoriErrorCode::OutOfRange; - case 12: - return ESatoriErrorCode::Unimplemented; - case 13: - return ESatoriErrorCode::Internal; - case 14: - return ESatoriErrorCode::Unavailable; - case 15: - return ESatoriErrorCode::DataLoss; - case 16: - return ESatoriErrorCode::Unauthenticated; - default: - return ESatoriErrorCode::Unknown; - } -} - -FSatoriError::FSatoriError(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - if (FJsonSerializer::Deserialize(JsonReader, JsonObject)) - { - if (JsonObject->HasField(TEXT("message"))) - { - Message = JsonObject->GetStringField(TEXT("message")); - } - else - { - Message = TEXT("Invalid or missing 'message' field"); - } - - int32 CodeValue; - if (JsonObject->TryGetNumberField(TEXT("code"), CodeValue)) - { - //Code = static_cast(CodeValue); - Code = ConvertSatoriErrorCode(CodeValue); - } - else - { - Code = ESatoriErrorCode::Unknown; - } - } - else - { - Message = TEXT("Failed to parse JSON"); - Code = ESatoriErrorCode::Unknown; - } -} diff --git a/Satori/Source/SatoriUnreal/Private/SatoriEvent.cpp b/Satori/Source/SatoriUnreal/Private/SatoriEvent.cpp deleted file mode 100644 index dab462945..000000000 --- a/Satori/Source/SatoriUnreal/Private/SatoriEvent.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "SatoriEvent.h" - -#include "SatoriUtils.h" - -FSatoriEvent::FSatoriEvent(const FString& JsonString) : FSatoriEvent(FSatoriUtils::DeserializeJsonObject(JsonString)) { -} - -FSatoriEvent::FSatoriEvent(const TSharedPtr JsonObject) -{ - if (JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("name"), Name); - JsonObject->TryGetStringField(TEXT("id"), ID); - JsonObject->TryGetStringField(TEXT("value"), Value); - - FString TimestampString; - if (JsonObject->TryGetStringField(TEXT("timestamp"), TimestampString)) - { - FDateTime::ParseIso8601(*TimestampString, Timestamp); - } - - const TSharedPtr* MetadataObject = nullptr; - if (JsonObject->TryGetObjectField(TEXT("metadata"), MetadataObject)) { - for (const TPair>& Pair : (*MetadataObject)->Values) - { - Metadata.Add(*Pair.Key, Pair.Value->AsString()); - } - } - - JsonObject->TryGetStringField(TEXT("identity_id"), IdentityId); - JsonObject->TryGetStringField(TEXT("session_id"), SessionId); - JsonObject->TryGetNumberField(TEXT("session_issued_at"), SessionIssuedAt); - JsonObject->TryGetNumberField(TEXT("session_expires_at"), SessionExpiresAt); - } -} - -FSatoriEvent::FSatoriEvent() : Timestamp(FDateTime::MinValue()), SessionIssuedAt(0), SessionExpiresAt(0) -{ -} diff --git a/Satori/Source/SatoriUnreal/Private/SatoriExperiment.cpp b/Satori/Source/SatoriUnreal/Private/SatoriExperiment.cpp deleted file mode 100644 index 576627f4c..000000000 --- a/Satori/Source/SatoriUnreal/Private/SatoriExperiment.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "SatoriExperiment.h" -#include "SatoriUtils.h" - -FSatoriExperiment::FSatoriExperiment(const FString& JsonString) : FSatoriExperiment(FSatoriUtils::DeserializeJsonObject(JsonString)) { -} - -FSatoriExperiment::FSatoriExperiment(const TSharedPtr JsonObject) -{ - if (JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("name"), Name); - JsonObject->TryGetStringField(TEXT("value"), Value); - } -} - -FSatoriExperiment::FSatoriExperiment() -{ -} - -FSatoriExperimentList::FSatoriExperimentList(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - const TArray>* ExperimentsJsonArray; - if (JsonObject->TryGetArrayField(TEXT("experiments"), ExperimentsJsonArray)) - { - for (const TSharedPtr& ExperimentJsonValue : *ExperimentsJsonArray) - { - if (TSharedPtr ExperimentJsonObject = ExperimentJsonValue->AsObject()) - { - FSatoriExperiment Experiment(ExperimentJsonObject); - if (!Experiment.Name.IsEmpty()) - { - Experiments.Add(Experiment); - } - } - } - } - } -} - -FSatoriExperimentList::FSatoriExperimentList() -{ -} diff --git a/Satori/Source/SatoriUnreal/Private/SatoriFlag.cpp b/Satori/Source/SatoriUnreal/Private/SatoriFlag.cpp deleted file mode 100644 index 9794e399c..000000000 --- a/Satori/Source/SatoriUnreal/Private/SatoriFlag.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "SatoriFlag.h" -#include "SatoriUtils.h" - -FSatoriFlagValueChangeReason::FSatoriFlagValueChangeReason(const FString& JsonString) : FSatoriFlagValueChangeReason(FSatoriUtils::DeserializeJsonObject(JsonString)) -{ -} - -FSatoriFlagValueChangeReason::FSatoriFlagValueChangeReason(const TSharedPtr JsonObject) -{ - if (JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("name"), Name); - JsonObject->TryGetStringField(TEXT("variant_name"), VariantName); - double typeNum; - if (JsonObject->TryGetNumberField(TEXT("type"), typeNum)) { - int typeInt = (int)typeNum; - if (typeInt >= static_cast(FSatoriFlagValueChangeReasonType::UNKNOWN) && typeInt <= static_cast(FSatoriFlagValueChangeReasonType::EXPERIMENT)) { - Type = static_cast(typeInt); - } - } - } -} - -FSatoriFlagValueChangeReason::FSatoriFlagValueChangeReason() : Type(FSatoriFlagValueChangeReasonType::UNKNOWN) -{ -} - -FSatoriFlag::FSatoriFlag(const FString& JsonString) : FSatoriFlag(FSatoriUtils::DeserializeJsonObject(JsonString)) -{ -} - -FSatoriFlag::FSatoriFlag(const TSharedPtr JsonObject) -{ - if(JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("name"), Name); - JsonObject->TryGetStringField(TEXT("value"), Value); - JsonObject->TryGetBoolField(TEXT("condition_changed"), bConditionChanged); - const TSharedPtr* ChangeReasonObject = nullptr; - if (JsonObject->TryGetObjectField(TEXT("change_reason"), ChangeReasonObject)) { - ChangeReason = FSatoriFlagValueChangeReason(*ChangeReasonObject); - } - } -} - -FSatoriFlag::FSatoriFlag() : bConditionChanged(false) -{ -} - -FSatoriFlagList::FSatoriFlagList(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - const TArray>* FlagsJsonArray; - if (JsonObject->TryGetArrayField(TEXT("flags"), FlagsJsonArray)) - { - for (const TSharedPtr& FlagJsonValue : *FlagsJsonArray) - { - if (TSharedPtr FlagJsonObject = FlagJsonValue->AsObject()) - { - FSatoriFlag Flag(FlagJsonObject); - if (!Flag.Name.IsEmpty()) - { - Flags.Add(Flag); - } - } - } - } - } -} - -FSatoriFlagList::FSatoriFlagList() -{ -} - -FSatoriFlagOverrideValue::FSatoriFlagOverrideValue(const FString& JsonString) : FSatoriFlagOverrideValue(FSatoriUtils::DeserializeJsonObject(JsonString)) -{ -} - -FSatoriFlagOverrideValue::FSatoriFlagOverrideValue(const TSharedPtr JsonObject) -{ - if (JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("name"), Name); - JsonObject->TryGetStringField(TEXT("variant_name"), VariantName); - JsonObject->TryGetStringField(TEXT("value"), Value); - JsonObject->TryGetNumberField(TEXT("create_time_sec"), CreateTimeSec); - double typeNum; - if (JsonObject->TryGetNumberField(TEXT("type"), typeNum)) { - int typeInt = (int)typeNum; - if (typeInt >= static_cast(FSatoriFlagOverrideType::FLAG) && typeInt <= static_cast(FSatoriFlagOverrideType::EXPERIMENT_PHASE_VARIANT_FLAG)) { - Type = static_cast(typeInt); - } - } - } -} - -FSatoriFlagOverrideValue::FSatoriFlagOverrideValue() : CreateTimeSec(0), Type(FSatoriFlagOverrideType::FLAG) -{ -} - -FSatoriFlagOverride::FSatoriFlagOverride(const FString& JsonString) : FSatoriFlagOverride(FSatoriUtils::DeserializeJsonObject(JsonString)) -{ -} - -FSatoriFlagOverride::FSatoriFlagOverride(const TSharedPtr JsonObject) -{ - if (JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("flag_name"), FlagName); - const TArray>* OverridesJsonArray; - if (JsonObject->TryGetArrayField(TEXT("overrides"), OverridesJsonArray)) - { - for (const TSharedPtr& OverrideJsonValue : *OverridesJsonArray) - { - if (TSharedPtr OverrideJsonObject = OverrideJsonValue->AsObject()) - { - FSatoriFlagOverrideValue Override(OverrideJsonObject); - Overrides.Add(Override); - } - } - } - } -} - -FSatoriFlagOverride::FSatoriFlagOverride() -{ -} - - -FSatoriFlagOverrideList::FSatoriFlagOverrideList(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - const TArray>* FlagOverridesJsonArray; - if (JsonObject->TryGetArrayField(TEXT("flags"), FlagOverridesJsonArray)) - { - for (const TSharedPtr& FlagOverrideJsonValue : *FlagOverridesJsonArray) - { - if (TSharedPtr FlagOverrideJsonObject = FlagOverrideJsonValue->AsObject()) - { - FSatoriFlagOverride FlagOverride(FlagOverrideJsonObject); - if (!FlagOverride.FlagName.IsEmpty()) - { - Flags.Add(FlagOverride); - } - } - } - } - } -} - -FSatoriFlagOverrideList::FSatoriFlagOverrideList() -{ -} diff --git a/Satori/Source/SatoriUnreal/Private/SatoriLiveEvent.cpp b/Satori/Source/SatoriUnreal/Private/SatoriLiveEvent.cpp deleted file mode 100644 index 64c2d171a..000000000 --- a/Satori/Source/SatoriUnreal/Private/SatoriLiveEvent.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "SatoriLiveEvent.h" -#include "SatoriUtils.h" - -FSatoriLiveEvent::FSatoriLiveEvent(const FString& JsonString) : FSatoriLiveEvent(FSatoriUtils::DeserializeJsonObject(JsonString)) { -} - -FSatoriLiveEvent::FSatoriLiveEvent(const TSharedPtr JsonObject) -{ - if (JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("name"), Name); - JsonObject->TryGetStringField(TEXT("description"), Description); - JsonObject->TryGetStringField(TEXT("value"), Value); - JsonObject->TryGetNumberField(TEXT("active_start_time_sec"), ActiveStartTimeSec); - JsonObject->TryGetNumberField(TEXT("active_end_time_sec"), ActiveEndTimeSec); - JsonObject->TryGetStringField(TEXT("id"), ID); - JsonObject->TryGetNumberField(TEXT("start_time_sec"), StartTimeSec); - JsonObject->TryGetNumberField(TEXT("end_time_sec"), EndTimeSec); - JsonObject->TryGetNumberField(TEXT("duration_sec"), DurationSec); - JsonObject->TryGetStringField(TEXT("reset_cron"), ResetCron); - } -} - -FSatoriLiveEvent::FSatoriLiveEvent() - : ActiveStartTimeSec(0), ActiveEndTimeSec(0), StartTimeSec(0), EndTimeSec(0), DurationSec(0) -{ -} - -FSatoriLiveEventList::FSatoriLiveEventList(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - const TArray>* LiveEventsJsonArray; - if (JsonObject->TryGetArrayField(TEXT("live_events"), LiveEventsJsonArray)) - { - for (const TSharedPtr& LiveEventJsonValue : *LiveEventsJsonArray) - { - if(TSharedPtr LiveEventJsonObject = LiveEventJsonValue->AsObject()) - { - FSatoriLiveEvent LiveEvent(LiveEventJsonObject); - if (!LiveEvent.Name.IsEmpty()) - { - LiveEvents.Add(LiveEvent); - } - } - } - } - } -} - -FSatoriLiveEventList::FSatoriLiveEventList() -{ -} diff --git a/Satori/Source/SatoriUnreal/Private/SatoriLogger.cpp b/Satori/Source/SatoriUnreal/Private/SatoriLogger.cpp deleted file mode 100644 index 71825fddc..000000000 --- a/Satori/Source/SatoriUnreal/Private/SatoriLogger.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "SatoriLogger.h" - -DEFINE_LOG_CATEGORY(LogSatoriUnreal); - -ESatoriLogLevel USatoriLogger::CurrentLogLevel = ESatoriLogLevel::Info; -bool USatoriLogger::bLoggingEnabled = false; - -USatoriLogger::USatoriLogger() -{ - -} - -void USatoriLogger::SetLogLevel(ESatoriLogLevel InLogLevel) -{ - CurrentLogLevel = InLogLevel; -} - -bool USatoriLogger::IsLoggable(ESatoriLogLevel InLogLevel) -{ - // Logging Disabled check - if (!bLoggingEnabled) - return false; - - // Debug - if(CurrentLogLevel == ESatoriLogLevel::Debug) - { - if(InLogLevel == ESatoriLogLevel::Debug) - return true; - - if(InLogLevel == ESatoriLogLevel::Info) - return true; - - if(InLogLevel == ESatoriLogLevel::Warn) - return true; - - if(InLogLevel == ESatoriLogLevel::Error) - return true; - - if(InLogLevel == ESatoriLogLevel::Fatal) - return true; - } - - // Info - if(CurrentLogLevel == ESatoriLogLevel::Info) - { - if(InLogLevel == ESatoriLogLevel::Info) - return true; - - if(InLogLevel == ESatoriLogLevel::Warn) - return true; - - if(InLogLevel == ESatoriLogLevel::Error) - return true; - - if(InLogLevel == ESatoriLogLevel::Fatal) - return true; - } - - // Warn - if(CurrentLogLevel == ESatoriLogLevel::Warn) - { - if(InLogLevel == ESatoriLogLevel::Warn) - return true; - - if(InLogLevel == ESatoriLogLevel::Error) - return true; - - if(InLogLevel == ESatoriLogLevel::Fatal) - return true; - } - - // Error - if(CurrentLogLevel == ESatoriLogLevel::Error) - { - if(InLogLevel == ESatoriLogLevel::Error) - return true; - - if(InLogLevel == ESatoriLogLevel::Fatal) - return true; - } - - // Fatal - if(CurrentLogLevel == ESatoriLogLevel::Fatal) - { - if(InLogLevel == ESatoriLogLevel::Fatal) - return true; - } - - return false; -} - -void USatoriLogger::Log(ESatoriLogLevel InLogLevel, const FString& Message) -{ - if (IsLoggable(InLogLevel)) - { - switch (InLogLevel) - { - case ESatoriLogLevel::Debug: - UE_LOG(LogSatoriUnreal, Display, TEXT("%s"), *Message); - break; - case ESatoriLogLevel::Info: - UE_LOG(LogSatoriUnreal, Display, TEXT("%s"), *Message); - break; - case ESatoriLogLevel::Warn: - UE_LOG(LogSatoriUnreal, Warning, TEXT("%s"), *Message); - break; - case ESatoriLogLevel::Error: - UE_LOG(LogSatoriUnreal, Error, TEXT("%s"), *Message); - break; - case ESatoriLogLevel::Fatal: - UE_LOG(LogSatoriUnreal, Fatal, TEXT("%s"), *Message); - break; - default: - break; - } - } -} - -void USatoriLogger::EnableLogging(bool bEnable) -{ - bLoggingEnabled = bEnable; -} diff --git a/Satori/Source/SatoriUnreal/Private/SatoriMessage.cpp b/Satori/Source/SatoriUnreal/Private/SatoriMessage.cpp deleted file mode 100644 index 6d5e2c1c3..000000000 --- a/Satori/Source/SatoriUnreal/Private/SatoriMessage.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "SatoriMessage.h" -#include "SatoriUtils.h" - -FSatoriMessage::FSatoriMessage(const FString& JsonString) : FSatoriMessage(FSatoriUtils::DeserializeJsonObject(JsonString)) { -} - -FSatoriMessage::FSatoriMessage(const TSharedPtr JsonObject) -{ - if (JsonObject.IsValid()) - { - JsonObject->TryGetStringField(TEXT("schedule_id"), ScheduleID); - JsonObject->TryGetNumberField(TEXT("send_time"), SendTime); - JsonObject->TryGetNumberField(TEXT("create_time"), CreateTime); - JsonObject->TryGetNumberField(TEXT("update_time"), UpdateTime); - JsonObject->TryGetNumberField(TEXT("read_time"), ReadTime); - JsonObject->TryGetNumberField(TEXT("consume_time"), ConsumeTime); - JsonObject->TryGetStringField(TEXT("text"), Text); - JsonObject->TryGetStringField(TEXT("id"), ID); - JsonObject->TryGetStringField(TEXT("title"), Title); - JsonObject->TryGetStringField(TEXT("image_url"), ImageURL); - const TSharedPtr* MetadataObject = nullptr; - if (JsonObject->TryGetObjectField(TEXT("metadata"), MetadataObject)) { - for (const TPair>& Pair : (*MetadataObject)->Values) - { - Metadata.Add(*Pair.Key, Pair.Value->AsString()); - } - } - } -} - -FSatoriMessage::FSatoriMessage() : SendTime(0), CreateTime(0), UpdateTime(0), ReadTime(0), ConsumeTime(0) -{ -} - -FSatoriMessageList::FSatoriMessageList(const FString& JsonString) -{ - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid()) - { - const TArray>* MessagesJsonArray; - if (JsonObject->TryGetArrayField(TEXT("messages"), MessagesJsonArray)) - { - for (const TSharedPtr& MessageJsonValue : *MessagesJsonArray) - { - if(TSharedPtr MessageJsonObject = MessageJsonValue->AsObject()) - { - FSatoriMessage Message(MessageJsonObject); - if (!Message.ID.IsEmpty()) - { - Messages.Add(Message); - } - } - } - } - } -} - -FSatoriMessageList::FSatoriMessageList() -{ -} diff --git a/Satori/Source/SatoriUnreal/Private/SatoriProperties.cpp b/Satori/Source/SatoriUnreal/Private/SatoriProperties.cpp deleted file mode 100644 index e743d98ed..000000000 --- a/Satori/Source/SatoriUnreal/Private/SatoriProperties.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "SatoriProperties.h" -#include "SatoriUtils.h" - -FSatoriProperties::FSatoriProperties(const FString& JsonString) : FSatoriProperties(FSatoriUtils::DeserializeJsonObject(JsonString)) { -} - -FSatoriProperties::FSatoriProperties(const TSharedPtr JsonObject) -{ - if (JsonObject.IsValid()) - { - const TSharedPtr* MetadataObject = nullptr; - if (JsonObject->TryGetObjectField(TEXT("default"), MetadataObject)) { - for (const TPair>& Pair : (*MetadataObject)->Values) - { - DefaultProperties.Add(*Pair.Key, Pair.Value->AsString()); - } - } - - MetadataObject = nullptr; - if (JsonObject->TryGetObjectField(TEXT("computed"), MetadataObject)) { - for (const TPair>& Pair : (*MetadataObject)->Values) - { - ComputedProperties.Add(*Pair.Key, Pair.Value->AsString()); - } - } - - MetadataObject = nullptr; - if (JsonObject->TryGetObjectField(TEXT("custom"), MetadataObject)) { - for (const TPair>& Pair : (*MetadataObject)->Values) - { - CustomProperties.Add(*Pair.Key, Pair.Value->AsString()); - } - } - } -} - -FSatoriProperties::FSatoriProperties() -{ -} diff --git a/Satori/Source/SatoriUnreal/Private/SatoriSession.cpp b/Satori/Source/SatoriUnreal/Private/SatoriSession.cpp deleted file mode 100644 index 8d46987a8..000000000 --- a/Satori/Source/SatoriUnreal/Private/SatoriSession.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "SatoriSession.h" - -#include "SatoriUtils.h" - - -USatoriSession* USatoriSession::SetupSession(const FString& AuthResponse) -{ - USatoriSession* ResultSession = NewObject(); - TSharedPtr JsonObject = MakeShareable(new FJsonObject()); - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(AuthResponse); - - if (FJsonSerializer::Deserialize(JsonReader, JsonObject)) - { - FString Token = JsonObject->GetStringField(TEXT("token")); - FString RefreshToken = JsonObject->GetStringField(TEXT("refresh_token")); - - ResultSession->SessionData.AuthToken = Token; - ResultSession->_AuthToken = Token; - ResultSession->SessionData.RefreshToken = RefreshToken; - ResultSession->_RefreshToken = RefreshToken; - - const TSharedPtr* PropertiesJson = nullptr; - if (JsonObject->TryGetObjectField(TEXT("properties"), PropertiesJson)) - { - FSatoriProperties Properties = FSatoriProperties(*PropertiesJson); - ResultSession->SessionData.Properties = Properties; - ResultSession->_Properties = Properties; - } - return ResultSession; - } - else - { - SATORI_LOG_ERROR(TEXT("Failed to deserialize Satori Session JSON object")); - } - return nullptr; -} - -const FString USatoriSession::GetAuthToken() const -{ - return _AuthToken; -} - -const FString USatoriSession::GetRefreshToken() const -{ - return _RefreshToken; -} - -const FSatoriProperties USatoriSession::GetProperties() const -{ - return _Properties; -} - -USatoriSession* USatoriSession::RestoreSession(FString Token, FString RefreshToken) -{ - USatoriSession* ResultSession = NewObject(); - ResultSession->SessionData.AuthToken = Token; - ResultSession->_AuthToken = Token; - ResultSession->SessionData.RefreshToken = RefreshToken; - ResultSession->_RefreshToken = RefreshToken; - return ResultSession; -} diff --git a/Satori/Source/SatoriUnreal/Private/SatoriUnreal.cpp b/Satori/Source/SatoriUnreal/Private/SatoriUnreal.cpp deleted file mode 100644 index 59abb1ddd..000000000 --- a/Satori/Source/SatoriUnreal/Private/SatoriUnreal.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "SatoriUnreal.h" -#include "Modules/ModuleManager.h" - -void FSatoriUnrealModule::StartupModule() -{ - // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module - - //NLogger::init(std::make_shared(), NLogLevel::Debug); - -} - -void FSatoriUnrealModule::ShutdownModule() -{ - // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, - // we call this function before unloading the module. - -} - -#undef LOCTEXT_NAMESPACE - -IMPLEMENT_MODULE(FSatoriUnrealModule, SatoriUnreal) diff --git a/Satori/Source/SatoriUnreal/Private/SatoriUtils.cpp b/Satori/Source/SatoriUnreal/Private/SatoriUtils.cpp deleted file mode 100644 index 894199ec8..000000000 --- a/Satori/Source/SatoriUnreal/Private/SatoriUtils.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "SatoriUtils.h" -#include "SatoriLogger.h" -#include "Dom/JsonObject.h" -#include "Misc/EngineVersionComparison.h" -#include "Interfaces/IHttpResponse.h" - -DEFINE_LOG_CATEGORY_STATIC(LogSatoriUtils, Log, Log); - -void FSatoriUtils::ProcessRequestComplete(FHttpRequestPtr Request, const FHttpResponsePtr& Response, bool bSuccess, const TFunction& SuccessCallback, const TFunction& ErrorCallback) -{ - if (bSuccess && Response.IsValid()) - { - const int32 ResponseCode = Response->GetResponseCode(); - const FString ResponseBody = Response->GetContentAsString(); - - if (ResponseCode == 200) - { - SATORI_LOG_DEBUG(FString::Printf(TEXT("Request Successful: %s"), *ResponseBody)); - if (SuccessCallback) - { - SuccessCallback(ResponseBody); - } - } - else - { - SATORI_LOG_WARN(FString::Printf(TEXT("Response (Code: %d) - Contents: %s"), ResponseCode, *ResponseBody)); - const FSatoriError Error(ResponseBody); - if (ErrorCallback) - { - ErrorCallback(Error); - } - } - } - else - { - // Handle request failure - SATORI_LOG_ERROR(TEXT("Failed to process request.")); - - if (Request.IsValid()) - { - SATORI_LOG_DEBUG(FString::Printf(TEXT("Request URL: %s"), *(Request->GetURL()))); - } - - FSatoriError Error; - Error.Code = ESatoriErrorCode::Unknown; - Error.Message = TEXT("Failed to proccess request. Request failed."); - - if (ErrorCallback) - { - ErrorCallback(Error); - } - } -} - -void FSatoriUtils::HandleJsonSerializationFailure(TFunction ErrorCallback) -{ - SATORI_LOG_ERROR(TEXT("Failed to generate request content.")); - FSatoriError Error; - Error.Code = ESatoriErrorCode::Unknown; - Error.Message = TEXT("Failed to generate request content."); - if (ErrorCallback) - { - ErrorCallback(Error); - } -} - -bool FSatoriUtils::IsSessionValid(const USatoriSession* Session, TFunction ErrorCallback) -{ - if (!Session || Session->SessionData.AuthToken.IsEmpty()) - { - SATORI_LOG_ERROR("Invalid session or session data."); - - FSatoriError Error; - Error.Message = "Invalid session or session data."; - if (ErrorCallback) - { - ErrorCallback(Error); - } - return false; - } - - return true; -} - -bool FSatoriUtils::IsResponseSuccessful(int32 ResponseCode) -{ - return ResponseCode == 200; -} - -FSatoriError FSatoriUtils::CreateRequestFailureError() -{ - SATORI_LOG_ERROR(TEXT("Failed to process request. Request failed.")); - FSatoriError Error; - Error.Code = ESatoriErrorCode::Unknown; - Error.Message = TEXT("Failed to process request. Request failed."); - return Error; -} - -TSharedRef FSatoriUtils::MakeRequest( - const FString& URL, - const FString& Content, - ESatoriRequestMethod RequestMethod, - const FString& SessionToken, - float Timeout) -{ - FHttpModule* HttpModule = &FHttpModule::Get(); - - // Create the HttpRequest -#if ENGINE_MAJOR_VERSION <= 4 && ENGINE_MINOR_VERSION <= 25 - TSharedRef HttpRequest = HttpModule->CreateRequest(); -#else - TSharedRef HttpRequest = HttpModule->CreateRequest(); -#endif - - HttpRequest->SetURL(URL); - HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json")); - HttpRequest->SetTimeout(Timeout); // Exposed to end user - - FString VerbString = ESatoriRequesMethodToFString(RequestMethod); - if (!VerbString.IsEmpty()) - { - HttpRequest->SetVerb(VerbString); - } - - // Set the content if it is not empty - if (!Content.IsEmpty()) - { - HttpRequest->SetContentAsString(Content); - } - - // Add authorization header if session token is provided - if (!SessionToken.IsEmpty()) - { - FString AuthorizationHeader = FString::Printf(TEXT("Bearer %s"), *SessionToken); - HttpRequest->SetHeader(TEXT("Authorization"), AuthorizationHeader); - } - - //SATORI_LOG_INFO(TEXT("...")); - //SATORI_LOG_INFO(FString::Printf(TEXT("Making Request to %s"), *Endpoint)); - SATORI_LOG_INFO(FString::Printf(TEXT("Making %s request to %s with content: %s"), *VerbString, *URL, *Content)); - return HttpRequest; -} diff --git a/Satori/Source/SatoriUnreal/Public/SatoriClient.h b/Satori/Source/SatoriUnreal/Public/SatoriClient.h deleted file mode 100644 index aa0bfd197..000000000 --- a/Satori/Source/SatoriUnreal/Public/SatoriClient.h +++ /dev/null @@ -1,430 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "Interfaces/IHttpRequest.h" -#include "HttpModule.h" -#include "SatoriError.h" -#include "SatoriSession.h" -#include "SatoriEvent.h" -#include "SatoriExperiment.h" -#include "SatoriLiveEvent.h" -#include "SatoriMessage.h" -#include "SatoriFlag.h" -#include "SatoriClient.generated.h" - -namespace Satori {} -using namespace Satori; -enum class ESatoriRequestMethod:uint8; - -// Delegates -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSatoriAuthUpdate, USatoriSession*, LoginData); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnAuthLogoutSent); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnDeleteIdentitySent); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSatoriError, const FSatoriError&, ErrorData); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnPostEventSent); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnPostServerEventSent); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGetExperiments, const FSatoriExperimentList&, Experiments); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGetFlags, const FSatoriFlagList&, Flags); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGetFlagOverrides, const FSatoriFlagOverrideList&, FlagOverrides); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGetLiveEvents, const FSatoriLiveEventList&, LiveEvents); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGetProperties, const FSatoriProperties&, Properties); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnUpdatePropertiesSent); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGetMessages, const FSatoriMessageList&, Messages); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnUpdateMessageSent); -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnDeleteMessageSent); - -UENUM(BlueprintType) -enum class ESatoriRequestMethod : uint8 -{ - GET, - POST, - DEL, - PUT, -}; - -/** - * - */ -UCLASS(Blueprintable, BlueprintType, meta=(BlueprintSpawnableComponent)) -class SATORIUNREAL_API USatoriClient : public UObject -{ - GENERATED_BODY() - -private: - - // End user can set this - float Timeout = 10.0f; - - bool bEnableDebug; -protected: - - // Variables - FString Hostname; - int32 Port; - FString ServerKey; - bool bUseSSL; - FHttpModule* HttpModule; - -public: - void InitializeClient( - const FString& InHostname, - int32 InPort, - const FString& InServerKey, - bool bInUseSSL, - bool EnableDebug - ); - - UPROPERTY() - bool bIsActive; - - // Initialize System, this has to be called first, done via the Library Action instead (removed BlueprintCallable) - UFUNCTION(Category = "Satori|Initialize") - void InitializeSystem(const FString& InServerKey, const FString& Host, int32 InPort, bool UseSSL, bool EnableDebug); - - /** - * Disconnects the client. This function kills all outgoing exchanges immediately without waiting. - */ - UFUNCTION(BlueprintCallable, Category = "Satori|Client", meta = (DeprecatedFunction, DeprecationMessage = "Use CancelAllRequests instead")) - void Disconnect(); - - /** - * Cancels all Requests. This function kills all outgoing exchanges immediately without waiting. - */ - UFUNCTION(BlueprintCallable, Category = "Satori|Client") - void CancelAllRequests(); - - /** - * Destroys the Client. - */ - UFUNCTION(BlueprintCallable, Category = "Satori|Client") - void Destroy(); - - // Manage Timeout - UFUNCTION(BlueprintCallable, Category = "Satori|Client") - void SetTimeout(float InTimeout); - - UFUNCTION(BlueprintCallable, Category = "Satori|Client") - float GetTimeout(); - - // Event that is called on cleanup - virtual void BeginDestroy() override; - - /** - * Creates a default client to interact with Satori server. - * - * @param ServerKey Server key should match the one on the Satori Server. (Selfhosted default for dev satori in docker, this will be found here http://localhost:7451/settings/server-keys) - * @param Host The endpoint host name. - * @param Port The port to use, default is 7450. - * @param UseSSL Use "https" scheme if you've setup SSL. - */ - UFUNCTION(BlueprintCallable, Category = "Satori|Client") - static USatoriClient* CreateDefaultClient( - const FString& ServerKey = "d93def3b-fcd2-424e-b719-cd93b852fc3c", - const FString& Host = "localhost", - int32 Port = 7450, - bool UseSSL = false, - bool EnableDebug = true - ); - - // --- Authentication --- // - - /** - * Authenticate to get a satori session. - * - * @param ID Must be between eight and 128 characters (inclusive). Must be an alphanumeric string with only underscores and hyphens allowed. - * @param DefaultProperties Optional default properties to update with this call. If not set, properties are left as they are on the server. - * @param CustomProperties Optional custom properties to update with this call. If not set, properties are left as they are on the server. - * @param bNoSession Modifies the request to only create/update an identity without creating a new session. If set to 'true' the response won't include a token and a refresh token. - * @param Success Delegate called upon successful authentication, providing the user session. - * @param Error Delegate called on authentication failure with error details. - */ - - UFUNCTION(Category = "Satori|Authentication") - void Authenticate( - const FString& ID, - const TMap& DefaultProperties, - const TMap& CustomProperties, - const bool bNoSession, - FOnSatoriAuthUpdate Success, - FOnSatoriError Error - ); - - void Authenticate( - const FString& ID, - const TMap& DefaultProperties, - const TMap& CustomProperties, - const bool bNoSession, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - UFUNCTION(Category = "Satori|Authentication") - void AuthenticateRefresh( - USatoriSession* Session, - FOnSatoriAuthUpdate Success, - FOnSatoriError Error - ); - - void AuthenticateRefresh( - USatoriSession* Session, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - UFUNCTION(Category = "Satori|Authentication") - void AuthenticateLogout( - USatoriSession* Session, - FOnAuthLogoutSent Success, - FOnSatoriError Error - ); - - void AuthenticateLogout( - USatoriSession* Session, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - UFUNCTION(Category = "Satori|Authentication") - void Identify( - USatoriSession* Session, - const FString& ID, - const TMap& defaultProperties, - const TMap& customProperties, - FOnSatoriAuthUpdate Success, - FOnSatoriError Error - ); - - void Identify( - USatoriSession* Session, - const FString& ID, - const TMap& DefaultProperties, - const TMap& CustomProperties, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - UFUNCTION(Category = "Satori|Authentication") - void ListIdentityProperties( - USatoriSession* Session, - FOnGetProperties Success, - FOnSatoriError Error - ); - - void ListIdentityProperties( - USatoriSession* Session, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - UFUNCTION(Category = "Satori|Authentication") - void UpdateProperties( - USatoriSession* Session, - const TMap& DefaultProperties, - const TMap& CustomProperties, - const bool bRecompute, - FOnUpdatePropertiesSent Success, - FOnSatoriError Error - ); - - void UpdateProperties( - USatoriSession* Session, - const TMap& DefaultProperties, - const TMap& CustomProperties, - const bool bRecompute, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - UFUNCTION(Category = "Satori|Authentication") - void DeleteIdentity( - USatoriSession* Session, - FOnDeleteIdentitySent Success, - FOnSatoriError Error - ); - - void DeleteIdentity( - USatoriSession* Session, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - - // --- Interface --- // - - UFUNCTION(Category = "Satori|Events") - void PostServerEvent( - const TArray& Events, - FOnPostServerEventSent Success, - FOnSatoriError Error - ); - - void PostServerEvent( - const TArray& Events, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - UFUNCTION(Category = "Satori|Events") - void PostEvent( - USatoriSession* Session, - const TArray& Events, - FOnPostEventSent Success, - FOnSatoriError Error - ); - - void PostEvent( - USatoriSession* Session, - const TArray& Events, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - UFUNCTION(Category = "Satori|Experiments") - void GetExperiments( - USatoriSession* Session, - const TArray& Names, - FOnGetExperiments Success, - FOnSatoriError Error - ); - - void GetExperiments( - USatoriSession* Session, - const TArray& Names, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - UFUNCTION(Category = "Satori|Flags") - void GetFlags( - USatoriSession* Session, - const TArray& Names, - FOnGetFlags Success, - FOnSatoriError Error - ); - - void GetFlags( - USatoriSession* Session, - const TArray& Names, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - UFUNCTION(Category = "Satori|Flags") - void GetFlagOverrides( - USatoriSession* Session, - const TArray& Names, - FOnGetFlagOverrides Success, - FOnSatoriError Error - ); - - void GetFlagOverrides( - USatoriSession* Session, - const TArray& Names, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - UFUNCTION(Category = "Satori|LiveEvents") - void GetLiveEvents( - USatoriSession* Session, - const TArray& LiveEventNames, - FOnGetLiveEvents Success, - FOnSatoriError Error - ); - - void GetLiveEvents( - USatoriSession* Session, - const TArray& LiveEventNames, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - UFUNCTION(Category = "Satori|Messages") - void GetMessages( - USatoriSession* Session, - int32 Limit, - bool Forward, - const FString& Cursor, - FOnGetMessages Success, - FOnSatoriError Error - ); - - void GetMessages( - USatoriSession* Session, - int32 Limit, - bool Forward, - const FString& Cursor, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - UFUNCTION(Category = "Satori|Messages") - void UpdateMessage( - USatoriSession* Session, - const FString& MessageId, - const FDateTime ReadTime, - const FDateTime ConsumeTime, - FOnUpdateMessageSent Success, - FOnSatoriError Error - ); - - void UpdateMessage( - USatoriSession* Session, - const FString& MessageId, - const FDateTime ReadTime, - const FDateTime ConsumeTime, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - UFUNCTION(Category = "Satori|Messages") - void DeleteMessage( - USatoriSession* Session, - const FString& MessageId, - FOnDeleteMessageSent Success, - FOnSatoriError Error - ); - - void DeleteMessage( - USatoriSession* Session, - const FString& MessageId, - TFunction SuccessCallback, - TFunction ErrorCallback - ); - - // --- Utilities --- // - - static bool IsClientActive(const USatoriClient* Client); - -private: - FString ConstructURL(const FString& Endpoint); - // Make HTTP request - TSharedRef MakeRequest( - const FString& Endpoint, - const FString& Content, - ESatoriRequestMethod RequestMethod, - const TMultiMap& QueryParams, - const FString& SessionToken - ); - -private: - // Requests - TArray ActiveRequests; - FCriticalSection ActiveRequestsMutex; -}; diff --git a/Satori/Source/SatoriUnreal/Public/SatoriError.h b/Satori/Source/SatoriUnreal/Public/SatoriError.h deleted file mode 100644 index dd9d9059d..000000000 --- a/Satori/Source/SatoriUnreal/Public/SatoriError.h +++ /dev/null @@ -1,62 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "SatoriError.generated.h" - -// Error Data -UENUM(BlueprintType) -enum class ESatoriErrorCode : uint8 -{ - Ok = 0 UMETA(DisplayName = "Ok"), - Cancelled = 1 UMETA(DisplayName = "Cancelled"), - Unknown = 2 UMETA(DisplayName = "Unknown"), - InvalidArgument = 3 UMETA(DisplayName = "Invalid Argument"), - DeadlineExceeded = 4 UMETA(DisplayName = "Deadline Exceeded"), - NotFound = 5 UMETA(DisplayName = "Not Found"), - AlreadyExists = 6 UMETA(DisplayName = "Already Exists"), - PermissionDenied = 7 UMETA(DisplayName = "Permission Denied"), - ResourceExhausted = 8 UMETA(DisplayName = "Resource Exhausted"), - FailedPrecondition = 9 UMETA(DisplayName = "Failed Precondition"), - Aborted = 10 UMETA(DisplayName = "Aborted"), - OutOfRange = 11 UMETA(DisplayName = "Out Of Range"), - Unimplemented = 12 UMETA(DisplayName = "Unimplemented"), - Internal = 13 UMETA(DisplayName = "Internal"), - Unavailable = 14 UMETA(DisplayName = "Unavailable"), - DataLoss = 15 UMETA(DisplayName = "Data Loss"), - Unauthenticated = 16 UMETA(DisplayName = "Unauthenticated") -}; - -USTRUCT(BlueprintType) -struct SATORIUNREAL_API FSatoriError -{ - GENERATED_BODY() - - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Satori Error") - FString Message; - - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Satori Error") - ESatoriErrorCode Code; - - FSatoriError(const FString& JsonString); - FSatoriError(): Code(ESatoriErrorCode::Unknown) { } - - ESatoriErrorCode ConvertSatoriErrorCode(int32 CodeValue); - - -}; \ No newline at end of file diff --git a/Satori/Source/SatoriUnreal/Public/SatoriEvent.h b/Satori/Source/SatoriUnreal/Public/SatoriEvent.h deleted file mode 100644 index e33a53362..000000000 --- a/Satori/Source/SatoriUnreal/Public/SatoriEvent.h +++ /dev/null @@ -1,70 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "SatoriProperties.h" -#include "SatoriEvent.generated.h" - - -// Events -USTRUCT(BlueprintType) -struct SATORIUNREAL_API FSatoriEvent -{ - GENERATED_BODY() - - // Optional event metadata - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Events") - TMap Metadata; - - // Event name. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Events") - FString Name; - - // Optional event ID assigned by the client, used to de-duplicate in retransmission scenarios. - // If not supplied the server will assign a randomly generated unique event identifier. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Events") - FString ID; - - // Optional value. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Events") - FString Value; - - // Optional identity ID for this event. (Ignored if published within a session) - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Events") - FString IdentityId; - - // Optional session ID for this event. (Ignored if published within a session) - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Events") - FString SessionId; - - // The time when the event was triggered on the producer side. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Events") - FDateTime Timestamp; - - // Optional session issued timestamp for this event. (Ignored if published within a session) - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Events") - int64 SessionIssuedAt; - - // Optional session expiry timestamp for this event. (Ignored if published within a session) - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Events") - int64 SessionExpiresAt; - - FSatoriEvent(const FString& JsonString); - FSatoriEvent(const TSharedPtr JsonObject); - FSatoriEvent(); // Default Constructor -}; diff --git a/Satori/Source/SatoriUnreal/Public/SatoriExperiment.h b/Satori/Source/SatoriUnreal/Public/SatoriExperiment.h deleted file mode 100644 index da93178eb..000000000 --- a/Satori/Source/SatoriUnreal/Public/SatoriExperiment.h +++ /dev/null @@ -1,53 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "SatoriExperiment.generated.h" - - -// Experiments -USTRUCT(BlueprintType) -struct SATORIUNREAL_API FSatoriExperiment -{ - GENERATED_BODY() - - // Experiment name. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Experiments") - FString Name; - - // Value associated with this Experiment. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Experiments") - FString Value; - - FSatoriExperiment(const FString& JsonString); - FSatoriExperiment(const TSharedPtr JsonObject); - FSatoriExperiment(); // Default Constructor -}; - -USTRUCT(BlueprintType) -struct SATORIUNREAL_API FSatoriExperimentList -{ - GENERATED_BODY() - - // Flags. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Experiments") - TArray Experiments; - - FSatoriExperimentList(const FString& JsonString); - FSatoriExperimentList(); // Default Constructor -}; diff --git a/Satori/Source/SatoriUnreal/Public/SatoriFlag.h b/Satori/Source/SatoriUnreal/Public/SatoriFlag.h deleted file mode 100644 index b3ab627aa..000000000 --- a/Satori/Source/SatoriUnreal/Public/SatoriFlag.h +++ /dev/null @@ -1,166 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "SatoriFlag.generated.h" - - -// Flags -UENUM(BlueprintType) -enum class FSatoriFlagValueChangeReasonType : uint8 -{ - UNKNOWN = 0, - FLAG_VARIANT = 1, - LIVE_EVENT = 2, - EXPERIMENT = 3 -}; - -USTRUCT(BlueprintType) -struct SATORIUNREAL_API FSatoriFlagValueChangeReason -{ - GENERATED_BODY() - - // The name of the configuration that overrides the flag value. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Flags") - FString Name; - - // The variant name of the configuration that overrides the flag value. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Flags") - FString VariantName; - - // The type of the configuration that declared the override. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Flags") - FSatoriFlagValueChangeReasonType Type; - - FSatoriFlagValueChangeReason(const FString& JsonString); - FSatoriFlagValueChangeReason(const TSharedPtr JsonObject); - FSatoriFlagValueChangeReason(); // Default Constructor -}; - -USTRUCT(BlueprintType) -struct SATORIUNREAL_API FSatoriFlag -{ - GENERATED_BODY() - - // Flag name. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Flags") - FString Name; - - // Value associated with this flag. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Flags") - FString Value; - - // Whether the value for this flag has conditionally changed from the default state. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Flags") - bool bConditionChanged; - - // The origin of change on the flag value returned. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Flags") - FSatoriFlagValueChangeReason ChangeReason; - - FSatoriFlag(const FString& JsonString); - FSatoriFlag(const TSharedPtr JsonObject); - FSatoriFlag(); // Default Constructor -}; - -USTRUCT(BlueprintType) -struct SATORIUNREAL_API FSatoriFlagList -{ - GENERATED_BODY() - - // Flags. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Flags") - TArray Flags; - - FSatoriFlagList(const FString& JsonString); - FSatoriFlagList(); // Default Constructor -}; - - -// Flag Overrides - -UENUM(BlueprintType) -enum class FSatoriFlagOverrideType : uint8 -{ - FLAG = 0, - FLAG_VARIANT = 1, - LIVE_EVENT_FLAG = 2, - LIVE_EVENT_FLAG_VARIANT = 3, - EXPERIMENT_PHASE_VARIANT_FLAG = 4 -}; - -USTRUCT(BlueprintType) -struct SATORIUNREAL_API FSatoriFlagOverrideValue -{ - GENERATED_BODY() - - // The name of the configuration that overrides the flag value. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Flags") - FString Name; - - // The variant name of the configuration that overrides the flag value. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Flags") - FString VariantName; - - // The value of the configuration that overrides the flag. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Flags") - FString Value; - - // The create time of the configuration that overrides the flag. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Flags") - int64 CreateTimeSec; - - // The type of the configuration that declared the override. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Flags") - FSatoriFlagOverrideType Type; - - FSatoriFlagOverrideValue(const FString& JsonString); - FSatoriFlagOverrideValue(const TSharedPtr JsonObject); - FSatoriFlagOverrideValue(); // Default Constructor -}; - -USTRUCT(BlueprintType) -struct SATORIUNREAL_API FSatoriFlagOverride -{ - GENERATED_BODY() - - // The list of configuration that affect the value of the flag. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Flags") - TArray Overrides; - - // Flag name - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Flags") - FString FlagName; - - FSatoriFlagOverride(const FString& JsonString); - FSatoriFlagOverride(const TSharedPtr JsonObject); - FSatoriFlagOverride(); // Default Constructor -}; - -USTRUCT(BlueprintType) -struct SATORIUNREAL_API FSatoriFlagOverrideList -{ - GENERATED_BODY() - - // Flags. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Flags") - TArray Flags; - - FSatoriFlagOverrideList(const FString& JsonString); - FSatoriFlagOverrideList(); // Default Constructor -}; diff --git a/Satori/Source/SatoriUnreal/Public/SatoriLiveEvent.h b/Satori/Source/SatoriUnreal/Public/SatoriLiveEvent.h deleted file mode 100644 index 176a028ab..000000000 --- a/Satori/Source/SatoriUnreal/Public/SatoriLiveEvent.h +++ /dev/null @@ -1,85 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "SatoriLiveEvent.generated.h" - - -// LiveEvents -USTRUCT(BlueprintType) -struct SATORIUNREAL_API FSatoriLiveEvent -{ - GENERATED_BODY() - - // The live event identifier. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|LiveEvents") - FString ID; - - // Name. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|LiveEvents") - FString Name; - - // Description. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|LiveEvents") - FString Description; - - // Event value. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|LiveEvents") - FString Value; - - // Reset CRON schedule, if configured. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|LiveEvents") - FString ResetCron; - - // Start time of current event run. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|LiveEvents") - int64 ActiveStartTimeSec; - - // End time of current event run. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|LiveEvents") - int64 ActiveEndTimeSec; - - // Start time. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|LiveEvents") - int64 StartTimeSec; - - // End time, 0 if it repeats forever. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|LiveEvents") - int64 EndTimeSec; - - // Duration in seconds. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|LiveEvents") - int64 DurationSec; - - FSatoriLiveEvent(const FString& JsonString); - FSatoriLiveEvent(const TSharedPtr JsonObject); - FSatoriLiveEvent(); // Default Constructor -}; - -USTRUCT(BlueprintType) -struct SATORIUNREAL_API FSatoriLiveEventList -{ - GENERATED_BODY() - - // Flags. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|LiveEvents") - TArray LiveEvents; - - FSatoriLiveEventList(const FString& JsonString); - FSatoriLiveEventList(); // Default Constructor -}; diff --git a/Satori/Source/SatoriUnreal/Public/SatoriLogger.h b/Satori/Source/SatoriUnreal/Public/SatoriLogger.h deleted file mode 100644 index 53c5f3c49..000000000 --- a/Satori/Source/SatoriUnreal/Public/SatoriLogger.h +++ /dev/null @@ -1,60 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "UObject/NoExportTypes.h" -#include "SatoriLogger.generated.h" - -//DECLARE_LOG_CATEGORY_EXTERN(LogSatori, Log, All); -DECLARE_LOG_CATEGORY_EXTERN(LogSatoriUnreal, Log, All); - -UENUM(BlueprintType, Category = "Satori") -enum class ESatoriLogLevel : uint8 -{ - Debug, - Info, - Warn, - Error, - Fatal -}; - -/** - * - */ -UCLASS() -class SATORIUNREAL_API USatoriLogger : public UObject -{ - GENERATED_BODY() - -public: - USatoriLogger(); - - UFUNCTION(BlueprintCallable, Category = "Satori") - static void SetLogLevel(ESatoriLogLevel InLogLevel); - - UFUNCTION(BlueprintCallable, Category = "Satori") - static void Log(ESatoriLogLevel InLogLevel, const FString& Message); - - UFUNCTION(BlueprintCallable, Category = "Satori") - static void EnableLogging(bool bEnable); - -private: - static ESatoriLogLevel CurrentLogLevel; - static bool bLoggingEnabled; - static bool IsLoggable(ESatoriLogLevel InLogLevel); -}; diff --git a/Satori/Source/SatoriUnreal/Public/SatoriLoggingMacros.h b/Satori/Source/SatoriUnreal/Public/SatoriLoggingMacros.h deleted file mode 100644 index 57df9f783..000000000 --- a/Satori/Source/SatoriUnreal/Public/SatoriLoggingMacros.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -// Toggle logging on/off by defining SATORI_LOGS_ENABLED -#define SATORI_LOGS_ENABLED - -#ifdef SATORI_LOGS_ENABLED - - #define SATORI_LOG_DEBUG(Message) \ - USatoriLogger::Log(ESatoriLogLevel::Debug, Message) - - #define SATORI_LOG_INFO(Message) \ - USatoriLogger::Log(ESatoriLogLevel::Info, Message) - - #define SATORI_LOG_WARN(Message) \ - USatoriLogger::Log(ESatoriLogLevel::Warn, Message) - - #define SATORI_LOG_ERROR(Message) \ - USatoriLogger::Log(ESatoriLogLevel::Error, Message) - - #define SATORI_LOG_FATAL(Message) \ - USatoriLogger::Log(ESatoriLogLevel::Fatal, Message) - -#else - - // Define empty macros if logging is disabled - #define SATORI_LOG_DEBUG(Message) - #define SATORI_LOG_INFO(Message) - #define SATORI_LOG_WARN(Message) - #define SATORI_LOG_ERROR(Message) - #define SATORI_LOG_FATAL(Message) - -#endif // SATORI_LOGS_ENABLED \ No newline at end of file diff --git a/Satori/Source/SatoriUnreal/Public/SatoriMessage.h b/Satori/Source/SatoriUnreal/Public/SatoriMessage.h deleted file mode 100644 index cf179f647..000000000 --- a/Satori/Source/SatoriUnreal/Public/SatoriMessage.h +++ /dev/null @@ -1,89 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "SatoriMessage.generated.h" - - -// Messages -USTRUCT(BlueprintType) -struct SATORIUNREAL_API FSatoriMessage -{ - GENERATED_BODY() - - // Key-value pairs of metadata. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Messages") - TMap Metadata; - - // The message's unique identifier. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Messages") - FString ID; - - // The message's text. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Messages") - FString Text; - - // The identifier of the schedule. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Messages") - FString ScheduleID; - - // The message's title. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Messages") - FString Title; - - // The message's image url. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Messages") - FString ImageURL; - - // The send time for the message. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Messages") - int64 SendTime; - - // The time the message was created. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Messages") - int64 CreateTime; - - // The time the message was updated. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Messages") - int64 UpdateTime; - - // The time the message was read by the client. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Messages") - int64 ReadTime; - - // The time the message was consumed by the identity. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Messages") - int64 ConsumeTime; - - FSatoriMessage(const FString& JsonString); - FSatoriMessage(const TSharedPtr JsonObject); - FSatoriMessage(); // Default Constructor -}; - -USTRUCT(BlueprintType) -struct SATORIUNREAL_API FSatoriMessageList -{ - GENERATED_BODY() - - // Flags. - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Satori|Messages") - TArray Messages; - - FSatoriMessageList(const FString& JsonString); - FSatoriMessageList(); // Default Constructor -}; diff --git a/Satori/Source/SatoriUnreal/Public/SatoriProperties.h b/Satori/Source/SatoriUnreal/Public/SatoriProperties.h deleted file mode 100644 index 913d94abd..000000000 --- a/Satori/Source/SatoriUnreal/Public/SatoriProperties.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "SatoriProperties.generated.h" - - -// Properties -USTRUCT(BlueprintType) -struct SATORIUNREAL_API FSatoriProperties -{ - GENERATED_BODY() - // Event default properties. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Satori|Authentication") - TMap DefaultProperties; - // Event computed properties. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Satori|Authentication") - TMap ComputedProperties; - // Event custom properties. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Satori|Authentication") - TMap CustomProperties; - - FSatoriProperties(const FString& JsonString); - FSatoriProperties(const TSharedPtr JsonObject); - FSatoriProperties(); // Default Constructor -}; diff --git a/Satori/Source/SatoriUnreal/Public/SatoriSession.h b/Satori/Source/SatoriUnreal/Public/SatoriSession.h deleted file mode 100644 index 2a23a6054..000000000 --- a/Satori/Source/SatoriUnreal/Public/SatoriSession.h +++ /dev/null @@ -1,72 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "SatoriUserSession.h" -#include "SatoriSession.generated.h" - -/** - * - */ -UCLASS(Blueprintable, BlueprintType) -class SATORIUNREAL_API USatoriSession : public UObject -{ - GENERATED_BODY() - -public: - - // Blueprint/C++ Exposed Struct with Data - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Satori|Authentication") - FSatoriUserSession SessionData; - - static USatoriSession* SetupSession(const FString& AuthResponse); - - /** - * @return The authentication token used to construct this session. - */ - UFUNCTION(BlueprintPure, Category = "Satori|Authentication") - const FString GetAuthToken() const; - - /** - * @return The refresh token used to construct this session. - */ - UFUNCTION(BlueprintPure, Category = "Satori|Authentication") - const FString GetRefreshToken() const; - - /** - * @return The refresh properties used to construct this session. - */ - UFUNCTION(BlueprintPure, Category = "Satori|Authentication") - const FSatoriProperties GetProperties() const; - - /** - * Restore User Session - * - * @param Token Authentication Token from Session - * @param RefreshToken RefreshToken retrieved from Session - */ - UFUNCTION(BlueprintCallable, Category = "Satori|Authentication") - static USatoriSession* RestoreSession(FString Token, FString RefreshToken); - -private: - USatoriSession() { } - - FString _AuthToken; - FString _RefreshToken; - FSatoriProperties _Properties; -}; diff --git a/Satori/Source/SatoriUnreal/Public/SatoriUserSession.h b/Satori/Source/SatoriUnreal/Public/SatoriUserSession.h deleted file mode 100644 index 4e8b79b9f..000000000 --- a/Satori/Source/SatoriUnreal/Public/SatoriUserSession.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" -#include "SatoriProperties.h" -#include "SatoriUserSession.generated.h" - -USTRUCT(BlueprintType) -struct SATORIUNREAL_API FSatoriUserSession -{ - GENERATED_BODY() - - // The authentication token used to construct this session. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Satori|Authentication") - FString AuthToken; - - // The refresh token used to construct this session. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Satori|Authentication") - FString RefreshToken; - - // Properties of this satori session. - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Satori|Authentication") - FSatoriProperties Properties; - - FSatoriUserSession(); // Default Constructor -}; \ No newline at end of file diff --git a/Satori/Source/SatoriUnreal/Public/SatoriUtils.h b/Satori/Source/SatoriUnreal/Public/SatoriUtils.h deleted file mode 100644 index 76a7eb945..000000000 --- a/Satori/Source/SatoriUnreal/Public/SatoriUtils.h +++ /dev/null @@ -1,258 +0,0 @@ -/* -* Copyright 2025 The Nakama Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "CoreMinimal.h" - -#include "SatoriClient.h" -#include "Templates/SharedPointer.h" -#include "SatoriLogger.h" -#include "Misc/Base64.h" -#include "Serialization/JsonSerializer.h" -#include "SatoriLoggingMacros.h" - -class FJsonObject; - -class SATORIUNREAL_API FSatoriUtils -{ -public: - // Handle Request Methods for REST API - static FString ESatoriRequesMethodToFString(ESatoriRequestMethod Verb) - { - switch (Verb) - { - case ESatoriRequestMethod::GET: - return TEXT("GET"); - case ESatoriRequestMethod::POST: - return TEXT("POST"); - case ESatoriRequestMethod::PUT: - return TEXT("PUT"); - case ESatoriRequestMethod::DEL: - return TEXT("DELETE"); - default: - // Handle unrecognized verb if needed - break; - } - - // Return an empty string for unrecognized verbs - return FString(); - } - - // Bool to String Helper - static FString BoolToString(bool Value) - { - return Value ? TEXT("true") : TEXT("false"); - } - - // Build Query String - static FString BuildQueryString(const TMultiMap& QueryParams) - { - FString QueryString; - - for (const auto& Param : QueryParams) - { - if (!QueryString.IsEmpty()) - { - QueryString += "&"; - } - - // Only specific inputs needs to be encoded - //FString EncodedKey = FGenericPlatformHttp::UrlEncode(Param.Key); - //FString EncodedValue = FGenericPlatformHttp::UrlEncode(Param.Value); - - QueryString += Param.Key + "=" + Param.Value; - } - - return QueryString; - } - - // Extra client checks for lambdas in requests - static bool IsClientActive(const USatoriClient *Client) - { - return IsValid(Client) && Client->bIsActive == true; - } - - // Json helpers - static FString EncodeJson(TSharedPtr JsonObject) - { - FString OutputString; - const TSharedRef> Writer = TJsonWriterFactory<>::Create(&OutputString); - FJsonSerializer::Serialize(JsonObject.ToSharedRef(), Writer); - return OutputString; - } - - static bool SerializeJsonObject(const TSharedPtr& JsonObject, FString& OutSerializedJson) - { - if (!JsonObject.IsValid()) - { - return false; - } - - const TSharedRef> JsonWriter = TJsonWriterFactory<>::Create(&OutSerializedJson); - if (!FJsonSerializer::Serialize(JsonObject.ToSharedRef(), JsonWriter)) - { - JsonWriter->Close(); - return false; - } - - JsonWriter->Close(); - - return true; - } - - static TSharedPtr DeserializeJsonObject(const FString& JsonString) { - TSharedPtr JsonObject; - const TSharedRef> JsonReader = TJsonReaderFactory<>::Create(JsonString); - if (!FJsonSerializer::Deserialize(JsonReader, JsonObject)) - { - JsonObject = nullptr; - } - return JsonObject; - } - - static void AddVarsToJson(const TSharedPtr& JsonObject, const TMap& Vars, const FString varsFieldName = TEXT("vars"), const bool addAlways = false) { - - if (addAlways || Vars.Num() > 0) - { - const TSharedPtr VarsJson = MakeShared(); - for (const auto& Var : Vars) - { - if (!Var.Key.IsEmpty() && !Var.Value.IsEmpty()) - { - VarsJson->SetStringField(Var.Key, Var.Value); - } - else - { - SATORI_LOG_WARN(TEXT("AddVarsToJson: Empty key or value detected.")); - } - } - JsonObject->SetObjectField(varsFieldName, VarsJson); - } - } - - // Enum as integer string - template - static FString GetEnumValueAsIntString(TEnum EnumValue) - { - const int32 IntValue = static_cast(EnumValue); - return FString::FromInt(IntValue); - } - - // Error handling - static FSatoriError HandleInvalidClient() - { - FSatoriError Error; - Error.Message = FString(TEXT("Client Missing")); - Error.Code = ESatoriErrorCode::InvalidArgument; - - SATORI_LOG_ERROR(Error.Message); - - return Error; - } - - static FSatoriError HandleInvalidSession() - { - FSatoriError Error; - Error.Message = FString(TEXT("Session Missing")); - Error.Code = ESatoriErrorCode::InvalidArgument; - - SATORI_LOG_ERROR(Error.Message); - - return Error; - } - - static FSatoriError HandleInvalidClientAndSession() - { - FSatoriError Error; - Error.Message = FString(TEXT("Client and Session Missing")); - Error.Code = ESatoriErrorCode::InvalidArgument; - - SATORI_LOG_ERROR(Error.Message); - - return Error; - } - - // Base64 Encode/Decode - static FString Base64Encode(const FString& Source) - { - TArray ByteArray; - FTCHARToUTF8 StringSrc = FTCHARToUTF8(Source.GetCharArray().GetData()); - ByteArray.Append((uint8*)StringSrc.Get(), StringSrc.Length()); - - return FBase64::Encode(ByteArray); - } - - static bool Base64Decode(const FString& Source, FString& Dest) - { - TArray ByteArray; - bool Success = FBase64::Decode(Source, ByteArray); - - FUTF8ToTCHAR StringSrc = FUTF8ToTCHAR((const ANSICHAR*)ByteArray.GetData(), ByteArray.Num()); - Dest.AppendChars(StringSrc.Get(), StringSrc.Length()); - - return Success; - } - - // Working with Optionals (mainly from Blueprints) - - template - static TOptional CreateOptional(const T& value, const T& defaultValue) - { - return value != defaultValue ? TOptional(value) : TOptional(); - } - - // Conversion - - static TMap ConvertFloatMapToDouble(const TMap& FloatMap) - { - TMap DoubleMap; - for (const auto& Pair : FloatMap) - { - DoubleMap.Add(Pair.Key, static_cast(Pair.Value)); - } - return DoubleMap; - } - - // Common functions used by multiple clients - static void ProcessRequestComplete(FHttpRequestPtr Request, const FHttpResponsePtr& Response, bool bSuccess, const TFunction& SuccessCallback, const TFunction& ErrorCallback); - - static void HandleJsonSerializationFailure(TFunction ErrorCallback); - static bool IsSessionValid(const USatoriSession* Session, TFunction ErrorCallback); - static bool IsResponseSuccessful(int32 ResponseCode); - static FSatoriError CreateRequestFailureError(); - - // Make HTTP request - static TSharedRef MakeRequest( - const FString& URL, - const FString& Content, - ESatoriRequestMethod RequestMethod, - const FString& SessionToken, - float Timeout - ); - - static void SetBasicAuthorizationHeader(TSharedRef HttpRequest, const FString& ServerKey) - { - FString AuthToken = FString::Printf(TEXT("%s:"), *ServerKey); - FTCHARToUTF8 Utf8Token = FTCHARToUTF8(*AuthToken); - FString EncodedAuthToken = FBase64::Encode((const uint8*)Utf8Token.Get(), Utf8Token.Length()); - FString AuthorizationHeader = FString::Printf(TEXT("Basic %s"), *EncodedAuthToken); - - //SATORI_LOG_DEBUG(FString::Printf( TEXT("Authorization Header: %s"), *AuthorizationHeader )); - - HttpRequest->SetHeader(TEXT("Authorization"), AuthorizationHeader); - } -}; diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 000000000..b4c1ca96d --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,222 @@ +version: '3' + +silent: true +dotenv: ['.env'] + +vars: + PROTO_FILE: "cmd/codegen/proto/nakama-types.proto" + PROTO_SERVICE_FILE: "cmd/codegen/proto/nakama-api.proto" + PROTO_RT_FILE: "cmd/codegen/proto/realtime.proto" + PROTO_SATORI_FILE: "cmd/codegen/proto/satori.proto" + TEMPLATE_VERSION_FILE: "cmd/codegen/templates/nakama.version.tmpl" + TEMPLATE_NAKAMA_H: "cmd/codegen/templates/Nakama.h.tmpl" + TEMPLATE_NAKAMA_CPP: "cmd/codegen/templates/Nakama.cpp.tmpl" + CODEGEN: './build/codegen{{if eq OS "windows"}}.exe{{end}}' + TESTRUNNER: 'build/testrunner{{if eq OS "windows"}}.exe{{end}}' + +tasks: + codegen: + desc: 'Fetch protos, build codegen tool, and generate all Nakama + Satori modules.' + cmds: + - task: fetch-protos + - cmd: | + go build -C ./cmd/codegen -o ../../{{.CODEGEN}} + {{.CODEGEN}} --proto {{.PROTO_FILE}} --template {{.TEMPLATE_VERSION_FILE}} > Nakama/Resources/nakama.version + {{.CODEGEN}} --proto {{.PROTO_FILE}} --proto {{.PROTO_SERVICE_FILE}} --template {{.TEMPLATE_NAKAMA_H}} > Nakama/Source/Nakama/Public/Nakama.gen.h + {{.CODEGEN}} --proto {{.PROTO_FILE}} --proto {{.PROTO_SERVICE_FILE}} --template {{.TEMPLATE_NAKAMA_CPP}} > Nakama/Source/Nakama/Private/Nakama.gen.cpp + + start-server: + cmds: + - cmd: | + docker compose -f IntegrationTests/server/docker-compose.yml down + docker compose -f IntegrationTests/server/docker-compose.yml up --build -d + + build: + desc: 'Build the IntegrationTests editor target.' + deps: + - task: check-unreal-engine + cmds: + - cmd: | + PROJECT="$(pwd)/IntegrationTests/IntegrationTests.uproject" + + echo "Building IntegrationTestsEditor (Development)..." + "$UNREAL_ENGINE/Engine/Build/BatchFiles/Build.bat" \ + IntegrationTestsEditor Win64 Development \ + -Project="$PROJECT" + platforms: [windows] + - cmd: | + PROJECT="$(pwd)/IntegrationTests/IntegrationTests.uproject" + + echo "Building IntegrationTestsEditor (Development)..." + "$UNREAL_ENGINE/Engine/Build/BatchFiles/Build.sh" \ + IntegrationTestsEditor Mac Development \ + -Project="$PROJECT" \ + -Architecture_Mac=arm64 + platforms: [darwin] + - cmd: | + PROJECT="$(pwd)/IntegrationTests/IntegrationTests.uproject" + + echo "Building IntegrationTestsEditor (Development)..." + "$UNREAL_ENGINE/Engine/Build/BatchFiles/Build.sh" \ + IntegrationTestsEditor Linux Development \ + -Project="$PROJECT" + platforms: [linux] + + check-unreal-engine: + desc: 'Check or auto-detect UNREAL_ENGINE environment variable.' + cmds: + - cmd: | + if [ -z "$UNREAL_ENGINE" ]; then + echo "UNREAL_ENGINE not set, searching for Unreal Engine installations..." + + FOUND="" + # macOS: use mdfind (Spotlight) + if command -v mdfind &> /dev/null; then + CANDIDATE=$(mdfind -name "RunUAT.sh" 2>/dev/null | grep "Engine/Build/BatchFiles/RunUAT.sh" | head -1) + if [ -n "$CANDIDATE" ]; then + FOUND="${CANDIDATE%/Engine/Build/BatchFiles/RunUAT.sh}" + fi + # Linux: use locate + elif command -v locate &> /dev/null; then + CANDIDATE=$(locate "Engine/Build/BatchFiles/RunUAT.sh" 2>/dev/null | head -1) + if [ -n "$CANDIDATE" ]; then + FOUND="${CANDIDATE%/Engine/Build/BatchFiles/RunUAT.sh}" + fi + fi + + if [ -z "$FOUND" ]; then + echo "Error: Could not find Unreal Engine installation!" + echo "" + echo "Set UNREAL_ENGINE manually in .env file:" + echo " echo 'UNREAL_ENGINE=/path/to/UnrealEngine' >> .env" + exit 1 + fi + + echo "Found Unreal Engine at: $FOUND" + echo "Saving to .env file..." + echo "UNREAL_ENGINE=$FOUND" >> .env + export UNREAL_ENGINE="$FOUND" + fi + echo "UNREAL_ENGINE=$UNREAL_ENGINE" + platforms: [darwin, linux] + - cmd: | + powershell -NoProfile -Command "& { + if (-not \$env:UNREAL_ENGINE) { + Write-Host 'UNREAL_ENGINE not set, searching for Unreal Engine installations...' + + \$searchPaths = @( + 'C:\Program Files\Epic Games', + 'D:\Program Files\Epic Games', + 'E:\Program Files\Epic Games', + \"\$env:USERPROFILE\UnrealEngine\" + ) + + \$found = \$null + foreach (\$basePath in \$searchPaths) { + if (Test-Path \$basePath) { + \$candidates = Get-ChildItem -Path \$basePath -Directory -Filter 'UE_*' -ErrorAction SilentlyContinue | Sort-Object Name -Descending + foreach (\$candidate in \$candidates) { + \$uatPath = Join-Path \$candidate.FullName 'Engine\Build\BatchFiles\RunUAT.bat' + if (Test-Path \$uatPath) { + \$found = \$candidate.FullName + break + } + } + if (\$found) { break } + } + } + + if (-not \$found) { + Write-Host 'Error: Could not find Unreal Engine installation!' + Write-Host '' + Write-Host 'Please set UNREAL_ENGINE in .env file:' + Write-Host ' echo UNREAL_ENGINE=C:\Path\To\UE_5.x >> .env' + Write-Host '' + Write-Host 'Or pass it as an argument:' + Write-Host ' task test UNREAL_ENGINE=C:\Path\To\UE_5.x' + exit 1 + } + + Write-Host \"Found Unreal Engine at: \$found\" + Write-Host 'Saving to .env file...' + Add-Content -Path '.env' -Value \"UNREAL_ENGINE=\$found\" + \$env:UNREAL_ENGINE = \$found + } + Write-Host \"UNREAL_ENGINE=\$env:UNREAL_ENGINE\" + }" + platforms: [windows] + + test: + desc: 'Build and run IntegrationTests C++ automation suite.' + cmds: + - task: check-unreal-engine + - cmd: | + if [ ! -d "IntegrationTests/satori/.git" ]; then + git clone git@github.com:heroiclabs/satori.git IntegrationTests/satori + git -C IntegrationTests/satori lfs pull + else + echo "Satori already cloned, skipping." + fi + if [ ! -d "IntegrationTests/dashboards/.git" ]; then + git clone git@github.com:heroiclabs/dashboards.git IntegrationTests/dashboards + else + echo "Dashboards already cloned, skipping." + fi + - task: start-server + - cmd: | + cmd /c "if exist IntegrationTests\Plugins\Nakama rmdir /s /q IntegrationTests\Plugins\Nakama" + cmd /c "mklink /J IntegrationTests\Plugins\Nakama Nakama" + cmd /c "if exist IntegrationTests\Plugins\Satori rmdir /s /q IntegrationTests\Plugins\Satori" + cmd /c "mklink /J IntegrationTests\Plugins\Satori Satori" + platforms: [windows] + - task: build + - cmd: | + cd cmd/testrunner && go build -o ../../{{.TESTRUNNER}} && cd ../.. + + ./{{.TESTRUNNER}} {{.FILTER}} + platforms: [windows] + - cmd: | + if [ -z "$UNREAL_ENGINE" ]; then + echo "Error: UNREAL_ENGINE is not set!" + echo "Set it in .env or pass it as an argument:" + echo " task test UNREAL_ENGINE=/path/to/engine" + exit 1 + fi + + cd cmd/testrunner && go build -o ../../{{.TESTRUNNER}} && cd ../.. + + ./{{.TESTRUNNER}} {{.FILTER}} + platforms: [darwin, linux] + + fetch-protos: + internal: true + vars: + PROTO_API_URL: 'https://raw.githubusercontent.com/heroiclabs/nakama-common/master/api/api.proto' + PROTO_SERVICE_URL: 'https://raw.githubusercontent.com/heroiclabs/nakama/master/apigrpc/apigrpc.proto' + PROTO_RT_URL: 'https://raw.githubusercontent.com/heroiclabs/nakama-common/master/rtapi/realtime.proto' + PROTO_SATORI_URL: 'https://api.github.com/repos/heroiclabs/satori/contents/api/satori.proto' + cmds: + - cmd: | + mkdir -p proto + + curl -o {{.PROTO_FILE}} "{{.PROTO_API_URL}}" + curl -o {{.PROTO_SERVICE_FILE}} "{{.PROTO_SERVICE_URL}}" + curl -o {{.PROTO_RT_FILE}} "{{.PROTO_RT_URL}}" + + if [ -z "$GITHUB_TOKEN" ]; then + creds=$(printf "protocol=https\nhost=github.com\n" | git credential fill) + for line in $creds; do + case "$line" in + password=*) + GITHUB_TOKEN="${line#password=}" + ;; + esac + done + fi + + # Satori is private, so need credentials + curl -o "{{.PROTO_SATORI_FILE}}" \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "Accept: application/vnd.github.v3.raw" \ + "{{.PROTO_SATORI_URL}}" + diff --git a/cmd/codegen/api.go b/cmd/codegen/api.go new file mode 100644 index 000000000..b3011cc25 --- /dev/null +++ b/cmd/codegen/api.go @@ -0,0 +1,179 @@ +// Copyright 2026 GameUp Online, Inc. +// All rights reserved. +// +// NOTICE: All information contained herein is, and remains the property of GameUp +// Online, Inc. and its suppliers, if any. The intellectual and technical concepts +// contained herein are proprietary to GameUp Online, Inc. and its suppliers and may +// be covered by U.S. and Foreign Patents, patents in process, and are protected by +// trade secret or copyright law. Dissemination of this information or reproduction of +// this material is strictly forbidden unless prior written permission is obtained +// from GameUp Online, Inc. + +package main + +import ( + "bytes" + "fmt" + "log" + "os" + "regexp" + "strings" + + "github.com/emicklei/proto" +) + +type Api struct { + // Use slices to preserve order of proto messages + Enums []*ProtoEnum + Messages []*ProtoMessage + Rpcs []*ProtoRpc + + EnumsByName map[string]*ProtoEnum + MessagesByName map[string]*ProtoMessage + RpcsByName map[string]*ProtoRpc +} + +func (api *Api) addFile(protoFile string) error { + fileBytes, err := os.ReadFile(protoFile) + if err != nil { + return fmt.Errorf("failed to read file %s: %s", protoFile, err.Error()) + } + + parser := proto.NewParser(bytes.NewReader(fileBytes)) + parsedProto, err := parser.Parse() + if err != nil { + return fmt.Errorf("failed to parse proto file %s: %s", protoFile, err.Error()) + } + + proto.Walk( + parsedProto, + proto.WithEnum( + func(enum *proto.Enum) { + comment := "" + if enum.Comment != nil { + comment = enum.Comment.Message() + + // Find everything between square brackets + re := regexp.MustCompile(`\[(.*?)\]`) + matches := re.FindAllStringSubmatch(comment, -1) + for _, match := range matches { + // match[0] is the entire match, match[1] is the first submatch. + comment = match[1] + } + } + + // Build fully qualified enum name including parent message if nested + enumName := enum.Name + if enum.Parent != nil { + if parentMsg, ok := enum.Parent.(*proto.Message); ok { + enumName = parentMsg.Name + "_" + enum.Name + } + } + + visitor := &enumVisitor{ + Enum: &ProtoEnum{ + Comment: strings.Trim(comment, " "), + Fields: make([]*enumField, 0), + Name: enumName, + }, + } + for _, each := range enum.Elements { + each.Accept(visitor) + } + api.Enums = append(api.Enums, visitor.Enum) + api.EnumsByName[visitor.Enum.Name] = visitor.Enum + }, + ), + proto.WithMessage( + func(message *proto.Message) { + if strings.HasPrefix(message.Name, "google.") { + return + } + + comment := "" + if message.Comment != nil { + comment = message.Comment.Message() + } + visitor := &messageVisitor{ + Message: &ProtoMessage{ + Comment: comment, + Fields: make([]*proto.NormalField, 0), + MapFields: make([]*proto.MapField, 0), + Name: message.Name, + }, + } + for _, each := range message.Elements { + each.Accept(visitor) + } + api.Messages = append(api.Messages, visitor.Message) + api.MessagesByName[visitor.Message.Name] = visitor.Message + }, + ), + proto.WithRPC( + func(rpc *proto.RPC) { + comment := "" + if rpc.Comment != nil { + comment = rpc.Comment.Message() + comment = strings.TrimSpace(comment) + } + + resolveType := func(fullTypeName string) *ProtoMessage { + if fullTypeName == "google.protobuf.Empty" { + return nil + } else { + // We get something like `api.MyRequestType`, so trim until the last dot. + t, ok := api.MessagesByName[TrimUntilLastDot(fullTypeName)] + if !ok { + log.Fatalf("Unable to find type %s for %s", fullTypeName, rpc.Name) + } + return t + } + } + + requestType := resolveType(rpc.RequestType) + returnType := resolveType(rpc.ReturnsType) + + visitor := &rpcVisitor{ + Rpc: &ProtoRpc{ + Comment: comment, + RequestType: requestType, + ReturnType: returnType, + Name: rpc.Name, + PathParams: make([]string, 0), + QueryParams: make([]string, 0), + BodyParams: make([]string, 0), + }, + } + + for _, each := range rpc.Elements { + each.Accept(visitor) + } + api.Rpcs = append(api.Rpcs, visitor.Rpc) + api.RpcsByName[visitor.Rpc.Name] = visitor.Rpc + }, + ), + ) + + return nil +} + +func LoadApi(protoFiles []string) (Api, error) { + api := Api{ + Enums: make([]*ProtoEnum, 0), + Messages: make([]*ProtoMessage, 0), + Rpcs: make([]*ProtoRpc, 0), + + EnumsByName: make(map[string]*ProtoEnum, 0), + MessagesByName: make(map[string]*ProtoMessage, 0), + RpcsByName: make(map[string]*ProtoRpc, 0), + } + + // Load file by file... + // Updates maps internally, so each subsequent + // file will have previous context to work with. + for _, f := range protoFiles { + api.addFile(f) + } + + return api, nil +} diff --git a/cmd/codegen/func_maps.go b/cmd/codegen/func_maps.go new file mode 100644 index 000000000..e08281239 --- /dev/null +++ b/cmd/codegen/func_maps.go @@ -0,0 +1,222 @@ +package main + +import ( + "bytes" + "os/exec" + "slices" + "strings" + "text/template" + "time" + + "github.com/emicklei/proto" + "github.com/golang-cz/textcase" +) + +func getGeneralFuncMap(api Api) template.FuncMap { + fnMap := template.FuncMap{ + "camelCase": textcase.CamelCase, + "pascalCase": textcase.PascalCase, + "snakeCase": textcase.SnakeCase, + "title": strings.Title, + "upperCase": strings.ToUpper, + "trimPrefix": strings.TrimPrefix, + "containsString": strings.Contains, + "containsInSlice": func(slice []string, value string) bool { + return slices.Contains(slice, value) + }, + "containsEnum": func(key string) bool { + _, ok := api.EnumsByName[key] + return ok + }, + "isEnumType": func(fieldType string) bool { + _, ok := api.EnumsByName[fieldType] + return ok + }, + "containsMessage": func(key string) bool { + _, ok := api.MessagesByName[key] + return ok + }, + "getMessage": func(key string) *ProtoMessage { + result, _ := api.MessagesByName[key] + return result + }, + "minus": func(a, b int) int { + return a - b + }, + "lowerCase": func(s string) string { + return strings.ToLower(s[:1]) + s[1:] + }, + "appendString": func(s1 string, s2 string) bool { + s1 += s2 + return true + }, + "appendStringNew": func(s1 string, s2 string) string { + return s1 + s2 + }, + "isEnumInput": func(messageName string) bool { + for _, enum := range api.EnumsByName { + for _, field := range enum.Fields { + if field.Input == messageName { + return true + } + } + } + return false + }, + "isLastField": func(slice []*proto.NormalField, index int) bool { + return index == len(slice)-1 + }, + "isLastMapElement": func(slice []*proto.MapField, index int) bool { + return index == len(slice)-1 + }, + "isLastEnumField": func(slice []*enumField, index int) bool { + return index == len(slice)-1 + }, + "currentYear": func() int { + return time.Now().Year() + }, + "stripDot": func(s string) string { + return TrimUntilLastDot(s) + }, + "getVersionData": func() string { + args := []string{"rev-parse", "HEAD"} + cmd := exec.Command("git", args...) + var out bytes.Buffer + cmd.Stdout = &out + + err := cmd.Run() + if err != nil { + return "unknown-build" + } + + return strings.TrimSpace(out.String()) + }, + } + + return fnMap +} + +func getUnrealFuncMap() template.FuncMap { + fnMap := template.FuncMap{ + "normalizeIdentifier": func(s string) string { + if _, isUeReserved := ueParamKeywords[s]; isUeReserved { + return s + "_" + } + if _, isCppReserved := cppKeywords[s]; isCppReserved { + return s + "_" + } + return s + }, + "toUnrealType": func(s string) string { + // Type checks + If message -> FNakamaType.. + return s + "_TODO" + }, + } + + return fnMap +} + +var ueParamKeywords = map[string]struct{}{ + "Self": {}, +} + +var cppKeywords = map[string]struct{}{ + "alignas": {}, + "alignof": {}, + "and": {}, + "and_eq": {}, + "asm": {}, + "atomic_cancel": {}, + "atomic_commit": {}, + "atomic_noexcept": {}, + "auto": {}, + "bitand": {}, + "bitor": {}, + "bool": {}, + "break": {}, + "case": {}, + "catch": {}, + "char": {}, + "char8_t": {}, + "char16_t": {}, + "char32_t": {}, + "class": {}, + "compl": {}, + "concept": {}, + "const": {}, + "consteval": {}, + "constexpr": {}, + "constinit": {}, + "const_cast": {}, + "continue": {}, + "contract_assert": {}, + "co_await": {}, + "co_return": {}, + "co_yield": {}, + "decltype": {}, + "default": {}, + "delete": {}, + "do": {}, + "double": {}, + "dynamic_cast": {}, + "else": {}, + "enum": {}, + "explicit": {}, + "export": {}, + "extern": {}, + "false": {}, + "float": {}, + "for": {}, + "friend": {}, + "goto": {}, + "if": {}, + "inline": {}, + "int": {}, + "long": {}, + "mutable": {}, + "namespace": {}, + "new": {}, + "noexcept": {}, + "not": {}, + "not_eq": {}, + "nullptr": {}, + "operator": {}, + "or": {}, + "or_eq": {}, + "private": {}, + "protected": {}, + "public": {}, + "reflexpr": {}, + "register": {}, + "reinterpret_cast": {}, + "requires": {}, + "return": {}, + "short": {}, + "signed": {}, + "sizeof": {}, + "static": {}, + "static_assert": {}, + "static_cast": {}, + "struct": {}, + "switch": {}, + "synchronized": {}, + "template": {}, + "this": {}, + "thread_local": {}, + "throw": {}, + "true": {}, + "try": {}, + "typedef": {}, + "typeid": {}, + "typename": {}, + "union": {}, + "unsigned": {}, + "using": {}, + "virtual": {}, + "void": {}, + "volatile": {}, + "wchar_t": {}, + "while": {}, + "xor": {}, + "xor_eq": {}, +} diff --git a/cmd/codegen/go.mod b/cmd/codegen/go.mod new file mode 100644 index 000000000..4bfe1dda4 --- /dev/null +++ b/cmd/codegen/go.mod @@ -0,0 +1,8 @@ +module heroiclabs.com/codegen + +go 1.22.2 + +require ( + github.com/emicklei/proto v1.14.3 + github.com/golang-cz/textcase v1.2.1 +) diff --git a/cmd/codegen/go.sum b/cmd/codegen/go.sum new file mode 100644 index 000000000..609ea0582 --- /dev/null +++ b/cmd/codegen/go.sum @@ -0,0 +1,4 @@ +github.com/emicklei/proto v1.14.3 h1:zEhlzNkpP8kN6utonKMzlPfIvy82t5Kb9mufaJxSe1Q= +github.com/emicklei/proto v1.14.3/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= +github.com/golang-cz/textcase v1.2.1 h1:0xRtKo+abtJojre5ONjuMzyg9fSfiKBj5bWZ6fpTYxI= +github.com/golang-cz/textcase v1.2.1/go.mod h1:aWsQknYwxtTS2zSCrGGoRIsxmzjsHomRqLeMeVb+SKU= diff --git a/cmd/codegen/main.go b/cmd/codegen/main.go new file mode 100644 index 000000000..a4559aa87 --- /dev/null +++ b/cmd/codegen/main.go @@ -0,0 +1,94 @@ +package main + +import ( + "flag" + "fmt" + "log" + "maps" + "os" + "path/filepath" + "text/template" +) + +func mergeFuncMaps(funcMaps ...template.FuncMap) template.FuncMap { + result := template.FuncMap{} + + for _, m := range funcMaps { + maps.Copy(result, m) + } + return result +} + +type fileList []string + +func (f *fileList) String() string { + return fmt.Sprint(*f) +} +func (f *fileList) Set(value string) error { + *f = append(*f, value) + return nil +} + +func main() { + // + // Parse command line args. + argTmpl := flag.String("template", "", "template file path.") + + var argProtoFiles fileList + flag.Var(&argProtoFiles, "proto", "list of proto files to parse. Proto files provided later can depend on files provided earlier.") + + flag.Parse() + + if *argTmpl == "" { + log.Fatalf("Template file is not given. Please provide it via --template.") + } + if len(argProtoFiles) == 0 { + log.Fatalf("No proto files are given. Please provide one or more proto files via --proto.") + } + + // + // Parse the API files and load structures. + api, err := LoadApi(argProtoFiles) + if err != nil { + log.Fatalf("Failed to load API: %s", err.Error()) + } + + // + // Make function maps that we will use in templates. + generalFuncMap := getGeneralFuncMap(api) + unrealFuncMap := getUnrealFuncMap() + + combinedFuncMap := mergeFuncMaps(generalFuncMap, unrealFuncMap) + + // + // Read and parse the template file (with partials) + tmpl := template.New("codegen").Funcs(combinedFuncMap) + + // Load partial templates (_*.tmpl) from the same directory as the main template + partialPattern := filepath.Join(filepath.Dir(*argTmpl), "_*.tmpl") + partials, _ := filepath.Glob(partialPattern) + for _, p := range partials { + pBytes, err := os.ReadFile(p) + if err != nil { + log.Fatalf("Failed to read partial template %s: %s", p, err.Error()) + } + if _, err = tmpl.Parse(string(pBytes)); err != nil { + log.Fatalf("Failed to parse partial template %s: %s", p, err.Error()) + } + } + + bytes, err := os.ReadFile(*argTmpl) + if err != nil { + log.Fatalf("Failed to read template file '%s': %s", *argTmpl, err.Error()) + } + + if _, err = tmpl.Parse(string(bytes)); err != nil { + log.Fatalf("Failed to parse template '%s': %s", *argTmpl, err.Error()) + } + + // + // Generate the code from parsed API using the parsed Template. + if err := tmpl.Execute(os.Stdout, api); err != nil { + log.Printf("Failed to execute template '%s': %s", *argTmpl, err.Error()) + } +} diff --git a/cmd/codegen/proto_visitors.go b/cmd/codegen/proto_visitors.go new file mode 100644 index 000000000..a77135510 --- /dev/null +++ b/cmd/codegen/proto_visitors.go @@ -0,0 +1,260 @@ +// Copyright 2026 GameUp Online, Inc. +// All rights reserved. +// +// NOTICE: All information contained herein is, and remains the property of GameUp +// Online, Inc. and its suppliers, if any. The intellectual and technical concepts +// contained herein are proprietary to GameUp Online, Inc. and its suppliers and may +// be covered by U.S. and Foreign Patents, patents in process, and are protected by +// trade secret or copyright law. Dissemination of this information or reproduction of +// this material is strictly forbidden unless prior written permission is obtained +// from GameUp Online, Inc. + +package main + +import ( + "regexp" + "slices" + "strings" + "text/scanner" + + "github.com/emicklei/proto" +) + +func TrimUntilLastDot(s string) string { + return s[strings.LastIndex(s, ".")+1:] +} + +// -------------------- +// Enums +type ProtoEnum struct { + Comment string + Fields []*enumField + Name string +} + +type enumField struct { + *proto.EnumField + Input string + Output string +} + +type enumVisitor struct { + proto.NoopVisitor + Enum *ProtoEnum +} + +func (v *enumVisitor) VisitEnumField(ef *proto.EnumField) { + if ef.Comment == nil { + ef.Comment = &proto.Comment{ + Position: scanner.Position{}, + Lines: []string{}, + } + } + var input, output string + for _, each := range ef.Elements { + option, ok := each.(*proto.Option) + if ok { + if strings.Contains(option.Name, "input") { + input = option.Constant.Source + } else if strings.Contains(option.Name, "output") { + output = option.Constant.Source + } + } + } + + v.Enum.Fields = append(v.Enum.Fields, &enumField{ + EnumField: ef, + Input: input, + Output: output, + }) +} + +// -------------------- +// Messages + +type ProtoOneofField struct { + *proto.OneOfField + Categories []string // Values of (category) field options, e.g. "REQUEST", "RESPONSE", "EVENT" + ResponseField string // Value of (response_field) field option, if present +} + +type ProtoMessage struct { + Comment string + Fields []*proto.NormalField + MapFields []*proto.MapField + OneofFields []*ProtoOneofField + Name string +} + +type messageVisitor struct { + proto.NoopVisitor + Message *ProtoMessage +} + +func (v *messageVisitor) VisitNormalField(nf *proto.NormalField) { + if nf.Comment == nil { + nf.Comment = &proto.Comment{ + Position: scanner.Position{}, + Lines: []string{}, + } + } + nf.Type = TrimUntilLastDot(nf.Type) + v.Message.Fields = append(v.Message.Fields, nf) +} + +func (v *messageVisitor) VisitMapField(mf *proto.MapField) { + if mf.Comment == nil { + mf.Comment = &proto.Comment{ + Position: scanner.Position{}, + Lines: []string{}, + } + } + mf.Type = TrimUntilLastDot(mf.Type) + v.Message.MapFields = append(v.Message.MapFields, mf) +} + +func (v *messageVisitor) VisitOneof(oneof *proto.Oneof) { + for _, o := range oneof.Elements { + o.Accept(v) + } +} + +func (v *messageVisitor) VisitOneofField(oneof *proto.OneOfField) { + oneof.Name = TrimUntilLastDot(oneof.Name) + oneof.Type = TrimUntilLastDot(oneof.Type) + + field := &ProtoOneofField{OneOfField: oneof} + for _, opt := range oneof.Options { + switch strings.TrimSpace(opt.Name) { + case "(category)": + field.Categories = append(field.Categories, opt.Constant.Source) + case "(response_field)": + field.ResponseField = opt.Constant.Source + } + } + + v.Message.OneofFields = append(v.Message.OneofFields, field) +} + +// -------------------- +// RPCs + +type ProtoRpc struct { + Name string + Comment string + RequestType *ProtoMessage + ReturnType *ProtoMessage + Endpoint string + Method string + PathParams []string + QueryParams []string + BodyParams []string + BodyField string // The field name to use as the body (from google.api.http body option) +} + +type rpcVisitor struct { + proto.NoopVisitor + Rpc *ProtoRpc +} + +func tryGetHttpMethod(str string) (string, bool) { + if strings.Contains(str, "post") { + return "POST", true + } + if strings.Contains(str, "get") { + return "GET", true + } + if strings.Contains(str, "delete") { + return "DELETE", true + } + if strings.Contains(str, "put") { + return "PUT", true + } + + return "", false +} + +func (v *rpcVisitor) VisitOption(o *proto.Option) { + if o.Constant.IsString { + v.Rpc.Endpoint = o.Constant.Source + } + + httpMethodFound := false + + // + // Sometimes we have an option like (google.api.http).post + method, found := tryGetHttpMethod(o.Name) + if found { + v.Rpc.Method = method + httpMethodFound = true + } + + // + // And sometimes the method is hidden inside option props. + if o.Name == "(google.api.http)" { + for _, l := range o.Constant.OrderedMap { + method, found = tryGetHttpMethod(l.Name) + if found { + v.Rpc.Method = method + v.Rpc.Endpoint = l.Literal.Source + httpMethodFound = true + } + // Extract the body field specification + if l.Name == "body" { + v.Rpc.BodyField = l.Literal.Source + } + } + } + + // Only deduce path/body/query params when processing an HTTP option. + // VisitOption is called for every RPC option (including openapiv2 annotations), + // so without this guard, params would be appended multiple times. + if !httpMethodFound { + return + } + + // + // Path params + if len(v.Rpc.PathParams) == 0 { + paramRegex := regexp.MustCompile(`\{([a-zA-Z0-9_]*)\}`) + matches := paramRegex.FindAllStringSubmatch(v.Rpc.Endpoint, -1) + for _, m := range matches { + v.Rpc.PathParams = append(v.Rpc.PathParams, m[1]) + } + } + + // + // Body/Query Params + if v.Rpc.BodyField == "*" { + if v.Rpc.RequestType != nil { + for _, f := range v.Rpc.RequestType.Fields { + if !slices.Contains(v.Rpc.PathParams, f.Name) { + v.Rpc.BodyParams = append(v.Rpc.BodyParams, f.Name) + } + } + for _, f := range v.Rpc.RequestType.MapFields { + if !slices.Contains(v.Rpc.PathParams, f.Name) { + v.Rpc.BodyParams = append(v.Rpc.BodyParams, f.Name) + } + } + } + } else if method == "GET" || v.Rpc.BodyField == "" { + if v.Rpc.RequestType != nil { + for _, f := range v.Rpc.RequestType.Fields { + if !slices.Contains(v.Rpc.PathParams, f.Name) { + v.Rpc.QueryParams = append(v.Rpc.QueryParams, f.Name) + } + } + } + } else if v.Rpc.BodyField != "" { + v.Rpc.BodyParams = append(v.Rpc.BodyParams, v.Rpc.BodyField) + // Non-path, non-body fields become query params (grpc-gateway named-body binding) + if v.Rpc.RequestType != nil { + for _, f := range v.Rpc.RequestType.Fields { + if !slices.Contains(v.Rpc.PathParams, f.Name) && f.Name != v.Rpc.BodyField { + v.Rpc.QueryParams = append(v.Rpc.QueryParams, f.Name) + } + } + } + } +} diff --git a/cmd/codegen/templates/Nakama.cpp.tmpl b/cmd/codegen/templates/Nakama.cpp.tmpl new file mode 100644 index 000000000..a6cd9def9 --- /dev/null +++ b/cmd/codegen/templates/Nakama.cpp.tmpl @@ -0,0 +1,60 @@ +/* + * Copyright {{currentYear}} The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + +{{ $prefix := "Nakama" }} + +#include "Nakama.gen.h" +#include "JsonObjectConverter.h" + +{{/*---------------- MESSAGES ------------------*/}} +{{ range $message := .Messages }} + +F{{ $prefix }}{{ $message.Name }} F{{ $prefix }}{{ $message.Name }}::FromJson(const TSharedPtr& Json) +{ + F{{ $prefix }}{{ $message.Name }} Result; + if (!FJsonObjectConverter::JsonObjectToUStruct(Json.ToSharedRef(), &Result, 0, 0)) + { + UE_LOG(LogTemp, Log, TEXT("Failed to create F{{ $prefix }}{{ $message.Name }} from JSON")); + } + + return Result; +} + +TSharedPtr F{{ $prefix }}{{ $message.Name }}::ToJson() const +{ + return FJsonObjectConverter::UStructToJsonObject(this); +} + +{{- end }} + +{{/*---------------- RPCs ------------------*/}} +{{- range $rpc := .Rpcs }} + +FNakamaApiRequestModel NakamaInternal::Build{{ $rpc.Name }}Request ( + {{- if ne nil $rpc.RequestType }} + const F{{ $prefix }}{{ $rpc.RequestType.Name }}& Params; + {{- end }} +) +{ + FNakamaApiRequestModel Request; + + // TODO: Fill the request + + return Request; +} +{{- end }} diff --git a/cmd/codegen/templates/Nakama.h.tmpl b/cmd/codegen/templates/Nakama.h.tmpl new file mode 100644 index 000000000..486c2c475 --- /dev/null +++ b/cmd/codegen/templates/Nakama.h.tmpl @@ -0,0 +1,101 @@ +/* + * Copyright {{currentYear}} The Nakama Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This code is auto-generated. DO NOT EDIT. */ + +{{ $prefix := "Nakama" }} + +#pragma once + +#include "CoreMinimal.h" +#include "Nakama.gen.generated.h" + +{{/*--------------- ENUMS ---------------------*/}} +{{ range $enum := .Enums }} +UENUM(BlueprintType) +enum class E{{ $prefix }}{{ $enum.Name }} : uint8 +{ + {{ range $field := $enum.Fields }} + {{ $field.Name }} = {{ $field.Integer }}, + {{- end }} +}; +{{- end }} + +{{/*--------------- TYPES ---------------------*/}} +{{ range $message := .Messages }} + +USTRUCT(BlueprintType) +struct NAKAMA_API F{{ $prefix }}{{ $message.Name }} +{ + GENERATED_BODY() + + {{- range $member := $message.Fields }} + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "{{ $member.Name }}")) + {{- if $member.Repeated }} + TArray<{{ $member.Type | toUnrealType }}> {{ $member.Name | pascalCase | normalizeIdentifier }}; + {{- else }} + {{ $member.Type | toUnrealType }} {{ $member.Name | pascalCase | normalizeIdentifier }}; + {{- end }} + {{- end }} + {{- range $member := $message.MapFields }} + + UPROPERTY(BlueprintReadWrite, Category = "Nakama", meta = (JsonName = "{{ $member.Name }}")) + TMap {{ $member.Name | pascalCase | normalizeIdentifier }}; + {{- end }} + + static F{{ $prefix }}{{ $message.Name }} FromJson(const TSharedPtr& Json); + TSharedPtr ToJson() const; +}; +{{- end }} + +{{/*--------------- RESPONSES (For Futures) ---------------------*/}} + +/** Tag type used as the value type for RPCs that return no data. */ +struct NAKAMA_API FNakamaVoid {}; +struct NAKAMA_API FNakamaVoidResult +{ + using ValueType = FNakamaVoid; + FNakamaVoid Value{}; + FNakamaError Error; + bool bIsError = true; +}; + +{{ range $rpc := .Rpcs }} +{{- if ne nil $rpc.ReturnType }} +struct NAKAMA_API F{{ $prefix }}{{ $rpc.ReturnType.Name }}Result +{ + using ValueType = F{{ $prefix }}{{ $rpc.ReturnType.Name }}; + F{{ $prefix }}{{ $rpc.ReturnType.Name }} Value {}; + FNakamaError Error; + bool bIsError = true; +}; +{{- end }} +{{- end }} + +{{/*--------------- REQUESTS ---------------------*/}} +namespace NakamaInternal +{ +{{- range $rpc := .Rpcs }} + +FNakamaApiRequestModel NAKAMA_API Build{{ $rpc.Name }}Request ( + {{- if ne nil $rpc.RequestType }} + const F{{ $prefix }}{{ $rpc.RequestType.Name }}& Params; + {{- end }} +); +{{- end }} +} + diff --git a/cmd/codegen/templates/nakama.version.tmpl b/cmd/codegen/templates/nakama.version.tmpl new file mode 100644 index 000000000..f6b26b10c --- /dev/null +++ b/cmd/codegen/templates/nakama.version.tmpl @@ -0,0 +1 @@ +{{ getVersionData }} diff --git a/cmd/protoverify/go.mod b/cmd/protoverify/go.mod new file mode 100644 index 000000000..c97aedfdc --- /dev/null +++ b/cmd/protoverify/go.mod @@ -0,0 +1,5 @@ +module heroic-labs.com/nakama/sdk/protoverify + +go 1.25.5 + +require github.com/emicklei/proto v1.14.2 diff --git a/cmd/protoverify/go.sum b/cmd/protoverify/go.sum new file mode 100644 index 000000000..712834f7b --- /dev/null +++ b/cmd/protoverify/go.sum @@ -0,0 +1,2 @@ +github.com/emicklei/proto v1.14.2 h1:wJPxPy2Xifja9cEMrcA/g08art5+7CGJNFNk35iXC1I= +github.com/emicklei/proto v1.14.2/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= diff --git a/cmd/protoverify/main.go b/cmd/protoverify/main.go new file mode 100644 index 000000000..6148a1554 --- /dev/null +++ b/cmd/protoverify/main.go @@ -0,0 +1,196 @@ +// Copyright 2026 GameUp Online, Inc. +// All rights reserved. +// +// NOTICE: All information contained herein is, and remains the property of GameUp +// Online, Inc. and its suppliers, if any. The intellectual and technical concepts +// contained herein are proprietary to GameUp Online, Inc. and its suppliers and may +// be covered by U.S. and Foreign Patents, patents in process, and are protected by +// trade secret or copyright law. Dissemination of this information or reproduction of +// this material is strictly forbidden unless prior written permission is obtained +// from GameUp Online, Inc. + +package main + +import ( + "bytes" + "flag" + "fmt" + "log" + "os" + "strings" + + "github.com/emicklei/proto" +) + +// Protobuf scalar types that don't need forward-declaration. +var builtinTypes = map[string]bool{ + "double": true, "float": true, + "int32": true, "int64": true, + "uint32": true, "uint64": true, + "sint32": true, "sint64": true, + "fixed32": true, "fixed64": true, + "sfixed32": true, "sfixed64": true, + "bool": true, "string": true, "bytes": true, +} + +type orderError struct { + File string + Context string // e.g. "rpc Authenticate" or "message Session" + Type string // the missing type name +} + +func (e orderError) String() string { + return fmt.Sprintf("%s: %s references %s, but it is not defined above", e.File, e.Context, e.Type) +} + +// stripPackage turns "api.MyType" into "MyType". +func stripPackage(typeName string) string { + if i := strings.LastIndex(typeName, "."); i >= 0 { + return typeName[i+1:] + } + return typeName +} + +// isExternal returns true for well-known google.protobuf types +// that are imported and don't need local declaration. +func isExternal(typeName string) bool { + return strings.HasPrefix(typeName, "google.protobuf.") +} + +func verifyFile(filename string, seen map[string]bool) []orderError { + fileBytes, err := os.ReadFile(filename) + if err != nil { + log.Fatalf("Failed to read file %s: %s", filename, err.Error()) + } + + parser := proto.NewParser(bytes.NewReader(fileBytes)) + parsed, err := parser.Parse() + if err != nil { + log.Fatalf("Failed to parse proto file %s: %s", filename, err.Error()) + } + + var errors []orderError + + // checkType verifies a type reference is already seen. + checkType := func(context, typeName string) { + if isExternal(typeName) || builtinTypes[typeName] { + return + } + stripped := stripPackage(typeName) + if builtinTypes[stripped] { + return + } + if !seen[stripped] { + errors = append(errors, orderError{ + File: filename, + Context: context, + Type: stripped, + }) + } + } + + proto.Walk( + parsed, + proto.WithEnum(func(enum *proto.Enum) { + name := enum.Name + if enum.Parent != nil { + if parentMsg, ok := enum.Parent.(*proto.Message); ok { + name = parentMsg.Name + "_" + enum.Name + } + } + seen[name] = true + }), + proto.WithMessage(func(message *proto.Message) { + if message.Name == "google.protobuf.EnumValueOptions" { + return + } + + // Register this message as seen before checking its fields, + // so self-referential types work. + seen[message.Name] = true + + // Check field types within this message. + for _, elem := range message.Elements { + switch f := elem.(type) { + case *proto.NormalField: + checkType(fmt.Sprintf("message %s field %s", message.Name, f.Name), f.Type) + case *proto.MapField: + checkType(fmt.Sprintf("message %s map field %s key", message.Name, f.Name), f.KeyType) + checkType(fmt.Sprintf("message %s map field %s value", message.Name, f.Name), f.Type) + case *proto.Oneof: + for _, oe := range f.Elements { + if oof, ok := oe.(*proto.OneOfField); ok { + checkType(fmt.Sprintf("message %s oneof field %s", message.Name, oof.Name), oof.Type) + } + } + } + } + }), + proto.WithRPC(func(rpc *proto.RPC) { + checkType(fmt.Sprintf("rpc %s request", rpc.Name), rpc.RequestType) + checkType(fmt.Sprintf("rpc %s return", rpc.Name), rpc.ReturnsType) + }), + ) + + return errors +} + +type fileList []string + +func (f *fileList) String() string { + return fmt.Sprint(*f) +} +func (f *fileList) Set(value string) error { + *f = append(*f, value) + return nil +} + +func main() { + var protoFiles fileList + flag.Var(&protoFiles, "proto", "Proto file(s) to verify, in processing order. May be specified multiple times.") + flag.Parse() + + if len(protoFiles) == 0 { + log.Fatalf("No proto files given. Usage: protoverify --proto file1.proto [--proto file2.proto ...]") + } + + // Types seen so far across all files (simulates codegen's sequential loading). + seen := make(map[string]bool) + var allErrors []orderError + + messageCount := 0 + enumCount := 0 + rpcCount := 0 + + for _, f := range protoFiles { + before := len(seen) + errs := verifyFile(f, seen) + allErrors = append(allErrors, errs...) + + // Count what was added in this file. + fileBytes, _ := os.ReadFile(f) + parser := proto.NewParser(bytes.NewReader(fileBytes)) + parsed, _ := parser.Parse() + proto.Walk(parsed, + proto.WithEnum(func(_ *proto.Enum) { enumCount++ }), + proto.WithMessage(func(m *proto.Message) { + if m.Name != "google.protobuf.EnumValueOptions" { + messageCount++ + } + }), + proto.WithRPC(func(_ *proto.RPC) { rpcCount++ }), + ) + _ = before + } + + if len(allErrors) > 0 { + for _, e := range allErrors { + fmt.Println(e) + } + fmt.Printf("FAIL: %d ordering error(s) found\n", len(allErrors)) + os.Exit(1) + } + + fmt.Printf("OK: all types are defined before use (%d file(s), %d enum(s), %d message(s), %d rpc(s))\n", + len(protoFiles), enumCount, messageCount, rpcCount) +} diff --git a/cmd/testrunner/go.mod b/cmd/testrunner/go.mod new file mode 100644 index 000000000..2b34ca50b --- /dev/null +++ b/cmd/testrunner/go.mod @@ -0,0 +1,8 @@ +module heroic-labs.com/nakama/sdk/testrunner + +go 1.25.5 + +require ( + golang.org/x/sys v0.41.0 // indirect + golang.org/x/term v0.40.0 // indirect +) diff --git a/cmd/testrunner/go.sum b/cmd/testrunner/go.sum new file mode 100644 index 000000000..64c0afa3e --- /dev/null +++ b/cmd/testrunner/go.sum @@ -0,0 +1,4 @@ +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= +golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= diff --git a/cmd/testrunner/main.go b/cmd/testrunner/main.go new file mode 100644 index 000000000..3cfb052bf --- /dev/null +++ b/cmd/testrunner/main.go @@ -0,0 +1,308 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "sync" + "time" + + "golang.org/x/term" +) + +var shards []*TestShard + +func addTestShard(name string, reportDir string) { + logFile := filepath.Join(reportDir, "stdout.log") + shards = append(shards, &TestShard{ + Index: len(shards), + Name: name, + ReportDir: reportDir, + + LogFile: logFile, + LogLines: make([]string, NumLogLines), + LogLineIdx: 0, + }) +} + +var defaultShardNames = []string{ + "IntegrationTests.NakamaTests", + "IntegrationTests.NakamaBlueprintTests", + "IntegrationTests.NakamaRtTests", + "IntegrationTests.NakamaRtConnectionTests", + "IntegrationTests.SatoriTests", + "IntegrationTests.SatoriBlueprintTests", +} + +var editorFlags = []string{ + "-nullrhi", + "-stdout", + "-FullStdOutLogOutput", + "-forcelogflush", + "-nosplash", + "-buildmachine", + "-unattended", + "-nopause", + "-nosound", +} + +type Report struct { + Devices []json.RawMessage `json:"devices"` + ReportCreatedOn string `json:"reportCreatedOn"` + Succeeded int `json:"succeeded"` + SucceededWithWarnings int `json:"succeededWithWarnings"` + Failed int `json:"failed"` + NotRun int `json:"notRun"` + InProcess int `json:"inProcess"` + TotalDuration float64 `json:"totalDuration"` + ComparisonExported bool `json:"comparisonExported"` + ComparisonExportDirectory string `json:"comparisonExportDirectory"` + Tests []json.RawMessage `json:"tests"` +} + +type TestEntry struct { + TestDisplayName string `json:"testDisplayName"` + FullTestPath string `json:"fullTestPath"` + State string `json:"state"` + Entries []struct { + Event struct { + Type string `json:"type"` + Message string `json:"message"` + } `json:"event"` + } `json:"entries"` +} + +func runEditor(editor, project string, testShard *TestShard) { + args := []string{project} + args = append(args, editorFlags...) + args = append(args, + fmt.Sprintf("-abslog=%s", filepath.Join(testShard.ReportDir, "unreal.log")), + fmt.Sprintf("-ReportOutputPath=%s", testShard.ReportDir), + fmt.Sprintf("-ExecCmds=Automation RunTests %s; Quit", testShard.Name), + ) + + cmd := exec.Command(editor, args...) + if cmd.Err != nil { + testShard.addLine(fmt.Sprintf("Error creating cmd: %s", cmd.Err)) + testShard.Status = ShardFailed + return + } + + if f, err := os.Create(testShard.LogFile); err == nil { + cmd.Stdout = f + cmd.Stderr = f + defer f.Close() + } + + if err := cmd.Run(); err != nil { + testShard.addLine(fmt.Sprintf("Editor exited with error: %s", err)) + testShard.Status = ShardFailed + return + } + + testShard.Status = ShardPassed +} + +func mergeReports(dir string) { + pattern := filepath.Join(dir, "shard-*", "index.json") + matches, _ := filepath.Glob(pattern) + + if len(matches) == 0 { + return + } + + var merged Report + for i, path := range matches { + data, err := os.ReadFile(path) + if err != nil { + fmt.Fprintf(os.Stderr, "Error reading %s: %v\n", path, err) + continue + } + + data = stripBOM(data) + + var report Report + if err := json.Unmarshal(data, &report); err != nil { + fmt.Fprintf(os.Stderr, "Error parsing %s: %v\n", path, err) + continue + } + + if i == 0 { + merged.Devices = report.Devices + merged.ReportCreatedOn = report.ReportCreatedOn + } + + merged.Succeeded += report.Succeeded + merged.SucceededWithWarnings += report.SucceededWithWarnings + merged.Failed += report.Failed + merged.NotRun += report.NotRun + merged.InProcess += report.InProcess + merged.TotalDuration += report.TotalDuration + merged.Tests = append(merged.Tests, report.Tests...) + } + + out, err := json.MarshalIndent(merged, "", "\t") + if err != nil { + fmt.Fprintf(os.Stderr, "Error marshaling merged report: %v\n", err) + return + } + + outPath := filepath.Join(dir, "index.json") + if err := os.WriteFile(outPath, out, 0644); err != nil { + fmt.Fprintf(os.Stderr, "Error writing %s: %v\n", outPath, err) + return + } + + fmt.Printf("Merged %d shards (%d tests) -> %s\n", len(matches), len(merged.Tests), outPath) +} + +func printSummary(reportDir string, shards []*TestShard) { + var failed []TestEntry + passed := 0 + var crashedShards []string + + // Collect results from each shard's report (if it exists) + for _, s := range shards { + reportPath := filepath.Join(s.ReportDir, "index.json") + data, err := os.ReadFile(reportPath) + if err != nil { + // No report — shard crashed before writing results + if s.Status == ShardFailed { + crashedShards = append(crashedShards, s.Name) + } + continue + } + + data = stripBOM(data) + + var report Report + if err := json.Unmarshal(data, &report); err != nil { + crashedShards = append(crashedShards, s.Name) + continue + } + + for _, raw := range report.Tests { + var t TestEntry + if err := json.Unmarshal(raw, &t); err != nil { + continue + } + if t.State == "Fail" { + failed = append(failed, t) + } else { + passed++ + } + } + } + + fmt.Println() + fmt.Println("=========================================") + + totalFailed := len(failed) + len(crashedShards) + fmt.Printf(" Passed: %d Failed: %d\n", passed, totalFailed) + fmt.Println("=========================================") + + for _, t := range failed { + fmt.Printf(" \033[31mFAIL:\033[0m %s\n", t.TestDisplayName) + for _, e := range t.Entries { + if e.Event.Type == "Error" { + fmt.Printf(" %s\n", e.Event.Message) + break + } + } + } + + for _, name := range crashedShards { + fmt.Printf(" \033[31mCRASH:\033[0m %s (shard exited before producing results)\n", name) + } + + fmt.Println() + + if totalFailed > 0 { + os.Exit(1) + } +} + +func stripBOM(data []byte) []byte { + if len(data) >= 3 && data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF { + return data[3:] + } + return data +} + +func main() { + ueRoot := os.Getenv("UNREAL_ENGINE") + if ueRoot == "" { + fmt.Fprintln(os.Stderr, "UNREAL_ENGINE is not set") + os.Exit(1) + } + + var editor string + switch runtime.GOOS { + case "windows": + editor = filepath.Join(ueRoot, "Engine", "Binaries", "Win64", "UnrealEditor-Cmd.exe") + case "darwin": + editor = filepath.Join(ueRoot, "Engine", "Binaries", "Mac", "UnrealEditor.app", "Contents", "MacOS", "UnrealEditor") + default: + editor = filepath.Join(ueRoot, "Engine", "Binaries", "Linux", "UnrealEditor") + } + + cwd, _ := os.Getwd() + project := filepath.Join(cwd, "IntegrationTests", "IntegrationTests.uproject") + reportDir := filepath.Join(cwd, "Reports", fmt.Sprintf("IntegrationTests_%s", time.Now().Format("2006-01-02_15-04-05"))) + + os.MkdirAll(reportDir, 0755) + + shardNames := defaultShardNames + if len(os.Args) > 1 { + shardNames = os.Args[1:] + } + + for i, shardName := range shardNames { + shardDir := filepath.Join(reportDir, fmt.Sprintf("shard-%d", i)) + os.MkdirAll(shardDir, 0755) + addTestShard(shardName, shardDir) + } + + fmt.Printf("Launching %d test shards in parallel...\n", len(shards)) + + // Do fancy stuff with logs if we're in a capable terminal. + isRichOutput := term.IsTerminal(int(os.Stdout.Fd())) + var wg sync.WaitGroup + for _, shard := range shards { + logDone := make(chan struct{}) + + // + // Start a test goroutine. + wg.Add(1) + go func(shard *TestShard) { + defer wg.Done() + defer close(logDone) + runEditor(editor, project, shard) + }(shard) + + // + // Watch for log file changes to update shard output. + if isRichOutput { + go func(shard *TestShard, done <-chan struct{}) { + updateShardLog(shard, done) + }(shard, logDone) + } + } + + // + // Make channel for when the tests wait group completes + testsComplete := make(chan struct{}) + go func() { + wg.Wait() + close(testsComplete) + }() + + drawStatusLoop(isRichOutput, shards, testsComplete) + + fmt.Println("All shards complete.") + mergeReports(reportDir) + printSummary(reportDir, shards) +} diff --git a/cmd/testrunner/testShard.go b/cmd/testrunner/testShard.go new file mode 100644 index 000000000..8d1730846 --- /dev/null +++ b/cmd/testrunner/testShard.go @@ -0,0 +1,235 @@ +package main + +import ( + "bufio" + "fmt" + "io" + "os" + "strings" + "sync" + "time" + + "golang.org/x/term" +) + +func moveCursorUp(n int) { fmt.Printf("\033[%dA", n) } +func moveCursorDown(n int) { fmt.Printf("\033[%dB", n) } +func clearLine() { fmt.Print("\033[2K\r") } +func hideCursor() { fmt.Print("\033[?25l") } +func showCursor() { fmt.Print("\033[?25h") } +func getTerminalWidth() int { + // term.GetSize returns (width, height, error) + width, _, err := term.GetSize(int(os.Stdout.Fd())) + if err != nil { + // This will happen if you pipe the output to a file + // or run it in an environment without a real TTY. + return 80 + } + return width - 10 +} + +var NumLogLines int = 5 + +type ShardStatus int + +const ( + ShardRunning ShardStatus = iota + ShardPassed + ShardFailed +) + +type TestShard struct { + lock sync.RWMutex + + Index int + Name string + ReportDir string + + LogFile string + LogLines []string + LogLineIdx int + + Status ShardStatus + DrawnStatus ShardStatus + HeaderDrawn bool +} + +func (s *TestShard) statusIndicator() string { + switch s.Status { + case ShardPassed: + return "\033[32m✓\033[0m" // green check + case ShardFailed: + return "\033[31m✗\033[0m" // red X + default: + return "\033[33m⋯\033[0m" // yellow ellipsis + } +} + +func (s *TestShard) draw() { + if !s.HeaderDrawn { + clearLine() + fmt.Printf(" %s \033[1mShard %d:\033[0m %s\n", s.statusIndicator(), s.Index+1, s.Name) + fmt.Printf(" log: \033]8;;file://%s\033\\\033[2m%s\033[0m\033]8;;\033\\\n", s.LogFile, truncateString(s.LogFile, getTerminalWidth())) + + s.HeaderDrawn = true + s.DrawnStatus = s.Status + } else if s.Status != s.DrawnStatus { + // Status changed — redraw only the header line, skip past the log link + clearLine() + fmt.Printf(" %s \033[1mShard %d:\033[0m %s\n", s.statusIndicator(), s.Index+1, s.Name) + moveCursorDown(1) + s.DrawnStatus = s.Status + } else { + // No change — skip past header + log link + moveCursorDown(2) + } + + s.lock.RLock() + defer s.lock.RUnlock() + + // Since we will ring the log buffer, + + // We start at logLineIdx + for i := s.LogLineIdx; i < NumLogLines; i++ { + clearLine() + fmt.Printf(" \033[2m\033[0m \033[48;5;236m\033[37m %s \033[0m\n", s.LogLines[i]) + } + // And then go to the buffer start + // for the rest of the messages + for i := 0; i < s.LogLineIdx; i++ { + clearLine() + fmt.Printf(" \033[2m\033[0m \033[48;5;236m\033[37m %s \033[0m\n", s.LogLines[i]) + } +} + +func drawStatusLoop(isRichOutput bool, shards []*TestShard, testsComplete <-chan struct{}) { + if !isRichOutput { + for _, s := range shards { + fmt.Printf(" %s Shard %d: %s\n", s.statusIndicator(), s.Index+1, s.Name) + } + <-testsComplete + for _, s := range shards { + fmt.Printf(" %s Shard %d: %s\n", s.statusIndicator(), s.Index+1, s.Name) + } + } else { + // + // Rate limit for UI output + ticker := time.NewTicker(100 * time.Millisecond) + defer ticker.Stop() + + hideCursor() + + ui: + for { + select { + case <-testsComplete: + // Break the outer loop when done + break ui + case <-ticker.C: + // Basically redraw UI every tick + for _, s := range shards { + s.draw() + } + moveCursorUp(len(shards) * (NumLogLines + 2)) + } + } + + // Final redraw to pick up any status changes that occurred + // between the last tick and testsComplete closing. + for _, s := range shards { + s.draw() + } + moveCursorUp(len(shards) * (NumLogLines + 2)) + + // + // Reset the cursor and final report + moveCursorDown(len(shards) * (NumLogLines + 2)) + showCursor() + } +} + +func (s *TestShard) addLine(line string) { + s.lock.Lock() + defer s.lock.Unlock() + s.LogLines[s.LogLineIdx] = line + s.LogLineIdx++ + if s.LogLineIdx >= NumLogLines { + s.LogLineIdx = 0 + } +} + +func truncateString(s string, maxChars int) string { + runes := []rune(s) + if len(runes) <= maxChars { + return s + } + + // We need 3 characters for the "..." + // Split the remaining allowed characters between the start and the end + keep := maxChars - 3 + keepStart := keep / 2 + keepEnd := keep - keepStart + + return string(runes[:keepStart]) + "..." + string(runes[len(runes)-keepEnd:]) +} + +func updateShardLog(s *TestShard, done <-chan struct{}) { + file, err := os.Create(s.LogFile) + if err != nil { + fmt.Printf("Failed to open log file %s: %v\n", s.LogFile, err) + return + } + defer file.Close() + + reader := bufio.NewReader(file) + var partialLine string + + for { + line, err := reader.ReadString('\n') + + // + // Handle partial lines + if len(line) > 0 { + partialLine += line + // Only process when we know we have a complete line + if strings.HasSuffix(partialLine, "\n") { + s.addLine(truncateString(strings.TrimRight(partialLine, "\r\n"), getTerminalWidth())) + partialLine = "" // reset buffer + } + } + + if err == io.EOF { + select { + case <-done: + return + case <-time.After(200 * time.Millisecond): + } + + // + // Check the state of the file path on disk + pathInfo, errStat := os.Stat(s.LogFile) + if errStat == nil { + fileInfo, _ := file.Stat() + + // Detect rotation/recreation + if !os.SameFile(pathInfo, fileInfo) { + file.Close() + file, err = os.Open(s.LogFile) // Reopen the new file + if err != nil { + fmt.Printf("Failed to reopen rotated file: %v\n", err) + return + } + reader.Reset(file) + continue + } + } + + // Clear the sticky EOF state from bufio.Reader + reader.Reset(file) + + } else if err != nil { + fmt.Printf("Error reading file: %v\n", err) + return + } + } +} diff --git a/images/Bindings-1.png b/images/Bindings-1.png index af680b6f6..63786421c 100644 Binary files a/images/Bindings-1.png and b/images/Bindings-1.png differ diff --git a/images/Bindings-2.png b/images/Bindings-2.png index 3dfa6f39c..bf65d9a37 100644 Binary files a/images/Bindings-2.png and b/images/Bindings-2.png differ diff --git a/images/Bindings-3.png b/images/Bindings-3.png index d8a6f2de5..c8a100e63 100644 Binary files a/images/Bindings-3.png and b/images/Bindings-3.png differ diff --git a/images/Bindings-4.png b/images/Bindings-4.png index 3c84656fa..4549afc78 100644 Binary files a/images/Bindings-4.png and b/images/Bindings-4.png differ diff --git a/images/Bindings-5.png b/images/Bindings-5.png index b99e561eb..8757d55f3 100644 Binary files a/images/Bindings-5.png and b/images/Bindings-5.png differ diff --git a/images/Bindings-6.png b/images/Bindings-6.png new file mode 100644 index 000000000..c8eaef9e7 Binary files /dev/null and b/images/Bindings-6.png differ diff --git a/images/Client-1.png b/images/Client-1.png index 562116f69..ece7cdd37 100644 Binary files a/images/Client-1.png and b/images/Client-1.png differ diff --git a/images/Client-2.png b/images/Client-2.png index d0b932739..1b87eb1b9 100644 Binary files a/images/Client-2.png and b/images/Client-2.png differ diff --git a/images/Client-3.png b/images/Client-3.png index 70bd46d2f..791c61000 100644 Binary files a/images/Client-3.png and b/images/Client-3.png differ diff --git a/images/Client-4.png b/images/Client-4.png index 9e650bd39..6b8ead3af 100644 Binary files a/images/Client-4.png and b/images/Client-4.png differ diff --git a/images/Client-5.png b/images/Client-5.png deleted file mode 100644 index c49ad807d..000000000 Binary files a/images/Client-5.png and /dev/null differ diff --git a/images/Client-6.png b/images/Client-6.png deleted file mode 100644 index a45e9bc3f..000000000 Binary files a/images/Client-6.png and /dev/null differ diff --git a/images/Cursors.png b/images/Cursors.png index da58092a1..63dbf25ec 100644 Binary files a/images/Cursors.png and b/images/Cursors.png differ diff --git a/images/ExampleProject-1.png b/images/ExampleProject-1.png deleted file mode 100644 index 859285378..000000000 Binary files a/images/ExampleProject-1.png and /dev/null differ diff --git a/images/ExampleProject-2.png b/images/ExampleProject-2.png deleted file mode 100644 index 1a8fc258c..000000000 Binary files a/images/ExampleProject-2.png and /dev/null differ diff --git a/images/ExampleProject-3.png b/images/ExampleProject-3.png deleted file mode 100644 index 8721a8c6a..000000000 Binary files a/images/ExampleProject-3.png and /dev/null differ diff --git a/images/Logging.png b/images/Logging.png deleted file mode 100644 index d682b3604..000000000 Binary files a/images/Logging.png and /dev/null differ diff --git a/images/RPC.png b/images/RPC.png index ac00d3f77..727c8af62 100644 Binary files a/images/RPC.png and b/images/RPC.png differ diff --git a/images/Requests.png b/images/Requests.png index 0cde7139c..7a2357108 100644 Binary files a/images/Requests.png and b/images/Requests.png differ diff --git a/images/Sessions-1.png b/images/Sessions-1.png index 7f7911326..6b98e9689 100644 Binary files a/images/Sessions-1.png and b/images/Sessions-1.png differ diff --git a/images/Sessions-2.png b/images/Sessions-2.png index 0f01d7c0a..7df311382 100644 Binary files a/images/Sessions-2.png and b/images/Sessions-2.png differ diff --git a/images/Sessions-3.png b/images/Sessions-3.png new file mode 100644 index 000000000..dfd33f462 Binary files /dev/null and b/images/Sessions-3.png differ diff --git a/images/Testing-1.png b/images/Testing-1.png deleted file mode 100644 index 987572b21..000000000 Binary files a/images/Testing-1.png and /dev/null differ