diff --git a/.pubnub.yml b/.pubnub.yml index a2b0e01..7c87a3a 100644 --- a/.pubnub.yml +++ b/.pubnub.yml @@ -1,8 +1,13 @@ name: unreal-engine schema: 1 -version: v1.0.0 +version: 1.2.0 scm: github.com/pubnub/unreal-engine changelog: + - date: 2025-11-12 + version: 1.2.0 + changes: + - type: feature + text: "The `ListUsersFromChannel` method now returns a maximum of 1,000 occupants per channel. Previously, it would return all occupants regardless of count. If you have channels with more than 1,000 occupants, you must use pagination (new `Limit` and `Offset` parameters) to retrieve the complete list. **BREAKING CHANGE**." - date: 2025-09-11 version: 1.1.0 changes: diff --git a/PubnubLibrary.uplugin b/PubnubLibrary.uplugin index d1985c2..aa92895 100644 --- a/PubnubLibrary.uplugin +++ b/PubnubLibrary.uplugin @@ -1,7 +1,7 @@ { "FileVersion": 3, - "Version": 12, - "VersionName": "1.1.0", + "Version": 13, + "VersionName": "1.2.0", "FriendlyName": "Pubnub Unreal SDK", "Description": "Quickly add interactive features to your game that scale without building your backend infrastructure.", "Category": "Code", diff --git a/Source/PubnubLibrary/Private/FunctionLibraries/PubnubJsonUtilities.cpp b/Source/PubnubLibrary/Private/FunctionLibraries/PubnubJsonUtilities.cpp index 500e917..5862ba4 100644 --- a/Source/PubnubLibrary/Private/FunctionLibraries/PubnubJsonUtilities.cpp +++ b/Source/PubnubLibrary/Private/FunctionLibraries/PubnubJsonUtilities.cpp @@ -250,8 +250,10 @@ void UPubnubJsonUtilities::ListUsersFromChannelJsonToData(FString ResponseJson, Result.ErrorMessage = "Failed to parse Response"; return; } - - Result = GetOperationResultFromJson(JsonObject); + FPubnubOperationResult ResultFromJson = GetOperationResultFromJson(JsonObject); + Result.ErrorMessage = ResultFromJson.ErrorMessage; + //Override status only if it was 0. Status could be set before in case of server error. + Result.Status = Result.Status == 0 ? ResultFromJson.Status : Result.Status; Result.Error = Result.Status != 200; JsonObject->TryGetNumberField(ANSI_TO_TCHAR("occupancy"), Data.Occupancy); diff --git a/Source/PubnubLibrary/Private/PubnubInternalMacros.h b/Source/PubnubLibrary/Private/PubnubInternalMacros.h index ec8e5d4..96ba3c9 100644 --- a/Source/PubnubLibrary/Private/PubnubInternalMacros.h +++ b/Source/PubnubLibrary/Private/PubnubInternalMacros.h @@ -63,7 +63,6 @@ * - Log an error message to the output log * - Invoke the provided delegate with a failure result and optional additional arguments * - Immediately return from the calling function - * */ #define PUBNUB_ENSURE_USER_ID_IS_SET(Delegate, ...) \ do { \ @@ -75,13 +74,30 @@ } \ } while (false) +/** + * Verifies that provided condition is met. + * + * If the condition is not met, this macro will: + * - Log an error message to the output log + * - Invoke the provided delegate with a failure result and optional additional arguments + * - Immediately return from the calling function + */ +#define PUBNUB_ENSURE_CONDITION(Condition, ErrorMessage, Delegate, ...) \ + do { \ + if (!(Condition)) \ + { \ + PubnubError(FString::Printf(TEXT("[%s]: %s Aborting operation."), *UPubnubUtilities::GetNameFromFunctionMacro(ANSI_TO_TCHAR(__FUNCTION__)), ErrorMessage)); \ + UPubnubUtilities::CallPubnubDelegateWithInvalidArgumentResult(Delegate, ErrorMessage, ##__VA_ARGS__); \ + return; \ + } \ + } while (false) + /** * Verifies that a valid Pubnub user ID has been set before continuing. * * If the user ID is not set, this macro will: * - Log an error message to the output log * - Immediately return from the calling function - * */ #define PUBNUB_RETURN_IF_USER_ID_NOT_SET(...) \ do { \ @@ -135,4 +151,4 @@ PubnubError(FString::Printf(TEXT("[%s]: %s field can't be empty. Aborting operation."), *UPubnubUtilities::GetNameFromFunctionMacro(ANSI_TO_TCHAR(__FUNCTION__)), TEXT(#Field)), EPubnubErrorType::PET_Warning); \ return __VA_ARGS__; \ } \ - } while (false) \ No newline at end of file + } while (false) diff --git a/Source/PubnubLibrary/Private/PubnubSubsystem.cpp b/Source/PubnubLibrary/Private/PubnubSubsystem.cpp index 22bb4bf..3c414fe 100644 --- a/Source/PubnubLibrary/Private/PubnubSubsystem.cpp +++ b/Source/PubnubLibrary/Private/PubnubSubsystem.cpp @@ -1880,6 +1880,8 @@ void UPubnubSubsystem::ListUsersFromChannel_priv(FString Channel, FOnListUsersFr { PUBNUB_ENSURE_USER_ID_IS_SET(ListUsersFromChannelResponse, FPubnubListUsersFromChannelWrapper()); PUBNUB_ENSURE_FIELD_NOT_EMPTY(Channel, ListUsersFromChannelResponse, FPubnubListUsersFromChannelWrapper()); + PUBNUB_ENSURE_CONDITION(ListUsersFromChannelSettings.Limit >= 0, TEXT("Limit can't be below 0."), ListUsersFromChannelResponse, FPubnubListUsersFromChannelWrapper()); + PUBNUB_ENSURE_CONDITION(ListUsersFromChannelSettings.Offset >= 0, TEXT("Offset can't be below 0."), ListUsersFromChannelResponse, FPubnubListUsersFromChannelWrapper()); //Set all options from ListUsersFromChannelSettings FUTF8StringHolder ChannelHolder(Channel); @@ -1895,8 +1897,17 @@ void UPubnubSubsystem::ListUsersFromChannel_priv(FString Channel, FOnListUsersFr FString JsonResponse = GetLastResponse(ctx_pub); - //Execute provided delegate with results FPubnubOperationResult Result; + + //If response is empty, there was server error. + if(JsonResponse.IsEmpty()) + { + JsonResponse = UPubnubUtilities::PubnubGetLastServerHttpResponse(ctx_pub); + //Presence api doesn't provide status code in the response, so we need to get it manually + Result.Status = pubnub_last_http_code(ctx_pub); + } + + //Execute provided delegate with results FPubnubListUsersFromChannelWrapper Data; UPubnubJsonUtilities::ListUsersFromChannelJsonToData(JsonResponse, Result, Data); UPubnubUtilities::CallPubnubDelegate(ListUsersFromChannelResponse, Result, Data); @@ -2867,6 +2878,8 @@ void UPubnubSubsystem::HereNowUESettingsToPubnubHereNowOptions(FPubnubListUsersF PubnubHereNowOptions.disable_uuids = HereNowSettings.DisableUserID; PubnubHereNowOptions.state = HereNowSettings.State; HereNowSettings.ChannelGroup.IsEmpty() ? PubnubHereNowOptions.channel_group = NULL : nullptr; + PubnubHereNowOptions.limit = HereNowSettings.Limit; + PubnubHereNowOptions.offset = HereNowSettings.Offset; } void UPubnubSubsystem::SetStateUESettingsToPubnubSetStateOptions(FPubnubSetStateSettings& SetStateSettings, pubnub_set_state_options& PubnubSetStateOptions) diff --git a/Source/PubnubLibrary/Public/PubnubStructLibrary.h b/Source/PubnubLibrary/Public/PubnubStructLibrary.h index 72f0f28..5e52b55 100644 --- a/Source/PubnubLibrary/Public/PubnubStructLibrary.h +++ b/Source/PubnubLibrary/Public/PubnubStructLibrary.h @@ -83,6 +83,8 @@ struct FPubnubListUsersFromChannelSettings UPROPERTY(BlueprintReadWrite, VisibleAnywhere, Category = "Pubnub") bool DisableUserID = true; //If true (and if DisableUserID is false), will give associated state alongside user info. UPROPERTY(BlueprintReadWrite, VisibleAnywhere, Category = "Pubnub") bool State = false; + UPROPERTY(BlueprintReadWrite, VisibleAnywhere, Category = "Pubnub") int Limit = 1000; + UPROPERTY(BlueprintReadWrite, VisibleAnywhere, Category = "Pubnub") int Offset = 0; }; USTRUCT(BlueprintType) diff --git a/Source/PubnubLibraryTests/Private/Tests/PubnubPresenceTests.cpp b/Source/PubnubLibraryTests/Private/Tests/PubnubPresenceTests.cpp index 8892f0d..b39b48d 100644 --- a/Source/PubnubLibraryTests/Private/Tests/PubnubPresenceTests.cpp +++ b/Source/PubnubLibraryTests/Private/Tests/PubnubPresenceTests.cpp @@ -19,6 +19,9 @@ IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(FPubnubListUsersFromChannelTest, FPubnub IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(FPubnubListUserSubscribedChannelsTest, FPubnubAutomationTestBase, "Pubnub.Integration.Presence.ListUserSubscribedChannels", EAutomationTestFlags::EditorContext | EAutomationTestFlags::ProductFilter); IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(FPubnubChannelSetGetStateTest, FPubnubAutomationTestBase, "Pubnub.Integration.Presence.SetGetState", EAutomationTestFlags::EditorContext | EAutomationTestFlags::ProductFilter); IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(FPubnubChannelSetGetStateForMultipleTest, FPubnubAutomationTestBase, "Pubnub.Integration.Presence.SetGetStateMultipleChannels", EAutomationTestFlags::EditorContext | EAutomationTestFlags::ProductFilter); +IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(FPubnubListUsersFromChannelWithLimitTest, FPubnubAutomationTestBase, "Pubnub.Integration.Presence.ListUsersFromChannelWithLimit", EAutomationTestFlags::EditorContext | EAutomationTestFlags::ProductFilter); +IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(FPubnubListUsersFromChannelWithOffsetTest, FPubnubAutomationTestBase, "Pubnub.Integration.Presence.ListUsersFromChannelWithOffset", EAutomationTestFlags::EditorContext | EAutomationTestFlags::ProductFilter); +IMPLEMENT_CUSTOM_SIMPLE_AUTOMATION_TEST(FPubnubListUsersFromChannelWithLimitAndOffsetTest, FPubnubAutomationTestBase, "Pubnub.Integration.Presence.ListUsersFromChannelWithLimitAndOffset", EAutomationTestFlags::EditorContext | EAutomationTestFlags::ProductFilter); bool FPubnubListUsersFromChannelTest::RunTest(const FString& Parameters) { @@ -992,5 +995,597 @@ bool FPubnubChannelSetGetStateForMultipleTest::RunTest(const FString& Parameters return true; } +bool FPubnubListUsersFromChannelWithLimitTest::RunTest(const FString& Parameters) +{ + // Initial variables + const FString TestUserID = SDK_PREFIX + "test_user_limit"; + const FString TestChannelName = SDK_PREFIX + "test_channel_limit"; + + TSharedPtr bListUsersOperationDone = MakeShared(false); + TSharedPtr bListUsersOperationSuccess = MakeShared(false); + TSharedPtr> CurrentListedUserIDs = MakeShared>(); + TSharedPtr CurrentOccupancy = MakeShared(0); + + TSharedPtr bSubscribeToChannelDone = MakeShared(false); + TSharedPtr bUnsubscribeFromChannelDone = MakeShared(false); + + if (!InitTest()) + { + AddError("TestInitialization failed for FPubnubListUsersFromChannelWithLimitTest"); + return false; + } + + // General error handler + PubnubSubsystem->OnPubnubErrorNative.AddLambda([this](FString ErrorMessage, EPubnubErrorType ErrorType) + { + AddError(FString::Printf(TEXT("General Pubnub Error in FPubnubListUsersFromChannelWithLimitTest: %s, Type: %d"), *ErrorMessage, ErrorType)); + }); + + // ListUsersFromChannel callback handler + FOnListUsersFromChannelResponseNative ListUsersCallback; + ListUsersCallback.BindLambda([this, bListUsersOperationDone, bListUsersOperationSuccess, CurrentListedUserIDs, CurrentOccupancy](FPubnubOperationResult Result, FPubnubListUsersFromChannelWrapper ResponseData) + { + *bListUsersOperationDone = true; + CurrentListedUserIDs->Empty(); + *CurrentOccupancy = ResponseData.Occupancy; + if (Result.Status == 200) + { + *bListUsersOperationSuccess = true; + ResponseData.UsersState.GetKeys(*CurrentListedUserIDs); + } + else + { + *bListUsersOperationSuccess = false; + AddError(FString::Printf(TEXT("ListUsersFromChannel failed. Status: %d"), Result.Status)); + } + }); + + // Create subscribe result callback + FOnSubscribeOperationResponseNative SubscribeToChannelCallback; + SubscribeToChannelCallback.BindLambda([this, bSubscribeToChannelDone](const FPubnubOperationResult& Result) + { + *bSubscribeToChannelDone = true; + TestFalse("SubscribeToChannel operation should not have failed", Result.Error); + TestEqual("SubscribeToChannel HTTP status should be 200", Result.Status, 200); + + if (Result.Error) + { + AddError(FString::Printf(TEXT("SubscribeToChannel failed with error: %s"), *Result.ErrorMessage)); + } + }); + + // Create unsubscribe result callback + FOnSubscribeOperationResponseNative UnsubscribeFromChannelCallback; + UnsubscribeFromChannelCallback.BindLambda([this, bUnsubscribeFromChannelDone](const FPubnubOperationResult& Result) + { + *bUnsubscribeFromChannelDone = true; + TestFalse("UnsubscribeFromChannel operation should not have failed", Result.Error); + TestEqual("UnsubscribeFromChannel HTTP status should be 200", Result.Status, 200); + + if (Result.Error) + { + AddError(FString::Printf(TEXT("UnsubscribeFromChannel failed with error: %s"), *Result.ErrorMessage)); + } + }); + + // Set UserID first + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, TestUserID]() + { + PubnubSubsystem->SetUserID(TestUserID); + }, 0.1f)); + + // Step 1: Subscribe to channel + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, TestChannelName, SubscribeToChannelCallback, bSubscribeToChannelDone]() + { + *bSubscribeToChannelDone = false; + PubnubSubsystem->SubscribeToChannel(TestChannelName, SubscribeToChannelCallback); + }, 0.1f)); + + // Wait until subscribe to channel result is received + ADD_LATENT_AUTOMATION_COMMAND(FWaitUntilLatentCommand([bSubscribeToChannelDone]() -> bool { + return *bSubscribeToChannelDone; + }, MAX_WAIT_TIME)); + + // Check whether subscribe to channel result was received + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, bSubscribeToChannelDone]() + { + if(!*bSubscribeToChannelDone) + { + AddError("SubscribeToChannel result callback was not received"); + } + }, 0.1f)); + + // Step 2: List users with Limit=1 + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, TestChannelName, ListUsersCallback, bListUsersOperationDone, bListUsersOperationSuccess]() + { + *bListUsersOperationDone = false; + *bListUsersOperationSuccess = false; + FPubnubListUsersFromChannelSettings Settings; + Settings.DisableUserID = false; + Settings.Limit = 1; + PubnubSubsystem->ListUsersFromChannel(TestChannelName, ListUsersCallback, Settings); + }, 0.1f)); + ADD_LATENT_AUTOMATION_COMMAND(FWaitUntilLatentCommand([bListUsersOperationDone]() { return *bListUsersOperationDone; }, MAX_WAIT_TIME)); + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, TestUserID, CurrentListedUserIDs, bListUsersOperationSuccess, CurrentOccupancy]() + { + TestTrue("ListUsersFromChannel with Limit=1 was successful", *bListUsersOperationSuccess); + if (*bListUsersOperationSuccess) + { + TestEqual("Should return maximum 1 user when Limit=1", CurrentListedUserIDs->Num(), 1); + TestTrue(FString::Printf(TEXT("TestUserID '%s' should be in the limited list"), *TestUserID), CurrentListedUserIDs->Contains(TestUserID)); + TestEqual("Occupancy should still be 1", *CurrentOccupancy, 1); + } + }, 0.1f)); + + // Step 3: List users with Limit=0 (should use default limit of 1000) + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, TestChannelName, ListUsersCallback, bListUsersOperationDone, bListUsersOperationSuccess]() + { + *bListUsersOperationDone = false; + *bListUsersOperationSuccess = false; + FPubnubListUsersFromChannelSettings Settings; + Settings.DisableUserID = false; + Settings.Limit = 0; + PubnubSubsystem->ListUsersFromChannel(TestChannelName, ListUsersCallback, Settings); + }, 0.1f)); + ADD_LATENT_AUTOMATION_COMMAND(FWaitUntilLatentCommand([bListUsersOperationDone]() { return *bListUsersOperationDone; }, MAX_WAIT_TIME)); + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, TestUserID, CurrentListedUserIDs, bListUsersOperationSuccess, CurrentOccupancy]() + { + TestTrue("ListUsersFromChannel with Limit=0 was successful", *bListUsersOperationSuccess); + if (*bListUsersOperationSuccess) + { + TestEqual("Should return 1 user when Limit=0 (uses default limit)", CurrentListedUserIDs->Num(), 1); + TestTrue(FString::Printf(TEXT("TestUserID '%s' should be in the list"), *TestUserID), CurrentListedUserIDs->Contains(TestUserID)); + TestEqual("Occupancy should be 1", *CurrentOccupancy, 1); + } + }, 0.1f)); + + // Step 4: List users with default limit (should work normally) + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, TestChannelName, ListUsersCallback, bListUsersOperationDone, bListUsersOperationSuccess]() + { + *bListUsersOperationDone = false; + *bListUsersOperationSuccess = false; + FPubnubListUsersFromChannelSettings Settings; + Settings.DisableUserID = false; + // Using default Limit (1000) + PubnubSubsystem->ListUsersFromChannel(TestChannelName, ListUsersCallback, Settings); + }, 0.1f)); + ADD_LATENT_AUTOMATION_COMMAND(FWaitUntilLatentCommand([bListUsersOperationDone]() { return *bListUsersOperationDone; }, MAX_WAIT_TIME)); + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, TestUserID, CurrentListedUserIDs, bListUsersOperationSuccess, CurrentOccupancy]() + { + TestTrue("ListUsersFromChannel with default limit was successful", *bListUsersOperationSuccess); + if (*bListUsersOperationSuccess) + { + TestEqual("Should return 1 user with default limit", CurrentListedUserIDs->Num(), 1); + TestTrue(FString::Printf(TEXT("TestUserID '%s' should be in the list"), *TestUserID), CurrentListedUserIDs->Contains(TestUserID)); + } + }, 0.1f)); + + // Step 5: Unsubscribe from channel + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, TestChannelName, UnsubscribeFromChannelCallback, bUnsubscribeFromChannelDone]() + { + *bUnsubscribeFromChannelDone = false; + PubnubSubsystem->UnsubscribeFromChannel(TestChannelName, UnsubscribeFromChannelCallback); + }, 0.1f)); + + // Wait until unsubscribe from channel result is received + ADD_LATENT_AUTOMATION_COMMAND(FWaitUntilLatentCommand([bUnsubscribeFromChannelDone]() -> bool { + return *bUnsubscribeFromChannelDone; + }, MAX_WAIT_TIME)); + + // Check whether unsubscribe from channel result was received + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, bUnsubscribeFromChannelDone]() + { + if(!*bUnsubscribeFromChannelDone) + { + AddError("UnsubscribeFromChannel result callback was not received"); + } + }, 0.1f)); + + CleanUp(); + return true; +} + +bool FPubnubListUsersFromChannelWithOffsetTest::RunTest(const FString& Parameters) +{ + // Initial variables + const FString TestUserID = SDK_PREFIX + "test_user_offset"; + const FString TestChannelName = SDK_PREFIX + "test_channel_offset"; + + TSharedPtr bListUsersOperationDone = MakeShared(false); + TSharedPtr bListUsersOperationSuccess = MakeShared(false); + TSharedPtr> CurrentListedUserIDs = MakeShared>(); + TSharedPtr CurrentOccupancy = MakeShared(0); + + TSharedPtr bSubscribeToChannelDone = MakeShared(false); + TSharedPtr bUnsubscribeFromChannelDone = MakeShared(false); + + if (!InitTest()) + { + AddError("TestInitialization failed for FPubnubListUsersFromChannelWithOffsetTest"); + return false; + } + + // General error handler + PubnubSubsystem->OnPubnubErrorNative.AddLambda([this](FString ErrorMessage, EPubnubErrorType ErrorType) + { + AddError(FString::Printf(TEXT("General Pubnub Error in FPubnubListUsersFromChannelWithOffsetTest: %s, Type: %d"), *ErrorMessage, ErrorType)); + }); + + // ListUsersFromChannel callback handler + FOnListUsersFromChannelResponseNative ListUsersCallback; + ListUsersCallback.BindLambda([this, bListUsersOperationDone, bListUsersOperationSuccess, CurrentListedUserIDs, CurrentOccupancy](FPubnubOperationResult Result, FPubnubListUsersFromChannelWrapper ResponseData) + { + *bListUsersOperationDone = true; + CurrentListedUserIDs->Empty(); + *CurrentOccupancy = ResponseData.Occupancy; + if (Result.Status == 200) + { + *bListUsersOperationSuccess = true; + ResponseData.UsersState.GetKeys(*CurrentListedUserIDs); + } + else + { + *bListUsersOperationSuccess = false; + AddError(FString::Printf(TEXT("ListUsersFromChannel failed. Status: %d"), Result.Status)); + } + }); + + // Create subscribe result callback + FOnSubscribeOperationResponseNative SubscribeToChannelCallback; + SubscribeToChannelCallback.BindLambda([this, bSubscribeToChannelDone](const FPubnubOperationResult& Result) + { + *bSubscribeToChannelDone = true; + TestFalse("SubscribeToChannel operation should not have failed", Result.Error); + TestEqual("SubscribeToChannel HTTP status should be 200", Result.Status, 200); + + if (Result.Error) + { + AddError(FString::Printf(TEXT("SubscribeToChannel failed with error: %s"), *Result.ErrorMessage)); + } + }); + + // Create unsubscribe result callback + FOnSubscribeOperationResponseNative UnsubscribeFromChannelCallback; + UnsubscribeFromChannelCallback.BindLambda([this, bUnsubscribeFromChannelDone](const FPubnubOperationResult& Result) + { + *bUnsubscribeFromChannelDone = true; + TestFalse("UnsubscribeFromChannel operation should not have failed", Result.Error); + TestEqual("UnsubscribeFromChannel HTTP status should be 200", Result.Status, 200); + + if (Result.Error) + { + AddError(FString::Printf(TEXT("UnsubscribeFromChannel failed with error: %s"), *Result.ErrorMessage)); + } + }); + + // Set UserID first + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, TestUserID]() + { + PubnubSubsystem->SetUserID(TestUserID); + }, 0.1f)); + + // Step 1: Subscribe to channel + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, TestChannelName, SubscribeToChannelCallback, bSubscribeToChannelDone]() + { + *bSubscribeToChannelDone = false; + PubnubSubsystem->SubscribeToChannel(TestChannelName, SubscribeToChannelCallback); + }, 0.1f)); + + // Wait until subscribe to channel result is received + ADD_LATENT_AUTOMATION_COMMAND(FWaitUntilLatentCommand([bSubscribeToChannelDone]() -> bool { + return *bSubscribeToChannelDone; + }, MAX_WAIT_TIME)); + + // Check whether subscribe to channel result was received + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, bSubscribeToChannelDone]() + { + if(!*bSubscribeToChannelDone) + { + AddError("SubscribeToChannel result callback was not received"); + } + }, 0.1f)); + + // Step 2: List users with Offset=0 (should return user) + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, TestChannelName, ListUsersCallback, bListUsersOperationDone, bListUsersOperationSuccess]() + { + *bListUsersOperationDone = false; + *bListUsersOperationSuccess = false; + FPubnubListUsersFromChannelSettings Settings; + Settings.DisableUserID = false; + Settings.Offset = 0; + PubnubSubsystem->ListUsersFromChannel(TestChannelName, ListUsersCallback, Settings); + }, 0.1f)); + ADD_LATENT_AUTOMATION_COMMAND(FWaitUntilLatentCommand([bListUsersOperationDone]() { return *bListUsersOperationDone; }, MAX_WAIT_TIME)); + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, TestUserID, CurrentListedUserIDs, bListUsersOperationSuccess, CurrentOccupancy]() + { + TestTrue("ListUsersFromChannel with Offset=0 was successful", *bListUsersOperationSuccess); + if (*bListUsersOperationSuccess) + { + TestEqual("Should return 1 user when Offset=0", CurrentListedUserIDs->Num(), 1); + TestTrue(FString::Printf(TEXT("TestUserID '%s' should be in the list"), *TestUserID), CurrentListedUserIDs->Contains(TestUserID)); + TestEqual("Occupancy should be 1", *CurrentOccupancy, 1); + } + }, 0.1f)); + + // Step 3: List users with Offset=1 (should skip the only user and return empty list) + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, TestChannelName, ListUsersCallback, bListUsersOperationDone, bListUsersOperationSuccess]() + { + *bListUsersOperationDone = false; + *bListUsersOperationSuccess = false; + FPubnubListUsersFromChannelSettings Settings; + Settings.DisableUserID = false; + Settings.Offset = 1; + PubnubSubsystem->ListUsersFromChannel(TestChannelName, ListUsersCallback, Settings); + }, 0.1f)); + ADD_LATENT_AUTOMATION_COMMAND(FWaitUntilLatentCommand([bListUsersOperationDone]() { return *bListUsersOperationDone; }, MAX_WAIT_TIME)); + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, CurrentListedUserIDs, bListUsersOperationSuccess, CurrentOccupancy]() + { + TestTrue("ListUsersFromChannel with Offset=1 was successful", *bListUsersOperationSuccess); + if (*bListUsersOperationSuccess) + { + TestEqual("Should return 0 users when Offset=1 (skipping the only user)", CurrentListedUserIDs->Num(), 0); + TestEqual("Occupancy should still be 1 (indicates total users, not affected by offset)", *CurrentOccupancy, 1); + } + }, 0.1f)); + + // Step 4: List users with Offset=10 (large offset, should return empty list) + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, TestChannelName, ListUsersCallback, bListUsersOperationDone, bListUsersOperationSuccess]() + { + *bListUsersOperationDone = false; + *bListUsersOperationSuccess = false; + FPubnubListUsersFromChannelSettings Settings; + Settings.DisableUserID = false; + Settings.Offset = 10; + PubnubSubsystem->ListUsersFromChannel(TestChannelName, ListUsersCallback, Settings); + }, 0.1f)); + ADD_LATENT_AUTOMATION_COMMAND(FWaitUntilLatentCommand([bListUsersOperationDone]() { return *bListUsersOperationDone; }, MAX_WAIT_TIME)); + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, CurrentListedUserIDs, bListUsersOperationSuccess]() + { + TestTrue("ListUsersFromChannel with Offset=10 was successful", *bListUsersOperationSuccess); + if (*bListUsersOperationSuccess) + { + TestEqual("Should return 0 users when Offset exceeds available users", CurrentListedUserIDs->Num(), 0); + } + }, 0.1f)); + + // Step 5: Unsubscribe from channel + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, TestChannelName, UnsubscribeFromChannelCallback, bUnsubscribeFromChannelDone]() + { + *bUnsubscribeFromChannelDone = false; + PubnubSubsystem->UnsubscribeFromChannel(TestChannelName, UnsubscribeFromChannelCallback); + }, 0.1f)); + + // Wait until unsubscribe from channel result is received + ADD_LATENT_AUTOMATION_COMMAND(FWaitUntilLatentCommand([bUnsubscribeFromChannelDone]() -> bool { + return *bUnsubscribeFromChannelDone; + }, MAX_WAIT_TIME)); + + // Check whether unsubscribe from channel result was received + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, bUnsubscribeFromChannelDone]() + { + if(!*bUnsubscribeFromChannelDone) + { + AddError("UnsubscribeFromChannel result callback was not received"); + } + }, 0.1f)); + + CleanUp(); + return true; +} + +bool FPubnubListUsersFromChannelWithLimitAndOffsetTest::RunTest(const FString& Parameters) +{ + // Initial variables + const FString TestUserID = SDK_PREFIX + "test_user_limit_offset"; + const FString TestChannelName = SDK_PREFIX + "test_channel_limit_offset"; + + TSharedPtr bListUsersOperationDone = MakeShared(false); + TSharedPtr bListUsersOperationSuccess = MakeShared(false); + TSharedPtr> CurrentListedUserIDs = MakeShared>(); + TSharedPtr CurrentOccupancy = MakeShared(0); + + TSharedPtr bSubscribeToChannelDone = MakeShared(false); + TSharedPtr bUnsubscribeFromChannelDone = MakeShared(false); + + if (!InitTest()) + { + AddError("TestInitialization failed for FPubnubListUsersFromChannelWithLimitAndOffsetTest"); + return false; + } + + // General error handler + PubnubSubsystem->OnPubnubErrorNative.AddLambda([this](FString ErrorMessage, EPubnubErrorType ErrorType) + { + AddError(FString::Printf(TEXT("General Pubnub Error in FPubnubListUsersFromChannelWithLimitAndOffsetTest: %s, Type: %d"), *ErrorMessage, ErrorType)); + }); + + // ListUsersFromChannel callback handler + FOnListUsersFromChannelResponseNative ListUsersCallback; + ListUsersCallback.BindLambda([this, bListUsersOperationDone, bListUsersOperationSuccess, CurrentListedUserIDs, CurrentOccupancy](FPubnubOperationResult Result, FPubnubListUsersFromChannelWrapper ResponseData) + { + *bListUsersOperationDone = true; + CurrentListedUserIDs->Empty(); + *CurrentOccupancy = ResponseData.Occupancy; + if (Result.Status == 200) + { + *bListUsersOperationSuccess = true; + ResponseData.UsersState.GetKeys(*CurrentListedUserIDs); + } + else + { + *bListUsersOperationSuccess = false; + AddError(FString::Printf(TEXT("ListUsersFromChannel failed. Status: %d"), Result.Status)); + } + }); + + // Create subscribe result callback + FOnSubscribeOperationResponseNative SubscribeToChannelCallback; + SubscribeToChannelCallback.BindLambda([this, bSubscribeToChannelDone](const FPubnubOperationResult& Result) + { + *bSubscribeToChannelDone = true; + TestFalse("SubscribeToChannel operation should not have failed", Result.Error); + TestEqual("SubscribeToChannel HTTP status should be 200", Result.Status, 200); + + if (Result.Error) + { + AddError(FString::Printf(TEXT("SubscribeToChannel failed with error: %s"), *Result.ErrorMessage)); + } + }); + + // Create unsubscribe result callback + FOnSubscribeOperationResponseNative UnsubscribeFromChannelCallback; + UnsubscribeFromChannelCallback.BindLambda([this, bUnsubscribeFromChannelDone](const FPubnubOperationResult& Result) + { + *bUnsubscribeFromChannelDone = true; + TestFalse("UnsubscribeFromChannel operation should not have failed", Result.Error); + TestEqual("UnsubscribeFromChannel HTTP status should be 200", Result.Status, 200); + + if (Result.Error) + { + AddError(FString::Printf(TEXT("UnsubscribeFromChannel failed with error: %s"), *Result.ErrorMessage)); + } + }); + + // Set UserID first + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, TestUserID]() + { + PubnubSubsystem->SetUserID(TestUserID); + }, 0.1f)); + + // Step 1: Subscribe to channel + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, TestChannelName, SubscribeToChannelCallback, bSubscribeToChannelDone]() + { + *bSubscribeToChannelDone = false; + PubnubSubsystem->SubscribeToChannel(TestChannelName, SubscribeToChannelCallback); + }, 0.1f)); + + // Wait until subscribe to channel result is received + ADD_LATENT_AUTOMATION_COMMAND(FWaitUntilLatentCommand([bSubscribeToChannelDone]() -> bool { + return *bSubscribeToChannelDone; + }, MAX_WAIT_TIME)); + + // Check whether subscribe to channel result was received + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, bSubscribeToChannelDone]() + { + if(!*bSubscribeToChannelDone) + { + AddError("SubscribeToChannel result callback was not received"); + } + }, 0.1f)); + + // Step 2: List users with Limit=1 and Offset=0 (pagination: first page with 1 item) + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, TestChannelName, ListUsersCallback, bListUsersOperationDone, bListUsersOperationSuccess]() + { + *bListUsersOperationDone = false; + *bListUsersOperationSuccess = false; + FPubnubListUsersFromChannelSettings Settings; + Settings.DisableUserID = false; + Settings.Limit = 1; + Settings.Offset = 0; + PubnubSubsystem->ListUsersFromChannel(TestChannelName, ListUsersCallback, Settings); + }, 0.1f)); + ADD_LATENT_AUTOMATION_COMMAND(FWaitUntilLatentCommand([bListUsersOperationDone]() { return *bListUsersOperationDone; }, MAX_WAIT_TIME)); + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, TestUserID, CurrentListedUserIDs, bListUsersOperationSuccess, CurrentOccupancy]() + { + TestTrue("ListUsersFromChannel with Limit=1, Offset=0 was successful", *bListUsersOperationSuccess); + if (*bListUsersOperationSuccess) + { + TestEqual("Should return 1 user with Limit=1, Offset=0", CurrentListedUserIDs->Num(), 1); + TestTrue(FString::Printf(TEXT("TestUserID '%s' should be in the first page"), *TestUserID), CurrentListedUserIDs->Contains(TestUserID)); + TestEqual("Occupancy should be 1", *CurrentOccupancy, 1); + } + }, 0.1f)); + + // Step 3: List users with Limit=1 and Offset=1 (pagination: second page should be empty) + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, TestChannelName, ListUsersCallback, bListUsersOperationDone, bListUsersOperationSuccess]() + { + *bListUsersOperationDone = false; + *bListUsersOperationSuccess = false; + FPubnubListUsersFromChannelSettings Settings; + Settings.DisableUserID = false; + Settings.Limit = 1; + Settings.Offset = 1; + PubnubSubsystem->ListUsersFromChannel(TestChannelName, ListUsersCallback, Settings); + }, 0.1f)); + ADD_LATENT_AUTOMATION_COMMAND(FWaitUntilLatentCommand([bListUsersOperationDone]() { return *bListUsersOperationDone; }, MAX_WAIT_TIME)); + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, CurrentListedUserIDs, bListUsersOperationSuccess, CurrentOccupancy]() + { + TestTrue("ListUsersFromChannel with Limit=1, Offset=1 was successful", *bListUsersOperationSuccess); + if (*bListUsersOperationSuccess) + { + TestEqual("Should return 0 users with Limit=1, Offset=1 (second page is empty)", CurrentListedUserIDs->Num(), 0); + TestEqual("Occupancy should still be 1", *CurrentOccupancy, 1); + } + }, 0.1f)); + + // Step 4: List users with Limit=0 and Offset=0 (Limit=0 should use default limit of 1000) + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, TestChannelName, ListUsersCallback, bListUsersOperationDone, bListUsersOperationSuccess]() + { + *bListUsersOperationDone = false; + *bListUsersOperationSuccess = false; + FPubnubListUsersFromChannelSettings Settings; + Settings.DisableUserID = false; + Settings.Limit = 0; + Settings.Offset = 0; + PubnubSubsystem->ListUsersFromChannel(TestChannelName, ListUsersCallback, Settings); + }, 0.1f)); + ADD_LATENT_AUTOMATION_COMMAND(FWaitUntilLatentCommand([bListUsersOperationDone]() { return *bListUsersOperationDone; }, MAX_WAIT_TIME)); + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, TestUserID, CurrentListedUserIDs, bListUsersOperationSuccess, CurrentOccupancy]() + { + TestTrue("ListUsersFromChannel with Limit=0, Offset=0 was successful", *bListUsersOperationSuccess); + if (*bListUsersOperationSuccess) + { + TestEqual("Should return 1 user when Limit=0 (uses default limit), Offset=0", CurrentListedUserIDs->Num(), 1); + TestTrue(FString::Printf(TEXT("TestUserID '%s' should be in the list"), *TestUserID), CurrentListedUserIDs->Contains(TestUserID)); + TestEqual("Occupancy should be 1", *CurrentOccupancy, 1); + } + }, 0.1f)); + + // Step 5: List users with Limit=5 and Offset=2 (offset beyond available users) + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, TestChannelName, ListUsersCallback, bListUsersOperationDone, bListUsersOperationSuccess]() + { + *bListUsersOperationDone = false; + *bListUsersOperationSuccess = false; + FPubnubListUsersFromChannelSettings Settings; + Settings.DisableUserID = false; + Settings.Limit = 5; + Settings.Offset = 2; + PubnubSubsystem->ListUsersFromChannel(TestChannelName, ListUsersCallback, Settings); + }, 0.1f)); + ADD_LATENT_AUTOMATION_COMMAND(FWaitUntilLatentCommand([bListUsersOperationDone]() { return *bListUsersOperationDone; }, MAX_WAIT_TIME)); + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, CurrentListedUserIDs, bListUsersOperationSuccess]() + { + TestTrue("ListUsersFromChannel with Limit=5, Offset=2 was successful", *bListUsersOperationSuccess); + if (*bListUsersOperationSuccess) + { + TestEqual("Should return 0 users when Offset exceeds available users", CurrentListedUserIDs->Num(), 0); + } + }, 0.1f)); + + // Step 6: Unsubscribe from channel + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, TestChannelName, UnsubscribeFromChannelCallback, bUnsubscribeFromChannelDone]() + { + *bUnsubscribeFromChannelDone = false; + PubnubSubsystem->UnsubscribeFromChannel(TestChannelName, UnsubscribeFromChannelCallback); + }, 0.1f)); + + // Wait until unsubscribe from channel result is received + ADD_LATENT_AUTOMATION_COMMAND(FWaitUntilLatentCommand([bUnsubscribeFromChannelDone]() -> bool { + return *bUnsubscribeFromChannelDone; + }, MAX_WAIT_TIME)); + + // Check whether unsubscribe from channel result was received + ADD_LATENT_AUTOMATION_COMMAND(FDelayedFunctionLatentCommand([this, bUnsubscribeFromChannelDone]() + { + if(!*bUnsubscribeFromChannelDone) + { + AddError("UnsubscribeFromChannel result callback was not received"); + } + }, 0.1f)); + + CleanUp(); + return true; +} + #endif // WITH_DEV_AUTOMATION_TESTS \ No newline at end of file diff --git a/Source/PubnubLibraryTests/Private/Tests/PubnubPublishTests.cpp b/Source/PubnubLibraryTests/Private/Tests/PubnubPublishTests.cpp index 2318473..0ec7589 100644 --- a/Source/PubnubLibraryTests/Private/Tests/PubnubPublishTests.cpp +++ b/Source/PubnubLibraryTests/Private/Tests/PubnubPublishTests.cpp @@ -162,7 +162,7 @@ bool FPubnubPublishMessageWithSettingsTest::RunTest(const FString& Parameters) const FString TestMessage = "\"Message from test\""; const FString TestUser = SDK_PREFIX + "test_user"; const FString TestChannel = SDK_PREFIX + "test_channel"; - const FString TestMetaData = "{\"metadata\": \"from test\"}"; + const FString TestMetaData = "{\"metadata\":\"from test\"}"; const FString TestCustomMessageType = "custom_type"; TSharedPtr TestMessageReceived = MakeShared(false); TSharedPtr TestSubscribeResultReceived = MakeShared(false); @@ -635,7 +635,7 @@ bool FPubnubPublishVariousMessageTypesTest::RunTest(const FString& Parameters) TestCases.Add(FMessageTestCase("Floating Point Number as JSON Number", "123.456")); TestCases.Add(FMessageTestCase("Boolean true as JSON Boolean", "true")); TestCases.Add(FMessageTestCase("Boolean false as JSON Boolean", "false")); - TestCases.Add(FMessageTestCase("JSON Array", "[\"element1\", 2, {\"nested_key\":\"nested_val\"}, false]")); + TestCases.Add(FMessageTestCase("JSON Array", "[\"element1\",2,{\"nested_key\":\"nested_val\"},false]")); TestCases.Add(FMessageTestCase("Empty String as JSON string", "\"\"")); TestCases.Add(FMessageTestCase("String with escapes as JSON string", "\"Text with \\\"quotes\\\", \\\\backslashes\\\\, a /slash, and a newline\\ncharacter.\"")); TestCases.Add(FMessageTestCase("JSON null", "null")); diff --git a/Source/ThirdParty/sdk/Include/core/pbpal.h b/Source/ThirdParty/sdk/Include/core/pbpal.h index c438428..6ee984f 100644 --- a/Source/ThirdParty/sdk/Include/core/pbpal.h +++ b/Source/ThirdParty/sdk/Include/core/pbpal.h @@ -37,13 +37,15 @@ enum pbpal_resolv_n_connect_result { enum pbpal_tls_result { pbtlsEstablished, pbtlsStarted, + pbtlsStartedWaitRead, + pbtlsStartedWaitWrite, pbtlsInProgress, pbtlsResourceFailure, pbtlsFailed }; /* Handles socket condition on given platform */ -enum pubnub_res pbpal_handle_socket_condition(int result, pubnub_t* pb, char const* file, int line); +enum pubnub_res pbpal_handle_socket_condition(int result, pubnub_t* pb, char const* file, int line, bool *needRead, bool* needWrite); /** Handles start of a TCP (HTTP) connection. It first handles DNS resolving for the context @p pb. If DNS is already resolved, it @@ -249,6 +251,9 @@ int pbpal_close(pubnub_t *pb); /** Sets blocking I/O option on the context for the communication */ int pbpal_set_blocking_io(pubnub_t *pb); +/** Sets user-provided TCP Keep-Alive configuration for active connection. */ +void pbpal_set_tcp_keepalive(const pubnub_t *pb); + /** Frees-up any resources allocated by the PAL for the given context. After this call, context is not safe for use by PAL any more (it is assumed it will be freed-up by the caller). diff --git a/Source/ThirdParty/sdk/Include/core/pubnub_api_types.h b/Source/ThirdParty/sdk/Include/core/pubnub_api_types.h index 606ee66..8ddd10e 100644 --- a/Source/ThirdParty/sdk/Include/core/pubnub_api_types.h +++ b/Source/ThirdParty/sdk/Include/core/pubnub_api_types.h @@ -136,7 +136,9 @@ enum pubnub_res { /** Access/Permission denied */ PNR_ACCESS_DENIED, /** No Channels in the ChannelGroup */ - PNR_GROUP_EMPTY + PNR_GROUP_EMPTY, + /** Presence API transaction reported an error */ + PNR_PRESENCE_API_ERROR }; /** 'pubnub_cancel()' return value */ diff --git a/Source/ThirdParty/sdk/Include/core/pubnub_ccore.h b/Source/ThirdParty/sdk/Include/core/pubnub_ccore.h index b3d6fa8..8735179 100644 --- a/Source/ThirdParty/sdk/Include/core/pubnub_ccore.h +++ b/Source/ThirdParty/sdk/Include/core/pubnub_ccore.h @@ -106,7 +106,9 @@ PUBNUB_EXTERN enum pubnub_res pbcc_here_now_prep(struct pbcc_context* p, const char* channel, const char* channel_group, enum pubnub_tribool disable_uuids, - enum pubnub_tribool state); + enum pubnub_tribool state, + unsigned limit, + unsigned offset); /** Prepares the Where-now operation (transaction), mostly by formatting the URI of the HTTP request. diff --git a/Source/ThirdParty/sdk/Include/core/pubnub_coreapi_ex.h b/Source/ThirdParty/sdk/Include/core/pubnub_coreapi_ex.h index f48eec6..a647d54 100644 --- a/Source/ThirdParty/sdk/Include/core/pubnub_coreapi_ex.h +++ b/Source/ThirdParty/sdk/Include/core/pubnub_coreapi_ex.h @@ -190,11 +190,17 @@ struct pubnub_here_now_options { state alongside uuid info */ bool state; + /** The maximum number of users to return. Has to + * be between 1 and 1000 users. + */ + unsigned limit; + /** Sets the offset for pagination in the here now response. */ + unsigned offset; }; /** This returns the default options for here-now transactions. Will - set `channel_group = NULL`, `disable_uuids=true` and `state = - false`. + set `channel_group = NULL`, `disable_uuids=false`, `state = + false`, `limit = 1000`, and `offset = 0`. */ PUBNUB_EXTERN struct pubnub_here_now_options pubnub_here_now_defopts(void); diff --git a/Source/ThirdParty/sdk/Include/core/pubnub_internal_common.h b/Source/ThirdParty/sdk/Include/core/pubnub_internal_common.h index 3086a73..7288bc1 100644 --- a/Source/ThirdParty/sdk/Include/core/pubnub_internal_common.h +++ b/Source/ThirdParty/sdk/Include/core/pubnub_internal_common.h @@ -197,6 +197,27 @@ struct pbntlm_context { typedef struct pbntlm_context pbntlm_ctx_t; +/** TCP Keep-Alive configuration object. */ +typedef struct pubnub_tcp_keepalive_ { + /** Whether TCP Keep-Alive should be used or not. */ + enum pubnub_tribool enabled; + + /** The time in seconds a socket needs to be @c idle before the first + keep-alive probe is sent. + */ + uint8_t time; + + /** The number of seconds that should pass between sends of + keep-alive probes if the last one wasn't acknowledged. + */ + uint8_t interval; + + /** The number of times a probe will be sent and not acknowledged + before the connection is deemed broken. + */ + uint8_t probes; +} pubnub_tcp_keepalive; + struct pubnub_options { #if PUBNUB_BLOCKING_IO_SETTABLE /** Indicates whether to use blocking I/O. Not implemented if @@ -214,6 +235,9 @@ struct pubnub_options { */ bool use_http_keep_alive : 1; + /** Per-context (because of one request per-context) TCP Keep-Alive + configuration. */ + pubnub_tcp_keepalive tcp_keepalive; #if PUBNUB_USE_IPV6 /* Connectivity type(true-Ipv6/false-Ipv4) chosen on a given context */ bool ipv6_connectivity : 1; diff --git a/Source/ThirdParty/sdk/Include/core/pubnub_pubsubapi.h b/Source/ThirdParty/sdk/Include/core/pubnub_pubsubapi.h index c435af1..b31eb0a 100644 --- a/Source/ThirdParty/sdk/Include/core/pubnub_pubsubapi.h +++ b/Source/ThirdParty/sdk/Include/core/pubnub_pubsubapi.h @@ -394,5 +394,39 @@ PUBNUB_EXTERN void pubnub_use_http_keep_alive(pubnub_t* p); */ PUBNUB_EXTERN void pubnub_dont_use_http_keep_alive(pubnub_t* p); +/** Enable the use of TCP Keep-Alive ("probes") on the context @p pb . + * + * @b Defaults: + * - @c time: @b 60 seconds + * - @c interval: @b 20 seconds + * - @c probes: @b 3 + * + * @b Important: this option works well @b only together with HTTP Keep-Alive, which + * is managed by @c pubnub_use_http_keep_alive and + * @c pubnub_dont_use_http_keep_alive. + * + * @param pb Pointer to the PubNub context which TCP KA should be enabled. + * @param time The time in seconds a socket needs to be @c idle before the + * first keep-alive probe is sent. + * @param interval The number of seconds that should pass between sends of + * keep-alive probes if the last one wasn't acknowledged. + * @param probes The number of times a probe will be sent and not acknowledged + * before the connection is deemed broken. + */ +PUBNUB_EXTERN void pubnub_use_tcp_keep_alive( + pubnub_t* pb, + uint8_t time, + uint8_t interval, + uint8_t probes); + +/** Disables the use of TCP Keep-Alive ("probes") on the context @p pb . + * + * @b Important: this option works @b only together with HTTP Keep-Alive, which + * is managed by @c pubnub_use_http_keep_alive and + * @c pubnub_dont_use_http_keep_alive. + * + * @param pb Pointer to the PubNub context which TCP KA should be disabled. + */ +PUBNUB_EXTERN void pubnub_dont_use_tcp_keep_alive(pubnub_t* pb); #endif /* !defined INC_PUBNUB_PUBSUBAPI */ diff --git a/Source/ThirdParty/sdk/Include/core/pubnub_server_limits.h b/Source/ThirdParty/sdk/Include/core/pubnub_server_limits.h index 1259f46..d0892fc 100644 --- a/Source/ThirdParty/sdk/Include/core/pubnub_server_limits.h +++ b/Source/ThirdParty/sdk/Include/core/pubnub_server_limits.h @@ -10,6 +10,11 @@ /** Default presence heartbeat value to use if user didn't set any. */ #define PUBNUB_DEFAULT_PRESENCE_HEARTBEAT_VALUE 300 +/** Default limit for here_now queries - maximum number of users returned. + Valid range is 1 to 1000. +*/ +#define PUBNUB_DEFAULT_HERE_NOW_LIMIT 1000 + /** The maximum channel name length */ #define PUBNUB_MAX_CHANNEL_NAME_LENGTH 92 diff --git a/Source/ThirdParty/sdk/Include/core/pubnub_version_internal.h b/Source/ThirdParty/sdk/Include/core/pubnub_version_internal.h index 1bfd5fc..4a7f6a1 100644 --- a/Source/ThirdParty/sdk/Include/core/pubnub_version_internal.h +++ b/Source/ThirdParty/sdk/Include/core/pubnub_version_internal.h @@ -3,7 +3,7 @@ #define INC_PUBNUB_VERSION_INTERNAL -#define PUBNUB_SDK_VERSION "5.1.6" +#define PUBNUB_SDK_VERSION "6.0.0" #endif /* !defined INC_PUBNUB_VERSION_INTERNAL */ diff --git a/Source/ThirdParty/sdk/Include/lib/md5/pbmd5.h b/Source/ThirdParty/sdk/Include/lib/md5/pbmd5.h index 76d9267..e600068 100644 --- a/Source/ThirdParty/sdk/Include/lib/md5/pbmd5.h +++ b/Source/ThirdParty/sdk/Include/lib/md5/pbmd5.h @@ -2,7 +2,6 @@ #if !defined INC_PBMD5 #define INC_PBMD5 - /** @file pbmd5.h This is the internal "MD5" Message Digest API of the Pubnub client @@ -75,4 +74,4 @@ MD5_Final((d), &M_ctx); \ } while (0) -#endif /* !defined INC_PBMD5 */ +#endif /* !defined INC_PBMD5 */ \ No newline at end of file diff --git a/Source/ThirdParty/sdk/lib/IOS/libpubnub.a b/Source/ThirdParty/sdk/lib/IOS/libpubnub.a index 5e50ca8..0770432 100644 Binary files a/Source/ThirdParty/sdk/lib/IOS/libpubnub.a and b/Source/ThirdParty/sdk/lib/IOS/libpubnub.a differ diff --git a/Source/ThirdParty/sdk/lib/MacOS/libpubnub.a b/Source/ThirdParty/sdk/lib/MacOS/libpubnub.a index a71321f..982119e 100644 Binary files a/Source/ThirdParty/sdk/lib/MacOS/libpubnub.a and b/Source/ThirdParty/sdk/lib/MacOS/libpubnub.a differ diff --git a/Source/ThirdParty/sdk/lib/arm64/libpubnub.a b/Source/ThirdParty/sdk/lib/arm64/libpubnub.a index 3432ce0..b3805c0 100644 Binary files a/Source/ThirdParty/sdk/lib/arm64/libpubnub.a and b/Source/ThirdParty/sdk/lib/arm64/libpubnub.a differ diff --git a/Source/ThirdParty/sdk/lib/linux/libpubnub.a b/Source/ThirdParty/sdk/lib/linux/libpubnub.a index 5a61710..529057a 100644 Binary files a/Source/ThirdParty/sdk/lib/linux/libpubnub.a and b/Source/ThirdParty/sdk/lib/linux/libpubnub.a differ diff --git a/Source/ThirdParty/sdk/lib/win64/pubnub.lib b/Source/ThirdParty/sdk/lib/win64/pubnub.lib index 4775c8e..2fbba52 100644 Binary files a/Source/ThirdParty/sdk/lib/win64/pubnub.lib and b/Source/ThirdParty/sdk/lib/win64/pubnub.lib differ