diff --git a/External/Xal/Source/Xal/Include/Xal/xal_version.h b/External/Xal/Source/Xal/Include/Xal/xal_version.h
index 5216e3dd..4b6ba39e 100644
--- a/External/Xal/Source/Xal/Include/Xal/xal_version.h
+++ b/External/Xal/Source/Xal/Include/Xal/xal_version.h
@@ -20,6 +20,6 @@ extern "C"
/// YYYYMMDD Date string describing the date the build was created
/// rrr QFE number (000 indicates base release)
///
-#define XAL_VERSION "2020.11.20201204.001"
+#define XAL_VERSION "2021.04.20210319.000"
}
diff --git a/Include/xsapi-c/multiplayer_c.h b/Include/xsapi-c/multiplayer_c.h
index a298a615..7527f701 100644
--- a/Include/xsapi-c/multiplayer_c.h
+++ b/Include/xsapi-c/multiplayer_c.h
@@ -3293,9 +3293,13 @@ STDAPI XblMultiplayerGetSessionAsync(
/// Gets the result of an XblMultiplayerGetSessionResult call.
///
/// The AsyncBlock for this operation.
-/// Passes back a handle to a new instance of a local multiplayer session object.
+/// Passes back a handle to a new instance of a local multiplayer session object.
/// It must be release by the caller with .
/// HRESULT return code for this API operation.
+///
+/// If the session does not exist, this API will return __HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND) and a null
+/// XblMultiplayerSessionHandle.
+///
STDAPI XblMultiplayerGetSessionResult(
_In_ XAsyncBlock* async,
_Out_ XblMultiplayerSessionHandle* handle
@@ -3326,6 +3330,10 @@ STDAPI XblMultiplayerGetSessionByHandleAsync(
/// Passes back a handle to a new instance of a local multiplayer session object.
/// It must be release by the caller with .
/// HRESULT return code for this API operation.
+///
+/// If the session does not exist, this API will return __HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND) and a null
+/// XblMultiplayerSessionHandle.
+///
STDAPI XblMultiplayerGetSessionByHandleResult(
_In_ XAsyncBlock* async,
_Out_ XblMultiplayerSessionHandle* handle
diff --git a/Include/xsapi-c/multiplayer_manager_c.h b/Include/xsapi-c/multiplayer_manager_c.h
index 80621dd2..300725d1 100644
--- a/Include/xsapi-c/multiplayer_manager_c.h
+++ b/Include/xsapi-c/multiplayer_manager_c.h
@@ -754,6 +754,7 @@ XBL_WARNING_POP
/// Changes are batched and written to the service on the next XblMultiplayerManagerDoWork().
/// All session properties and members contain updated response returned from the server
/// upon calling XblMultiplayerManagerDoWork().
+/// When attempting to join a session, the service will return HTTP_E_STATUS_BAD_REQUEST (HTTP status 400) in the event the server is full.
///
STDAPI XblMultiplayerManagerLobbySessionAddLocalUser(
_In_ XblUserHandle user
@@ -1112,6 +1113,7 @@ STDAPI XblMultiplayerManagerGameSessionSetSynchronizedHost(
/// After joining, you can set the host via XblMultiplayerManagerLobbySessionSetSynchronizedHost() if one doesn't exist.
/// Instead, if you don't need a lobby session, and if you haven't added the local users through
/// XblMultiplayerManagerLobbySessionAddLocalUser(), you can pass in the list of users via the XblMultiplayerManagerJoinGame() API.
+/// When attempting to join a session, the service will return HTTP_E_STATUS_BAD_REQUEST (HTTP status 400) in the event the server is full.
///
STDAPI XblMultiplayerManagerJoinLobby(
_In_z_ const char* handleId,
@@ -1129,6 +1131,7 @@ STDAPI XblMultiplayerManagerJoinLobby(
/// through XblMultiplayerManagerDoWork().
/// This does not migrate existing lobby session properties over to the game session.
/// After joining, you can set the properties or the host via XblMultiplayerManagerGameSessionSetSynchronized APIs.
+/// When attempting to join a session, the service will return HTTP_E_STATUS_BAD_REQUEST (HTTP status 400) in the event the server is full.
///
STDAPI XblMultiplayerManagerJoinGameFromLobby(
_In_z_ const char* sessionTemplateName
@@ -1147,6 +1150,7 @@ STDAPI XblMultiplayerManagerJoinGameFromLobby(
///
/// The result is delivered via an event of type XblMultiplayerEventType::JoinGameCompleted through XblMultiplayerManagerDoWork().
/// After joining, you can set the properties or the host via XblMultiplayerManagerGameSessionSetSynchronized APIs.
+/// When attempting to join a session, the service will return HTTP_E_STATUS_BAD_REQUEST (HTTP status 400) in the event the server is full.
///
STDAPI XblMultiplayerManagerJoinGame(
_In_z_ const char* sessionName,
diff --git a/Include/xsapi-c/social_manager_c.h b/Include/xsapi-c/social_manager_c.h
index 32da9fe8..25a84e5b 100644
--- a/Include/xsapi-c/social_manager_c.h
+++ b/Include/xsapi-c/social_manager_c.h
@@ -63,11 +63,17 @@ enum class XblPresenceFilter : uint32_t
///
/// Has played this title and is offline.
///
+ ///
+ /// This filter option requires ::TitleHistoryLevel to be set in
+ ///
TitleOffline,
///
/// Has played this title, is online but not currently playing this title.
///
+ ///
+ /// This filter option requires ::TitleHistoryLevel to be set in
+ ///
TitleOnlineOutsideTitle,
///
@@ -83,6 +89,9 @@ enum class XblPresenceFilter : uint32_t
///
/// Everyone who has played or is playing the title.
///
+ ///
+ /// This filter option requires ::TitleHistoryLevel to be set in
+ ///
AllTitle,
///
diff --git a/Source/Services/Achievements/Manager/achievements_manager_internal.cpp b/Source/Services/Achievements/Manager/achievements_manager_internal.cpp
index 359fac79..9eda12cc 100644
--- a/Source/Services/Achievements/Manager/achievements_manager_internal.cpp
+++ b/Source/Services/Achievements/Manager/achievements_manager_internal.cpp
@@ -10,9 +10,9 @@ using namespace xbox::services;
XblAchievementsManagerResult::XblAchievementsManagerResult(_In_ const XblAchievement & achievement)
: m_achievements({ achievement }),
- m_explicitCleanup(false),
m_achievementsData(nullptr),
- m_achievementsCount()
+ m_achievementsCount(),
+ m_explicitCleanup(false)
{
}
@@ -63,9 +63,9 @@ AchievementsManagerUser::AchievementsManagerUser(
_In_ User&& localUser,
_In_ const TaskQueue& queue
) noexcept :
- m_queue{ queue.DeriveWorkerQueue() },
m_xuid{ localUser.Xuid() },
- m_rtaManager{ GlobalState::Get()->RTAManager() }
+ m_rtaManager{ GlobalState::Get()->RTAManager() },
+ m_queue{ queue.DeriveWorkerQueue() }
{
// Maintain legacy RTA activation count.
m_rtaManager->Activate(localUser);
diff --git a/Source/Services/Achievements/achievement_service_internal.cpp b/Source/Services/Achievements/achievement_service_internal.cpp
index 265ae2c9..85993fcc 100644
--- a/Source/Services/Achievements/achievement_service_internal.cpp
+++ b/Source/Services/Achievements/achievement_service_internal.cpp
@@ -74,10 +74,10 @@ AchievementsService::AchievementsService(
_In_ std::shared_ptr rtaManager
) :
m_user{ std::move(user) },
- m_xboxLiveContextSettings(std::move(xboxLiveContextSettings)),
- m_appConfig(std::move(appConfig)),
- m_xboxLiveContextImpl(std::move(xboxLiveContextImpl)),
- m_rtaManager{ std::move(rtaManager) }
+ m_xboxLiveContextSettings{ std::move(xboxLiveContextSettings) },
+ m_appConfig{ std::move(appConfig) },
+ m_rtaManager{ std::move(rtaManager) },
+ m_xboxLiveContextImpl{ std::move(xboxLiveContextImpl) }
{
}
diff --git a/Source/Services/Common/xbox_live_context.cpp b/Source/Services/Common/xbox_live_context.cpp
index b5c03b29..31b262fe 100644
--- a/Source/Services/Common/xbox_live_context.cpp
+++ b/Source/Services/Common/xbox_live_context.cpp
@@ -167,8 +167,8 @@ HRESULT XblContext::Initialize(
RETURN_HR_IF_FAILED(userResult.Hresult());
#if XSAPI_NOTIFICATION_SERVICE
#if !XSAPI_UNIT_TESTS && (HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM_IS_EXTERNAL)
- m_notificationService = MakeShared(userResult.ExtractPayload(), m_xboxLiveContextSettings, rtaManager);
- m_notificationService->RegisterForSpopNotificationEvents();
+ m_notificationService = MakeShared(userResult.ExtractPayload(), globalQueue, m_xboxLiveContextSettings, rtaManager);
+ RETURN_HR_IF_FAILED(m_notificationService->Initialize());
#elif HC_PLATFORM == HC_PLATFORM_ANDROID || HC_PLATFORM == HC_PLATFORM_IOS
m_notificationService = MakeShared(userResult.ExtractPayload(), m_xboxLiveContextSettings);
#elif HC_PLATFORM == HC_PLATFORM_UWP
diff --git a/Source/Services/Common/xbox_live_context_internal.h b/Source/Services/Common/xbox_live_context_internal.h
index 01522a49..4594cdaa 100644
--- a/Source/Services/Common/xbox_live_context_internal.h
+++ b/Source/Services/Common/xbox_live_context_internal.h
@@ -169,7 +169,7 @@ private:
std::shared_ptr m_eventsService;
#endif
- XblFunctionContext m_userChangeEventToken{ 0 };
+ uint64_t m_userChangeEventToken{ 0 };
uint64_t m_xuid{ 0 };
xbox::services::User m_user;
diff --git a/Source/Services/Leaderboard/leaderboard_result.cpp b/Source/Services/Leaderboard/leaderboard_result.cpp
index f2c8c98d..719dd0ea 100644
--- a/Source/Services/Leaderboard/leaderboard_result.cpp
+++ b/Source/Services/Leaderboard/leaderboard_result.cpp
@@ -68,36 +68,39 @@ LeaderboardResult::ParseAdditionalColumns(const xsapi_internal_vectorsecond == legacy::leaderboard_stat_type::stat_other)
+ if (row.m_metadata.IsObject() && row.m_metadata.HasMember(columnName.data()))
{
- if(val.IsBool())
+ const JsonValue& val = row.m_metadata[columnName.c_str()];
+ if (stat == stats.end() || stat->second == legacy::leaderboard_stat_type::stat_other)
{
- stats[columnName] = legacy::leaderboard_stat_type::stat_boolean;
+ if (val.IsBool())
+ {
+ stats[columnName] = legacy::leaderboard_stat_type::stat_boolean;
+ }
+ else if (val.IsNumber())
+ {
+ stats[columnName] = legacy::leaderboard_stat_type::stat_double;
+ }
+ else if (val.IsString())
+ {
+ stats[columnName] = legacy::leaderboard_stat_type::stat_string;
+ }
+ else
+ {
+ stats[columnName] = legacy::leaderboard_stat_type::stat_other;
+ }
+
}
- else if (val.IsNumber())
+
+ auto columnValues = JsonUtils::SerializeJson(val);
+ if (i >= row.m_columnValues.size() - 1)
{
- stats[columnName] = legacy::leaderboard_stat_type::stat_double;
- }
- else if (val.IsString())
- {
- stats[columnName] = legacy::leaderboard_stat_type::stat_string;
+ row.m_columnValues.push_back(columnValues);
}
else
{
- stats[columnName] = legacy::leaderboard_stat_type::stat_other;
+ row.m_columnValues[i] = columnValues;
}
-
- }
-
- auto columnValues = JsonUtils::SerializeJson(val);
- if (i >= row.m_columnValues.size() - 1)
- {
- row.m_columnValues.push_back(columnValues);
- }
- else
- {
- row.m_columnValues[i] = columnValues;
}
}
}
diff --git a/Source/Services/Multiplayer/multiplayer_service.cpp b/Source/Services/Multiplayer/multiplayer_service.cpp
index d9d285a8..6791bd1d 100644
--- a/Source/Services/Multiplayer/multiplayer_service.cpp
+++ b/Source/Services/Multiplayer/multiplayer_service.cpp
@@ -2025,7 +2025,7 @@ STDAPI XblMultiplayerGetActivitiesWithPropertiesForSocialGroupResult(
size_t count{ 0 };
size_t verifiedSize{ 0 };
- for (; verifiedSize < *bufferUsed - XBL_ALIGN_SIZE; ++count)
+ for (; *bufferUsed > 0 && verifiedSize < *bufferUsed - XBL_ALIGN_SIZE; ++count)
{
verifiedSize += sizeof(XblMultiplayerActivityDetails);
verifiedSize += strlen((*ptrToBuffer)[count].CustomSessionPropertiesJson) + 1;
diff --git a/Source/Services/MultiplayerActivity/multiplayer_activity_api.cpp b/Source/Services/MultiplayerActivity/multiplayer_activity_api.cpp
index d8b48fc5..8ad6906b 100644
--- a/Source/Services/MultiplayerActivity/multiplayer_activity_api.cpp
+++ b/Source/Services/MultiplayerActivity/multiplayer_activity_api.cpp
@@ -291,7 +291,7 @@ STDAPI_(XblFunctionContext) XblMultiplayerActivityAddInviteHandler(
}
auto rtaNotificationService = std::dynamic_pointer_cast(xblContext->NotificationService());
- return rtaNotificationService->AddGameInviteHandler(GameInviteSubscription::MultiplayerActivityInviteHandler{
+ return rtaNotificationService->AddGameInviteHandler(NotificationSubscription::MultiplayerActivityInviteHandler{
[
handler, context
]
@@ -316,7 +316,7 @@ STDAPI XblMultiplayerActivityRemoveInviteHandler(
{
RETURN_HR_INVALIDARGUMENT_IF_NULL(xblContext);
auto rtaNotificationService = std::dynamic_pointer_cast(xblContext->NotificationService());
- rtaNotificationService->RemoveGameInviteHandler(token);
+ rtaNotificationService->RemoveNotificationHandler(token);
return S_OK;
}
#endif
diff --git a/Source/Services/Notification/RTA/achievement_unlock_subscription.cpp b/Source/Services/Notification/RTA/achievement_unlock_subscription.cpp
deleted file mode 100644
index d03b153d..00000000
--- a/Source/Services/Notification/RTA/achievement_unlock_subscription.cpp
+++ /dev/null
@@ -1,169 +0,0 @@
-// Copyright (c) Microsoft Corporation
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-#include "pch.h"
-#include "achievement_unlock_subscription.h"
-
-
-#if HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM_IS_EXTERNAL
-
-NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_BEGIN
-
-AchievementUnlockSubscription::AchievementUnlockSubscription(uint64_t xuid, uint32_t titleId) noexcept
-{
- Stringstream ss;
- ss << "https://notify.xboxlive.com/users/xuid(" << xuid << ")/deviceId/current/titleId/" << titleId;
- m_resourceUri = ss.str();
-}
-
-const String &
-AchievementUnlockSubscription::ResourceUri() const noexcept
-{
- return m_resourceUri;
-}
-
-XblFunctionContext
-AchievementUnlockSubscription::AddHandler(EventHandler handler) noexcept
-{
- std::lock_guard lock{ m_mutex };
- m_handlers[m_nextId] = std::move(handler);
- return m_nextId++;
-}
-
-size_t
-AchievementUnlockSubscription::RemoveHandler(XblFunctionContext id) noexcept
-{
- std::lock_guard lock{ m_mutex };
- m_handlers.erase(id);
- return m_handlers.size();
-}
-
-
-void
-AchievementUnlockSubscription::OnEvent(_In_ const JsonValue& data) noexcept
-{
- String originatingService;
- JsonUtils::ExtractJsonString(data, "service", originatingService);
- if (!data.IsObject() || originatingService != "achievements")
- {
- return;
- }
-
- // Deserialize and send invite notification
- auto deserializationResult = AchievementUnlockEvent::Deserialize(data);
- if (Succeeded(deserializationResult))
- {
- std::unique_lock lock{ m_mutex };
- auto handlers{ m_handlers };
- lock.unlock();
-
- for (auto& handler : handlers)
- {
- handler.second(deserializationResult.Payload());
- }
- }
-}
-
-void
-AchievementUnlockSubscription::OnResync() noexcept
-{
- LOGS_ERROR << __FUNCTION__ << ": Achievement Unlock event may have been discarded by RTA service";
-}
-
-AchievementUnlockEvent::AchievementUnlockEvent( AchievementUnlockEvent&& event ) noexcept:
- m_achievementId(std::move(event.m_achievementId)),
- m_achievementName(std::move(event.m_achievementName)),
- m_achievementDescription(std::move(event.m_achievementDescription)),
- m_achievementIconUri(std::move(event.m_achievementIconUri)),
- m_deepLink(event.m_deepLink)
-{
- // strings for the C API
- achievementId = m_achievementId.c_str();
- achievementName = m_achievementName.c_str();
- achievementDescription = m_achievementDescription.c_str();
- achievementIcon = m_achievementIconUri.c_str();
- titleId = event.titleId;
- gamerscore = event.gamerscore;
- rarityPercentage = event.rarityPercentage;
- rarityCategory = event.rarityCategory;
- timeUnlocked = event.timeUnlocked;
- xboxUserId = event.xboxUserId;
-}
-
-AchievementUnlockEvent::AchievementUnlockEvent( const AchievementUnlockEvent& event ) :
- m_achievementId(event.m_achievementId),
- m_achievementName(event.m_achievementName),
- m_achievementDescription(event.m_achievementDescription),
- m_achievementIconUri(event.m_achievementIconUri),
- m_deepLink(event.m_deepLink)
-{
- // strings for the C API
- achievementId = m_achievementId.c_str();
- achievementName = m_achievementName.c_str();
- achievementDescription = m_achievementDescription.c_str();
- achievementIcon = m_achievementIconUri.c_str();
-
- titleId = event.titleId;
- gamerscore = event.gamerscore;
- rarityPercentage = event.rarityPercentage;
- rarityCategory = event.rarityCategory;
- timeUnlocked = event.timeUnlocked;
- xboxUserId = event.xboxUserId;
-}
-
-
-Result AchievementUnlockEvent::Deserialize( _In_ const JsonValue& json ) noexcept
-{
- AchievementUnlockEvent result{};
- if (!json.IsObject())
- {
- return result;
- }
-
- double rarityPercentage = 0.0; // We are guaranteed that rarityPercentage is only float, but can only extract as double.
- uint64_t titleId = 0; // We are guaranteed that titleId is only uint32, but can only extract as uint64.
- String rarityCategory; // Will be converted into an enum value after extraction
-
- RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "achievementId", result.m_achievementId, true));
-
- RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "achievementDescription", result.m_achievementDescription, false));
-
- RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "achievementName", result.m_achievementName, true));
-
- RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "achievementIcon", result.m_achievementIconUri, true));
-
- RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToUInt64( json, "titleId", titleId, true));
-
- RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonUInt64( json, "gamerscore", result.gamerscore, true));
-
- RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonXuid(json, "xuid", result.xboxUserId, true));
-
- RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "extendedInfoUrl", result.m_deepLink, true));
-
- // RarityPercentage and rarityCategory are only in payload version 2 so make them optional
- RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonDouble(json, "rarityPercentage", rarityPercentage, false));
-
- RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "rarityCategory", rarityCategory, false));
-
- RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonTimeT(json, "unlockTime", result.timeUnlocked));
-
- result.titleId = static_cast(titleId);
- result.rarityPercentage = static_cast(rarityPercentage);
- result.rarityCategory = EnumValue(rarityCategory.c_str());
-
- // strings for the C API
- result.achievementId = result.m_achievementId.c_str();
- result.achievementName = result.m_achievementName.c_str();
- if (!result.m_achievementDescription.empty())
- {
- result.achievementDescription = result.m_achievementDescription.c_str();
- }
- result.achievementIcon = result.m_achievementIconUri.c_str();
- result.m_deepLink = result.m_deepLink.c_str();
-
- return result;
-}
-
-NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_END
-
-#endif
\ No newline at end of file
diff --git a/Source/Services/Notification/RTA/achievement_unlock_subscription.h b/Source/Services/Notification/RTA/achievement_unlock_subscription.h
deleted file mode 100644
index 6cb8da65..00000000
--- a/Source/Services/Notification/RTA/achievement_unlock_subscription.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (c) Microsoft Corporation
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-#pragma once
-#include "xsapi-c/achievements_c.h"
-#include "real_time_activity_subscription.h"
-
-#if HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM_IS_EXTERNAL
-
-NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_BEGIN
-
- struct AchievementUnlockEvent : public XblAchievementUnlockEvent
- {
- public:
-
- AchievementUnlockEvent() = default;
- AchievementUnlockEvent( AchievementUnlockEvent&& event) noexcept;
- AchievementUnlockEvent(const AchievementUnlockEvent& event);
-
- static Result Deserialize(_In_ const JsonValue& json ) noexcept;
-
- private:
-
- String m_achievementId;
- String m_achievementDescription;
- String m_achievementName;
- String m_achievementIconUri;
- String m_deepLink;
- };
-
-
- class AchievementUnlockSubscription : public real_time_activity::Subscription
- {
- public:
-
- AchievementUnlockSubscription(uint64_t xuid, uint32_t titleId) noexcept;
-
- using EventHandler = Function< void(const AchievementUnlockEvent&) >;
- XblFunctionContext AddHandler(EventHandler handler) noexcept;
- size_t RemoveHandler(XblFunctionContext id) noexcept;
- const String & ResourceUri() const noexcept;
-
-
- protected:
-
- void OnEvent(_In_ const JsonValue& data) noexcept override;
- void OnResync() noexcept override;
-
- private:
-
- Map< XblFunctionContext, EventHandler> m_handlers;
- XblFunctionContext m_nextId{ 0 };
-
- std::mutex m_mutex;
- };
-
-NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_END
-
-#endif
\ No newline at end of file
diff --git a/Source/Services/Notification/RTA/notification_service_rta.cpp b/Source/Services/Notification/RTA/notification_service_rta.cpp
index b85a24ab..76f41de9 100644
--- a/Source/Services/Notification/RTA/notification_service_rta.cpp
+++ b/Source/Services/Notification/RTA/notification_service_rta.cpp
@@ -11,112 +11,52 @@
NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_BEGIN
-template
-HRESULT RTANotificationService::RegisterForEvents(std::shared_ptr& subscription,
- AsyncContext async) noexcept
+RTANotificationService::RTANotificationService(
+ _In_ User&& user,
+ _In_ const TaskQueue& queue,
+ _In_ std::shared_ptr contextSettings,
+ _In_ std::shared_ptr rtaManager
+) noexcept :
+ NotificationService(std::move(user), contextSettings),
+ m_taskQueue{ queue.DeriveWorkerQueue() },
+ m_rtaManager{ std::move(rtaManager) }
{
+}
+
+RTANotificationService::~RTANotificationService() noexcept
+{
+ m_rtaManager->RemoveSubscription(m_user, m_rtaSubscription);
+ m_rtaManager->Deactivate(m_user);
+}
+
+HRESULT RTANotificationService::Initialize() noexcept
+{
+ // Always register with notification service as it powers SPOP for Win32
+
// Subscribing to events requires two separate steps:
// 1) Creating and adding an RTA subscription to the notification endpoint
// 2) Registering with the notification service
std::lock_guard lock{ m_mutex };
- if (!subscription)
- {
- m_rtaManager->Activate(m_user);
- subscription = MakeShared(m_user.Xuid(), AppConfig::Instance()->TitleId());
- RETURN_HR_IF_FAILED(m_rtaManager->AddSubscription(m_user, subscription));
- }
+
+ auto copyUserResult = m_user.Copy();
+ RETURN_HR_IF_FAILED(copyUserResult.Hresult());
+
+ m_rtaManager->Activate(m_user);
+ m_rtaSubscription = MakeShared(copyUserResult.ExtractPayload(), m_taskQueue, AppConfig::Instance()->TitleId());
+ RETURN_HR_IF_FAILED(m_rtaManager->AddSubscription(m_user, m_rtaSubscription));
return RegisterWithNotificationService(
- subscription->ResourceUri(),
- std::move(async));
-};
-
-
-template
-HRESULT RTANotificationService::RTANotificationService::UnregisterForEvents(std::shared_ptr& subscription,
- AsyncContext async) noexcept
-{
- std::lock_guard lock{ m_mutex };
- if (subscription)
- {
- m_rtaManager->RemoveSubscription(m_user, subscription);
- subscription.reset();
- m_rtaManager->Deactivate(m_user);
- }
-
- if (!m_gameInviteSubscription && !m_achievementUnlockSubscription && !m_spopNotificationSubscription)
- {
- return UnregisterFromNotificationService(
- std::move(async));
- }
-
- return S_OK;
-}
-
-RTANotificationService::RTANotificationService(
- _In_ User&& user,
- _In_ std::shared_ptr contextSettings,
- _In_ std::shared_ptr rtaManager
-) noexcept :
- NotificationService(std::move(user), contextSettings),
- m_rtaManager{ std::move(rtaManager) }
-{
-}
-
-HRESULT RTANotificationService::RegisterForSpopNotificationEvents() noexcept
-{
- std::lock_guard lock{ m_mutex };
-
- auto copiedUserResult = m_user.Copy();
- RETURN_HR_IF_FAILED(copiedUserResult.Hresult());
- m_rtaManager->Activate(copiedUserResult.ExtractPayload());
-
- {
- auto subscriptionUserCopyResult = m_user.Copy();
- RETURN_HR_IF_FAILED(subscriptionUserCopyResult.Hresult());
- m_spopNotificationSubscription = MakeShared(subscriptionUserCopyResult.ExtractPayload(), AppConfig::Instance()->TitleId());
- }
-
- {
- auto subscriptionUserCopyResult = m_user.Copy();
- RETURN_HR_IF_FAILED(subscriptionUserCopyResult.Hresult());
- RETURN_HR_IF_FAILED(m_rtaManager->AddSubscription(subscriptionUserCopyResult.ExtractPayload(), m_spopNotificationSubscription));
- }
-
- return RegisterWithNotificationService(
- m_spopNotificationSubscription->ResourceUri(),
- {});
-}
-
-HRESULT RTANotificationService::RegisterForGameInviteEvents(
- _In_ AsyncContext async
-) noexcept
-{
- return RegisterForEvents(m_gameInviteSubscription, async);
-};
-
-HRESULT RTANotificationService::UnregisterForGameInviteEvents(
- _In_ AsyncContext async
-) noexcept
-{
- return UnregisterForEvents(m_gameInviteSubscription, async);
-}
-
-
-HRESULT RTANotificationService::RegisterForAchievementUnlockEvents(
- _In_ AsyncContext async
-) noexcept
-{
- return RegisterForEvents(m_achievementUnlockSubscription, async);
-};
-
-
-HRESULT RTANotificationService::UnregisterForAchievementUnlockEvents(
- _In_ AsyncContext async
-) noexcept
-{
- return UnregisterForEvents(m_achievementUnlockSubscription, async);
+ m_rtaSubscription->ResourceUri(),
+ AsyncContext{ m_taskQueue,
+ [](HRESULT hr)
+ {
+ if (FAILED(hr))
+ {
+ LOGS_ERROR << "Failed to register with Notification Service hr=" << std::hex << hr;
+ }
+ }
+ });
}
HRESULT RTANotificationService::RegisterWithNotificationService(
@@ -124,128 +64,68 @@ HRESULT RTANotificationService::RegisterWithNotificationService(
_In_ AsyncContext async
) noexcept
{
- std::lock_guard lock{ m_mutex };
+ std::lock_guard lock{ m_mutex };
- Stringstream titleId;
- titleId << AppConfig::Instance()->TitleId();
+ Stringstream titleId;
+ titleId << AppConfig::Instance()->TitleId();
- const char platform[]{
+ const char platform[]{
#if HC_PLATFORM == HC_PLATFORM_WIN32
- "Win32",
+ "Win32",
#elif HC_PLATFORM == HC_PLATFORM_NINTENDO_SWITCH
- "NintendoSwitch",
+ "NintendoSwitch",
#endif
- };
+ };
- Vector notificationFilterList;
- notificationFilterList.push_back({ NotificationTypeFilterSourceType::Multiplayer, 1 });
- notificationFilterList.push_back({ NotificationTypeFilterSourceType::Multiplayer, 8 });
- notificationFilterList.push_back({ NotificationTypeFilterSourceType::Achievements, 1 });
+ Vector notificationFilterList;
+ notificationFilterList.push_back({ NotificationTypeFilterSourceType::Multiplayer, 1 });
+ notificationFilterList.push_back({ NotificationTypeFilterSourceType::Multiplayer, 8 });
+ notificationFilterList.push_back({ NotificationTypeFilterSourceType::Achievements, 1 });
- return NotificationService::RegisterForNotificationsHelper(
- utils::create_guid(true), // applicationInstanceId
- uriData,
- platform,
- "",
- "",
- notificationFilterList,
- AsyncContext{
- async.Queue(),
- [thisWeakPtr = std::weak_ptr{ shared_from_this() }, async](HRESULT hr)
- {
- if (auto pThis{ thisWeakPtr.lock() })
- {
- auto derivedPtr = dynamic_cast(pThis.get());
- if (SUCCEEDED(hr))
- {
- if (pThis->m_registrationStatus == RegistrationStatus::PendingUnregistration)
- {
- // Immediately kick off unregistration
- derivedPtr->UnregisterForGameInviteEvents();
- derivedPtr->UnregisterForAchievementUnlockEvents();
- }
-
- return async.Complete(hr);
- }
- else
- {
-
- return async.Complete(E_XBL_AUTH_RUNTIME_ERROR);
- }
- }
- }
- });
+ return NotificationService::RegisterForNotificationsHelper(
+ utils::create_guid(true), // applicationInstanceId
+ uriData,
+ platform,
+ "",
+ "",
+ notificationFilterList,
+ std::move(async)
+ );
}
XblFunctionContext RTANotificationService::AddGameInviteHandler(
- _In_ GameInviteSubscription::MPSDInviteHandler handler
+ _In_ NotificationSubscription::MPSDInviteHandler handler
) noexcept
{
std::lock_guard lock{ m_mutex };
-
- RegisterForGameInviteEvents();
-
- // Subscription should always be created after registering for invites
- assert(m_gameInviteSubscription);
- return m_gameInviteSubscription->AddHandler(std::move(handler));
+ assert(m_rtaSubscription);
+ return m_rtaSubscription->AddHandler(std::move(handler));
}
XblFunctionContext RTANotificationService::AddGameInviteHandler(
- _In_ GameInviteSubscription::MultiplayerActivityInviteHandler handler
+ _In_ NotificationSubscription::MultiplayerActivityInviteHandler handler
) noexcept
{
std::lock_guard lock{ m_mutex };
-
- RegisterForGameInviteEvents();
-
- // Subscription should always be created after registering for invites
- assert(m_gameInviteSubscription);
- return m_gameInviteSubscription->AddHandler(std::move(handler));
-}
-
-void RTANotificationService::RemoveGameInviteHandler(
- _In_ XblFunctionContext token
-) noexcept
-{
- std::lock_guard lock{ m_mutex };
- if (m_gameInviteSubscription)
- {
- size_t remainingHandlers = m_gameInviteSubscription->RemoveHandler(token);
- if (!remainingHandlers)
- {
- // Unregister if that was the last client handler
- UnregisterForGameInviteEvents();
- }
- }
+ assert(m_rtaSubscription);
+ return m_rtaSubscription->AddHandler(std::move(handler));
}
XblFunctionContext RTANotificationService::AddAchievementUnlockNotificationHandler(
- _In_ AchievementUnlockSubscription::EventHandler handler
+ _In_ NotificationSubscription::AchievementUnlockHandler handler
) noexcept
{
std::lock_guard lock{ m_mutex };
-
- RegisterForAchievementUnlockEvents();
-
- // Subscription should always be created after registering for invites
- assert(m_achievementUnlockSubscription);
- return m_achievementUnlockSubscription->AddHandler(std::move(handler));
+ assert(m_rtaSubscription);
+ return m_rtaSubscription->AddHandler(std::move(handler));
}
-void RTANotificationService::RemoveAchievementUnlockNotificationHandler(
+void RTANotificationService::RemoveNotificationHandler(
_In_ XblFunctionContext token
) noexcept
{
std::lock_guard lock{ m_mutex };
- if (m_achievementUnlockSubscription)
- {
- size_t remainingHandlers = m_achievementUnlockSubscription->RemoveHandler(token);
- if (!remainingHandlers)
- {
- // Unregister if that was the last client handler
- UnregisterForAchievementUnlockEvents();
- }
- }
+ m_rtaSubscription->RemoveHandler(token);
}
NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_END
diff --git a/Source/Services/Notification/RTA/game_invite_subscription.cpp b/Source/Services/Notification/RTA/notification_subscription.cpp
similarity index 52%
rename from Source/Services/Notification/RTA/game_invite_subscription.cpp
rename to Source/Services/Notification/RTA/notification_subscription.cpp
index 1d3d945f..1a4cffba 100644
--- a/Source/Services/Notification/RTA/game_invite_subscription.cpp
+++ b/Source/Services/Notification/RTA/notification_subscription.cpp
@@ -1,9 +1,5 @@
-// Copyright (c) Microsoft Corporation
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-
#include "pch.h"
-#include "notification_internal.h"
+#include "notification_subscription.h"
#include "real_time_activity_manager.h"
#include "multiplayer_internal.h"
@@ -11,55 +7,63 @@
NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_BEGIN
-GameInviteSubscription::GameInviteSubscription(
- _In_ uint64_t xuid,
+NotificationSubscription::NotificationSubscription(
+ _In_ User&& user,
+ _In_ TaskQueue queue,
_In_ uint32_t titleId
-) noexcept
+) noexcept :
+ m_user{ std::move(user) },
+ m_queue{ std::move(queue) }
{
Stringstream ss;
- ss << "https://notify.xboxlive.com/users/xuid(" << xuid << ")/deviceId/current/titleId/" << titleId;
+ ss << "https://notify.xboxlive.com/users/xuid(" << m_user.Xuid() << ")/deviceId/current/titleId/" << titleId;
m_resourceUri = ss.str();
}
-const String& GameInviteSubscription::ResourceUri() const noexcept
+const String& NotificationSubscription::ResourceUri() const noexcept
{
return m_resourceUri;
}
-XblFunctionContext GameInviteSubscription::AddHandler(
- MPSDInviteHandler handler
-) noexcept
+XblFunctionContext NotificationSubscription::AddHandler(MPSDInviteHandler&& handler) noexcept
{
std::lock_guard lock{ m_mutex };
m_mpsdInviteHandlers[m_nextToken] = std::move(handler);
return m_nextToken++;
}
-XblFunctionContext GameInviteSubscription::AddHandler(
- MultiplayerActivityInviteHandler handler
-) noexcept
+XblFunctionContext NotificationSubscription::AddHandler(MultiplayerActivityInviteHandler&& handler) noexcept
{
std::lock_guard lock{ m_mutex };
m_mpaInviteHandlers[m_nextToken] = std::move(handler);
return m_nextToken++;
}
-size_t GameInviteSubscription::RemoveHandler(
- XblFunctionContext token
-) noexcept
+XblFunctionContext NotificationSubscription::AddHandler(AchievementUnlockHandler&& handler) noexcept
{
std::lock_guard lock{ m_mutex };
- m_mpaInviteHandlers.erase(token);
- m_mpsdInviteHandlers.erase(token);
- return m_mpaInviteHandlers.size() + m_mpsdInviteHandlers.size();
+ m_achievementUnlockHandlers[m_nextToken] = std::move(handler);
+ return m_nextToken++;
}
-void GameInviteSubscription::OnEvent(
+size_t NotificationSubscription::RemoveHandler(XblFunctionContext token) noexcept
+{
+ std::lock_guard lock{ m_mutex };
+
+ m_mpaInviteHandlers.erase(token);
+ m_mpsdInviteHandlers.erase(token);
+ m_achievementUnlockHandlers.erase(token);
+
+ // return the total number of handlers remaining
+ return m_mpaInviteHandlers.size() + m_mpsdInviteHandlers.size() + m_achievementUnlockHandlers.size();
+}
+
+void NotificationSubscription::OnEvent(
_In_ const JsonValue& data
) noexcept
{
- // MPSD Invites and MPA invites are both delivered via the same RTA subscription, but the payloads for each
- // are slightly different. We examine the json here to distinguish between the two.
+ // Notifications from several services are delivered via the same RTA subscription. Examine the payload to
+ // distinguish between them.
if (!data.IsObject())
{
@@ -102,12 +106,73 @@ void GameInviteSubscription::OnEvent(
}
}
}
+ else if (data.HasMember("KickNotification"))
+ {
+ XalUserGetTokenAndSignatureArgs args;
+ args.forceRefresh = true;
+ args.url = "https://xboxlive.com/";
+ args.method = "GET";
+
+ auto async = MakeUnique();
+ async->queue = m_queue.GetHandle();
+ async->callback = [](XAsyncBlock* async)
+ {
+ // Take ownership of async block
+ UniquePtr asyncUnique{ async };
+
+ // Get the result even though we aren't using it so Xal can release it
+ size_t bufferSize{};
+ HRESULT hr = XalUserGetTokenAndSignatureSilentlyResultSize(async, &bufferSize);
+
+ if (SUCCEEDED(hr))
+ {
+ Vector buffer(bufferSize);
+ XalUserGetTokenAndSignatureData* xalTokenSignatureData{ nullptr };
+
+ hr = XalUserGetTokenAndSignatureSilentlyResult(async, bufferSize, buffer.data(), &xalTokenSignatureData, nullptr);
+ UNREFERENCED_PARAMETER(hr);
+ }
+ };
+
+ if (SUCCEEDED(XalUserGetTokenAndSignatureSilentlyAsync(m_user.Handle(), &args, async.get())))
+ {
+ async.release();
+ }
+ }
+ else if (data.HasMember("service"))
+ {
+ String originatingService;
+ JsonUtils::ExtractJsonString(data, "service", originatingService);
+
+ if (originatingService == "achievements")
+ {
+ // Deserialize and send invite notification
+ auto deserializationResult = AchievementUnlockEvent::Deserialize(data);
+ if (Succeeded(deserializationResult))
+ {
+ std::unique_lock lock{ m_mutex };
+ auto handlers{ m_achievementUnlockHandlers };
+ lock.unlock();
+
+ for (auto& handler : handlers)
+ {
+ handler.second(deserializationResult.Payload());
+ }
+ }
+ }
+ }
else
{
LOGS_ERROR << __FUNCTION__ << ": Ignoring unrecognized payload";
}
}
+void NotificationSubscription::OnResync() noexcept
+{
+ // Don't think there is much we can do here - just log an error
+ LOGS_ERROR << __FUNCTION__ << ": Notification service events may have been missed";
+}
+
GameInviteNotificationEventArgs::GameInviteNotificationEventArgs(
const GameInviteNotificationEventArgs& other
) noexcept
@@ -230,11 +295,100 @@ Result MultiplayerActivityInviteData::Deserialize
return data;
}
-void GameInviteSubscription::OnResync() noexcept
+AchievementUnlockEvent::AchievementUnlockEvent(AchievementUnlockEvent&& event) noexcept :
+ m_achievementId(std::move(event.m_achievementId)),
+ m_achievementName(std::move(event.m_achievementName)),
+ m_achievementDescription(std::move(event.m_achievementDescription)),
+ m_achievementIconUri(std::move(event.m_achievementIconUri)),
+ m_deepLink(event.m_deepLink)
{
- // Don't think there is much we can do here - just log an error
- LOGS_ERROR << __FUNCTION__ << ": Game invites may have been discarded by RTA service";
+ // strings for the C API
+ achievementId = m_achievementId.c_str();
+ achievementName = m_achievementName.c_str();
+ achievementDescription = m_achievementDescription.c_str();
+ achievementIcon = m_achievementIconUri.c_str();
+ titleId = event.titleId;
+ gamerscore = event.gamerscore;
+ rarityPercentage = event.rarityPercentage;
+ rarityCategory = event.rarityCategory;
+ timeUnlocked = event.timeUnlocked;
+ xboxUserId = event.xboxUserId;
+}
+
+AchievementUnlockEvent::AchievementUnlockEvent(const AchievementUnlockEvent& event) :
+ m_achievementId(event.m_achievementId),
+ m_achievementName(event.m_achievementName),
+ m_achievementDescription(event.m_achievementDescription),
+ m_achievementIconUri(event.m_achievementIconUri),
+ m_deepLink(event.m_deepLink)
+{
+ // strings for the C API
+ achievementId = m_achievementId.c_str();
+ achievementName = m_achievementName.c_str();
+ achievementDescription = m_achievementDescription.c_str();
+ achievementIcon = m_achievementIconUri.c_str();
+
+ titleId = event.titleId;
+ gamerscore = event.gamerscore;
+ rarityPercentage = event.rarityPercentage;
+ rarityCategory = event.rarityCategory;
+ timeUnlocked = event.timeUnlocked;
+ xboxUserId = event.xboxUserId;
+}
+
+
+Result AchievementUnlockEvent::Deserialize(_In_ const JsonValue& json) noexcept
+{
+ AchievementUnlockEvent result{};
+ if (!json.IsObject())
+ {
+ return result;
+ }
+
+ double rarityPercentage = 0.0; // We are guaranteed that rarityPercentage is only float, but can only extract as double.
+ uint64_t titleId = 0; // We are guaranteed that titleId is only uint32, but can only extract as uint64.
+ String rarityCategory; // Will be converted into an enum value after extraction
+
+ RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "achievementId", result.m_achievementId, true));
+
+ RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "achievementDescription", result.m_achievementDescription, false));
+
+ RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "achievementName", result.m_achievementName, true));
+
+ RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "achievementIcon", result.m_achievementIconUri, true));
+
+ RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonStringToUInt64(json, "titleId", titleId, true));
+
+ RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonUInt64(json, "gamerscore", result.gamerscore, true));
+
+ RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonXuid(json, "xuid", result.xboxUserId, true));
+
+ RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "extendedInfoUrl", result.m_deepLink, true));
+
+ // RarityPercentage and rarityCategory are only in payload version 2 so make them optional
+ RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonDouble(json, "rarityPercentage", rarityPercentage, false));
+
+ RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(json, "rarityCategory", rarityCategory, false));
+
+ RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonTimeT(json, "unlockTime", result.timeUnlocked));
+
+ result.titleId = static_cast(titleId);
+ result.rarityPercentage = static_cast(rarityPercentage);
+ result.rarityCategory = EnumValue(rarityCategory.c_str());
+
+ // strings for the C API
+ result.achievementId = result.m_achievementId.c_str();
+ result.achievementName = result.m_achievementName.c_str();
+ if (!result.m_achievementDescription.empty())
+ {
+ result.achievementDescription = result.m_achievementDescription.c_str();
+ }
+ result.achievementIcon = result.m_achievementIconUri.c_str();
+ result.m_deepLink = result.m_deepLink.c_str();
+
+ return result;
}
NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_END
+
#endif
\ No newline at end of file
diff --git a/Source/Services/Notification/RTA/notification_subscription.h b/Source/Services/Notification/RTA/notification_subscription.h
new file mode 100644
index 00000000..cd260b67
--- /dev/null
+++ b/Source/Services/Notification/RTA/notification_subscription.h
@@ -0,0 +1,107 @@
+// Copyright (c) Microsoft Corporation
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#pragma once
+
+#include "xsapi-c/game_invite_c.h"
+#include "xsapi-c/achievements_c.h"
+#include "xsapi-c/multiplayer_activity_c.h"
+#include "real_time_activity_subscription.h"
+
+#if HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM_IS_EXTERNAL
+
+NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_BEGIN
+
+// Event args for MPSD game invites
+struct GameInviteNotificationEventArgs : public XblGameInviteNotificationEventArgs
+{
+public:
+ GameInviteNotificationEventArgs() noexcept = default;
+ GameInviteNotificationEventArgs(const GameInviteNotificationEventArgs& other) noexcept;
+ GameInviteNotificationEventArgs& operator=(GameInviteNotificationEventArgs other) noexcept = delete;
+
+ static Result Deserialize(
+ _In_ const JsonValue& json
+ ) noexcept;
+
+private:
+ String m_inviteHandleId;
+ String m_inviteProtocol;
+ String m_senderImageUrl;
+};
+
+struct MultiplayerActivityInviteData : public XblMultiplayerActivityInviteData
+{
+public:
+ MultiplayerActivityInviteData() noexcept = default;
+ MultiplayerActivityInviteData(const MultiplayerActivityInviteData& other) noexcept;
+ MultiplayerActivityInviteData& operator=(MultiplayerActivityInviteData other) noexcept = delete;
+
+ static Result Deserialize(
+ const JsonValue& json
+ ) noexcept;
+
+private:
+ String m_senderImageUrl;
+ String m_titleName;
+ String m_titleImageUrl;
+ String m_connectionString;
+};
+
+struct AchievementUnlockEvent : public XblAchievementUnlockEvent
+{
+public:
+
+ AchievementUnlockEvent() = default;
+ AchievementUnlockEvent(AchievementUnlockEvent&& event) noexcept;
+ AchievementUnlockEvent(const AchievementUnlockEvent& event);
+
+ static Result Deserialize(_In_ const JsonValue& json) noexcept;
+
+private:
+
+ String m_achievementId;
+ String m_achievementDescription;
+ String m_achievementName;
+ String m_achievementIconUri;
+ String m_deepLink;
+};
+
+class NotificationSubscription : public real_time_activity::Subscription
+{
+public:
+ NotificationSubscription(
+ User&& user,
+ TaskQueue queue,
+ uint32_t titleId
+ ) noexcept;
+
+ const String& ResourceUri() const noexcept;
+
+ using MPSDInviteHandler = Function;
+ using MultiplayerActivityInviteHandler = Function;
+ using AchievementUnlockHandler = Function;
+
+ XblFunctionContext AddHandler(MPSDInviteHandler&& handler) noexcept;
+ XblFunctionContext AddHandler(MultiplayerActivityInviteHandler&& handler) noexcept;
+ XblFunctionContext AddHandler(AchievementUnlockHandler&& handler) noexcept;
+
+ size_t RemoveHandler(XblFunctionContext token) noexcept;
+
+protected:
+ void OnEvent(_In_ const JsonValue& event) noexcept override;
+ void OnResync() noexcept override;
+
+private:
+ User m_user;
+ TaskQueue m_queue;
+ std::mutex m_mutex;
+ XblFunctionContext m_nextToken{ 0 };
+
+ Map m_mpsdInviteHandlers;
+ Map m_mpaInviteHandlers;
+ Map m_achievementUnlockHandlers;
+};
+
+NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_END
+#endif
\ No newline at end of file
diff --git a/Source/Services/Notification/RTA/spop_kick_subscription.cpp b/Source/Services/Notification/RTA/spop_kick_subscription.cpp
deleted file mode 100644
index 516ad4e3..00000000
--- a/Source/Services/Notification/RTA/spop_kick_subscription.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-#include "pch.h"
-#include "spop_kick_subscription.h"
-#include "real_time_activity_manager.h"
-#include "multiplayer_internal.h"
-
-#if HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM_IS_EXTERNAL
-
-NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_BEGIN
-
-SpopKickSubscription::SpopKickSubscription(
- _In_ User&& user,
- _In_ uint32_t titleId
-) noexcept :
- m_user(std::move(user))
-{
- Stringstream ss;
- ss << "https://notify.xboxlive.com/users/xuid(" << m_user.Xuid() << ")/deviceId/current/titleId/" << titleId;
- m_resourceUri = ss.str();
-}
-
-const String& SpopKickSubscription::ResourceUri() const noexcept
-{
- return m_resourceUri;
-}
-
-void SpopKickSubscription::OnEvent(
- _In_ const JsonValue& data
-) noexcept
-{
- auto datastr = JsonUtils::SerializeJson(data);
- UNREFERENCED_PARAMETER(datastr);
- if (data.IsObject() && data.HasMember("KickNotification"))
- {
- auto args = std::make_unique();
- args->forceRefresh = true;
- args->url = "https://xboxlive.com/";
- args->method = "GET";
-
- auto async = std::make_unique();
- async->callback = [](XAsyncBlock* async)
- {
- size_t bufferSize{};
- auto h = XalUserGetTokenAndSignatureSilentlyResultSize(async, &bufferSize);
- UNREFERENCED_PARAMETER(h);
- };
-
- if (SUCCEEDED(XalUserGetTokenAndSignatureSilentlyAsync(m_user.Handle(), args.get(), async.get())))
- {
- args.release();
- async.release();
- }
- }
-}
-
-void SpopKickSubscription::OnResync() noexcept
-{
- // Don't think there is much we can do here - just log an error
- LOGS_ERROR << __FUNCTION__ << ": SPOP notifications event may have been discarded by RTA service";
-}
-NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_END
-#endif
\ No newline at end of file
diff --git a/Source/Services/Notification/RTA/spop_kick_subscription.h b/Source/Services/Notification/RTA/spop_kick_subscription.h
deleted file mode 100644
index 3750ae39..00000000
--- a/Source/Services/Notification/RTA/spop_kick_subscription.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) Microsoft Corporation
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-#pragma once
-#include "xsapi-c/game_invite_c.h"
-#include "real_time_activity_subscription.h"
-
-#if HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM_IS_EXTERNAL
-
-NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_BEGIN
-
-class SpopKickSubscription : public real_time_activity::Subscription
-{
-public:
- SpopKickSubscription(
- User&& user,
- uint32_t titleId
- ) noexcept;
-
- const String& ResourceUri() const noexcept;
-
-protected:
- void OnEvent(_In_ const JsonValue& event) noexcept override;
- void OnResync() noexcept override;
-
-private:
- User m_user;
-};
-NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_END
-#endif
\ No newline at end of file
diff --git a/Source/Services/Notification/notification_api.cpp b/Source/Services/Notification/notification_api.cpp
index 677139e9..a7d37a37 100644
--- a/Source/Services/Notification/notification_api.cpp
+++ b/Source/Services/Notification/notification_api.cpp
@@ -3,7 +3,6 @@
#include "pch.h"
-#include "RTA/achievement_unlock_subscription.h"
#include "notification_internal.h"
#include "multiplayer_internal.h"
#include "xbox_live_context_internal.h"
@@ -200,22 +199,10 @@ STDAPI XblGameInviteRegisterForEventAsync(
{
switch (op)
{
- case XAsyncOp::DoWork:
+ case XAsyncOp::Begin:
{
- auto rtaNotificationService = std::dynamic_pointer_cast(xblContext->NotificationService());
- RETURN_HR_IF_FAILED(rtaNotificationService->RegisterForGameInviteEvents({ data->async->queue,
- [
- asyncBlock{ data->async }
- ]
- (Result result)
- {
- // We want to return a dummy subscription handle to maintain legacy behavior, so make sure to
- // indicate that there is a result payload
- XAsyncComplete(asyncBlock, result.Hresult(), sizeof(XblRealTimeActivitySubscriptionHandle));
- }
- }));
-// Do we need E-PENDING
- return E_PENDING;
+ XAsyncComplete(data->async, S_OK, sizeof(XblRealTimeActivitySubscriptionHandle));
+ return S_OK;
}
case XAsyncOp::GetResult:
{
@@ -269,13 +256,11 @@ STDAPI XblGameInviteUnregisterForEventAsync(
{
switch (op)
{
- case XAsyncOp::DoWork:
+ case XAsyncOp::Begin:
{
-
- auto rtaNotificationService = std::dynamic_pointer_cast(xblContext->NotificationService());
- RETURN_HR_IF_FAILED(rtaNotificationService->UnregisterForGameInviteEvents(data->async));
Delete(subscriptionHandle);
- return E_PENDING;
+ XAsyncComplete(data->async, S_OK, 0);
+ return S_OK;
}
default: return S_OK;
}
@@ -295,7 +280,7 @@ STDAPI_(XblFunctionContext) XblGameInviteAddNotificationHandler(
}
auto rtaNotificationService = std::dynamic_pointer_cast(xblContextHandle->NotificationService());
- return rtaNotificationService->AddGameInviteHandler(GameInviteSubscription::MPSDInviteHandler{
+ return rtaNotificationService->AddGameInviteHandler(NotificationSubscription::MPSDInviteHandler{
[
handler, context
]
@@ -321,7 +306,7 @@ STDAPI_(void) XblGameInviteRemoveNotificationHandler(
if (xblContextHandle)
{
auto rtaNotificationService = std::dynamic_pointer_cast(xblContextHandle->NotificationService());
- rtaNotificationService->RemoveGameInviteHandler(token);
+ rtaNotificationService->RemoveNotificationHandler(token);
}
}
@@ -361,7 +346,7 @@ STDAPI_(void) XblAchievementUnlockRemoveNotificationHandler(
) XBL_NOEXCEPT
{
auto rtaNotificationService = std::dynamic_pointer_cast(xblContextHandle->NotificationService());
- rtaNotificationService->RemoveAchievementUnlockNotificationHandler(functionContext);
+ rtaNotificationService->RemoveNotificationHandler(functionContext);
}
diff --git a/Source/Services/Notification/notification_internal.h b/Source/Services/Notification/notification_internal.h
index 0733598b..b9be9278 100644
--- a/Source/Services/Notification/notification_internal.h
+++ b/Source/Services/Notification/notification_internal.h
@@ -4,11 +4,8 @@
#pragma once
#if HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM_IS_EXTERNAL
-#include "xsapi-c/game_invite_c.h"
-#include "xsapi-c/multiplayer_activity_c.h"
#include "real_time_activity_subscription.h"
-#include "RTA/achievement_unlock_subscription.h"
-#include "RTA/spop_kick_subscription.h"
+#include "RTA/notification_subscription.h"
#endif
NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_BEGIN
@@ -99,117 +96,35 @@ public:
#endif
#if HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM_IS_EXTERNAL
-// Event args for MPSD game invites
-struct GameInviteNotificationEventArgs : public XblGameInviteNotificationEventArgs
-{
-public:
- GameInviteNotificationEventArgs() noexcept = default;
- GameInviteNotificationEventArgs(const GameInviteNotificationEventArgs& other) noexcept;
- GameInviteNotificationEventArgs& operator=(GameInviteNotificationEventArgs other) noexcept = delete;
-
- static Result Deserialize(
- _In_ const JsonValue& json
- ) noexcept;
-
-private:
- String m_inviteHandleId;
- String m_inviteProtocol;
- String m_senderImageUrl;
-};
-
-struct MultiplayerActivityInviteData : public XblMultiplayerActivityInviteData
-{
-public:
- MultiplayerActivityInviteData() noexcept = default;
- MultiplayerActivityInviteData(const MultiplayerActivityInviteData& other) noexcept;
- MultiplayerActivityInviteData& operator=(MultiplayerActivityInviteData other) noexcept = delete;
-
- static Result Deserialize(
- const JsonValue& json
- ) noexcept;
-
-private:
- String m_senderImageUrl;
- String m_titleName;
- String m_titleImageUrl;
- String m_connectionString;
-};
-
-class GameInviteSubscription : public real_time_activity::Subscription
-{
-public:
- GameInviteSubscription(
- _In_ uint64_t xuid,
- _In_ uint32_t titleId
- ) noexcept;
-
- const String& ResourceUri() const noexcept;
-
- typedef Function MPSDInviteHandler;
- typedef Function MultiplayerActivityInviteHandler;
-
- XblFunctionContext AddHandler(MPSDInviteHandler handler) noexcept;
- XblFunctionContext AddHandler(MultiplayerActivityInviteHandler handler) noexcept;
- size_t RemoveHandler(XblFunctionContext token) noexcept;
-
-protected:
- void OnEvent(_In_ const JsonValue& data) noexcept override;
- void OnResync() noexcept override;
-
-private:
- UnorderedMap m_mpsdInviteHandlers;
- UnorderedMap m_mpaInviteHandlers;
- XblFunctionContext m_nextToken{ 0 };
-
- std::mutex m_mutex;
-};
class RTANotificationService : public NotificationService
{
public:
RTANotificationService(
_In_ User&& user,
+ _In_ const TaskQueue& taskQueue,
_In_ std::shared_ptr contextSettings,
_In_ std::shared_ptr rtaManager
) noexcept;
- HRESULT RegisterForSpopNotificationEvents() noexcept;
+ ~RTANotificationService() noexcept;
- // Keeping these APIs public to continue supporting XblGameInviteRegisterForEventResult and XblGameInviteUnregisterForEventAsync
- HRESULT RegisterForGameInviteEvents(
- _In_ AsyncContext async = {}
- ) noexcept;
+ HRESULT Initialize() noexcept;
- HRESULT UnregisterForGameInviteEvents(
- _In_ AsyncContext async = {}
- ) noexcept;
-
- HRESULT RegisterForAchievementUnlockEvents(
- _In_ AsyncContext async = {}
- ) noexcept;
-
- HRESULT UnregisterForAchievementUnlockEvents(
- _In_ AsyncContext async = {}
+ XblFunctionContext AddGameInviteHandler(
+ _In_ NotificationSubscription::MPSDInviteHandler handler
) noexcept;
XblFunctionContext AddGameInviteHandler(
- _In_ GameInviteSubscription::MPSDInviteHandler handler
+ _In_ NotificationSubscription::MultiplayerActivityInviteHandler handler
) noexcept;
- XblFunctionContext AddGameInviteHandler(
- _In_ GameInviteSubscription::MultiplayerActivityInviteHandler handler
- ) noexcept;
-
- void RemoveGameInviteHandler(
+ void RemoveNotificationHandler(
_In_ XblFunctionContext token
) noexcept;
XblFunctionContext AddAchievementUnlockNotificationHandler(
- _In_ AchievementUnlockSubscription::EventHandler handler
- ) noexcept;
-
- void RemoveAchievementUnlockNotificationHandler(
- _In_ XblFunctionContext token
+ _In_ NotificationSubscription::AchievementUnlockHandler handler
) noexcept;
HRESULT RegisterWithNotificationService(
@@ -218,19 +133,9 @@ public:
) noexcept override;
private:
- template
- HRESULT RegisterForEvents(std::shared_ptr& subscription,
- AsyncContext async) noexcept;
-
- template
- HRESULT UnregisterForEvents(std::shared_ptr& subscription,
- AsyncContext async) noexcept;
-
+ TaskQueue m_taskQueue;
std::shared_ptr m_rtaManager;
-
- std::shared_ptr m_gameInviteSubscription;
- std::shared_ptr m_achievementUnlockSubscription;
- std::shared_ptr m_spopNotificationSubscription;
+ std::shared_ptr m_rtaSubscription;
};
#elif HC_PLATFORM == HC_PLATFORM_UWP
class UWPNotificationService : public NotificationService
diff --git a/Source/Services/Social/Manager/social_graph.cpp b/Source/Services/Social/Manager/social_graph.cpp
index b95fadf7..6be7d195 100644
--- a/Source/Services/Social/Manager/social_graph.cpp
+++ b/Source/Services/Social/Manager/social_graph.cpp
@@ -50,6 +50,9 @@ struct ServiceCallManager : public std::enable_shared_from_this lock) noexcept;
HRESULT PollPeopleHubServiceCall(std::unique_lock lock) noexcept;
@@ -367,6 +370,15 @@ void SocialGraph::RegisterGroup(std::shared_ptr group
if (iter == m_groups.end() || iter->second == GroupInitializationStage::Complete)
{
m_groups[group] = GroupInitializationStage::Pending;
+
+ // Check if the filter is one that relies on title history but TitleHistoryLevel is not set
+ if ((group->presenceFilter == XblPresenceFilter::TitleOffline ||
+ group->presenceFilter == XblPresenceFilter::TitleOnlineOutsideTitle ||
+ group->presenceFilter == XblPresenceFilter::AllTitle) &&
+ (m_serviceCallManager->GetDetailLevel() & XblSocialManagerExtraDetailLevel::TitleHistoryLevel) != XblSocialManagerExtraDetailLevel::TitleHistoryLevel)
+ {
+ LOGS_DEBUG << "TitleOffline, TitleOnlineOutsideTitle, and AllTitle filters require XblSocialManagerExtraDetailLevel::TitleHistoryLevel to be set for this user";
+ }
}
}
@@ -982,4 +994,9 @@ HRESULT ServiceCallManager::PollPeopleHubServiceCall(std::unique_lock&& trackedUsers
) noexcept :
type{ XblSocialUserGroupType::UserListType },
- m_trackedUsersView{ std::move(trackedUsers) },
- m_trackedUsers{ m_trackedUsersView.begin(), m_trackedUsersView.end() },
m_localUser{ socialGraph->LocalUser() },
- m_graph{ socialGraph }
+ m_graph{ socialGraph },
+ m_trackedUsersView{ std::move(trackedUsers) },
+ m_trackedUsers{ m_trackedUsersView.begin(), m_trackedUsersView.end() }
{
m_usersView.reserve(m_trackedUsersView.size());
socialGraph->TrackUsers(m_trackedUsersView);
diff --git a/Source/Shared/build_version.h b/Source/Shared/build_version.h
index 8b6483be..2d6f8507 100644
--- a/Source/Shared/build_version.h
+++ b/Source/Shared/build_version.h
@@ -9,4 +9,4 @@
//*********************************************************
#pragma once
-#define XBOX_SERVICES_API_VERSION_STRING "2021.02.20210318.2"
+#define XBOX_SERVICES_API_VERSION_STRING "2021.04.20210317.0"
diff --git a/Source/Shared/user.cpp b/Source/Shared/user.cpp
index 2654e351..61d6525d 100644
--- a/Source/Shared/user.cpp
+++ b/Source/Shared/user.cpp
@@ -12,7 +12,7 @@ User::User(XblUserHandle userHandle) noexcept
User::User(User&& other) noexcept
- : m_handle{ other.m_handle }, m_localId { std::move(other.m_localId) }, m_xuid {other.m_xuid }
+ : m_handle{ other.m_handle }, m_xuid{ other.m_xuid }, m_localId { std::move(other.m_localId) }
{
Map::iterator it = other.m_gamertags.begin();
@@ -349,7 +349,7 @@ void User::SetTokenExpired(uint64_t xuid) noexcept
}
-Result User::RegisterChangeEventHandler(
+Result User::RegisterChangeEventHandler(
UserChangeEventHandler handler
) noexcept
{
@@ -383,14 +383,14 @@ Result User::RegisterChangeEventHandler(
state->SetUserChangeHandler(token.token, context);
}
}
- return Result{ static_cast(token.token), hr };
+ return Result{token.token, hr };
}
void User::UnregisterChangeEventHandle(
- XblFunctionContext token
+ uint64_t token
) noexcept
{
- XalUserUnregisterChangeEventHandler(XalRegistrationToken{ static_cast(token) });
+ XalUserUnregisterChangeEventHandler(XalRegistrationToken{ token });
auto state{ GlobalState::Get() };
if (state)
{
diff --git a/Source/Shared/user.h b/Source/Shared/user.h
index 9486b390..9e4b0785 100644
--- a/Source/Shared/user.h
+++ b/Source/Shared/user.h
@@ -63,12 +63,12 @@ public:
static void SetTokenExpired(uint64_t xuid) noexcept;
- static Result RegisterChangeEventHandler(
+ static Result RegisterChangeEventHandler(
UserChangeEventHandler handler
) noexcept;
static void UnregisterChangeEventHandle(
- XblFunctionContext token
+ uint64_t token
) noexcept;
private:
@@ -90,7 +90,7 @@ class Result
{
public:
Result(User&& user) : m_payload{ std::move(user) } {}
- Result(User&& user, HRESULT error) : m_payload{ std::move(user) }, m_result{ error } {}
+ Result(User&& user, HRESULT error) : m_result{ error }, m_payload{ std::move(user) } {}
Result(Result&& other) = default;
Result(const Result& other) = delete;
diff --git a/Tests/ApiExplorer/APIExplorer.Shared.vcxitems b/Tests/ApiExplorer/APIExplorer.Shared.vcxitems
index 58ac1317..7ee2be20 100644
--- a/Tests/ApiExplorer/APIExplorer.Shared.vcxitems
+++ b/Tests/ApiExplorer/APIExplorer.Shared.vcxitems
@@ -514,7 +514,7 @@
true
-
+
true
diff --git a/Tests/ApiExplorer/APIs/apis_xblc_achievement_unlock_notification.cpp b/Tests/ApiExplorer/APIs/apis_xblc_achievement_unlock_notification.cpp
index dba9d10b..d8c4a21f 100644
--- a/Tests/ApiExplorer/APIs/apis_xblc_achievement_unlock_notification.cpp
+++ b/Tests/ApiExplorer/APIs/apis_xblc_achievement_unlock_notification.cpp
@@ -130,24 +130,33 @@ void UnsetHandler(XblFunctionContext id)
namespace lua
{
+ int XblAchievementUnlockAddNotificationHandler(lua_State *L)
+ {
+ auto id = SetHandler();
+ lua_pushinteger(L, id);
+ LuaReturnHR(L, S_OK, 1);
+ return 0;
+ }
+
+ int XblAchievementUnlockRemoveNotificationHandler(lua_State *L)
+ {
+ uint32_t id = GetUint32FromLua(L, 1, 0);
+ UnsetHandler(id);
+ return 0;
+ }
int RunAchievementUnlock(lua_State *L)
{
std::string achievementId = luaL_checkstring(L, 1);
-
+
HRESULT hr = 0;
- XblFunctionContext id;
errorCode = status::ERROR_NO_MSG;
- id = SetHandler();
-
Sleep(1000);
-
- hr = UnlockAchievement(achievementId);
-
- lua_pushinteger(L, id);
- return LuaReturnHR(L, hr, 1);
+ hr = UnlockAchievement(achievementId);
+
+ return LuaReturnHR(L, hr);
}
@@ -191,6 +200,8 @@ void SetupAPIs_XblAchievementUnlockNotification()
lua_register(Data()->L, "Cleanup", xbl::apirunner::lua::Cleanup);
lua_register(Data()->L, "CheckStatus", xbl::apirunner::lua::CheckStatus);
lua_register(Data()->L, "IsAchievementLocked", xbl::apirunner::lua::IsAchievementLocked);
+ lua_register(Data()->L, "XblAchievementUnlockAddNotificationHandler", xbl::apirunner::lua::XblAchievementUnlockAddNotificationHandler);
+ lua_register(Data()->L, "XblAchievementUnlockRemoveNotificationHandler", xbl::apirunner::lua::XblAchievementUnlockRemoveNotificationHandler);
}
#endif
\ No newline at end of file
diff --git a/Tests/ApiExplorer/APIs/apis_xblc_stats2017.cpp b/Tests/ApiExplorer/APIs/apis_xblc_stats2017.cpp
index 34d7ae1c..483e66a1 100644
--- a/Tests/ApiExplorer/APIs/apis_xblc_stats2017.cpp
+++ b/Tests/ApiExplorer/APIs/apis_xblc_stats2017.cpp
@@ -167,7 +167,8 @@ int XblTitleManagedStatsWriteAsyncWithSVD_Lua(lua_State *L)
{
LogToFile("XblTitleManagedStatsWriteAsyncWithSVD: hr = %s", ConvertHR(hr).data());
s_svd = nullptr;
- return LuaReturnHR(L, hr);
+ CallLuaFunction("OnXblTitleManagedStatsUnableToGetTokenAndSignature");
+ return LuaReturnHR(L, S_OK);
}
}
@@ -186,6 +187,13 @@ int XblTitleManagedStatsWriteAsyncWithSVD_Lua(lua_State *L)
{
std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock*
HRESULT hr = XAsyncGetStatus(asyncBlock, false);
+
+ if (hr == E_FAIL)
+ {
+ CallLuaFunction("OnXblTitleManagedStatsUnableToGetTokenAndSignature");
+ return;
+ }
+
CallLuaFunctionWithHr(hr, "OnXblTitleManagedStatsWriteAsyncWithSVD"); // CODE SNIP SKIP
};
@@ -238,6 +246,13 @@ int XblTitleManagedStatsWriteAsync_Lua(lua_State *L)
{
LogToScreen("XblTitleManagedStatsWriteAsync: 0x%0.8x", hr);
}
+
+ if (hr == E_FAIL)
+ {
+ CallLuaFunction("OnXblTitleManagedStatsUnableToGetTokenAndSignature");
+ return;
+ }
+
CallLuaFunctionWithHr(hr, "OnXblTitleManagedStatsWriteAsync"); // CODE SNIP SKIP
};
@@ -270,7 +285,8 @@ int XblTitleManagedStatsUpdateStatsAsync_Lua(lua_State* L)
{
LogToFile("XblTitleManagedStatsWriteAsyncWithSVD: hr = %s", ConvertHR(hr).data());
s_svd = nullptr;
- return LuaReturnHR(L, hr);
+ CallLuaFunction("OnXblTitleManagedStatsUnableToGetTokenAndSignature");
+ return LuaReturnHR(L, S_OK);
}
}
@@ -297,6 +313,13 @@ int XblTitleManagedStatsUpdateStatsAsync_Lua(lua_State* L)
{
std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock*
HRESULT hr = XAsyncGetStatus(asyncBlock, false);
+
+ if (hr == E_FAIL)
+ {
+ CallLuaFunction("OnXblTitleManagedStatsUnableToGetTokenAndSignature");
+ return;
+ }
+
CallLuaFunctionWithHr(hr, "OnXblTitleManagedStatsUpdateStatsAsync"); // CODE SNIP SKIP
};
@@ -329,7 +352,8 @@ int XblTitleManagedStatsDeleteStatsAsync_Lua(lua_State* L)
{
LogToFile("XblTitleManagedStatsWriteAsyncWithSVD: hr = %s", ConvertHR(hr).data());
s_svd = nullptr;
- return LuaReturnHR(L, hr);
+ CallLuaFunction("OnXblTitleManagedStatsUnableToGetTokenAndSignature");
+ return LuaReturnHR(L, S_OK);
}
}
@@ -343,6 +367,13 @@ int XblTitleManagedStatsDeleteStatsAsync_Lua(lua_State* L)
{
std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock*
HRESULT hr = XAsyncGetStatus(asyncBlock, false);
+
+ if (hr == E_FAIL)
+ {
+ CallLuaFunction("OnXblTitleManagedStatsUnableToGetTokenAndSignature");
+ return;
+ }
+
CallLuaFunctionWithHr(hr, "OnXblTitleManagedStatsDeleteStatsAsync"); // CODE SNIP SKIP
};
@@ -401,7 +432,8 @@ int ClearSVD_Lua(lua_State* L)
{
LogToFile("XblTitleManagedStatsWriteAsyncWithSVD: hr = %s", ConvertHR(hr).data());
s_svd = nullptr;
- return LuaReturnHR(L, hr);
+ lua_pushnumber(L, 1);
+ return 1;
}
}
s_svd->ClearStats();
diff --git a/Tests/ApiExplorer/APIs/apis_xblc_title_storage.cpp b/Tests/ApiExplorer/APIs/apis_xblc_title_storage.cpp
index 7e977a2a..417281dd 100644
--- a/Tests/ApiExplorer/APIs/apis_xblc_title_storage.cpp
+++ b/Tests/ApiExplorer/APIs/apis_xblc_title_storage.cpp
@@ -589,26 +589,30 @@ int XblTitleStorageUploadBinaryBlobAsync_Lua(lua_State *L)
return LuaReturnHR(L, hr);
}
-int RestCallForEachBlob_Lua(lua_State *L)
+int RestCallToDownloadJsonBlob_Lua(lua_State *L)
{
CreateQueueIfNeeded();
- auto response = Data()->responseString;
+ auto response = Data()->metadataResponseString;
HRESULT hr = S_OK;
- auto result = DeserializeResult("https://titlestorage.xboxlive.com/universalplatform/users/xuid(2814654044759996)/scids/00000000-0000-0000-0000-000076029b4d/data/", response);
+ std::string methodName = GetStringFromLua(L, 1, "GET");
+ char url[300];
+ sprintf_s(url, "https://titlestorage.xboxlive.com/json/users/xuid(%" PRIu64 ")/scids/00000000-0000-0000-0000-000076029b4d/data/", Data()->xboxUserId);
+
+ auto result = DeserializeResult(url, response);
for (const auto& blobMetadata : result.m_items)
{
// Download the blob
- XblHttpCallHandle output;
- hr = XblHttpCallCreate(Data()->xboxLiveContext, "GET", blobMetadata.blobPath.c_str(), &output);
- XblHttpCallRequestSetHeader(output, "Content-Type", "application/json; charset=utf-8", true);
- XblHttpCallRequestSetHeader(output, "Accept-Language", "en-US,en", true);
- XblHttpCallRequestSetHeader(output, "x-xbl-contract-version", "2", true);
- Data()->downloadHttpCalls.push_back(output);
+ XblHttpCallHandle httpCallHandle;
+ hr = XblHttpCallCreate(Data()->xboxLiveContext, "GET", blobMetadata.blobPath.c_str(), &httpCallHandle);
+ XblHttpCallRequestSetHeader(httpCallHandle, "Content-Type", "application/json; charset=utf-8", true);
+ XblHttpCallRequestSetHeader(httpCallHandle, "Accept-Language", "en-US,en", true);
+ XblHttpCallRequestSetHeader(httpCallHandle, "x-xbl-contract-version", "2", true);
+ Data()->titleStorageHttpCalls.push_back(httpCallHandle);
auto asyncBlock = std::make_unique();
asyncBlock->queue = Data()->queue;
- asyncBlock->context = Data()->downloadHttpCalls[blobMetadata.positionInList];
+ asyncBlock->context = Data()->titleStorageHttpCalls[blobMetadata.positionInList];
asyncBlock->callback = [](XAsyncBlock* asyncBlock)
{
std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock*
@@ -621,9 +625,12 @@ int RestCallForEachBlob_Lua(lua_State *L)
const char* responseString;
hr = XblHttpCallGetResponseString(httpCall, &responseString);
- LogToScreen("Response String: length %d, hr=%s", strlen(responseString), ConvertHR(hr).c_str());
+ LogToFile("Response String: length %d, hr=%s", strlen(responseString), ConvertHR(hr).c_str());
+ LogToFile("Response: %s", responseString);
CHECKHR(hr);
+ Data()->blobResponseStrings.push_back(responseString);
+
uint32_t statusCode;
hr = XblHttpCallGetStatusCode(httpCall, &statusCode);
LogToScreen("Status Code: %d, hr=%s", statusCode, ConvertHR(hr).c_str());
@@ -631,42 +638,99 @@ int RestCallForEachBlob_Lua(lua_State *L)
}
Cleanup:
- Data()->completedDownloads++;
- if (Data()->completedDownloads == Data()->downloadHttpCalls.size())
+ Data()->titleStorageCompletedHttpCalls++;
+ if (Data()->titleStorageCompletedHttpCalls == Data()->titleStorageHttpCalls.size())
{
- CallLuaFunctionWithHr(hr, "OnRestCallForEachBlob");
+ CallLuaFunctionWithHr(hr, "OnDownloadBlobs");
}
LogToScreen("XblHttpCallPerformAsync Completion: hr=%s", ConvertHR(hr).c_str());
};
LogToScreen("Downloading %s", blobMetadata.blobPath.c_str());
- hr = XblHttpCallPerformAsync(output, XblHttpCallResponseBodyType::String, asyncBlock.get());
+ hr = XblHttpCallPerformAsync(httpCallHandle, XblHttpCallResponseBodyType::String, asyncBlock.get());
if (SUCCEEDED(hr))
{
// The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership.
// If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock*
asyncBlock.release();
}
-
}
return LuaReturnHR(L, hr);
}
-int RestCallForTMSMetadata_Lua(lua_State *L)
+int RestCallToUploadJsonBlob_Lua(lua_State *L)
+{
+ CreateQueueIfNeeded();
+ auto response = Data()->metadataResponseString;
+ HRESULT hr = S_OK;
+
+ std::string methodName = GetStringFromLua(L, 1, "GET");
+ char url[300];
+ sprintf_s(url, "https://titlestorage.xboxlive.com/json/users/xuid(%" PRIu64 ")/scids/00000000-0000-0000-0000-000076029b4d/data/apirunner/test/json/file.json,json", Data()->xboxUserId);
+
+ std::string blobContent = GetStringFromLua(L, 1, "{}");
+
+ // Upload the blob
+ XblHttpCallHandle httpCallHandle;
+ hr = XblHttpCallCreate(Data()->xboxLiveContext, "PUT", url, &httpCallHandle);
+ XblHttpCallRequestSetHeader(httpCallHandle, "Content-Type", "application/json; charset=utf-8", true);
+ XblHttpCallRequestSetHeader(httpCallHandle, "Accept-Language", "en-US,en", true);
+ XblHttpCallRequestSetHeader(httpCallHandle, "x-xbl-contract-version", "2", true);
+
+ XblHttpCallRequestSetRequestBodyString(httpCallHandle, blobContent.c_str());
+
+ Data()->xblHttpCall = httpCallHandle;
+
+ auto asyncBlock = std::make_unique();
+ asyncBlock->queue = Data()->queue;
+ asyncBlock->context = Data()->xblHttpCall;
+ asyncBlock->callback = [](XAsyncBlock* asyncBlock)
+ {
+ std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock*
+ HRESULT hr = XAsyncGetStatus(asyncBlock, false);
+
+ if (SUCCEEDED(hr))
+ {
+ auto httpCall = static_cast(asyncBlock->context);
+
+ uint32_t statusCode;
+ hr = XblHttpCallGetStatusCode(httpCall, &statusCode);
+ LogToScreen("Status Code: %d, hr=%s", statusCode, ConvertHR(hr).c_str());
+ CHECKHR(hr);
+ }
+
+ Cleanup:
+ CallLuaFunctionWithHr(hr, "OnXblTitleStorageRestUpload");
+ LogToScreen("XblHttpCallPerformAsync Completion: hr=%s", ConvertHR(hr).c_str());
+ };
+
+ LogToScreen("Uploading %s", url);
+ hr = XblHttpCallPerformAsync(httpCallHandle, XblHttpCallResponseBodyType::String, asyncBlock.get());
+ if (SUCCEEDED(hr))
+ {
+ // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership.
+ // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock*
+ asyncBlock.release();
+ }
+
+ return LuaReturnHR(L, hr);
+}
+
+int RestCallForJsonMetadata_Lua(lua_State *L)
{
CreateQueueIfNeeded();
std::string methodName = GetStringFromLua(L, 1, "GET");
char url[300];
- sprintf_s(url, "https://titlestorage.xboxlive.com/universalplatform/users/xuid(%" PRIu64 ")/scids/00000000-0000-0000-0000-000076029b4d/data?maxItems=100", Data()->xboxUserId);
+ sprintf_s(url, "https://titlestorage.xboxlive.com/json/users/xuid(%" PRIu64 ")/scids/00000000-0000-0000-0000-000076029b4d/data/apirunner/test/json?maxItems=100", Data()->xboxUserId);
- XblHttpCallHandle output;
- HRESULT hr = XblHttpCallCreate(Data()->xboxLiveContext, methodName.c_str(), url, &output);
- XblHttpCallRequestSetHeader(output, "Content-Type", "application/json; charset=utf-8", true);
- XblHttpCallRequestSetHeader(output, "Accept-Language", "en-US,en", true);
- XblHttpCallRequestSetHeader(output, "x-xbl-contract-version", "2", true);
- Data()->xblHttpCall = output;
+ XblHttpCallHandle httpCallHandle;
+ HRESULT hr = XblHttpCallCreate(Data()->xboxLiveContext, methodName.c_str(), url, &httpCallHandle);
+ XblHttpCallRequestSetHeader(httpCallHandle, "Content-Type", "application/json; charset=utf-8", true);
+ XblHttpCallRequestSetHeader(httpCallHandle, "Accept-Language", "en-US,en", true);
+ XblHttpCallRequestSetHeader(httpCallHandle, "x-xbl-contract-version", "2", true);
+ Data()->xblHttpCall = httpCallHandle;
auto asyncBlock = std::make_unique();
asyncBlock->queue = Data()->queue;
@@ -681,7 +745,7 @@ int RestCallForTMSMetadata_Lua(lua_State *L)
{
const char* responseString;
hr = XblHttpCallGetResponseString(Data()->xblHttpCall, &responseString);
- Data()->responseString = responseString;
+ Data()->metadataResponseString = responseString;
LogToScreen("XblHttpCallResponseGetResponseString: length %d, hr=%s", strlen(responseString), ConvertHR(hr).c_str());
CHECKHR(hr);
@@ -694,7 +758,7 @@ int RestCallForTMSMetadata_Lua(lua_State *L)
Cleanup:
LogToScreen("XblHttpCallPerformAsync Completion: hr=%s", ConvertHR(hr).c_str());
- CallLuaFunctionWithHr(hr, "OnXblTitleStorageRestTMSMetadata");
+ CallLuaFunctionWithHr(hr, "OnDownloadMetadataBlobs");
};
hr = XblHttpCallPerformAsync(Data()->xblHttpCall, XblHttpCallResponseBodyType::String, asyncBlock.get());
@@ -708,10 +772,13 @@ int RestCallForTMSMetadata_Lua(lua_State *L)
return LuaReturnHR(L, hr);
}
+
+
void SetupAPIs_XblTitleStorage()
{
- lua_register(Data()->L, "RestCallForTMSMetadata", RestCallForTMSMetadata_Lua);
- lua_register(Data()->L, "RestCallForEachBlob", RestCallForEachBlob_Lua);
+ lua_register(Data()->L, "RestCallForJsonMetadata", RestCallForJsonMetadata_Lua);
+ lua_register(Data()->L, "RestCallToDownloadJsonBlob", RestCallToDownloadJsonBlob_Lua);
+ lua_register(Data()->L, "RestCallToUploadJsonBlob", RestCallToUploadJsonBlob_Lua);
lua_register(Data()->L, "XblTitleStorageBlobMetadataResultGetItems", XblTitleStorageBlobMetadataResultGetItems_Lua);
lua_register(Data()->L, "XblTitleStorageBlobMetadataResultHasNext", XblTitleStorageBlobMetadataResultHasNext_Lua);
lua_register(Data()->L, "XblTitleStorageBlobMetadataResultGetNextAsync", XblTitleStorageBlobMetadataResultGetNextAsync_Lua);
diff --git a/Tests/ApiExplorer/Include/api_explorer.h b/Tests/ApiExplorer/Include/api_explorer.h
index e4500358..0dc00de3 100644
--- a/Tests/ApiExplorer/Include/api_explorer.h
+++ b/Tests/ApiExplorer/Include/api_explorer.h
@@ -144,9 +144,10 @@ struct ApiExplorerData
#endif
// Title Storage Rest API Calls Data
- std::string responseString;
- std::vector downloadHttpCalls;
- size_t completedDownloads = 0;
+ std::vector blobResponseStrings;
+ std::string metadataResponseString;
+ std::vector titleStorageHttpCalls;
+ size_t titleStorageCompletedHttpCalls = 0;
size_t filesToDownload = 0;
// MP
diff --git a/Tests/ApiExplorer/Shared/apirunnercloudfns.h b/Tests/ApiExplorer/Shared/apirunnercloudfns.h
deleted file mode 100644
index b34dbe53..00000000
--- a/Tests/ApiExplorer/Shared/apirunnercloudfns.h
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) Microsoft Corporation
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-#define APIRUNNER_GET_JOINABLE "https://xblapirunnerazurefn20191216050459.azurewebsites.net/api/XblGetJoinable?code=0PsXYMnieKRiJhkhmLg52pmFGY24tCayBgOQm7PFsyxa1ANiOEn7wA=="
-#define APIRUNNER_HOST_SESSION "https://xblapirunnerazurefn20191216050459.azurewebsites.net/api/XblHostSession?code=q0d2RbFpaB2h2K3tnC4OUKsaIFwAapXTQ1tSF7wCoMaGMNlEqQSKpg=="
-#define APIRUNNER_JOIN_SESSION "https://xblapirunnerazurefn20191216050459.azurewebsites.net/api/XblJoinSession?code=VEhjW85xwYp4RVIS0PXTnatAVjN1pgWshiMokLAaDu8fWRIFaB4aUg=="
-#define APIRUNNER_SET_STATE "https://xblapirunnerazurefn20191216050459.azurewebsites.net/api/XblSetState?code=Xmlo7wectT9hUYB6GpT4yXthnbDPO3OweMCtreLkY444ykwgELOQnQ=="
-#define APIRUNNER_GET_STATE "https://xblapirunnerazurefn20191216050459.azurewebsites.net/api/XblGetState?code=677yKK5hKpLrH9KekQKHfqsoywL5NmcZ0fZQx7rMeNyeh2pHi1UDvQ=="
diff --git a/Tests/ApiExplorer/Shared/pch_common.h b/Tests/ApiExplorer/Shared/pch_common.h
index b27d4545..e9ffccfd 100644
--- a/Tests/ApiExplorer/Shared/pch_common.h
+++ b/Tests/ApiExplorer/Shared/pch_common.h
@@ -51,7 +51,7 @@
#undef RAPIDJSON_NAMESPACE_BEGIN
#undef RAPIDJSON_NAMESPACE_END
#endif
-#include "../External/rapidjson/include/rapidjson/document.h"
+#include "../../../External/rapidjson/include/rapidjson/document.h"
#include "../Include/multidevice.h"
#include "../Include/api_explorer.h"
diff --git a/Tests/ApiExplorer/Shared/utils.h b/Tests/ApiExplorer/Shared/utils.h
index e0ddf357..b30a852a 100644
--- a/Tests/ApiExplorer/Shared/utils.h
+++ b/Tests/ApiExplorer/Shared/utils.h
@@ -8,7 +8,7 @@
// internal test-only APIs
#define NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN namespace xbox { namespace services {
#define NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END }}
-#include "../Source/Shared/fault_injection.h"
+#include "../../../Source/Shared/fault_injection.h"
#define VERIFY_IS_TRUE(x, y) if (SUCCEEDED(hr)) { hr = VerifyIsTrue(x, y); }
#define ENSURE_IS_TRUE(x, y) if (FAILED(VerifyIsTrue(x, y))) return LuaReturnHR(L, E_ABORT);
diff --git a/Tests/ApiExplorer/Tests/notification/achievement_unlock_notification.lua b/Tests/ApiExplorer/Tests/notification/achievement_unlock_notification.lua
index 55c110e0..8941871f 100644
--- a/Tests/ApiExplorer/Tests/notification/achievement_unlock_notification.lua
+++ b/Tests/ApiExplorer/Tests/notification/achievement_unlock_notification.lua
@@ -22,8 +22,12 @@ function achievement_unlock_notification_test()
return;
end
- -- register handler and unlock achievement, returns handler id
- local id = RunAchievementUnlock(achievementId);
+ -- register achievement unlock handler invite handler
+ local id = XblAchievementUnlockAddNotificationHandler()
+ XblGameInviteAddNotificationHandler()
+
+ -- unlock achievement
+ RunAchievementUnlock(achievementId);
local status;
@@ -38,8 +42,9 @@ function achievement_unlock_notification_test()
-- anything other than 0 is an error
test.equal(status,0);
- -- unregister handler
- Cleanup(id);
+ -- unregister handlers
+ XblAchievementUnlockRemoveNotificationHandler(id);
+ XblGameInviteRemoveNotificationHandler()
test.stopTest();
end
diff --git a/Tests/ApiExplorer/Tests/rta/simpleRTA.lua b/Tests/ApiExplorer/Tests/rta/simpleRTA.lua
index a77ce038..f46625e3 100644
--- a/Tests/ApiExplorer/Tests/rta/simpleRTA.lua
+++ b/Tests/ApiExplorer/Tests/rta/simpleRTA.lua
@@ -11,6 +11,7 @@ end
function OnXblRealTimeActivityAddConnectionStateChangeHandler_Connected()
print("RTA connection connected");
XblSocialRemoveSocialRelationshipChangedHandler();
+ XblContextCloseHandle();
end
function OnXblRealTimeActivityAddConnectionStateChangeHandler_Disconnected()
diff --git a/Tests/ApiExplorer/Tests/stats2017/stats2017.lua b/Tests/ApiExplorer/Tests/stats2017/stats2017.lua
index 36ff3d2d..1c72bdb4 100644
--- a/Tests/ApiExplorer/Tests/stats2017/stats2017.lua
+++ b/Tests/ApiExplorer/Tests/stats2017/stats2017.lua
@@ -14,9 +14,17 @@ function OnXblTitleManagedStatsDeleteStatsAsync()
test.stopTest()
end
+function OnXblTitleManagedStatsUnableToGetTokenAndSignature()
+ print("OnXblTitleManagedStatsUnableToGetTokenAndSignature")
+ test.stopTest()
+end
+
function TitleManagedStats_Handler()
- ClearSVD()
- XblTitleManagedStatsWriteAsyncWithSVD()
+ if ClearSVD() == 1 then
+ OnXblTitleManagedStatsUnableToGetTokenAndSignature()
+ else
+ XblTitleManagedStatsWriteAsyncWithSVD()
+ end
end
test.TitleManagedStats = function()
diff --git a/Tests/ApiExplorer/Tests/titleStorage/title_storage-restCalls.lua b/Tests/ApiExplorer/Tests/titleStorage/title_storage-restCalls.lua
index 82b10114..c3c36f1e 100644
--- a/Tests/ApiExplorer/Tests/titleStorage/title_storage-restCalls.lua
+++ b/Tests/ApiExplorer/Tests/titleStorage/title_storage-restCalls.lua
@@ -3,20 +3,26 @@ common = require 'common'
function TitleStorageRestAPI()
print("TitleStorage");
- RestCallForTMSMetadata();
+ RestCallToUploadJsonBlob(
+ "{\"difficulty\":1,\"level\":[{\"number\":\"1\",\"quest\":\"swords\"},{\"number\":\"2\",\"quest\":\"iron\"},{\"number\":\"3\",\"quest\":\"gold\"}],\"weapon\":{\"name\":\"poison\",\"timeleft\":\"2mins\"}}"
+ );
end
-function OnXblTitleStorageRestTMSMetadata()
- print('Calling RestCallForEachBlob')
- RestCallForEachBlob();
+function OnXblTitleStorageRestUpload()
+ print('Calling RestCallToUploadJsonBlob')
+ RestCallForJsonMetadata();
end
-function OnRestCallForEachBlob()
- print('OnRestCallForEachBlob')
+function OnDownloadMetadataBlobs()
+ print('OnUploadBlobs')
+ RestCallToDownloadJsonBlob();
+end
+
+function OnDownloadBlobs()
+ print('OnDownloadBlobs')
test.stopTest();
end
-test.skip = true
test.TitleStorageRestAPI = function()
common.init(TitleStorageRestAPI)
end
\ No newline at end of file
diff --git a/Tests/GDK/APIRunner.GDK/APIRunner.GDK.Bin.vcxproj b/Tests/GDK/APIRunner.GDK/APIRunner.GDK.Bin.vcxproj
index 339ff3e4..f7368e59 100644
--- a/Tests/GDK/APIRunner.GDK/APIRunner.GDK.Bin.vcxproj
+++ b/Tests/GDK/APIRunner.GDK/APIRunner.GDK.Bin.vcxproj
@@ -50,7 +50,7 @@
x64
-
+
Application
@@ -383,7 +383,7 @@
- uuid.lib;$(Console_Libs);%(XboxExtensionsDependencies);%(AdditionalDependencies)
+ uuid.lib;$(Console_Libs);%(XboxExtensionsDependencies);dbghelp.lib;%(AdditionalDependencies)
Windows
true
@@ -514,7 +514,6 @@
-
%(Filename)%(Extension)
@@ -552,8 +551,6 @@
true
-
-
diff --git a/Tests/GDK/APIRunner.GDK/APIRunner.GDK.Src.vcxproj b/Tests/GDK/APIRunner.GDK/APIRunner.GDK.Src.vcxproj
index 73b74858..c4527e34 100644
--- a/Tests/GDK/APIRunner.GDK/APIRunner.GDK.Src.vcxproj
+++ b/Tests/GDK/APIRunner.GDK/APIRunner.GDK.Src.vcxproj
@@ -560,7 +560,6 @@
true
-
diff --git a/Tests/UnitTests/Tests/Services/AchievementsTests.cpp b/Tests/UnitTests/Tests/Services/AchievementsTests.cpp
index 55a79bd3..7ef39156 100644
--- a/Tests/UnitTests/Tests/Services/AchievementsTests.cpp
+++ b/Tests/UnitTests/Tests/Services/AchievementsTests.cpp
@@ -512,6 +512,11 @@ public:
xbox::services::Vector requirements;
};
xbox::services::Vector entries;
+
+ // To keep the strings alive outside the handler for this test
+ xbox::services::String idContainer{};
+ xbox::services::String currentProgressContainer{};
+ xbox::services::String targetProgressContainer{};
} context;
auto handlerToken = XblAchievementsAddAchievementProgressChangeHandler(xboxLiveContext.get(),
@@ -524,10 +529,13 @@ public:
const XblAchievementProgressChangeEntry& updateEntry = args->updatedAchievementEntries[entryIndex];
entry.achievementId = updateEntry.achievementId;
entry.progressState = updateEntry.progressState;
- entry.requirements = xbox::services::Vector(
- updateEntry.progression.requirements,
- updateEntry.progression.requirements + updateEntry.progression.requirementsCount
- );
+ entry.requirements = xbox::services::Vector(updateEntry.progression.requirementsCount);
+
+ c->idContainer = updateEntry.progression.requirements[0].id;
+ c->currentProgressContainer = updateEntry.progression.requirements[0].currentProgressValue;
+ c->targetProgressContainer = updateEntry.progression.requirements[0].targetProgressValue;
+ entry.requirements[0] = { c->idContainer.c_str(), c->currentProgressContainer.c_str(), c->targetProgressContainer.c_str() };
+
c->entries.push_back(entry);
}
diff --git a/Utilities/CMake/CMakeLists.txt b/Utilities/CMake/CMakeLists.txt
index 77348e8a..3e93e413 100644
--- a/Utilities/CMake/CMakeLists.txt
+++ b/Utilities/CMake/CMakeLists.txt
@@ -367,11 +367,8 @@ if ( PCWIN32 )
list(APPEND
Notification_Source_Files
../../Source/Services/Notification/RTA/notification_service_rta.cpp
- ../../Source/Services/Notification/RTA/game_invite_subscription.cpp
- ../../Source/Services/Notification/RTA/achievement_unlock_subscription.h
- ../../Source/Services/Notification/RTA/achievement_unlock_subscription.cpp
- ../../Source/Services/Notification/RTA/spop_kick_subscription.h
- ../../Source/Services/Notification/RTA/spop_kick_subscription.cpp
+ ../../Source/Services/Notification/RTA/notification_subscription.h
+ ../../Source/Services/Notification/RTA/notification_subscription.cpp
)
endif()
diff --git a/Utilities/VSOBuildScripts/CopyGDKToGithub.cmd b/Utilities/VSOBuildScripts/CopyGDKToGithub.cmd
index 58bd8928..59427252 100644
--- a/Utilities/VSOBuildScripts/CopyGDKToGithub.cmd
+++ b/Utilities/VSOBuildScripts/CopyGDKToGithub.cmd
@@ -102,6 +102,7 @@ rmdir "%githubPath%Tests\ApiExplorer\UWP" /s /q
rmdir "%githubPath%Tests\ApiExplorer\Win" /s /q
rmdir "%githubPath%Tests\ApiExplorer\Win32" /s /q
rmdir "%githubPath%Tests\ApiExplorer\XDK" /s /q
+del "%githubPath%Tests\APIExplorer\Shared\apirunnercloudfns.h"
robocopy "%adoPath%Tests\GDK" "%githubPath%Tests\GDK" /s
robocopy "%adoPath%Tests\UnitTests" "%githubPath%Tests\UnitTests" /s
diff --git a/Utilities/VSOBuildScripts/fetchTools.cmd b/Utilities/VSOBuildScripts/fetchTools.cmd
index 34614794..e2f145e3 100644
--- a/Utilities/VSOBuildScripts/fetchTools.cmd
+++ b/Utilities/VSOBuildScripts/fetchTools.cmd
@@ -7,6 +7,7 @@ set buildToolsBranchArg=%4
cd /D %BUILD_STAGINGDIRECTORY%
call git clone https://anything:%patArg%@microsoft.visualstudio.com/DefaultCollection/Xbox.Services/_git/sdk.buildtools
cd sdk.buildtools
+git reset --hard HEAD
if "%buildToolsBranchArg%" NEQ "" call git checkout %buildToolsBranchArg%
cd /D %BUILD_STAGINGDIRECTORY%
dir "%BUILD_STAGINGDIRECTORY%\sdk.buildtools\buildMachine
@@ -17,6 +18,7 @@ goto skipExt
cd /D %BUILD_STAGINGDIRECTORY%
call git clone https://anything:%patArg%@microsoft.visualstudio.com/Xbox.Services/_git/sdk.external
cd sdk.external
+git reset --hard HEAD
if "%externalBranchArg%" NEQ "" call git checkout %externalBranchArg%
cd /D %BUILD_STAGINGDIRECTORY%
dir "%BUILD_STAGINGDIRECTORY%\sdk.external\ExtractedGDK\"