This commit is contained in:
Nathan Iskandar 2021-04-30 14:39:20 -07:00 коммит произвёл GitHub
Родитель 730f579d41
Коммит df1907c417
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
43 изменённых файлов: 679 добавлений и 799 удалений

Просмотреть файл

@ -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\"