Родитель
730f579d41
Коммит
df1907c417
|
@ -20,6 +20,6 @@ extern "C"
|
|||
/// YYYYMMDD Date string describing the date the build was created
|
||||
/// rrr QFE number (000 indicates base release)
|
||||
/// </summary>
|
||||
#define XAL_VERSION "2020.11.20201204.001"
|
||||
#define XAL_VERSION "2021.04.20210319.000"
|
||||
|
||||
}
|
||||
|
|
|
@ -3293,9 +3293,13 @@ STDAPI XblMultiplayerGetSessionAsync(
|
|||
/// Gets the result of an XblMultiplayerGetSessionResult call.
|
||||
/// </summary>
|
||||
/// <param name="async">The AsyncBlock for this operation.</param>
|
||||
/// <param name="handle">Passes back a handle to a new instance of a local multiplayer session object.
|
||||
/// <param name="handle">Passes back a handle to a new instance of a local multiplayer session object.
|
||||
/// It must be release by the caller with <see cref="XblMultiplayerSessionCloseHandle"/>.</param>
|
||||
/// <returns>HRESULT return code for this API operation.</returns>
|
||||
/// <remarks>
|
||||
/// If the session does not exist, this API will return __HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND) and a null
|
||||
/// XblMultiplayerSessionHandle.
|
||||
/// </remarks>
|
||||
STDAPI XblMultiplayerGetSessionResult(
|
||||
_In_ XAsyncBlock* async,
|
||||
_Out_ XblMultiplayerSessionHandle* handle
|
||||
|
@ -3326,6 +3330,10 @@ STDAPI XblMultiplayerGetSessionByHandleAsync(
|
|||
/// <param name="handle">Passes back a handle to a new instance of a local multiplayer session object.
|
||||
/// It must be release by the caller with <see cref="XblMultiplayerSessionCloseHandle"/>.</param>
|
||||
/// <returns>HRESULT return code for this API operation.</returns>
|
||||
/// <remarks>
|
||||
/// If the session does not exist, this API will return __HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND) and a null
|
||||
/// XblMultiplayerSessionHandle.
|
||||
/// </remarks>
|
||||
STDAPI XblMultiplayerGetSessionByHandleResult(
|
||||
_In_ XAsyncBlock* async,
|
||||
_Out_ XblMultiplayerSessionHandle* handle
|
||||
|
|
|
@ -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.
|
||||
/// </remarks>
|
||||
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.
|
||||
/// </remarks>
|
||||
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.
|
||||
/// </remarks>
|
||||
STDAPI XblMultiplayerManagerJoinGameFromLobby(
|
||||
_In_z_ const char* sessionTemplateName
|
||||
|
@ -1147,6 +1150,7 @@ STDAPI XblMultiplayerManagerJoinGameFromLobby(
|
|||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
STDAPI XblMultiplayerManagerJoinGame(
|
||||
_In_z_ const char* sessionName,
|
||||
|
|
|
@ -63,11 +63,17 @@ enum class XblPresenceFilter : uint32_t
|
|||
/// <summary>
|
||||
/// Has played this title and is offline.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This filter option requires <see cref="XblSocialManagerExtraDetailLevel"/>::TitleHistoryLevel to be set in <see cref="XblSocialManagerAddLocalUser"/>
|
||||
/// </remarks>
|
||||
TitleOffline,
|
||||
|
||||
/// <summary>
|
||||
/// Has played this title, is online but not currently playing this title.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This filter option requires <see cref="XblSocialManagerExtraDetailLevel"/>::TitleHistoryLevel to be set in <see cref="XblSocialManagerAddLocalUser"/>
|
||||
/// </remarks>
|
||||
TitleOnlineOutsideTitle,
|
||||
|
||||
/// <summary>
|
||||
|
@ -83,6 +89,9 @@ enum class XblPresenceFilter : uint32_t
|
|||
/// <summary>
|
||||
/// Everyone who has played or is playing the title.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This filter option requires <see cref="XblSocialManagerExtraDetailLevel"/>::TitleHistoryLevel to be set in <see cref="XblSocialManagerAddLocalUser"/>
|
||||
/// </remarks>
|
||||
AllTitle,
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -74,10 +74,10 @@ AchievementsService::AchievementsService(
|
|||
_In_ std::shared_ptr<xbox::services::real_time_activity::RealTimeActivityManager> 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) }
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -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<xbox::services::notification::RTANotificationService>(userResult.ExtractPayload(), m_xboxLiveContextSettings, rtaManager);
|
||||
m_notificationService->RegisterForSpopNotificationEvents();
|
||||
m_notificationService = MakeShared<xbox::services::notification::RTANotificationService>(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<xbox::services::notification::MobileNotificationService>(userResult.ExtractPayload(), m_xboxLiveContextSettings);
|
||||
#elif HC_PLATFORM == HC_PLATFORM_UWP
|
||||
|
|
|
@ -169,7 +169,7 @@ private:
|
|||
std::shared_ptr<xbox::services::events::IEventsService> m_eventsService;
|
||||
#endif
|
||||
|
||||
XblFunctionContext m_userChangeEventToken{ 0 };
|
||||
uint64_t m_userChangeEventToken{ 0 };
|
||||
uint64_t m_xuid{ 0 };
|
||||
|
||||
xbox::services::User m_user;
|
||||
|
|
|
@ -68,36 +68,39 @@ LeaderboardResult::ParseAdditionalColumns(const xsapi_internal_vector<xsapi_inte
|
|||
{
|
||||
const xsapi_internal_string& columnName = additionalColumnNames[i];
|
||||
auto stat = stats.find(columnName);
|
||||
const JsonValue& val = row.m_metadata[columnName.c_str()];
|
||||
if (stat == stats.end() || stat->second == 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -291,7 +291,7 @@ STDAPI_(XblFunctionContext) XblMultiplayerActivityAddInviteHandler(
|
|||
}
|
||||
|
||||
auto rtaNotificationService = std::dynamic_pointer_cast<RTANotificationService>(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<RTANotificationService>(xblContext->NotificationService());
|
||||
rtaNotificationService->RemoveGameInviteHandler(token);
|
||||
rtaNotificationService->RemoveNotificationHandler(token);
|
||||
return S_OK;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -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<std::mutex> lock{ m_mutex };
|
||||
m_handlers[m_nextId] = std::move(handler);
|
||||
return m_nextId++;
|
||||
}
|
||||
|
||||
size_t
|
||||
AchievementUnlockSubscription::RemoveHandler(XblFunctionContext id) noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<std::mutex> 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> 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<uint32_t>(titleId);
|
||||
result.rarityPercentage = static_cast<float>(rarityPercentage);
|
||||
result.rarityCategory = EnumValue<XblAchievementRarityCategory>(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
|
|
@ -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<AchievementUnlockEvent> 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
|
|
@ -11,112 +11,52 @@
|
|||
|
||||
NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_BEGIN
|
||||
|
||||
template<typename T>
|
||||
HRESULT RTANotificationService::RegisterForEvents(std::shared_ptr<T>& subscription,
|
||||
AsyncContext<HRESULT> async) noexcept
|
||||
RTANotificationService::RTANotificationService(
|
||||
_In_ User&& user,
|
||||
_In_ const TaskQueue& queue,
|
||||
_In_ std::shared_ptr<xbox::services::XboxLiveContextSettings> contextSettings,
|
||||
_In_ std::shared_ptr<xbox::services::real_time_activity::RealTimeActivityManager> 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<std::recursive_mutex> lock{ m_mutex };
|
||||
if (!subscription)
|
||||
{
|
||||
m_rtaManager->Activate(m_user);
|
||||
subscription = MakeShared<T>(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<NotificationSubscription>(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<typename T>
|
||||
HRESULT RTANotificationService::RTANotificationService::UnregisterForEvents(std::shared_ptr<T>& subscription,
|
||||
AsyncContext<HRESULT> async) noexcept
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> 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<xbox::services::XboxLiveContextSettings> contextSettings,
|
||||
_In_ std::shared_ptr<xbox::services::real_time_activity::RealTimeActivityManager> rtaManager
|
||||
) noexcept :
|
||||
NotificationService(std::move(user), contextSettings),
|
||||
m_rtaManager{ std::move(rtaManager) }
|
||||
{
|
||||
}
|
||||
|
||||
HRESULT RTANotificationService::RegisterForSpopNotificationEvents() noexcept
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> 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<SpopKickSubscription>(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<HRESULT> async
|
||||
) noexcept
|
||||
{
|
||||
return RegisterForEvents(m_gameInviteSubscription, async);
|
||||
};
|
||||
|
||||
HRESULT RTANotificationService::UnregisterForGameInviteEvents(
|
||||
_In_ AsyncContext<HRESULT> async
|
||||
) noexcept
|
||||
{
|
||||
return UnregisterForEvents(m_gameInviteSubscription, async);
|
||||
}
|
||||
|
||||
|
||||
HRESULT RTANotificationService::RegisterForAchievementUnlockEvents(
|
||||
_In_ AsyncContext<HRESULT> async
|
||||
) noexcept
|
||||
{
|
||||
return RegisterForEvents(m_achievementUnlockSubscription, async);
|
||||
};
|
||||
|
||||
|
||||
HRESULT RTANotificationService::UnregisterForAchievementUnlockEvents(
|
||||
_In_ AsyncContext<HRESULT> async
|
||||
) noexcept
|
||||
{
|
||||
return UnregisterForEvents(m_achievementUnlockSubscription, async);
|
||||
m_rtaSubscription->ResourceUri(),
|
||||
AsyncContext<HRESULT>{ 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<HRESULT> async
|
||||
) noexcept
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock{ m_mutex };
|
||||
std::lock_guard<std::recursive_mutex> 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<NotificationFilter> notificationFilterList;
|
||||
notificationFilterList.push_back({ NotificationTypeFilterSourceType::Multiplayer, 1 });
|
||||
notificationFilterList.push_back({ NotificationTypeFilterSourceType::Multiplayer, 8 });
|
||||
notificationFilterList.push_back({ NotificationTypeFilterSourceType::Achievements, 1 });
|
||||
Vector<NotificationFilter> 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<HRESULT>{
|
||||
async.Queue(),
|
||||
[thisWeakPtr = std::weak_ptr<NotificationService>{ shared_from_this() }, async](HRESULT hr)
|
||||
{
|
||||
if (auto pThis{ thisWeakPtr.lock() })
|
||||
{
|
||||
auto derivedPtr = dynamic_cast<RTANotificationService*>(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<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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
|
||||
|
|
|
@ -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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<XAsyncBlock>();
|
||||
async->queue = m_queue.GetHandle();
|
||||
async->callback = [](XAsyncBlock* async)
|
||||
{
|
||||
// Take ownership of async block
|
||||
UniquePtr<XAsyncBlock> 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<uint8_t> 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<std::mutex> 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> 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> 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<uint32_t>(titleId);
|
||||
result.rarityPercentage = static_cast<float>(rarityPercentage);
|
||||
result.rarityCategory = EnumValue<XblAchievementRarityCategory>(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
|
|
@ -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<GameInviteNotificationEventArgs> 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<MultiplayerActivityInviteData> 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<AchievementUnlockEvent> 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<void(GameInviteNotificationEventArgs&)>;
|
||||
using MultiplayerActivityInviteHandler = Function<void(MultiplayerActivityInviteData&)>;
|
||||
using AchievementUnlockHandler = Function<void(const AchievementUnlockEvent&)>;
|
||||
|
||||
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<XblFunctionContext, MPSDInviteHandler> m_mpsdInviteHandlers;
|
||||
Map<XblFunctionContext, MultiplayerActivityInviteHandler> m_mpaInviteHandlers;
|
||||
Map<XblFunctionContext, AchievementUnlockHandler> m_achievementUnlockHandlers;
|
||||
};
|
||||
|
||||
NAMESPACE_MICROSOFT_XBOX_SERVICES_NOTIFICATION_CPP_END
|
||||
#endif
|
|
@ -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<XalUserGetTokenAndSignatureArgs>();
|
||||
args->forceRefresh = true;
|
||||
args->url = "https://xboxlive.com/";
|
||||
args->method = "GET";
|
||||
|
||||
auto async = std::make_unique<XAsyncBlock>();
|
||||
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
|
|
@ -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
|
|
@ -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<RTANotificationService>(xblContext->NotificationService());
|
||||
RETURN_HR_IF_FAILED(rtaNotificationService->RegisterForGameInviteEvents({ data->async->queue,
|
||||
[
|
||||
asyncBlock{ data->async }
|
||||
]
|
||||
(Result<void> 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<RTANotificationService>(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<RTANotificationService>(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<RTANotificationService>(xblContextHandle->NotificationService());
|
||||
rtaNotificationService->RemoveGameInviteHandler(token);
|
||||
rtaNotificationService->RemoveNotificationHandler(token);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -361,7 +346,7 @@ STDAPI_(void) XblAchievementUnlockRemoveNotificationHandler(
|
|||
) XBL_NOEXCEPT
|
||||
{
|
||||
auto rtaNotificationService = std::dynamic_pointer_cast<RTANotificationService>(xblContextHandle->NotificationService());
|
||||
rtaNotificationService->RemoveAchievementUnlockNotificationHandler(functionContext);
|
||||
rtaNotificationService->RemoveNotificationHandler(functionContext);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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<GameInviteNotificationEventArgs> 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<MultiplayerActivityInviteData> 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<void(const GameInviteNotificationEventArgs&)> MPSDInviteHandler;
|
||||
typedef Function<void(const MultiplayerActivityInviteData&)> 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<XblFunctionContext, MPSDInviteHandler> m_mpsdInviteHandlers;
|
||||
UnorderedMap<XblFunctionContext, MultiplayerActivityInviteHandler> 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<xbox::services::XboxLiveContextSettings> contextSettings,
|
||||
_In_ std::shared_ptr<xbox::services::real_time_activity::RealTimeActivityManager> rtaManager
|
||||
) noexcept;
|
||||
|
||||
HRESULT RegisterForSpopNotificationEvents() noexcept;
|
||||
~RTANotificationService() noexcept;
|
||||
|
||||
// Keeping these APIs public to continue supporting XblGameInviteRegisterForEventResult and XblGameInviteUnregisterForEventAsync
|
||||
HRESULT RegisterForGameInviteEvents(
|
||||
_In_ AsyncContext<HRESULT> async = {}
|
||||
) noexcept;
|
||||
HRESULT Initialize() noexcept;
|
||||
|
||||
HRESULT UnregisterForGameInviteEvents(
|
||||
_In_ AsyncContext<HRESULT> async = {}
|
||||
) noexcept;
|
||||
|
||||
HRESULT RegisterForAchievementUnlockEvents(
|
||||
_In_ AsyncContext<HRESULT> async = {}
|
||||
) noexcept;
|
||||
|
||||
HRESULT UnregisterForAchievementUnlockEvents(
|
||||
_In_ AsyncContext<HRESULT> 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<typename T>
|
||||
HRESULT RegisterForEvents(std::shared_ptr<T>& subscription,
|
||||
AsyncContext<HRESULT> async) noexcept;
|
||||
|
||||
template<typename T>
|
||||
HRESULT UnregisterForEvents(std::shared_ptr<T>& subscription,
|
||||
AsyncContext<HRESULT> async) noexcept;
|
||||
|
||||
TaskQueue m_taskQueue;
|
||||
std::shared_ptr<xbox::services::real_time_activity::RealTimeActivityManager> m_rtaManager;
|
||||
|
||||
std::shared_ptr<GameInviteSubscription> m_gameInviteSubscription;
|
||||
std::shared_ptr<AchievementUnlockSubscription> m_achievementUnlockSubscription;
|
||||
std::shared_ptr<SpopKickSubscription> m_spopNotificationSubscription;
|
||||
std::shared_ptr<NotificationSubscription> m_rtaSubscription;
|
||||
};
|
||||
#elif HC_PLATFORM == HC_PLATFORM_UWP
|
||||
class UWPNotificationService : public NotificationService
|
||||
|
|
|
@ -50,6 +50,9 @@ struct ServiceCallManager : public std::enable_shared_from_this<ServiceCallManag
|
|||
// automatically. Result delivered via 'handler' arg
|
||||
HRESULT PeopleHubGetFollowedUsers(PeopleHubResultHandler handler) const noexcept;
|
||||
|
||||
// Needed to check compatibility between determined detail level and XblPresenceFilter for a social group
|
||||
XblSocialManagerExtraDetailLevel GetDetailLevel() const noexcept;
|
||||
|
||||
private:
|
||||
HRESULT PollPresenceServiceCall(std::unique_lock<std::mutex> lock) noexcept;
|
||||
HRESULT PollPeopleHubServiceCall(std::unique_lock<std::mutex> lock) noexcept;
|
||||
|
@ -367,6 +370,15 @@ void SocialGraph::RegisterGroup(std::shared_ptr<XblSocialManagerUserGroup> 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<std::mutex
|
|||
return hr;
|
||||
}
|
||||
|
||||
XblSocialManagerExtraDetailLevel ServiceCallManager::GetDetailLevel() const noexcept
|
||||
{
|
||||
return m_peoplehubDetailLevel;
|
||||
}
|
||||
|
||||
NAMESPACE_MICROSOFT_XBOX_SERVICES_SOCIAL_MANAGER_CPP_END
|
|
@ -26,10 +26,10 @@ XblSocialManagerUserGroup::XblSocialManagerUserGroup(
|
|||
Vector<uint64_t>&& 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);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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<XalGamertagComponent, String>::iterator it = other.m_gamertags.begin();
|
||||
|
||||
|
@ -349,7 +349,7 @@ void User::SetTokenExpired(uint64_t xuid) noexcept
|
|||
|
||||
}
|
||||
|
||||
Result<XblFunctionContext> User::RegisterChangeEventHandler(
|
||||
Result<uint64_t> User::RegisterChangeEventHandler(
|
||||
UserChangeEventHandler handler
|
||||
) noexcept
|
||||
{
|
||||
|
@ -383,14 +383,14 @@ Result<XblFunctionContext> User::RegisterChangeEventHandler(
|
|||
state->SetUserChangeHandler(token.token, context);
|
||||
}
|
||||
}
|
||||
return Result<XblFunctionContext>{ static_cast<XblFunctionContext>(token.token), hr };
|
||||
return Result<uint64_t>{token.token, hr };
|
||||
}
|
||||
|
||||
void User::UnregisterChangeEventHandle(
|
||||
XblFunctionContext token
|
||||
uint64_t token
|
||||
) noexcept
|
||||
{
|
||||
XalUserUnregisterChangeEventHandler(XalRegistrationToken{ static_cast<uint64_t>(token) });
|
||||
XalUserUnregisterChangeEventHandler(XalRegistrationToken{ token });
|
||||
auto state{ GlobalState::Get() };
|
||||
if (state)
|
||||
{
|
||||
|
|
|
@ -63,12 +63,12 @@ public:
|
|||
|
||||
static void SetTokenExpired(uint64_t xuid) noexcept;
|
||||
|
||||
static Result<XblFunctionContext> RegisterChangeEventHandler(
|
||||
static Result<uint64_t> RegisterChangeEventHandler(
|
||||
UserChangeEventHandler handler
|
||||
) noexcept;
|
||||
|
||||
static void UnregisterChangeEventHandle(
|
||||
XblFunctionContext token
|
||||
uint64_t token
|
||||
) noexcept;
|
||||
|
||||
private:
|
||||
|
@ -90,7 +90,7 @@ class Result<User>
|
|||
{
|
||||
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;
|
||||
|
||||
|
|
|
@ -514,7 +514,7 @@
|
|||
<None Include="$(MSBuildThisFileDirectory)Tests\titleStorage\title_storage-cpp.lua">
|
||||
<DeploymentContent>true</DeploymentContent>
|
||||
</None>
|
||||
<None Include="$(MSBuildThisFileDirectory)Tests\titleStorage\title_storage-restCalls.lua">
|
||||
<None Include="$(MSBuildThisFileDirectory)Tests\titleStorage\title_storage-restCalls.lua">
|
||||
<DeploymentContent>true</DeploymentContent>
|
||||
</None>
|
||||
<None Include="$(MSBuildThisFileDirectory)Tests\titleStorage\title_storage.lua">
|
||||
|
|
|
@ -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
|
|
@ -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<XAsyncBlock> 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<XAsyncBlock> 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<XAsyncBlock> 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();
|
||||
|
|
|
@ -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<XAsyncBlock>();
|
||||
asyncBlock->queue = Data()->queue;
|
||||
asyncBlock->context = Data()->downloadHttpCalls[blobMetadata.positionInList];
|
||||
asyncBlock->context = Data()->titleStorageHttpCalls[blobMetadata.positionInList];
|
||||
asyncBlock->callback = [](XAsyncBlock* asyncBlock)
|
||||
{
|
||||
std::unique_ptr<XAsyncBlock> 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<XAsyncBlock>();
|
||||
asyncBlock->queue = Data()->queue;
|
||||
asyncBlock->context = Data()->xblHttpCall;
|
||||
asyncBlock->callback = [](XAsyncBlock* asyncBlock)
|
||||
{
|
||||
std::unique_ptr<XAsyncBlock> asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock*
|
||||
HRESULT hr = XAsyncGetStatus(asyncBlock, false);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
auto httpCall = static_cast<XblHttpCallHandle>(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<XAsyncBlock>();
|
||||
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);
|
||||
|
|
|
@ -144,9 +144,10 @@ struct ApiExplorerData
|
|||
#endif
|
||||
|
||||
// Title Storage Rest API Calls Data
|
||||
std::string responseString;
|
||||
std::vector<XblHttpCallHandle> downloadHttpCalls;
|
||||
size_t completedDownloads = 0;
|
||||
std::vector<std::string> blobResponseStrings;
|
||||
std::string metadataResponseString;
|
||||
std::vector<XblHttpCallHandle> titleStorageHttpCalls;
|
||||
size_t titleStorageCompletedHttpCalls = 0;
|
||||
size_t filesToDownload = 0;
|
||||
|
||||
// MP
|
||||
|
|
|
@ -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=="
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -11,6 +11,7 @@ end
|
|||
function OnXblRealTimeActivityAddConnectionStateChangeHandler_Connected()
|
||||
print("RTA connection connected");
|
||||
XblSocialRemoveSocialRelationshipChangedHandler();
|
||||
XblContextCloseHandle();
|
||||
end
|
||||
|
||||
function OnXblRealTimeActivityAddConnectionStateChangeHandler_Disconnected()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
|
@ -50,7 +50,7 @@
|
|||
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
|
||||
</PropertyGroup>
|
||||
<Import Condition="Exists($(ATGBuildProps))" Project="$(ATGBuildProps)" />
|
||||
<Import Project="$(MSBuildThisFileDirectory)..\..\..\Build\xsapi.gdk.bwoi.props" />
|
||||
<Import Project="$(MSBuildThisFileDirectory)..\..\..\Build\xsapi.gdk.bwoi.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Gaming.Xbox.XboxOne.x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
|
@ -383,7 +383,7 @@
|
|||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Gaming.Xbox.Scarlett.x64'">
|
||||
<Link>
|
||||
<AdditionalDependencies>uuid.lib;$(Console_Libs);%(XboxExtensionsDependencies);%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>uuid.lib;$(Console_Libs);%(XboxExtensionsDependencies);dbghelp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
|
@ -514,7 +514,6 @@
|
|||
<ItemGroup>
|
||||
<MGCCompile Include="MicrosoftGame.Config" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Media\Fonts\SegoeUI_18.spritefont">
|
||||
<Link>%(Filename)%(Extension)</Link>
|
||||
|
@ -552,8 +551,6 @@
|
|||
<None Include="Media\Fonts\XboxOneControllerLegendSmall.spritefont">
|
||||
<DeploymentContent>true</DeploymentContent>
|
||||
</None>
|
||||
<None Include="MicrosoftGame.Config" />
|
||||
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="Kits\DirectXTK12\DirectXTK12_GDK_2017.vcxproj">
|
||||
|
|
|
@ -560,7 +560,6 @@
|
|||
<None Include="Media\Fonts\XboxOneControllerLegendSmall.spritefont">
|
||||
<DeploymentContent>true</DeploymentContent>
|
||||
</None>
|
||||
<None Include="MicrosoftGame.Config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="Kits\DirectXTK12\DirectXTK12_GDK_2017.vcxproj">
|
||||
|
|
|
@ -512,6 +512,11 @@ public:
|
|||
xbox::services::Vector<XblAchievementRequirement> requirements;
|
||||
};
|
||||
xbox::services::Vector<Entry> 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<XblAchievementRequirement>(
|
||||
updateEntry.progression.requirements,
|
||||
updateEntry.progression.requirements + updateEntry.progression.requirementsCount
|
||||
);
|
||||
entry.requirements = xbox::services::Vector<XblAchievementRequirement>(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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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\"
|
||||
|
|
Загрузка…
Ссылка в новой задаче