diff --git a/.gitignore b/.gitignore
index 7e153e62..daa0214c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,7 +24,6 @@ External/Packages/*
#mobile
External/sdk.mobile
-.gradle/
# Xcode - from github
## User settings
@@ -161,8 +160,9 @@ Microsoft.Xbox.Services.*.sln.log
Microsoft.Xbox.Services.*.sln.out
**/apirunner-log.txt
-# Android Studio generated files
+# Android and Android Studio files
**/.idea
**/*.iml
+**/.cxx
**/local.properties
-**/.cxx
\ No newline at end of file
+**/.gradle/*
diff --git a/Build/Microsoft.Xbox.Services.141.GDK.C.Thunks/Microsoft.Xbox.Services.141.GDK.C.Thunks.vcxproj b/Build/Microsoft.Xbox.Services.141.GDK.C.Thunks/Microsoft.Xbox.Services.141.GDK.C.Thunks.vcxproj
index 6ee64284..54c3d4db 100644
--- a/Build/Microsoft.Xbox.Services.141.GDK.C.Thunks/Microsoft.Xbox.Services.141.GDK.C.Thunks.vcxproj
+++ b/Build/Microsoft.Xbox.Services.141.GDK.C.Thunks/Microsoft.Xbox.Services.141.GDK.C.Thunks.vcxproj
@@ -30,7 +30,7 @@
14.010.0.19041.0false
- Microsoft_Xbox_Services_141_GDK_C_Thunks
+ Microsoft.Xbox.Services.141.GDK.C.ThunksMicrosoft.Xbox.ServicesGDK1
diff --git a/Build/Microsoft.Xbox.Services.141.GDK.C.Thunks/dll/Microsoft.Xbox.Services.141.GDK.C.Thunks.def b/Build/Microsoft.Xbox.Services.141.GDK.C.Thunks/dll/Microsoft.Xbox.Services.141.GDK.C.Thunks.def
index 05a1e393..cbd31449 100644
--- a/Build/Microsoft.Xbox.Services.141.GDK.C.Thunks/dll/Microsoft.Xbox.Services.141.GDK.C.Thunks.def
+++ b/Build/Microsoft.Xbox.Services.141.GDK.C.Thunks/dll/Microsoft.Xbox.Services.141.GDK.C.Thunks.def
@@ -1,4 +1,4 @@
-LIBRARY Microsoft_Xbox_Services_141_GDK_C_Thunks
+LIBRARY Microsoft.Xbox.Services.141.GDK.C.Thunks.dll
EXPORTS
XblAchievementsGetAchievementAsync
XblAchievementsGetAchievementResult
diff --git a/Build/Microsoft.Xbox.Services.141.GDK.C.Thunks/generator/ThunksGenerator/Program.cs b/Build/Microsoft.Xbox.Services.141.GDK.C.Thunks/generator/ThunksGenerator/Program.cs
index 44cbed0d..be33a5a7 100644
--- a/Build/Microsoft.Xbox.Services.141.GDK.C.Thunks/generator/ThunksGenerator/Program.cs
+++ b/Build/Microsoft.Xbox.Services.141.GDK.C.Thunks/generator/ThunksGenerator/Program.cs
@@ -26,7 +26,7 @@ namespace ThunksGenerator
fns.Sort();
Console.WriteLine($"Writing apis to {thunksDefFile.FullName}");
- string content = "LIBRARY Microsoft_Xbox_Services_141_GDK_C_Thunks\n";
+ string content = "LIBRARY Microsoft.Xbox.Services.141.GDK.C.Thunks.dll\n";
content += "EXPORTS\n";
foreach (string fn in fns)
{
diff --git a/External/Xal/External/libHttpClient b/External/Xal/External/libHttpClient
index d34b3b12..02825da8 160000
--- a/External/Xal/External/libHttpClient
+++ b/External/Xal/External/libHttpClient
@@ -1 +1 @@
-Subproject commit d34b3b125f3cb23da81428aa350f66930bc99f8c
+Subproject commit 02825da86c6d6e773782fcc8c59abef091a85b03
diff --git a/External/Xal/Source/Xal/Include/Xal/xal_version.h b/External/Xal/Source/Xal/Include/Xal/xal_version.h
index 0aaf8483..74ddbcc0 100644
--- a/External/Xal/Source/Xal/Include/Xal/xal_version.h
+++ b/External/Xal/Source/Xal/Include/Xal/xal_version.h
@@ -20,6 +20,6 @@ extern "C"
/// YYYYMMDD Date string describing the date the build was created
/// rrr QFE number (000 indicates base release)
///
-#define XAL_VERSION "2021.06.20210518.000"
+#define XAL_VERSION "2021.10.20210913.000"
}
diff --git a/Include/xsapi-c/achievements_c.h b/Include/xsapi-c/achievements_c.h
index f7310600..0c3bda56 100644
--- a/Include/xsapi-c/achievements_c.h
+++ b/Include/xsapi-c/achievements_c.h
@@ -801,6 +801,7 @@ STDAPI_(void) XblAchievementsResultCloseHandle(
_In_ XblAchievementsResultHandle handle
) XBL_NOEXCEPT;
+#if HC_PLATFORM == HC_PLATFORM_WIN32 || HC_PLATFORM_IS_EXTERNAL
///
/// Handle for Function handling achievement unlock events.
///
@@ -836,6 +837,7 @@ STDAPI_(void) XblAchievementUnlockRemoveNotificationHandler(
_In_ XblContextHandle xblContextHandle,
_In_ XblFunctionContext functionContext
) XBL_NOEXCEPT;
+#endif
///
/// A callback invoked when a progress is made on an achievement.
diff --git a/Include/xsapi-c/game_invite_c.h b/Include/xsapi-c/game_invite_c.h
index 812994de..d092ac85 100644
--- a/Include/xsapi-c/game_invite_c.h
+++ b/Include/xsapi-c/game_invite_c.h
@@ -64,6 +64,13 @@ typedef struct XblGameInviteNotificationEventArgs
///
_Field_z_ const char* inviteProtocol;
+ ///
+ /// Invite Context.
+ /// The memory for the returned string pointer only remains valid inside the XblGameInviteHandler,
+ /// so deep copy the string if you need to refer to it outside the handler.
+ ///
+ _Field_z_ const char* inviteContext;
+
///
/// Sender Image URL.
/// The memory for the returned string pointer only remains valid inside the XblGameInviteHandler,
diff --git a/Include/xsapi-c/multiplayer_c.h b/Include/xsapi-c/multiplayer_c.h
index c265fc91..b134a38b 100644
--- a/Include/xsapi-c/multiplayer_c.h
+++ b/Include/xsapi-c/multiplayer_c.h
@@ -2771,6 +2771,8 @@ STDAPI_(void) XblMultiplayerSessionSetInitializationSucceeded(
///
///
/// If "peerToHostRequirements" is set and this is set, the measurement stage assumes the given host is the correct host and only measures metrics to that host.
+/// Note that host device tokens are generated from a session member's secure device address, so ensure that the secure device address is set
+/// (see ) for the desired host prior to calling this method.
///
STDAPI_(void) XblMultiplayerSessionSetHostDeviceToken(
_In_ XblMultiplayerSessionHandle handle,
@@ -2904,7 +2906,8 @@ STDAPI XblMultiplayerSessionCurrentUserSetStatus(
/// HRESULT return code for this API operation.
///
/// On platforms that don't have a secure device address, call XblFormatSecureDeviceAddress
-/// to generate a value that can be used by this function.
+/// to generate a value that can be used by this function. Note that setting a secure device address is required
+/// to manually set a session host.
///
STDAPI XblMultiplayerSessionCurrentUserSetSecureDeviceAddressBase64(
_In_ XblMultiplayerSessionHandle handle,
diff --git a/Include/xsapi-c/multiplayer_manager_c.h b/Include/xsapi-c/multiplayer_manager_c.h
index 300725d1..b19ab9dc 100644
--- a/Include/xsapi-c/multiplayer_manager_c.h
+++ b/Include/xsapi-c/multiplayer_manager_c.h
@@ -264,7 +264,7 @@ enum class XblMultiplayerEventType : uint32_t
LeaveGameCompleted,
///
- /// Indicates that the XblMultiplayerManagerJoinLobby() operation has completed.
+ /// Indicates that the operation has completed.
/// Once the join succeeds, the member is now part of the lobby session,
/// and can use data in the session to connect to other lobby members.
/// You can call XblMultiplayerEventArgsXuid for the xuid.
@@ -414,7 +414,9 @@ typedef struct XblMultiplayerManagerMember
///
/// The first member.
/// The second member.
-/// Returns true if on same device, false if not on same device.
+/// Returns true if both members are on the same device, false if both members are not on the same device.
+/// This function compares the device tokens of both members. If the device tokens match, both members are on the same device.
+/// For more information, see .
STDAPI_(bool) XblMultiplayerManagerMemberAreMembersOnSameDevice(
_In_ const XblMultiplayerManagerMember* first,
_In_ const XblMultiplayerManagerMember* second
@@ -422,12 +424,22 @@ STDAPI_(bool) XblMultiplayerManagerMemberAreMembersOnSameDevice(
///
-/// A handle to multiplayer event args that can be used to additional information depending on the type of event.
+/// A handle to multiplayer event arguments that can be used to retrieve additional information for a multiplayer event, depending on the type of event.
///
+///
+///
+///
+///
+///
+///
+///
+///
+///
+///
typedef struct XblMultiplayerEventArgs* XblMultiplayerEventArgsHandle;
///
-/// A multiplayer event that will be returned from XblMultiplayerManagerDoWork().
+/// A multiplayer event that is returned from .
///
typedef struct XblMultiplayerEvent
{
@@ -437,8 +449,8 @@ typedef struct XblMultiplayerEvent
HRESULT Result;
///
- /// Returns call specific debug information if join fails.
- /// It is not localized, so only use for debugging purposes.
+ /// Call-specific debug information if the API operation fails.
+ /// The debug information is not localized; use only for debugging purposes.
///
_Field_z_ const char* ErrorMessage;
@@ -448,12 +460,12 @@ typedef struct XblMultiplayerEvent
void* Context;
///
- /// Type of the event triggered.
+ /// The type of the event triggered.
///
XblMultiplayerEventType EventType;
///
- /// A handle to the event args.
+ /// A handle to the event arguments for the multiplayer event.
///
XblMultiplayerEventArgsHandle EventArgsHandle;
@@ -481,7 +493,7 @@ typedef struct XblMultiplayerConnectionAddressDeviceTokenPair
} XblMultiplayerConnectionAddressDeviceTokenPair;
///
-/// Event args returned for XblMultiplayerEventType::PerformQosMeasurements events.
+/// Event arguments returned for `XblMultiplayerEventType::PerformQosMeasurements` events.
///
typedef struct XblMultiplayerPerformQoSMeasurementsArgs
{
@@ -491,46 +503,89 @@ typedef struct XblMultiplayerPerformQoSMeasurementsArgs
const XblMultiplayerConnectionAddressDeviceTokenPair* remoteClients;
///
- /// The number of items in the remoteClients array.
+ /// The size of the `remoteClients` array.
///
size_t remoteClientsSize;
} XblMultiplayerPerformQoSMeasurementsArgs;
///
-/// Retrieves additional information for XblMultiplayerEventType::UserAdded, XblMultiplayerEventType::UserRemoved,
-/// and XblMultiplayerEventType::JoinLobbyCompleted events.
+/// Retrieves additional information for `XblMultiplayerEventType::UserAdded`, `XblMultiplayerEventType::UserRemoved`,
+/// and `XblMultiplayerEventType::JoinLobbyCompleted` events.
///
-/// The event args handle from the XblMultiplayerEvent.
-/// Passes back the Xbox User ID for the following events:
-/// XblMultiplayerEventType::UserAdded - Xbox User ID of the member that that was added.
-/// XblMultiplayerEventType::UserRemoved - Xbox User ID of the member that that was removed.
-/// XblMultiplayerEventType::JoinLobbyCompleted - Xbox User ID of the member that was invited.
+/// The event arguments handle for the multiplayer event.
+/// The applicable Xbox User ID, depending on the multiplayer event:
+/// `XblMultiplayerEventType::UserAdded` - The Xbox User ID of the member that was added.
+/// `XblMultiplayerEventType::UserRemoved` - The Xbox User ID of the member that was removed.
+/// `XblMultiplayerEventType::JoinLobbyCompleted` - The Xbox User ID of the member that was invited.
+///
/// HRESULT return code for this API operation.
+/// Call this function to get more information about multiplayer events returned by
+/// for which the `EventType` member of the for a multiplayer event is set to either
+/// `XblMultiplayerEventType::UserAdded`, `XblMultiplayerEventType::UserRemoved`, or `XblMultiplayerEventType::JoinLobbyCompleted`.
+/// The event arguments handle for a multiplayer event can be retrieved from the `EventArgsHandle` member of
+/// the structure for that multiplayer event.
+/// For more information about multiplayer events, see
+/// Multiplayer Manager API overview.
+///
+///
+///
+///
+///
+///
+///
+///
STDAPI XblMultiplayerEventArgsXuid(
_In_ XblMultiplayerEventArgsHandle argsHandle,
_Out_ uint64_t* xuid
) XBL_NOEXCEPT;
///
-/// Retrieves additional information for XblMultiplayerEventType::MemberJoined and XblMultiplayerEventType::MemberLeft events.
+/// Retrieves additional information for `XblMultiplayerEventType::MemberJoined` and `XblMultiplayerEventType::MemberLeft` events.
///
-/// The event args handle from the XblMultiplayerEvent.
-/// Passes back the required size of the members array for .
+/// The event arguments handle for the multiplayer event.
+/// The size of the `members` caller-allocated array for .
/// HRESULT return code for this API operation.
+/// Call this function before you call the function, to return
+/// the size of the array you must allocate for the `members` parameter of the function.
+/// The event arguments handle for a multiplayer event can be retrieved from the `EventArgsHandle` member of
+/// the structure for that multiplayer event.
+/// For more information about multiplayer events, see
+/// Multiplayer Manager API overview.
+///
+///
+///
+///
+///
+///
+///
STDAPI XblMultiplayerEventArgsMembersCount(
_In_ XblMultiplayerEventArgsHandle argsHandle,
_Out_ size_t* memberCount
) XBL_NOEXCEPT;
///
-/// Retrieves additional information for XblMultiplayerEventType::MemberJoined and XblMultiplayerEventType::MemberLeft events.
+/// Retrieves additional information for `XblMultiplayerEventType::MemberJoined` and `XblMultiplayerEventType::MemberLeft` events.
///
-/// The event args handle from the XblMultiplayerEvent.
-/// The size of the members array.
-/// A caller allocated array to pass back the following event results:
-/// XblMultiplayerEventType::MemberJoined - A list of members that joined the game.
-/// XblMultiplayerEventType::MemberLeft - A list of members that left the game.
+/// The event arguments handle for the multiplayer event.
+/// The size of the `members` array.
+/// A caller-allocated array that passes back a list of members, depending on the multiplayer event:
+/// `XblMultiplayerEventType::MemberJoined` - A list of members that joined the game.
+/// `XblMultiplayerEventType::MemberLeft` - A list of members that left the game.
+///
/// HRESULT return code for this API operation.
+/// Call the function before you call this function, to return
+/// the size of the array you must allocate for the `members` parameter of this function.
+/// The event arguments handle for a multiplayer event can be retrieved from the `EventArgsHandle` member of
+/// the structure for that multiplayer event.
+/// For more information about multiplayer events, see
+/// Multiplayer Manager API overview.
+///
+///
+///
+///
+///
+///
+///
STDAPI XblMultiplayerEventArgsMembers(
_In_ XblMultiplayerEventArgsHandle argsHandle,
_In_ size_t membersCount,
@@ -538,42 +593,88 @@ STDAPI XblMultiplayerEventArgsMembers(
) XBL_NOEXCEPT;
///
-/// Retrieves additional information for XblMultiplayerEventType::HostChanged and XblMultiplayerEventType::MemberPropertyChanged events.
+/// Retrieves additional information for `XblMultiplayerEventType::HostChanged` and `XblMultiplayerEventType::MemberPropertyChanged` events.
///
-/// The event args handle from the XblMultiplayerEvent.
-/// Passes back a given member for the following event results:
-/// XblMultiplayerEventType::HostChanged - The new host member. If an existing host leaves, there won't be a member to pass back so this function will return HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND).
-/// XblMultiplayerEventType::MemberPropertyChanged - The member whose property changed.
+/// The event arguments handle for the multiplayer event.
+/// The applicable member, depending on the multiplayer event:
+/// `XblMultiplayerEventType::HostChanged` - The new host member. If an existing host leaves, there is no new host member to return
+/// in this parameter. In this case, this function returns `HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND)`.
+/// `XblMultiplayerEventType::MemberPropertyChanged` - The member whose property changed.
+///
/// HRESULT return code for this API operation.
+/// Call this function to get more information about multiplayer events returned by
+/// for which the `EventType` member of the for a multiplayer event is set to either
+/// `XblMultiplayerEventType::HostChanged` or `XblMultiplayerEventType::MemberPropertyChanged`.
+/// The event arguments handle for a multiplayer event can be retrieved from the `EventArgsHandle` member of
+/// the structure for that multiplayer event.
+/// For more information about multiplayer events, see
+/// Multiplayer Manager API overview.
+///
+///
+///
+///
+///
+///
+///
STDAPI XblMultiplayerEventArgsMember(
_In_ XblMultiplayerEventArgsHandle argsHandle,
_Out_ XblMultiplayerManagerMember* member
) XBL_NOEXCEPT;
///
-/// Retrieves additional information for XblMultiplayerEventType::MemberPropertyChanged
-/// and XblMultiplayerEventType::SessionPropertyChanged events.
+/// Retrieves additional information for `XblMultiplayerEventType::MemberPropertyChanged`
+/// and `XblMultiplayerEventType::SessionPropertyChanged` events.
///
-/// The event args handle from the XblMultiplayerEvent.
-/// Passes back a pointer for the following event results:
-/// XblMultiplayerEventType::MemberPropertyChanged - The JSON of the property that changed.
-/// XblMultiplayerEventType::SessionPropertyChanged - The JSON of the property that changed.
-/// The memory for the pointer remains valid for the life of the XblMultiplayerEventArgsHandle object until it is closed.
+/// The event arguments handle for the multiplayer event.
+/// A pointer to a JSON string, depending on the multiplayer event:
+/// `XblMultiplayerEventType::MemberPropertyChanged` - The JSON string of the member property that changed.
+/// `XblMultiplayerEventType::SessionPropertyChanged` - The JSON string of the session property that changed.
+/// The memory for the pointer remains valid for the life of the `XblMultiplayerEventArgsHandle` object, until the handle is closed.
+///
/// HRESULT return code for this API operation.
+/// Call this function to get more information about multiplayer events returned by
+/// for which the `EventType` member of the for a multiplayer event is set to either
+/// `XblMultiplayerEventType::MemberPropertyChanged` or `XblMultiplayerEventType::SessionPropertyChanged`.
+/// The event arguments handle for a multiplayer event can be retrieved from the `EventArgsHandle` member of
+/// the structure for that multiplayer event.
+/// For more information about multiplayer events, see
+/// Multiplayer Manager API overview.
+///
+///
+///
+///
+///
+///
+///
STDAPI XblMultiplayerEventArgsPropertiesJson(
_In_ XblMultiplayerEventArgsHandle argsHandle,
_Out_ const char** properties
) XBL_NOEXCEPT;
///
-/// Retrieves additional information for XblMultiplayerEventType::FindMatchCompleted events.
+/// Retrieves additional information for `XblMultiplayerEventType.FindMatchCompleted` multiplayer events.
///
-/// The event args handle from the XblMultiplayerEvent.
-/// A caller allocated struct that passes back the current matchmaking status.
-/// A caller allocated struct that passes back the cause of why
-/// the initialization failed, or XblMultiplayerMeasurementFailure::None if there was no failure.
-/// Set when transitioning out of the "joining" or "measuring" stage if this member doesn't pass.
+/// The event arguments handle for the multiplayer event.
+/// A caller-allocated structure that describes the current matchmaking status.
+/// A caller-allocated structure that passes back the cause of why
+/// the initialization failed, or `XblMultiplayerMeasurementFailure::None` if there was no failure.
+/// This value is set when transitioning out of the `XblMultiplayerMatchStatus::Joining` or
+/// `XblMultiplayerMatchStatus::Measuring` initialization stages, if this member doesn't pass the initializaton stage.
/// HRESULT return code for this API operation.
+/// Call this function to get more information about multiplayer events returned by
+/// for which the `EventType` member of the for a multiplayer event is set to
+/// `XblMultiplayerEventType.FindMatchCompleted`.
+/// The event arguments handle for a multiplayer event can be retrieved from the `EventArgsHandle` member of
+/// the structure for that multiplayer event.
+/// For more information about multiplayer events, see
+/// Multiplayer Manager API overview.
+///
+///
+///
+///
+///
+///
+///
STDAPI XblMultiplayerEventArgsFindMatchCompleted(
_In_ XblMultiplayerEventArgsHandle argsHandle,
_Out_opt_ XblMultiplayerMatchStatus* matchStatus,
@@ -605,46 +706,63 @@ STDAPI_XBL_DEPRECATED XblMultiplayerEventArgsTournamentGameSessionReady(
) XBL_NOEXCEPT;
///
-/// Retrieves additional information for XblMultiplayerEventType::PerformQosMeasurements events.
+/// Retrieves additional information for `XblMultiplayerEventType::PerformQosMeasurements` events.
///
-/// The event args handle from the XblMultiplayerEvent.
-/// A caller allocated struct that passes back the
-/// args containing info about the remote clients for which QoS info is needed.
+/// The event arguments handle for the multiplayer event.
+/// A caller-allocated structure that passes back the remote clients for which QoS information is needed.
/// HRESULT return code for this API operation.
+/// Call this function to get more information about multiplayer events returned by
+/// for which the `EventType` member of the for a multiplayer event is set to
+/// `XblMultiplayerEventType::PerformQosMeasurements`.
+/// The event arguments handle for a multiplayer event can be retrieved from the `EventArgsHandle` member of
+/// the structure for that multiplayer event.
+/// For more information about multiplayer events, see
+/// Multiplayer Manager API overview.
+///
+///
+///
+///
+///
+///
+///
STDAPI XblMultiplayerEventArgsPerformQoSMeasurements(
_In_ XblMultiplayerEventArgsHandle argsHandle,
_Out_ XblMultiplayerPerformQoSMeasurementsArgs* performQoSMeasurementsArgs
) XBL_NOEXCEPT;
///
-/// Initializes MultiplayerManager.
+/// Initializes Multiplayer Manager (MPM).
///
-/// The name of the template for the lobby session to be based on.
-/// Queue where all MultiplayerManager work should be scheduled.
+/// The name of the session template for the lobby session to be based on.
+/// The task queue where all Multiplayer Manager work should be scheduled.
/// HRESULT return code for this API operation.
///
-/// There are two sessions that are maintained when using multiplayer manager.
-/// One is the "lobby session" which is where friends you invite will join.
-/// Another is the "game session" which contains people that your lobby has been matched with.
+/// This function initializes the lobby session with which Multiplayer Manager (MPM) interacts, based on a session template configured for the title.
+/// You must call this function before calling other MPM functions, otherwise errors may occur. For more information about
+/// configuring session templates, see Configuring the Multiplayer service.
///
+///
STDAPI XblMultiplayerManagerInitialize(
_In_z_ const char* lobbySessionTemplateName,
_In_opt_ XTaskQueueHandle asyncQueue
) XBL_NOEXCEPT;
///
-/// Ensures proper game state updates are maintained between the title and the Xbox Live Multiplayer Service.
-/// To ensure best performance, XblMultiplayerManagerDoWork() must be called frequently, such as once per frame.
+/// Maintains game state updates between the title and Multiplayer Manager (MPM).
///
-/// An array of all events for the game to handle.
-/// Will be null if no events are triggered during this update.
-/// The returned events are owned by MultiplayerManager and are valid until XblMultiplayerManagerDoWork is called again.
-/// Passes back the number of events in the multiplayerEvents array.
+/// An array of multiplayer events for the game to handle.
+/// This is set to null if no multiplayer events occur during this update.
+/// The size of the `multiplayerEvents` array.
/// HRESULT return code for this API operation.
///
-/// Title needs to be thread safe when calling XblMultiplayerManagerDoWork() since this is when the state will change.
-/// For example, if you looping through the list of members on a different thread than your calling
-/// XblMultiplayerManagerDoWork() on, it may change when XblMultiplayerManagerDoWork() is called.
+/// This function sends and receives game state updates between the title and Multiplayer Manager (MPM), returning an array
+/// of structures that represent significant multiplayer events, such as remote players
+/// joining or leaving. You must call this function on a regular and frequent basis, such as once per frame, so that MPM can
+/// properly maintain game state. For more information, see Multiplayer Manager overview.
+/// The multiplayer events returned by this function are owned by MPM, and remain valid only until `XblMultiplayerManagerDoWork` is called again.
+/// In addition, the title must be thread-safe when calling `XblMultiplayerManagerDoWork`, because game state changes at the time this function is called.
+/// For example, if you're iterating through the list of members on a thread other than the one from which you're calling this function,
+/// the list may change when this function is called.
///
STDAPI XblMultiplayerManagerDoWork(
_Deref_out_opt_ const XblMultiplayerEvent** multiplayerEvents,
@@ -652,83 +770,146 @@ STDAPI XblMultiplayerManagerDoWork(
) XBL_NOEXCEPT;
///
-/// A unique ID to the session used to query trace logs for entries that relate to the session.
+/// Retrieves the correlation handle for the lobby session.
///
-/// Passes back the unique ID given to the session.
+/// Passes back the correlation handle for the lobby session,
+/// or null if a lobby session doesn't exist. The memory for the returned pointer remains
+/// valid until the next call to .
/// HRESULT return code for this API operation.
+///
+/// This function retrieves the correlation handle for the lobby session in Multiplayer Manager (MPM). The correlation handle serves as
+/// an alias for the lobby session, allowing a game to refer to a lobby session by using only the correlation handle.
+/// The correlation handle can be used to query trace logs for entries that relate to the lobby session.
+/// For more information, see Multiplayer concepts overview.
+///
+///
STDAPI XblMultiplayerManagerLobbySessionCorrelationId(
_Out_ XblGuid* correlationId
) XBL_NOEXCEPT;
///
-/// Returns identifying information for the lobby session.
+/// Retrieves the session reference for the lobby session.
///
-/// Passes back a reference that uniquely identifies the session.
+/// Passes back a pointer to the session reference for the lobby session,
+/// or null if a lobby session doesn't exist. The memory for the returned pointer remains valid until
+/// the next call to .
/// HRESULT return code for this API operation.
+///
+/// This function retrieves a pointer to the session reference for the lobby session, if the lobby session exists in
+/// Multiplayer Manager. The session reference contains the service configuration ID (SCID),
+/// session template, and session name for the lobby session, and uniquely identifies
+/// the lobby session in Multiplayer Session Directory (MPSD). For more information about session references, see
+/// Multiplayer Session advanced topics.
+///
+///
+///
STDAPI XblMultiplayerManagerLobbySessionSessionReference(
_Out_ XblMultiplayerSessionReference* sessionReference
) XBL_NOEXCEPT;
///
-/// The number of members that are local to the device.
+/// Retrieves the number of local members in the lobby session.
///
-/// The local member count for the lobby session.
+/// The number of members in the lobby session that are local to the device.
+///
+///
STDAPI_(size_t) XblMultiplayerManagerLobbySessionLocalMembersCount() XBL_NOEXCEPT;
///
-/// A collection of members that are local to this device.
+/// Retrieves member information for the local members in the lobby session.
///
-/// The size of the localMembers array.
-/// The required size can be obtained with XblMultiplayerLobbySessionLocalMembersCount.
-/// A caller allocated array that passes back the list of local members.
+/// The size of the `localMembers` array.
+/// The required size can be obtained by calling .
+/// A caller-allocated array to write into.
/// HRESULT return code for this API operation.
+///
+/// This function returns member information for each member in the lobby session that is local to the device.
+/// You can also call the function to return member
+/// information for all members in the lobby session.
+/// The member information returned by this function is valid only until is called again.
+///
+///
STDAPI XblMultiplayerManagerLobbySessionLocalMembers(
_In_ size_t localMembersCount,
_Out_writes_(localMembersCount) XblMultiplayerManagerMember* localMembers
) XBL_NOEXCEPT;
///
-/// A number of members that are in the lobby.
-/// When a friend accepts a game invite, members will be added to the lobby.
+/// Retrieves the number of members in the lobby session.
///
/// The number of members that are in the lobby session.
+///
+///
+///
STDAPI_(size_t) XblMultiplayerManagerLobbySessionMembersCount() XBL_NOEXCEPT;
///
-/// A collection of members that are in the lobby.
+/// Retrieves member information for the members in the lobby session.
///
-/// The size of the members array.
-/// The required size can be obtained with XblMultiplayerLobbySessionMembersCount.
-/// A caller allocated array that passes back the list of lobby members.
+/// The size of the `members` array.
+/// The required size can be obtained by calling .
+/// A caller-allocated array to write into.
/// HRESULT return code for this API operation.
+///
+/// This function returns member information for each member in the lobby session.
+/// You can also call the function to return member
+/// information for each member in the lobby session that is local to the device.
+/// The member information returned by this function is valid only until is called again.
+///
+///
STDAPI XblMultiplayerManagerLobbySessionMembers(
_In_ size_t membersCount,
_Out_writes_(membersCount) XblMultiplayerManagerMember* members
) XBL_NOEXCEPT;
///
-/// Returns the host member for the lobby.
-/// The host is defined as the user with the lowest index on the host device.
+/// Retrieves member information for the host member in the lobby session.
///
-/// Passes back the host member.
+/// A caller-allocated structure to be populated with member information for the host member.
/// HRESULT return code for this API operation.
-/// Will return __HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER) if there is no host.
+/// Returns `__HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER)` if a host member doesn't exist.
+///
+/// This function retrieves member information about the member that represents the host for a lobby session.
+/// If a lobby session doesn't exist, or if a host member doesn't exist for the lobby session, the function
+/// returns `__HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER)`. The host member is defined as the user with the lowest
+/// index on the host device.
+/// The information returned by this function is valid only until is called again.
+///
+///
+///
+///
STDAPI XblMultiplayerManagerLobbySessionHost(
_Out_ XblMultiplayerManagerMember* hostMember
) XBL_NOEXCEPT;
///
-/// JSON string that specify the custom properties for the game.
+/// Retrieves the custom properties for the lobby session, as a JSON string.
///
-/// The JSON string that specifies the custom properties for the game.
-/// The memory for the returned string pointer remains valid until the next call to XblMultiplayerManagerDoWork.
+/// A JSON string that specifies the custom properties for the lobby session, or null if a lobby session doesn't exist.
+/// The memory for the returned pointer remains valid until the next call to .
+///
+/// This function retrieves the custom properties for the lobby session, represented as a JSON string. These custom properties
+/// can be changed at any time. If custom properties are shared between devices, or may be updated by several devices
+/// at the same time, use the function to change custom properties.
+/// Otherwise, you can use the function to change custom properties.
+///
+///
STDAPI_(const char*) XblMultiplayerManagerLobbySessionPropertiesJson() XBL_NOEXCEPT;
///
-/// A set of constants associated with this session. These can only be set through the session template.
+/// Retrieves the session constants associated with the lobby session.
///
-/// A pointer to the constant values for the multiplayer session.
-/// The pointer returned remains valid until the next call to XblMultiplayerManagerDoWork.
+/// A pointer to the session constants for the lobby session, or null if a lobby session doesn't exist.
+/// The memory for the returned pointer remains valid until the next call to .
+///
+/// This function retrieves a pointer to the session constants for a lobby session, if the lobby session exists
+/// in Multiplayer Manager. The session constants contain constants, such as session visibility
+/// and session capabilities, defined by the session template used for the lobby session. Unlike session properties,
+/// session constants can only be set through the session template, and are set at the time the lobby session is created.
+/// For more information about session constants, see Multiplayer Session advanced topics.
+///
+///
+///
STDAPI_(const XblMultiplayerSessionConstants*) XblMultiplayerManagerLobbySessionConstants() XBL_NOEXCEPT;
XBL_WARNING_PUSH
@@ -743,55 +924,73 @@ STDAPI_XBL_DEPRECATED_(const XblTournamentTeamResult*) XblMultiplayerManagerLobb
XBL_WARNING_POP
///
-/// Hosts a new lobby when the first user is added.
-/// For all other users, they will be added to the existing lobby as secondary users.
-/// This API will also advertise the lobby for friends to join.
+/// Joins an Xbox user to the lobby session.
///
-/// The associated system User.
+/// The user handle of the user joining the lobby session.
/// HRESULT return code for this API operation.
///
-/// You can send invites, set lobby properties, and access lobby members only once you've added the local user.
-/// 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.
+/// This function creates a new lobby session and adds the Xbox user specified in to the session.
+/// Subsequent users are added to the newly-hosted lobby session as secondary users. You can send
+/// invites, set session properties, and access members of the lobby session only after the first
+/// local user is added to the lobby session.
+/// The result of this function is delivered as a multiplayer event with an event type set to `XblMultiplayerEventType::JoinLobbyCompleted`.
+/// You can call to retrieve multiplayer events.
+/// When attempting to join a lobby session, the service returns `HTTP_E_STATUS_BAD_REQUEST` if the server is full.
+/// After joining, you can set the properties for the lobby session by calling
+/// , or you can set the host for the lobby session by
+/// calling if the lobby session doesn't already have a host.
+/// You can also send an invite to another user by calling either
+/// or .
+/// If you don't need a lobby session, and if you haven't added local users by calling this function,
+/// you can instead call and specify the list of users to join the game.
///
+///
+///
STDAPI XblMultiplayerManagerLobbySessionAddLocalUser(
_In_ XblUserHandle user
) XBL_NOEXCEPT;
///
-/// Removes the local user from the lobby and game session.
-/// After this method is called, if no local users are active, title will not be able to perform
-/// any further multiplayer operations.
+/// Removes the local user from both the lobby session and game session.
///
-/// The associated system User.
+/// The local user to be removed.
/// HRESULT return code for this API operation.
///
-/// You can join another game or re-add the local user.
-/// 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().
+/// If there are no local users remaining after this function is called, the title cannot
+/// perform any further multiplayer operations. Changes are batched and written to the service
+/// when is called.
+/// The result of this function is delivered as a multiplayer event with an event type set to
+/// `XblMultiplayerEventType::UserRemoved`.
+/// You can call to retrieve multiplayer events.
+/// After leaving, you can join a different game by calling either or
+/// , or you can re-add the
+/// local user by calling .
///
+///
+///
+///
STDAPI XblMultiplayerManagerLobbySessionRemoveLocalUser(
_In_ XblUserHandle user
) XBL_NOEXCEPT;
///
-/// Set a custom property on the local member to the specified JSON string.
+/// Set a custom property for a local member to the specified JSON string.
///
-/// The associated system User you want to set the property for.
+/// The user you want to set the property for.
/// The name of the property to set.
-/// The JSON value to assign to the property. (Optional)
-/// The application-defined data to correlate the XblMultiplayerEvent to the initiating call. (Optional)
+/// Optional. The JSON value to assign to the property.
+/// Optional. The application-defined data to correlate the to the initiating call.
/// HRESULT return code for this API operation.
///
-/// The result is delivered via XblMultiplayerEvent of type LocalMemberPropertyWriteCompleted
-/// through XblMultiplayerManagerDoWork().
-/// 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().
+/// This function sets the value, represented as a JSON string, of a custom property for a local member in
+/// the lobby session. Custom properties can be changed at any time. Changes are batched and written to the
+/// service when is called.
+/// The result of this function is delivered as a multiplayer event with an event type set
+/// to `XblMultiplayerEventType::LocalMemberPropertyWriteCompleted`.
+/// You can call to retrieve multiplayer events.
///
+///
+///
STDAPI XblMultiplayerManagerLobbySessionSetLocalMemberProperties(
_In_ XblUserHandle user,
_In_z_ const char* name,
@@ -800,19 +999,21 @@ STDAPI XblMultiplayerManagerLobbySessionSetLocalMemberProperties(
) XBL_NOEXCEPT;
///
-/// Delete a custom property on the local member.
+/// Deletes a custom property from a local member of the lobby session.
///
-/// The associated system User you want to delete the property for.
-/// The name of the property to delete.
-/// The application-defined data to correlate the XblMultiplayerEvent to the initiating call. (Optional)
+/// The user handle of the local member.
+/// The name of the custom property to delete.
+/// Optional. The application-defined data to correlate the to the initiating call.
/// HRESULT return code for this API operation.
///
-/// The result is delivered via XblMultiplayerEvent of type LocalMemberPropertyWriteCompleted
-/// through XblMultiplayerManagerDoWork().
-/// 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().
+/// This function deletes a custom property from a local member of the lobby session. Custom properties
+/// can be changed at any time. Changes are batched and written to the service when is called.
+/// The result of this function is delivered as a multiplayer event with an event type set to
+/// `XblMultiplayerEventType::LocalMemberPropertyWriteCompleted`.
+/// You can call to retrieve multiplayer events.
///
+///
+///
STDAPI XblMultiplayerManagerLobbySessionDeleteLocalMemberProperties(
_In_ XblUserHandle user,
_In_z_ const char* name,
@@ -820,20 +1021,22 @@ STDAPI XblMultiplayerManagerLobbySessionDeleteLocalMemberProperties(
) XBL_NOEXCEPT;
///
-/// Set connection address for the local member.
-/// The address can be used for network and secure socket connection.
+/// Sets the connection address for the local member.
///
-/// The associated system User you want to set the property for.
+/// The user you want to set the property for.
/// The network connection address to set.
-/// The application-defined data to correlate the XblMultiplayerEvent to the initiating call. (Optional)
+/// Optional. The application-defined data to correlate the to the initiating call.
/// HRESULT return code for this API operation.
///
-/// The result is delivered via XblMultiplayerEvent of type XblMultiplayerEventType::LocalMemberConnectionAddressWriteCompleted
-/// through XblMultiplayerManagerDoWork().
-/// 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().
+/// This function sets the network connection address of a local member in the lobby session. You can use
+/// the connection address for network and secure socket connections to that local member. Changes are batched
+/// and written to the service when is called.
+/// The result of this function is delivered as a multiplayer event with an event type set
+/// to `XblMultiplayerEventType::LocalMemberConnectionAddressWriteCompleted`.
+/// You can call to retrieve multiplayer events.
///
+///
+///
STDAPI XblMultiplayerManagerLobbySessionSetLocalMemberConnectionAddress(
_In_ XblUserHandle user,
_In_z_ const char* connectionAddress,
@@ -841,28 +1044,39 @@ STDAPI XblMultiplayerManagerLobbySessionSetLocalMemberConnectionAddress(
) XBL_NOEXCEPT;
///
-/// Whether or not the Xbox User ID is the host.
+/// Indicates whether the specified user is the host for the lobby session.
///
-/// The Xbox User ID of the user.
-/// Returns true if Xuid is host, false if Xuid is not host.
+/// The Xbox User ID (XUID) of the user.
+/// Returns true if the XUID is the host of the lobby session; otherwise, false.
+///
+/// This function returns false if a lobby session doesn't exist, or if
+/// the Xbox User ID (XUID) specified in `xuid` isn't the host for the lobby session. You can
+/// retrieve the host for a lobby session by calling .
+/// For more information, see Multiplayer Manager API overview.
+///
+///
STDAPI_(bool) XblMultiplayerManagerLobbySessionIsHost(
_In_ uint64_t xuid
) XBL_NOEXCEPT;
///
-/// Set a custom game property to the specified JSON string.
+/// Sets the value of a custom property for the lobby session.
///
-/// The name of the property to set.
-/// The JSON value to assign to the property. (Optional)
-/// The application-defined data to correlate the XblMultiplayerEvent to the initiating call. (Optional)
+/// The name of the custom property to set.
+/// The value to assign to the property, as a JSON string.
+/// Optional. The application-defined data to correlate the to the initiating call.
/// HRESULT return code for this API operation.
///
-/// The result is delivered via XblMultiplayerEvent of type XblMultiplayerEventType::SessionPropertyWriteCompleted
-/// through XblMultiplayerManagerDoWork().
-/// 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().
+/// This function sets the value, represented as a JSON string, of a custom property for the lobby session. Custom properties
+/// can be changed at any time. Changes are batched and written to the service when is called.
+/// If custom properties are shared between devices, or may be updated by several devices
+/// at the same time, use the function to change custom properties.
+/// Otherwise, you can use this function to change custom properties.
+/// The result of this function is delivered as a multiplayer event with an event type set to `XblMultiplayerEventType::SessionPropertyWriteCompleted`.
+/// You can call to retrieve multiplayer events.
///
+///
+///
STDAPI XblMultiplayerManagerLobbySessionSetProperties(
_In_z_ const char* name,
_In_z_ const char* valueJson,
@@ -870,21 +1084,26 @@ STDAPI XblMultiplayerManagerLobbySessionSetProperties(
) XBL_NOEXCEPT;
///
-/// Sets a custom property to the specified JSON string using XblMultiplayerSessionWriteMode::SynchronizedUpdate.
+/// Sets the value of a custom property for the lobby session, using `XblMultiplayerSessionWriteMode::SynchronizedUpdate`.
///
-/// The name of the property to set.
-/// The JSON value to assign to the property. (Optional)
-/// The application-defined data to correlate the XblMultiplayerEvent to the initiating call. (Optional)
+/// The name of the custom property to set.
+/// The value to assign to the property, as a JSON string.
+/// Optional. The application-defined data to correlate the to the initiating call.
/// HRESULT return code for this API operation.
///
-/// Use this method to resolve any conflicts between devices while trying to set properties to a shared portion
-/// that other devices can also modify. It ensures that updates to the session are atomic.
-/// If writing to non-sharable properties, use XblMultiplayerLobbySessionSetProperties() instead.
-/// The service may reject your request if a race condition occurred (due to a conflict) resulting in HTTP_E_STATUS_PRECOND_FAILED (HTTP status 412).
-/// To resolve this, evaluate the need to write again and re-submit if needed.
-/// The result is delivered via XblMultiplayerEvent of type XblMultiplayerEventType::SessionSynchronizedPropertyWriteCompleted
-/// through XblMultiplayerManagerDoWork().
+/// This function sets the value, represented as a JSON string, of a custom property for the lobby session. Custom properties
+/// can be changed at any time. If custom properties are shared between devices, or may be updated by several devices
+/// at the same time, use this function to ensure atomicity and resolve any conflicts between devices while changing the values of those cusotm properties.
+/// If a custom property isn't shared across devices, use the function instead
+/// to change the value of that custom property.
+/// The service may reject the request to change the custom property if a race condition occurs due to a conflict.
+/// If the request is rejected, the service returns `HTTP_E_STATUS_PRECOND_FAILED`. If a conflict occurs, re-evaluate the need to
+/// change the custom property and, if needed, call this function again to re-submit the request.
+/// The result of this function is delivered as a multiplayer event with an event type set to `XblMultiplayerEventType::SessionSynchronizedPropertyWriteCompleted`.
+/// You can call to retrieve multiplayer events.
///
+///
+///
STDAPI XblMultiplayerManagerLobbySessionSetSynchronizedProperties(
_In_z_ const char* name,
_In_z_ const char* valueJson,
@@ -892,19 +1111,25 @@ STDAPI XblMultiplayerManagerLobbySessionSetSynchronizedProperties(
) XBL_NOEXCEPT;
///
-/// Sets the host for the game using XblMultiplayerSessionWriteMode::SynchronizedUpdate.
+/// Sets the host for the lobby session, using `XblMultiplayerSessionWriteMode::SynchronizedUpdate`.
///
/// The device token of the host.
-/// The application-defined data to correlate the XblMultiplayerEvent to the initiating call. (Optional)
+/// Optional. The application-defined data to correlate the to the initiating call.
/// HRESULT return code for this API operation.
///
-/// Use this method to resolve any conflicts between devices trying to set the host at the same time.
-/// It ensures that updates to the session are atomic.
-/// The service may reject your request if a race condition occurred(due to a conflict) resulting in HTTP_E_STATUS_PRECOND_FAILED (HTTP status 412).
-/// To resolve this, evaluate the need to write again and re-submit if needed.
-/// The result is delivered via XblMultiplayerEvent of type XblMultiplayerEventType::SynchronizedHostWriteCompleted
-/// through XblMultiplayerManagerDoWork().
+/// This function sets the host for the lobby session. Use this function to ensure atomicity and resolve any conflicts between devices
+/// trying to set the host at the same time.
+/// The service may reject the request to set the host if a race condition occurs due to a conflict.
+/// If the request is rejected, the service returns `HTTP_E_STATUS_PRECOND_FAILED`. If a conflict occurs, re-evaluate the need to
+/// change the host and, if needed, call this function again to re-submit the request.
+/// The result of this function is delivered as a multiplayer event with an event type set to `XblMultiplayerEventType::SynchronizedHostWriteCompleted`.
+/// You can call to retrieve multiplayer events.
+/// Note that host device tokens are generated from a session member's secure device address, so ensure that the secure device address is set for the
+/// desired host prior to calling this method.
///
+///
+///
+///
STDAPI XblMultiplayerManagerLobbySessionSetSynchronizedHost(
_In_ const char* deviceToken,
_In_opt_ void* context
@@ -912,20 +1137,27 @@ STDAPI XblMultiplayerManagerLobbySessionSetSynchronizedHost(
#if HC_PLATFORM_IS_MICROSOFT
///
-/// Displays the invite UI and allows the user to select people from the user's people list and invite them to join the user's party.
-/// If a user accepts that notification the title will be notified.
+/// Displays the standard Xbox UI, allowing the user to select friends or recent players and invite them to the game.
///
/// The user who is sending the invite.
-/// The custom context string ID.
-/// This string ID is defined during Xbox Live ingestion to identify the invitation text that is additional to the standard invitation text.
-/// The ID string must be prefixed with "///". (Optional)
-/// The activation context string.
-/// A game defined string that is passed to the invited game client and interpreted as desired. (Optional)
+/// Optional. The custom context string ID, a string that is defined
+/// during Xbox Live ingestion, to identify the custom invitation text
+/// that is added to the standard invitation text. The ID string must be prefixed with
+/// three slash characters ("///").
+/// Optional. The activation context string, a game-defined string
+/// that is passed to the invited game client and interpreted as desired by the game.
/// HRESULT return code for this API operation.
///
-/// In the case of GDK based games, the title will be notified via the callback set by XGameInviteRegisterForEvent.
-/// For other platforms, the title will be activated.
+/// If this function is invoked, Multiplayer Manager sends invites to the selected players when the user
+/// confirms the player selection in the standard Xbox UI. If a selected player accepts the invite, the title is notified.
+/// For GDK-based games, the title is notified by invoking the callback function specified
+/// when the function was invoked.
+/// For games based on other platforms, the title is activated.
+/// For more information, see Receiving invites.
///
+///
+///
+///
STDAPI XblMultiplayerManagerLobbySessionInviteFriends(
_In_ XblUserHandle requestingUser,
_In_opt_z_ const char* contextStringId,
@@ -934,24 +1166,29 @@ STDAPI XblMultiplayerManagerLobbySessionInviteFriends(
#endif
///
-/// Invites the specified users to a game without additional UI.
-/// This will result in a notification being shown to each invited user using standard invite text.
-/// If a user accepts that notification the title will be notified.
+/// Invites the specified users to the game without displaying additional UI.
///
-/// The associated system User.
-/// The array of xbox user IDs who will be invited.
-/// The size of the xuids array.
-/// The custom context string ID. This string ID is defined
-/// during Xbox Live ingestion to identify the invitation text that is additional to the standard
-/// invitation text. The ID string must be prefixed with "///". (Optional)
-/// The activation context string.
-/// A game defined string that is passed to the invited game client and interpreted as desired. (Optional)
+/// The user who is sending the invite.
+/// The array of Xbox User IDs (XUIDs) to be invited.
+/// The size of the `xuids` array.
+/// Optional. The custom context string ID, a string that is defined
+/// during Xbox Live ingestion, to identify the custom invitation text
+/// that is added to the standard invitation text. The ID string must be prefixed with
+/// three slash characters ("///").
+/// Optional. The activation context string, a game-defined string
+/// that is passed to the invited game client and interpreted as desired by the game.
/// HRESULT return code for this API operation.
///
-/// In the case of GDK based games, the title will be notified via the callback
-/// set by XGameInviteRegisterForEvent.
-/// For other platforms, the title will be activated.
+/// Multiplayer Manager sends invites to the selected players when this function is invoked. If a selected
+/// player accepts the invite, the title is notified.
+/// For GDK-based games, the title is notified by invoking the callback function specified
+/// when the function was invoked.
+/// For games based on other platforms, the title is activated.
+/// For more information, see Receiving invites.
///
+///
+///
+///
STDAPI XblMultiplayerManagerLobbySessionInviteUsers(
_In_ XblUserHandle user,
_In_ const uint64_t* xuids,
@@ -961,96 +1198,150 @@ STDAPI XblMultiplayerManagerLobbySessionInviteUsers(
) XBL_NOEXCEPT;
///
-/// Returns whether or not there is an active multiplayer manager game session.
+/// Indicates whether there is an active game session.
///
-/// Returns true if active game session, false if not active game session.
+/// Returns true if a game session exists for the lobby session in Multiplayer Manager; otherwise, false.
+///
+///
STDAPI_(bool) XblMultiplayerManagerGameSessionActive() XBL_NOEXCEPT;
///
-/// A unique ID to the session used to query trace logs for entries that relate to the session.
-/// Returns null if a game session has not yet been established.
+/// Retrieves the correlation handle for the game session.
///
-/// The unique ID given to the game session.
-/// The memory for the returned string pointer remains valid until the next call to XblMultiplayerManagerDoWork.
+/// The correlation handle for the game session, or null if a game session doesn't exist.
+/// The memory for the returned pointer remains valid until the next call to .
+///
+/// This function retrieves the correlation handle for the game session. The correlation handle serves as
+/// an alias for the game session, allowing a game to refer to a game session by using only the correlation handle.
+/// The correlation handle can be used to query trace logs for entries that relate to the game session.
+/// For more information, see Multiplayer concepts overview.
+///
+///
STDAPI_(const char*) XblMultiplayerManagerGameSessionCorrelationId() XBL_NOEXCEPT;
///
-/// Object containing identifying information for the session.
-/// Returns null if a game session has not yet been established.
+/// Retrieves the session reference for the game session.
///
-/// A pointer to the multiplayer session reference.
-/// The memory for the returned string pointer remains valid until the next call to XblMultiplayerManagerDoWork.
+/// A pointer to the session reference for the game session, or null if a game session doesn't exist.
+/// The memory for the returned pointer remains valid until the next call to .
+///
+/// This function retrieves a pointer to the session reference for a game session, if the game session exists for
+/// the lobby session in Multiplayer Manager. The session reference contains the service configuration ID (SCID),
+/// session template, and session name for the game session, and uniquely identifies
+/// the game session in Multiplayer Session Directory (MPSD). For more information about session references, see
+/// Multiplayer Session advanced topics.
+///
+///
+///
STDAPI_(const XblMultiplayerSessionReference*) XblMultiplayerManagerGameSessionSessionReference() XBL_NOEXCEPT;
///
-/// A number of members that are in the game.
-/// When a friend accepts a game invite, members will be added to the lobby and the game session members list.
+/// Retrieves the number of members in the game session.
///
-/// The number of members that are in the game.
+/// The number of members that are in the game session.
+///
+/// When a friend accepts a game invite, the corresponding member is added to the lobby and the game session members list.
+///
+///
+///
STDAPI_(size_t) XblMultiplayerManagerGameSessionMembersCount() XBL_NOEXCEPT;
///
-/// A collection of members that are in the game.
+/// Retrieves member information for the members in the game session.
///
-/// The size of the members array.
-/// The required size can be obtained with XblMultiplayerManagerGameSessionMembersCount.
-/// A caller allocated array of XblMultiplayerManagerMember to write into.
+/// The size of the `members` array.
+/// The required size can be obtained by calling .
+/// A caller-allocated array to write into.
/// HRESULT return code for this API operation.
+///
+/// This function returns member information for each member in a game session.
+/// The member information returned by this function is valid only until is called again.
+///
STDAPI XblMultiplayerManagerGameSessionMembers(
_In_ size_t membersCount,
_Out_writes_(membersCount) XblMultiplayerManagerMember* members
) XBL_NOEXCEPT;
///
-/// Returns the host member for the game.
-/// The host is defined as the user with the lowest index on the host device.
+/// Retrieves member information for the host member in the game session.
///
-/// A caller allocated struct to be filled with the host member reference to populate.
+/// A caller-allocated structure to be populated with member information for the host member.
/// HRESULT return code for this API operation.
-/// Will return __HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER) if there is no host.
+/// Returns `__HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER)` if a host member doesn't exist.
+///
+/// This function retrieves member information about the member that represents the host for a game session.
+/// If a game session doesn't exist, or if a host member doesn't exist for the game session, the function
+/// returns `__HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER)`. The host member is defined as the user with the lowest
+/// index on the host device.
+/// The information returned by this function is valid only until is called again.
+///
+///
+///
STDAPI XblMultiplayerManagerGameSessionHost(
_Out_ XblMultiplayerManagerMember* hostMember
) XBL_NOEXCEPT;
///
-/// JSON string that specify the custom properties for the game.
-/// These can be changed anytime.
+/// Retrieves the custom properties for the game session, as a JSON string.
///
-/// A JSON string that specifies the custom properties for the game.
-/// The memory for the returned string pointer remains valid until the next call to XblMultiplayerManagerDoWork.
-/// Will return null if the game session has not yet been established.
+/// A JSON string that specifies the custom properties for the game session, or null if a game session doesn't exist.
+/// The memory for the returned pointer remains valid until the next call to .
+///
+/// This function retrieves the custom properties for the game session, represented as a JSON string. These custom properties
+/// can be changed at any time. If custom properties are shared between devices, or may be updated by several devices
+/// at the same time, use the function to change custom properties.
+/// Otherwise, you can use the function to change custom properties.
+///
+///
STDAPI_(const char*) XblMultiplayerManagerGameSessionPropertiesJson() XBL_NOEXCEPT;
///
-/// A set of constants associated with this session.
-/// These can only be set through the session template.
+/// Retrieves the session constants associated with the game session.
///
-/// A pointer to the constant values for the multiplayer session.
-/// The memory for the returned string pointer remains valid until the next call to XblMultiplayerManagerDoWork.
-/// Will return null if the game session has not yet been established.
+/// A pointer to the session constants for the game session, or null if a game session doesn't exist.
+/// The memory for the returned pointer remains valid until the next call to .
+///
+/// This function retrieves a pointer to the session constants for a game session, if the game session exists for
+/// the lobby session in Multiplayer Manager. The session constants contain constants, such as session visibility
+/// and session capabilities, defined by the session template used for the game session. Unlike session properties,
+/// session constants can only be set through the session template, and are set at the time the game session is created.
+/// For more information about session constants, see Multiplayer Session advanced topics.
+///
+///
+///
STDAPI_(const XblMultiplayerSessionConstants*) XblMultiplayerManagerGameSessionConstants() XBL_NOEXCEPT;
///
-/// Whether or not the Xbox User ID is the host.
+/// Indicates whether the specified user is the host for the game session.
///
-/// The Xbox User ID of the user.
-/// Returns true if Xuid is host, false if Xuid is not host.
+/// The Xbox User ID (XUID) of the user.
+/// Returns true if the XUID is the host of the game session; otherwise, false.
+///
+/// This function returns false if a game session doesn't exist for the lobby session, or if
+/// the Xbox User ID (XUID) specified in `xuid` isn't the host for the game session. You can
+/// retrieve the host for a game session by calling .
+/// For more information, see Multiplayer Manager API overview.
+///
+///
STDAPI_(bool) XblMultiplayerManagerGameSessionIsHost(
_In_ uint64_t xuid
) XBL_NOEXCEPT;
///
-/// Set a custom game property to the specified JSON string.
+/// Sets the value of a custom property for the game session.
///
-/// The name of the property to set.
-/// The JSON value to assign to the property. (Optional)
-/// The application-defined data to correlate the XblMultiplayerEvent to the initiating call. (Optional)
+/// The name of the custom property to set.
+/// The value to assign to the property, as a JSON string.
+/// Optional. The application-defined data to correlate the to the initiating call.
/// HRESULT return code for this API operation.
///
-/// 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().
+/// This function sets the value, represented as a JSON string, of a custom property for the game session. Custom properties
+/// can be changed at any time. Changes are batched and written to the service when is called.
+/// If custom properties are shared between devices, or may be updated by several devices
+/// at the same time, use the function to change custom properties.
+/// Otherwise, you can use this function to change custom properties.
///
+///
STDAPI XblMultiplayerManagerGameSessionSetProperties(
_In_z_ const char* name,
_In_z_ const char* valueJson,
@@ -1058,21 +1349,26 @@ STDAPI XblMultiplayerManagerGameSessionSetProperties(
) XBL_NOEXCEPT;
///
-/// Sets a custom property to the specified JSON string using XblMultiplayerSessionWriteMode::SynchronizedUpdate.
+/// Sets the value of a custom property for the game session, using `XblMultiplayerSessionWriteMode::SynchronizedUpdate`.
///
-/// The name of the property to set.
-/// The JSON value to assign to the property. (Optional)
-/// The application-defined data to correlate the XblMultiplayerEvent to the initiating call. (Optional)
+/// The name of the custom property to set.
+/// The value to assign to the property, as a JSON string.
+/// Optional. The application-defined data to correlate the to the initiating call.
/// HRESULT return code for this API operation.
///
-/// Use this method to resolve any conflicts between devices while trying to set properties to a shared portion that other devices can also modify.
-/// It ensures that updates to the session are atomic.
-/// If writing to non-sharable properties, use XblMultiplayerManagerGameSessionSetProperties() instead.
-/// The service may reject your request if a race condition occurred (due to a conflict) resulting in HTTP_E_STATUS_PRECOND_FAILED (HTTP status 412).
-/// To resolve this, evaluate the need to write again and re-submit if needed.
-/// The result is delivered via XblMultiplayerEvent of type XblMultiplayerEventType::SessionSynchronizedPropertyWriteCompleted
-/// through XblMultiplayerManagerDoWork().
+/// This function sets the value, represented as a JSON string, of a custom property for the game session. Custom properties
+/// can be changed at any time. If custom properties are shared between devices, or may be updated by several devices
+/// at the same time, use this function to ensure atomicity and resolve any conflicts between devices while changing the values of those cusotm properties.
+/// If a custom property isn't shared across devices, use the function instead
+/// to change the value of that custom property.
+/// The service may reject the request to change the custom property if a race condition occurs due to a conflict.
+/// If the request is rejected, the service returns `HTTP_E_STATUS_PRECOND_FAILED`. If a conflict occurs, re-evaluate the need to
+/// change the custom property and, if needed, call this function again to re-submit the request.
+/// The result of this function is delivered as a multiplayer event with an event type set to `XblMultiplayerEventType::SessionSynchronizedPropertyWriteCompleted`.
+/// You can call to retrieve multiplayer events.
///
+///
+///
STDAPI XblMultiplayerManagerGameSessionSetSynchronizedProperties(
_In_z_ const char* name,
_In_z_ const char* valueJson,
@@ -1080,77 +1376,105 @@ STDAPI XblMultiplayerManagerGameSessionSetSynchronizedProperties(
) XBL_NOEXCEPT;
///
-/// Sets the host for the game using XblMultiplayerSessionWriteMode::SynchronizedUpdate.
+/// Sets the host for the game session, using `XblMultiplayerSessionWriteMode::SynchronizedUpdate`.
///
/// The device token of the host.
-/// The application-defined data to correlate the XblMultiplayerEvent to the initiating call. (Optional)
+/// Optional. The application-defined data to correlate the to the initiating call.
/// HRESULT return code for this API operation.
///
-/// Use this method to resolve any conflicts between devices trying to set the host at the same time.
-/// It ensures that updates to the session are atomic.
-/// The service may reject your request if a race condition occurred(due to a conflict) resulting in HTTP_E_STATUS_PRECOND_FAILED (HTTP status 412).
-/// To resolve this, evaluate the need to write again and re-submit if needed.
-/// The result is delivered via XblMultiplayerEvent of type XblMultiplayerEventType::SynchronizedHostWriteCompleted
-/// through XblMultiplayerManagerDoWork().
+/// This function sets the host for the game session. Use this function to ensure atomicity and resolve any conflicts between devices
+/// trying to set the host at the same time.
+/// The service may reject the request to set the host if a race condition occurs due to a conflict.
+/// If the request is rejected, the service returns `HTTP_E_STATUS_PRECOND_FAILED`. If a conflict occurs, re-evaluate the need to
+/// change the host and, if needed, call this function again to re-submit the request.
+/// The result of this function is delivered as a multiplayer event with an event type set to `XblMultiplayerEventType::SynchronizedHostWriteCompleted`.
+/// You can call to retrieve multiplayer events.
+/// Note that host device tokens are generated from a session member's secure device address, so ensure that the secure device address is set for the
+/// desired host prior to calling this method.
///
+///
+///
+///
STDAPI XblMultiplayerManagerGameSessionSetSynchronizedHost(
_In_ const char* deviceToken,
_In_opt_ void* context
) XBL_NOEXCEPT;
///
-/// Joins a lobby session given a session handle ID.
-/// Callers will usually get the handle ID from another member's XblMultiplayerActivityDetails
-/// via the XblMultiplayerGetActivitiesForUsersAsync() API, or an invite.
+/// Joins an Xbox user to a lobby session.
///
-/// A multiplayer handle id, which uniquely identifies the game session you want to join.
-/// The system User joining the lobby.
+/// The activity handle for the lobby session.
+/// The user handle of the user joining the lobby session.
/// HRESULT return code for this API operation.
///
-/// You can send an invite to another user via XblMultiplayerManagerLobbySessionInviteUsers or XblMultiplayerManagerLobbySessionInviteFriends.
-/// The result of XblMultiplayerManagerJoinLobby is delivered via an event returned
-/// from XblMultiplayerManagerDoWork() of the type XblMultiplayerEventType::JoinLobbyCompleted.
-/// 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.
+/// This function joins the Xbox user specified in user to the lobby session specified in handleId.
+/// The activity handle for the lobby session is typically retrieved either from a game invite or from the `HandleId` value
+/// of another user's , by calling .
+/// For more information about multiplayer activities, see Activities.
+/// The result of this function is delivered as a multiplayer event with an event type set to `XblMultiplayerEventType::JoinLobbyCompleted`.
+/// You can call to retrieve multiplayer events.
+/// When attempting to join a lobby session, the service returns `HTTP_E_STATUS_BAD_REQUEST` if the server is full.
+/// After joining, you can set the properties for the lobby session by calling
+/// , or you can set the host for the lobby session by
+/// calling if the lobby session doesn't already have a host.
+/// You can also send an invite to another user by calling either
+/// or .
+/// If you don't need a lobby session, and if you haven't added local users by calling ,
+/// you can instead call and specify the list of users to join the game.
///
+///
+///
STDAPI XblMultiplayerManagerJoinLobby(
_In_z_ const char* handleId,
_In_ XblUserHandle user
) XBL_NOEXCEPT;
///
-/// Join the lobby's game session if one exists and if there is room.
-/// If the session doesn't exist, it creates a new game session with the existing lobby members.
+/// Creates a new game session for the lobby session, or joins an existing game session if one exists for the lobby session.
///
-/// The name of the template for the game session to be based on.
+/// The name of the session template for the game session to be based on.
/// HRESULT return code for this API operation.
///
-/// The result is delivered via an event of type XblMultiplayerEventType::JoinGameCompleted
-/// 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.
+/// This function creates a new game session and adds the current members of the lobby session to the game session,
+/// if a game session doesn't already exist. If a new user joins the lobby session after a game session is already
+/// created, this function finds the existing game session in Multiplayer Session Directory (MPSD) by using a
+/// transfer handle in the lobby session, and then adds the new user to the game session using that transfer handle.
+/// For more information, see Multiplayer concepts overview.
+/// If a lobby session doesn't exist, likely because wasn't called before
+/// calling this function, an error occurs. An error also occurs if matchmaking is in progress.
+/// This function does not migrate existing lobby session properties to the game session.
+/// The result of this function is delivered as a multiplayer event with an event type set to `XblMultiplayerEventType::JoinGameCompleted`.
+/// You can call to retrieve multiplayer events.
+/// When attempting to join a lobby session, the service returns `HTTP_E_STATUS_BAD_REQUEST` if the server is full.
+/// After joining, you can set the properties for the game session by calling
+/// or ,
+/// or you can set the host for the game session by calling .
///
+///
+///
+///
STDAPI XblMultiplayerManagerJoinGameFromLobby(
_In_z_ const char* sessionTemplateName
) XBL_NOEXCEPT;
///
-/// Joins a game given a globally unique session name.
-/// Callers can get the unique session name as a result of the title's third party matchmaking.
-/// Call this on all clients that needs to join this game.
+/// Joins a game session, using the globally unique session name.
///
-/// A unique name for the session.
-/// The name of the template for the game session to be based on.
-/// Array of xbox user IDs you want to be part of the game.
-/// The number of elements in the xuids array.
+/// The globally unique session name for the game session.
+/// The name of the session template for the game session to be based on.
+/// An array of Xbox User IDs (XUIDs) that represents the users you want to be part of the game.
+/// The number of elements in the array specified for `xuids`.
/// HRESULT return code for this API operation.
///
-/// 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.
+/// This function joins a list of Xbox users, specified in `xuids`, to the game session identified by the globally
+/// unique session name specified in `sessionName.` You can get the globally unique session name from the results of the title's third-party matchmaking,
+/// and you should call this function for all clients that need to join the game.
+/// The result of this function is delivered as a multiplayer event with an event type set to `XblMultiplayerEventType::JoinGameCompleted`.
+/// You can call to retrieve multiplayer events.
+/// When attempting to join a game session, the service returns `HTTP_E_STATUS_BAD_REQUEST` if the server is full.
+/// After joining, you can set the properties for the game session by calling
+/// or ,
+/// or you can set the host for the game session by calling .
///
STDAPI XblMultiplayerManagerJoinGame(
_In_z_ const char* sessionName,
@@ -1160,25 +1484,40 @@ STDAPI XblMultiplayerManagerJoinGame(
) XBL_NOEXCEPT;
///
-/// Leaving the game will put you back into the lobby.
-/// This will remove all local users from the game.
+/// Leaves the game session, returning the Xbox user and all other local users to the lobby session.
///
/// HRESULT return code for this API operation.
///
-/// The result is delivered via and event of type XblMultiplayerEventType::LeaveGameCompelted through XblMultiplayerManagerDoWork().
+/// This function removes the Xbox user from the game session and returns the user back to the lobby session. The game session is set to null,
+/// and all local users are also removed from the game session and returned to the lobby session. Any matchmaking request in progress is
+/// canceled when this function is called.
+/// The result of this function is delivered as a multiplayer event with an event type set to `XblMultiplayerEventType::LeaveGameCompleted`.
+/// You can call to retrieve multiplayer events.
+/// After leaving, you can join a different game by calling either or
+/// .
///
+///
+///
STDAPI XblMultiplayerManagerLeaveGame() XBL_NOEXCEPT;
///
-/// Sends a matchmaking request to the server.
+/// Submits a matchmaking request to the server.
///
-/// The name of the hopper.
-/// The ticket attributes for the match. (Optional)
-/// The maximum time to wait for members to join the match.
+/// The name of the hopper for this request.
+/// Optional. The attributes of the match ticket for this request, as a JSON string.
+/// The maximum time, in seconds, to wait for users to join the match.
/// HRESULT return code for this API operation.
///
-/// When a match is found, the manager will join the game and notify the title via an event of type XblMultiplayerEventType::FindMatchCompleted.
+/// This function submits a matchmaking request for the lobby session to Multiplayer Manager (MPM). Before you can use this function,
+/// you must first configure hoppers in the service configuration for your title. A hopper defines the rules that SmartMatch uses
+/// to match players.
+/// For more information about hoppers, see Matchmaking overview.
+/// If a lobby session doesn't exist, likely because wasn't called, or if local users weren't added
+/// to the lobby session before calling this function, an error occurs. An error also occurs if matchmaking is already in progress.
+/// The result of this function is delivered as a multiplayer event with an event type set to `XblMultiplayerEventType::FindMatchCompleted`.
+/// You can call to retrieve multiplayer events.
///
+///
STDAPI XblMultiplayerManagerFindMatch(
_In_z_ const char* hopperName,
_In_opt_z_ const char* attributesJson,
@@ -1189,6 +1528,16 @@ STDAPI XblMultiplayerManagerFindMatch(
/// Cancels the match request on the server, if one exists.
///
///
+///
+/// This function cancels a previously submitted match ticket. This function is ignored if the status of the match ticket
+/// is set to `XblMultiplayerMatchStatus::None`, `XblMultiplayerMatchStatus::Expired`, `XblMultiplayerMatchStatus::Canceled`, or
+/// `XblMultiplayerMatchStatus::Failed`, or if a different host submitted the match ticket.
+/// If this function is called, the status of the match ticket is set to `XblMultiplayerMatchStatus::Canceling` until
+/// the match ticket is canceled on the server.
+/// For more information about match tickets, see Multiplayer concepts overview.
+///
+///
+///
STDAPI_(void) XblMultiplayerManagerCancelMatch() XBL_NOEXCEPT;
///
@@ -1198,16 +1547,28 @@ STDAPI_(void) XblMultiplayerManagerCancelMatch() XBL_NOEXCEPT;
STDAPI_(XblMultiplayerMatchStatus) XblMultiplayerManagerMatchStatus() XBL_NOEXCEPT;
///
-/// Estimated wait time in seconds for a match request to be matched with other members.
-/// Only applies after XblMultiplayerManagerFindMatch() has been called.
+/// Retrieves the estimated wait time, in seconds, to complete a matchmaking request in progress.
///
-/// The wait time in seconds.
+/// The estimated wait time, in seconds.
+///
+/// Call this function only after the function has been called
+/// to submit a matchmaking request. The matchmaking request uses SmartMatch to find an existing game that has enough open
+/// player slots for all the members in the lobby session. If a matchmaking request isn't in progress,
+/// this function returns zero (0) seconds. For more information about finding a multiplayer game,
+/// see Enable finding a multiplayer game by using SmartMatch using Multiplayer Manager.
+///
+///
STDAPI_(uint32_t) XblMultiplayerManagerEstimatedMatchWaitTime() XBL_NOEXCEPT;
///
-/// Indicates whether the game should auto fill open slots during gameplay.
+/// Indicates whether the game should auto-fill open slots during gameplay.
///
-/// Returns true if should auto fill, false if should not auto fill.
+/// Returns true if the game should auto-fill open slots during gameplay; otherwise, false.
+/// Call the function to discover
+/// whether the game should use matchmaking to auto-fill open slots during gameplay. You can also call
+/// the function to specify whether the game
+/// should auto-fill open slots during gameplay. For more information about matchmaking,
+/// see Matchmaking overview.
STDAPI_(bool) XblMultiplayerManagerAutoFillMembersDuringMatchmaking() XBL_NOEXCEPT;
///
@@ -1242,9 +1603,9 @@ STDAPI XblMultiplayerManagerSetQosMeasurements(
) XBL_NOEXCEPT;
///
-/// Indicates who can join your game via the lobby.
+/// Indicates which users can join your lobby session.
///
-/// The joinability settings for your lobby.
+/// The joinability setting for your lobby session.
STDAPI_(XblMultiplayerJoinability) XblMultiplayerManagerJoinability() XBL_NOEXCEPT;
///
diff --git a/Include/xsapi-cpp/impl/notification.hpp b/Include/xsapi-cpp/impl/notification.hpp
index 79f18e4a..7ff906b5 100644
--- a/Include/xsapi-cpp/impl/notification.hpp
+++ b/Include/xsapi-cpp/impl/notification.hpp
@@ -13,7 +13,7 @@ notification_service::notification_service(_In_ XblContextHandle contextHandle)
notification_service::~notification_service()
{
-#if !XSAPI_UNIT_TESTS && !HC_PLATFORM_IS_EXTERNAL
+#if !XSAPI_UNIT_TESTS
unsubscribe_from_notifications().wait();
#endif
XblContextCloseHandle(m_xblContext);
@@ -77,6 +77,11 @@ string_t invite_notification_event_args::invite_protocol() const
return Utils::StringTFromUtf8(m_gameInviteArgs.inviteProtocol);
}
+string_t invite_notification_event_args::invite_context() const
+{
+ return Utils::StringTFromUtf8(m_gameInviteArgs.inviteContext);
+}
+
utility::datetime invite_notification_event_args::expiration() const
{
return Utils::DatetimeFromTimeT(m_gameInviteArgs.expiration);
@@ -194,13 +199,10 @@ pplx::task> notification_service::unsubscribe_from_notifi
#elif HC_PLATFORM == HC_PLATFORM_WIN32 && !XSAPI_UNIT_TESTS
pplx::task> notification_service::unsubscribe_from_notifications()
{
- auto xblContext = m_xblContext;
- auto asyncWrapper = new AsyncWrapper();
+ XblGameInviteRemoveNotificationHandler(m_xblContext, m_gameinviteFunctionContext);
+ XblAchievementUnlockRemoveNotificationHandler(m_xblContext, m_achievementUnlockFunctionContext);
- XblGameInviteRemoveNotificationHandler(xblContext, m_gameinviteFunctionContext);
- XblAchievementUnlockRemoveNotificationHandler(xblContext, m_achievementUnlockFunctionContext);
-
- return asyncWrapper->Task(S_OK);
+ return pplx::task_from_result(xbox::services::xbox_live_result());
}
#endif
#endif
diff --git a/Include/xsapi-cpp/notification_service.h b/Include/xsapi-cpp/notification_service.h
index 85d635e3..a1ce530c 100644
--- a/Include/xsapi-cpp/notification_service.h
+++ b/Include/xsapi-cpp/notification_service.h
@@ -21,6 +21,7 @@ public:
inline string_t sender_gamertag() const;
inline string_t invite_handle_id() const;
inline string_t invite_protocol() const;
+ inline string_t invite_context() const;
inline utility::datetime expiration() const;
inline const multiplayer::multiplayer_session_reference session_reference() const;
@@ -87,9 +88,7 @@ public:
#endif
);
-#if !HC_PLATFORM_IS_EXTERNAL
inline pplx::task> unsubscribe_from_notifications();
-#endif
#if HC_PLATFORM == HC_PLATFORM_WIN32 && !XSAPI_UNIT_TESTS
inline std::function& game_invite_handler();
diff --git a/Source/Services/Achievements/Manager/achievements_manager_internal.cpp b/Source/Services/Achievements/Manager/achievements_manager_internal.cpp
index 9eda12cc..e9c82d13 100644
--- a/Source/Services/Achievements/Manager/achievements_manager_internal.cpp
+++ b/Source/Services/Achievements/Manager/achievements_manager_internal.cpp
@@ -107,6 +107,7 @@ Result AchievementsManagerUser::Initialize(
{
assert(!m_isInitialized);
RETURN_HR_IF_FAILED(m_xblContext->Initialize(m_rtaManager));
+ m_xblContext->Settings()->SetHttpUserAgent(HttpCallAgent::AchievementsManager);
std::weak_ptr weakThis{ shared_from_this() };
m_achievementProgressToken = m_xblContext->AchievementsService()->AddAchievementProgressChangeHandler(
diff --git a/Source/Services/Common/xbox_live_context_settings.cpp b/Source/Services/Common/xbox_live_context_settings.cpp
index 53c67d15..e8c695b7 100644
--- a/Source/Services/Common/xbox_live_context_settings.cpp
+++ b/Source/Services/Common/xbox_live_context_settings.cpp
@@ -57,6 +57,16 @@ void XboxLiveContextSettings::SetWebsocketTimeoutWindow(_In_ uint32_t timeoutInS
m_websocketTimeoutWindowInSeconds = timeoutInSeconds;
}
+HttpCallAgent XboxLiveContextSettings::HttpUserAgent() const
+{
+ return m_userAgent;
+}
+
+void XboxLiveContextSettings::SetHttpUserAgent(_In_ HttpCallAgent userAgent)
+{
+ m_userAgent = userAgent;
+}
+
#if XSAPI_WINRT
bool XboxLiveContextSettings::UseCoreDispatcherForEventRouting() const
{
diff --git a/Source/Services/Common/xbox_live_context_settings_internal.h b/Source/Services/Common/xbox_live_context_settings_internal.h
index 042e1dae..aabad1ef 100644
--- a/Source/Services/Common/xbox_live_context_settings_internal.h
+++ b/Source/Services/Common/xbox_live_context_settings_internal.h
@@ -27,6 +27,15 @@ NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN
#define DEFAULT_RETRY_DELAY_SECONDS (2)
#define MIN_RETRY_DELAY_SECONDS (2)
+enum class HttpCallAgent : uint32_t
+{
+ Title,
+ MultiplayerManager,
+ SocialManager,
+ MultiplayerActivity,
+ AchievementsManager
+};
+
class XboxLiveContextSettings
{
public:
@@ -44,6 +53,9 @@ public:
uint32_t WebsocketTimeoutWindow() const;
void SetWebsocketTimeoutWindow(_In_ uint32_t timeoutInSeconds);
+ HttpCallAgent HttpUserAgent() const;
+ void SetHttpUserAgent(_In_ HttpCallAgent userAgent);
+
#if XSAPI_WINRT // WinRT only
bool UseCoreDispatcherForEventRouting() const;
@@ -65,6 +77,7 @@ private:
uint32_t m_httpRetryDelayInSeconds{ DEFAULT_RETRY_DELAY_SECONDS };
uint32_t m_httpTimeoutWindowInSeconds{ DEFAULT_HTTP_RETRY_WINDOW_SECONDS };
uint32_t m_websocketTimeoutWindowInSeconds{ DEFAULT_WEBSOCKET_TIMEOUT_SECONDS };
+ HttpCallAgent m_userAgent{ HttpCallAgent::Title };
#if XSAPI_WINRT
bool m_useCoreDispatcherForEventRouting{ false };
#endif
diff --git a/Source/Services/Events/events_service_gdk.cpp b/Source/Services/Events/events_service_gdk.cpp
index 3ea8e57f..6a6d2a76 100644
--- a/Source/Services/Events/events_service_gdk.cpp
+++ b/Source/Services/Events/events_service_gdk.cpp
@@ -43,9 +43,11 @@ HRESULT EventsService::WriteInGameEvent(
return hr;
}
+ String lowercaseScid = utils::ToLower(scid);
+
return XGameEventWrite(
m_user.Handle(),
- scid,
+ lowercaseScid.c_str(),
m_playSession.c_str(),
eventName,
dimensions,
diff --git a/Source/Services/Multiplayer/Manager/multiplayer_local_user.cpp b/Source/Services/Multiplayer/Manager/multiplayer_local_user.cpp
index 89387a3f..0db0ba84 100644
--- a/Source/Services/Multiplayer/Manager/multiplayer_local_user.cpp
+++ b/Source/Services/Multiplayer/Manager/multiplayer_local_user.cpp
@@ -27,6 +27,7 @@ MultiplayerLocalUser::MultiplayerLocalUser(
m_xboxLiveContextImpl = XblContext::Make(std::move(user));
m_xboxLiveContextImpl->Initialize(GlobalState::Get()->RTAManager());
+ m_xboxLiveContextImpl->Settings()->SetHttpUserAgent(HttpCallAgent::MultiplayerManager);
}
MultiplayerLocalUser::~MultiplayerLocalUser()
diff --git a/Source/Services/MultiplayerActivity/multiplayer_activity_service.cpp b/Source/Services/MultiplayerActivity/multiplayer_activity_service.cpp
index 51bcabfd..13253a3d 100644
--- a/Source/Services/MultiplayerActivity/multiplayer_activity_service.cpp
+++ b/Source/Services/MultiplayerActivity/multiplayer_activity_service.cpp
@@ -96,13 +96,15 @@ HRESULT MultiplayerActivityService::FlushRecentPlayers(
Stringstream path{};
path << "/titles/" << titleId << "/recentplayers";
- return XblHttpCall::Init(
+ RETURN_HR_IF_FAILED(XblHttpCall::Init(
contextSettings,
"POST",
XblHttpCall::BuildUrl(MPA_SERVICE_NAME, path.str()),
- xbox_live_api::post_recent_players,
- HttpCallAgent::MultiplayerActivity
- );
+ xbox_live_api::post_recent_players
+ ));
+ RETURN_HR_IF_FAILED(XblHttpCall::SetUserAgent(HttpCallAgent::MultiplayerActivity));
+
+ return S_OK;
}
HRESULT PerformWithRetry(
diff --git a/Source/Services/Notification/RTA/notification_subscription.cpp b/Source/Services/Notification/RTA/notification_subscription.cpp
index bbb3165c..dd4889da 100644
--- a/Source/Services/Notification/RTA/notification_subscription.cpp
+++ b/Source/Services/Notification/RTA/notification_subscription.cpp
@@ -179,6 +179,7 @@ GameInviteNotificationEventArgs::GameInviteNotificationEventArgs(
: XblGameInviteNotificationEventArgs{ other },
m_inviteHandleId{ other.m_inviteHandleId },
m_inviteProtocol{ other.m_inviteProtocol },
+ m_inviteContext{ other.m_inviteContext },
m_senderImageUrl{ other.m_senderImageUrl }
{
if (!m_inviteHandleId.empty())
@@ -189,6 +190,10 @@ GameInviteNotificationEventArgs::GameInviteNotificationEventArgs(
{
inviteProtocol = m_inviteProtocol.data();
}
+ if (!m_inviteContext.empty())
+ {
+ inviteContext = m_inviteContext.data();
+ }
if (!m_senderImageUrl.empty())
{
senderImageUrl = m_senderImageUrl.data();
@@ -209,6 +214,16 @@ Result GameInviteNotificationEventArgs::Deseria
RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonXuid(inviteHandle, "senderXuid", result.senderXboxUserId, true));
RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(inviteHandle, "id", result.m_inviteHandleId, true));
RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(inviteHandle, "inviteProtocol", result.m_inviteProtocol, true));
+ if (inviteHandle.IsObject() && inviteHandle.HasMember("inviteAttributes"))
+ {
+ const JsonValue& inviteAttributesValue = inviteHandle["inviteAttributes"];
+
+ if (inviteAttributesValue.HasMember("context"))
+ {
+ RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonString(inviteAttributesValue, "context", result.m_inviteContext, true));
+ }
+ }
+
RETURN_HR_IF_FAILED(JsonUtils::ExtractJsonTimeT(inviteHandle, "expiration", result.expiration, true));
result.sessionReference = xbox::services::multiplayer::Serializers::DeserializeSessionReference(inviteHandle["sessionRef"]).Payload();
diff --git a/Source/Services/Notification/RTA/notification_subscription.h b/Source/Services/Notification/RTA/notification_subscription.h
index cd260b67..886984c6 100644
--- a/Source/Services/Notification/RTA/notification_subscription.h
+++ b/Source/Services/Notification/RTA/notification_subscription.h
@@ -27,6 +27,7 @@ public:
private:
String m_inviteHandleId;
String m_inviteProtocol;
+ String m_inviteContext;
String m_senderImageUrl;
};
diff --git a/Source/Services/Notification/notification_service.cpp b/Source/Services/Notification/notification_service.cpp
index e260d1d7..dd61c27b 100644
--- a/Source/Services/Notification/notification_service.cpp
+++ b/Source/Services/Notification/notification_service.cpp
@@ -156,6 +156,7 @@ HRESULT NotificationService::RegisterForNotificationsHelper(
}
default:
{
+ auto workQueue = async.Queue().DeriveWorkerQueue();
m_registrationAsync = AsyncContext::Collapse({ std::move(m_registrationAsync), std::move(async) });
m_registrationStatus = RegistrationStatus::Registering;
@@ -213,17 +214,15 @@ HRESULT NotificationService::RegisterForNotificationsHelper(
RETURN_HR_IF_FAILED(httpCall->SetRequestBody(payload));
return httpCall->Perform(AsyncContext{
- async.Queue().DeriveWorkerQueue(),
+ workQueue,
[
- thisWeakPtr = std::weak_ptr{ shared_from_this() },
- async
+ thisWeakPtr = std::weak_ptr{ shared_from_this() }
](HttpResult httpResult)
{
std::shared_ptr pThis(thisWeakPtr.lock());
if (pThis == nullptr)
{
- async.Complete(E_XBL_RUNTIME_ERROR);
return;
}
diff --git a/Source/Services/Presence/presence_title_request.cpp b/Source/Services/Presence/presence_title_request.cpp
index 7aa8b0eb..f421d4e6 100644
--- a/Source/Services/Presence/presence_title_request.cpp
+++ b/Source/Services/Presence/presence_title_request.cpp
@@ -40,7 +40,7 @@ void TitleRequest::Serialize(_Out_ JsonValue& serializedObject, _In_ JsonDocumen
{
JsonValue presenceTokenIDsJson(rapidjson::kArrayType);
JsonUtils::SerializeVector(JsonUtils::JsonStringSerializer, m_presenceTokenIds, presenceTokenIDsJson, allocator);
- serializedObject.AddMember("params", presenceTokenIDsJson, allocator);
+ richPresenceJson.AddMember("params", presenceTokenIDsJson, allocator);
}
serializedObject.AddMember("activity", JsonValue(rapidjson::kObjectType).AddMember("richPresence", richPresenceJson, allocator).Move(), allocator);
diff --git a/Source/Services/RealTimeActivityManager/real_time_activity_connection.cpp b/Source/Services/RealTimeActivityManager/real_time_activity_connection.cpp
index 366103de..56add7a2 100644
--- a/Source/Services/RealTimeActivityManager/real_time_activity_connection.cpp
+++ b/Source/Services/RealTimeActivityManager/real_time_activity_connection.cpp
@@ -400,7 +400,14 @@ void Connection::SubscribeResponseHandler(_In_ const JsonValue& message) noexcep
sub->m_state->serviceId = message[3].GetInt();
const auto& data = message[4];
- m_activeSubs[sub->m_state->serviceId] = sub;
+ if (m_activeSubs.find(sub->m_state->serviceId) == m_activeSubs.end())
+ {
+ m_activeSubs[sub->m_state->serviceId] = { { sub->m_state->clientId, sub } };
+ }
+ else
+ {
+ m_activeSubs[sub->m_state->serviceId][sub->m_state->clientId] = sub;
+ }
switch (sub->m_state->serviceStatus)
{
@@ -515,7 +522,7 @@ void Connection::UnsubscribeResponseHandler(_In_ const JsonValue& message) noexc
return;
}
auto sub{ subIter->second };
- m_activeSubs.erase(sub->m_state->serviceId);
+ m_activeSubs[sub->m_state->serviceId].erase(clientId);
LOGS_DEBUG << __FUNCTION__ << ": [" << sub->m_state->clientId <<"] ServiceStatus=" << EnumName(sub->m_state->serviceStatus);
@@ -561,11 +568,14 @@ void Connection::EventHandler(_In_ const JsonValue& message) const noexcept
auto subIter{ m_activeSubs.find(serviceId) };
assert(subIter != m_activeSubs.end());
- auto sub = subIter->second;
+ auto subs = subIter->second;
lock.unlock();
- sub->OnEvent(data);
+ for (auto& subPair : subs)
+ {
+ subPair.second->OnEvent(data);
+ }
}
void Connection::ResyncHandler() const noexcept
diff --git a/Source/Services/RealTimeActivityManager/real_time_activity_connection.h b/Source/Services/RealTimeActivityManager/real_time_activity_connection.h
index 4e9beaf9..74c2306d 100644
--- a/Source/Services/RealTimeActivityManager/real_time_activity_connection.h
+++ b/Source/Services/RealTimeActivityManager/real_time_activity_connection.h
@@ -82,7 +82,7 @@ private:
Map> m_subs;
Map>> m_subscribeAsyncContexts;
Map>> m_unsubscribeAsyncContexts;
- Map> m_activeSubs;
+ Map>> m_activeSubs;
uint32_t m_nextSubId{ 1 };
diff --git a/Source/Services/Social/Manager/peoplehub_service.cpp b/Source/Services/Social/Manager/peoplehub_service.cpp
index 787bc7fa..0b4fce7c 100644
--- a/Source/Services/Social/Manager/peoplehub_service.cpp
+++ b/Source/Services/Social/Manager/peoplehub_service.cpp
@@ -96,8 +96,7 @@ HRESULT PeoplehubService::MakeServiceCall(
m_httpSettings,
relationshipType == RelationshipType::Batch ? "POST" : "GET",
XblHttpCall::BuildUrl("peoplehub", subpath.str()),
- xbox_live_api::get_social_graph,
- HttpCallAgent::SocialManager
+ xbox_live_api::get_social_graph
));
httpCall->SetXblServiceContractVersion(3);
diff --git a/Source/Services/Social/Manager/social_graph.cpp b/Source/Services/Social/Manager/social_graph.cpp
index 6852b419..ea075240 100644
--- a/Source/Services/Social/Manager/social_graph.cpp
+++ b/Source/Services/Social/Manager/social_graph.cpp
@@ -106,6 +106,7 @@ HRESULT SocialGraph::Initialize() noexcept
m_xblContext = XblContext::Make(copiedUser.ExtractPayload());
RETURN_HR_IF_FAILED(m_xblContext->Initialize(m_rtaManager));
+ m_xblContext->Settings()->SetHttpUserAgent(HttpCallAgent::SocialManager);
return S_OK;
}
diff --git a/Source/Services/Stats/statistic_change_subscription.cpp b/Source/Services/Stats/statistic_change_subscription.cpp
index 0bac44b0..bdc7d969 100644
--- a/Source/Services/Stats/statistic_change_subscription.cpp
+++ b/Source/Services/Stats/statistic_change_subscription.cpp
@@ -51,6 +51,36 @@ void StatisticChangeSubscription::OnSubscribe(
stream << field.GetInt();
value = stream.str();
}
+ else if (field.IsInt64())
+ {
+ Stringstream stream;
+ stream << field.GetInt64();
+ value = stream.str();
+ }
+ else if (field.IsUint())
+ {
+ Stringstream stream;
+ stream << field.GetUint();
+ value = stream.str();
+ }
+ else if (field.IsUint64())
+ {
+ Stringstream stream;
+ stream << field.GetUint64();
+ value = stream.str();
+ }
+ else if (field.IsDouble())
+ {
+ Stringstream stream;
+ stream << field.GetDouble();
+ value = stream.str();
+ }
+ else if (field.IsBool())
+ {
+ Stringstream stream;
+ stream << field.GetBool();
+ value = stream.str();
+ }
else if (field.IsString())
{
value = field.GetString();
@@ -106,17 +136,18 @@ void StatisticChangeSubscription::OnResync() noexcept
if (Succeeded(result))
{
auto& payload{ result.Payload() };
- assert(payload.ServiceConfigurationStatistics().size() == 1);
- assert(payload.ServiceConfigurationStatistics()[0].Statistics().size() == 1);
-
- statisticsService->HandleStatisticChanged(StatisticChangeEventArgs
- {
- sharedThis->m_xuid,
- sharedThis->m_scid,
- sharedThis->m_statisticName,
- sharedThis->m_statisticType,
- payload.ServiceConfigurationStatistics()[0].Statistics()[0].Value()
- });
+ if (payload.ServiceConfigurationStatistics().size() >= 1 &&
+ payload.ServiceConfigurationStatistics()[0].Statistics().size() >= 1)
+ {
+ statisticsService->HandleStatisticChanged(StatisticChangeEventArgs
+ {
+ sharedThis->m_xuid,
+ sharedThis->m_scid,
+ sharedThis->m_statisticName,
+ sharedThis->m_statisticType,
+ payload.ServiceConfigurationStatistics()[0].Statistics()[0].Value()
+ });
+ }
}
}
});
diff --git a/Source/Shared/async_helpers.cpp b/Source/Shared/async_helpers.cpp
index 95785319..3df1015a 100644
--- a/Source/Shared/async_helpers.cpp
+++ b/Source/Shared/async_helpers.cpp
@@ -130,6 +130,13 @@ TaskQueue TaskQueue::DeriveWorkerQueue(XTaskQueueHandle handle) noexcept
TaskQueue derivedQueue{ nullptr };
TaskQueue queue{ handle };
+ // If handle is null, derive a queue from XSAPI global queue
+ if (!queue.m_handle)
+ {
+ queue = TaskQueue{};
+ }
+
+ // If queue is still null, try to derive from the process default queue
if (!queue.m_handle)
{
TaskQueue processQueue{ nullptr };
@@ -141,7 +148,7 @@ TaskQueue TaskQueue::DeriveWorkerQueue(XTaskQueueHandle handle) noexcept
}
assert(queue.m_handle);
-
+
XTaskQueuePortHandle worker{ nullptr };
auto hr = XTaskQueueGetPort(queue.m_handle, XTaskQueuePort::Work, &worker);
assert(SUCCEEDED(hr));
diff --git a/Source/Shared/build_version.h b/Source/Shared/build_version.h
index cfb64cda..dcb5e927 100644
--- a/Source/Shared/build_version.h
+++ b/Source/Shared/build_version.h
@@ -9,4 +9,4 @@
//*********************************************************
#pragma once
-#define XBOX_SERVICES_API_VERSION_STRING "2021.06.20210908.3"
+#define XBOX_SERVICES_API_VERSION_STRING "2021.10.20210928.0"
diff --git a/Source/Shared/global_state.cpp b/Source/Shared/global_state.cpp
index 85887bab..a1896db4 100644
--- a/Source/Shared/global_state.cpp
+++ b/Source/Shared/global_state.cpp
@@ -51,6 +51,13 @@ HRESULT GlobalState::Create(
RETURN_HR_INVALIDARGUMENT_IF_EMPTY_STRING(args->scid);
#endif
+ XTaskQueueHandle processQueue{ nullptr };
+ bool haveProcessQueue = XTaskQueueGetCurrentProcessTaskQueue(&processQueue);
+ if (!haveProcessQueue && args->queue == nullptr)
+ {
+ return E_NO_TASK_QUEUE;
+ }
+
if (AccessHelper(AccessMode::GET))
{
return E_XBL_ALREADY_INITIALIZED;
diff --git a/Source/Shared/http_call_wrapper_internal.cpp b/Source/Shared/http_call_wrapper_internal.cpp
index e56c7e8c..d477114a 100644
--- a/Source/Shared/http_call_wrapper_internal.cpp
+++ b/Source/Shared/http_call_wrapper_internal.cpp
@@ -546,8 +546,7 @@ HRESULT XblHttpCall::Init(
_In_ std::shared_ptr contextSettings,
_In_ const xsapi_internal_string& httpMethod,
_In_ const xsapi_internal_string& fullUrl,
- _In_ xbox_live_api xboxLiveApi,
- _In_ HttpCallAgent agent
+ _In_ xbox_live_api xboxLiveApi
)
{
m_httpMethod = httpMethod;
@@ -562,39 +561,9 @@ HRESULT XblHttpCall::Init(
RETURN_HR_IF_FAILED(SetHeader(CONTRACT_VERSION_HEADER, "1"));
RETURN_HR_IF_FAILED(SetHeader(CONTENT_TYPE_HEADER, "application/json; charset=utf-8"));
RETURN_HR_IF_FAILED(SetHeader(ACCEPT_LANGUAGE_HEADER, utils::get_locales()));
-
- xsapi_internal_string userAgent = DEFAULT_USER_AGENT;
- if (agent != HttpCallAgent::Title)
- {
- userAgent += EnumName(agent);
- }
+ RETURN_HR_IF_FAILED(SetUserAgent(contextSettings->HttpUserAgent()));
- XblApiType apiType{ XblApiType::XblCApi };
- {
- auto state{ GlobalState::Get() };
-
- if (state)
- {
- apiType = state->ApiType;
- }
- }
-
- switch (apiType)
- {
- case XblApiType::XblCApi:
- {
- userAgent += " c";
- break;
- }
-
- case XblApiType::XblCPPApi:
- {
- userAgent += " cpp";
- break;
- }
- }
-
- return SetHeader(USER_AGENT_HEADER, userAgent);
+ return S_OK;
}
HRESULT XblHttpCall::CalcHttpTimeout()
@@ -626,6 +595,40 @@ HRESULT XblHttpCall::SetHeader(
return HttpCall::SetHeader(key, value, allowTracing);
}
+HRESULT XblHttpCall::SetUserAgent(_In_ HttpCallAgent userAgent)
+{
+ String headerValue{ DEFAULT_USER_AGENT };
+ if (userAgent != HttpCallAgent::Title)
+ {
+ headerValue += EnumName(userAgent);
+ }
+
+ XblApiType apiType{ XblApiType::XblCApi };
+ {
+ auto state{ GlobalState::Get() };
+ if (state)
+ {
+ apiType = state->ApiType;
+ }
+ }
+
+ switch (apiType)
+ {
+ case XblApiType::XblCApi:
+ {
+ headerValue += " c";
+ break;
+ }
+ case XblApiType::XblCPPApi:
+ {
+ headerValue += " cpp";
+ break;
+ }
+ }
+
+ return SetHeader(USER_AGENT_HEADER, headerValue);
+}
+
HRESULT XblHttpCall::SetRequestBody(const xsapi_internal_vector& bytes)
{
m_requestBody = bytes;
diff --git a/Source/Shared/http_call_wrapper_internal.h b/Source/Shared/http_call_wrapper_internal.h
index 88ae12f0..61d2898d 100644
--- a/Source/Shared/http_call_wrapper_internal.h
+++ b/Source/Shared/http_call_wrapper_internal.h
@@ -189,15 +189,6 @@ protected:
HRESULT ResetAndCopyForRetry();
};
-enum class HttpCallAgent : uint32_t
-{
- Title,
- MultiplayerManager,
- SocialManager,
- StatsManager,
- MultiplayerActivity
-};
-
NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END
@@ -215,8 +206,7 @@ public:
_In_ std::shared_ptr contextSettings,
_In_ const xsapi_internal_string& httpMethod,
_In_ const xsapi_internal_string& fullUrl,
- _In_ xbox_live_api xboxLiveApi,
- _In_ HttpCallAgent agent = HttpCallAgent::Title
+ _In_ xbox_live_api xboxLiveApi
);
HRESULT SetHeader(
@@ -225,6 +215,9 @@ public:
_In_ bool allowTracing = true
) override;
+ // Override the UserAgent that was specified in the contextSettings
+ HRESULT SetUserAgent(_In_ HttpCallAgent userAgent);
+
void SetLongHttpCall(_In_ bool longHttpCall);
HRESULT SetXblServiceContractVersion(uint32_t contractVersion);
diff --git a/Tests/ApiExplorer/APIExplorer.Shared.vcxitems b/Tests/ApiExplorer/APIExplorer.Shared.vcxitems
index 7ee2be20..a7832d56 100644
--- a/Tests/ApiExplorer/APIExplorer.Shared.vcxitems
+++ b/Tests/ApiExplorer/APIExplorer.Shared.vcxitems
@@ -254,7 +254,9 @@
true
-
+
+ true
+ true
@@ -282,7 +284,9 @@
true
-
+
+ true
+ true
@@ -481,9 +485,15 @@
true
+
+ true
+ true
+
+ true
+ true
diff --git a/Tests/ApiExplorer/APIExplorer.Shared.vcxitems.filters b/Tests/ApiExplorer/APIExplorer.Shared.vcxitems.filters
index 87d385d2..c890e58b 100644
--- a/Tests/ApiExplorer/APIExplorer.Shared.vcxitems.filters
+++ b/Tests/ApiExplorer/APIExplorer.Shared.vcxitems.filters
@@ -789,5 +789,11 @@
Tests\titleStorage
+
+ Tests\social
+
+
+ Tests\social
+
\ No newline at end of file
diff --git a/Tests/ApiExplorer/APIs/apis.cpp b/Tests/ApiExplorer/APIs/apis.cpp
index e895ea68..3f33310f 100644
--- a/Tests/ApiExplorer/APIs/apis.cpp
+++ b/Tests/ApiExplorer/APIs/apis.cpp
@@ -17,6 +17,7 @@ int GetCheckHR_Lua(lua_State *L)
int Sleep_Lua(lua_State *L)
{
DWORD time = (DWORD)GetUint32FromLua(L, 1, 0);
+ LogToScreen("Sleep(%d)", time);
pal::Sleep(time);
return LuaReturnHR(L, S_OK);
}
@@ -66,7 +67,6 @@ int LogToFile_Lua(lua_State *L)
int LogToScreen_Lua(lua_State *L)
{
-
return LogHelper(false, L);
}
@@ -223,6 +223,16 @@ int APIRunner_MemLogUnhookedStats_Lua(lua_State *L)
return LuaReturnHR(L, S_OK);
}
+int IsGDKPlatform_Lua(lua_State *L)
+{
+#if HC_PLATFORM == HC_PLATFORM_GDK
+ lua_pushboolean(L, true);
+#else
+ lua_pushboolean(L, false);
+#endif
+ return 1;
+}
+
void RegisterLuaAPIs()
{
SetupAPIs_Xal();
@@ -296,6 +306,7 @@ void RegisterLuaAPIs()
lua_register(Data()->L, "APIRunner_LogStats", APIRunner_LogStats_Lua);
lua_register(Data()->L, "APIRunner_AssertOnAllocOfId", APIRunner_AssertOnAllocOfId_Lua);
lua_register(Data()->L, "APIRunner_MemLogUnhookedStats", APIRunner_MemLogUnhookedStats_Lua);
+ lua_register(Data()->L, "IsGDKPlatform", IsGDKPlatform_Lua);
}
void SetupAPIS_Platform()
diff --git a/Tests/ApiExplorer/APIs/apis_async.cpp b/Tests/ApiExplorer/APIs/apis_async.cpp
index 449fcfe4..e146a373 100644
--- a/Tests/ApiExplorer/APIs/apis_async.cpp
+++ b/Tests/ApiExplorer/APIs/apis_async.cpp
@@ -47,7 +47,10 @@ int XTaskQueueCloseHandle_Lua(lua_State *L)
XTaskQueueHandle queue = Data()->queue;
// CODE SNIPPET START: XTaskQueueCloseHandle
- XTaskQueueCloseHandle(queue);
+ if (queue != nullptr)
+ {
+ XTaskQueueCloseHandle(queue);
+ }
// CODE SNIPPET END
Data()->queue = nullptr;
diff --git a/Tests/ApiExplorer/APIs/apis_cpp_multiplayer.cpp b/Tests/ApiExplorer/APIs/apis_cpp_multiplayer.cpp
index 9e5f63a8..ce9870bc 100644
--- a/Tests/ApiExplorer/APIs/apis_cpp_multiplayer.cpp
+++ b/Tests/ApiExplorer/APIs/apis_cpp_multiplayer.cpp
@@ -811,10 +811,10 @@ int MultiplayerSessionSetCurrentUserSecureDeviceAddressBase64Cpp_Lua(lua_State *
int MultiplayerSessionSetCurrentUserRolesCpp_Lua(lua_State *L)
{
- string_t roleTypeName1 = L"roleTypeName1";
- string_t roleName1 = L"roleName1";
- string_t roleTypeName2 = L"roleTypeName2";
- string_t roleName2 = L"roleName2";
+ string_t roleTypeName1 = _T("roleTypeName1");
+ string_t roleName1 = _T("roleName1");
+ string_t roleTypeName2 = _T("roleTypeName2");
+ string_t roleName2 = _T("roleName2");
std::unordered_map roleInfo{
std::pair(roleTypeName1, roleName1),
@@ -1049,7 +1049,7 @@ int MultiplayerServiceGetCurrentSessionByHandle_Lua(lua_State *L)
auto sessionIndex{ GetUint64FromLua(L, 2, 0) };
if (handleId.empty())
{
- handleId = L"86191619-4002-044f-4846-f8f903c71512";
+ handleId = _T("86191619-4002-044f-4846-f8f903c71512");
}
LogToFile("handleId: %s", xbox::services::Utils::StringFromStringT(handleId).c_str());
@@ -1400,9 +1400,9 @@ int MultiplayerServiceSetTransferHandle_Lua(lua_State* L)
int MultiplayerServiceSetSearchHandle_Lua(lua_State *L)
{
- std::vector tags{ L"SessionTag" };
- std::unordered_map numbersMetadata{ std::pair(L"numberattributename", 1.1) };
- std::unordered_map stringsMetadata{ std::pair(L"stringattributename", L"string attribute value") };
+ std::vector tags{ _T("SessionTag") };
+ std::unordered_map numbersMetadata{ std::pair(_T("numberattributename"), 1.1) };
+ std::unordered_map stringsMetadata{ std::pair(_T("stringattributename"), _T("string attribute value")) };
xbox::services::multiplayer::multiplayer_session_reference sessionRef = MPStateCpp()->sessionRef;
xbox::services::multiplayer::multiplayer_search_handle_request searchRequest(sessionRef);
@@ -1447,10 +1447,10 @@ int MultiplayerServiceClearSearchHandle_Lua(lua_State *L)
int MultiplayerServiceGetSearchHandles_Lua(lua_State *L)
{
string_t serviceConfigurationId = xbox::services::Utils::StringTFromUtf8(Data()->scid);
- string_t sessionTemplateName = L"MinGameSession";
- string_t orderByAttribute = L"";
bool orderAscending{ false };
- string_t searchFilter = L"";
+ string_t sessionTemplateName = _T("MinGameSession");
+ string_t orderByAttribute = _T("");
+ string_t searchFilter = _T("");
std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext);
xblc->multiplayer_service().get_search_handles(
diff --git a/Tests/ApiExplorer/APIs/apis_cpp_presence.cpp b/Tests/ApiExplorer/APIs/apis_cpp_presence.cpp
index c755eb88..6d93e5b6 100644
--- a/Tests/ApiExplorer/APIs/apis_cpp_presence.cpp
+++ b/Tests/ApiExplorer/APIs/apis_cpp_presence.cpp
@@ -142,7 +142,7 @@ int PresenceServiceGetPresence_Lua(lua_State *L)
int PresenceServiceGetPresenceForMultipleUsers_Lua(lua_State* L)
{
#if CPP_TESTS_ENABLED
- std::vector xuids{ L"2814639011617876", L"2814641789541994" };
+ std::vector xuids{ _T("2814639011617876"), _T("2814641789541994") };
std::vector deviceTypes;
std::vector titleIds;
xbox::services::presence::presence_detail_level detailLevel = xbox::services::presence::presence_detail_level::all;
@@ -206,8 +206,7 @@ int PresenceServiceGetPresenceForSocialGroup_Lua(lua_State *L)
int PresenceServiceSubscribeToDevicePresenceChange_Lua(lua_State* L)
{
#if CPP_TESTS_ENABLED
- string_t xuid{ L"2814639011617876" };
-
+ string_t xuid{ _T("2814639011617876") };
std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext);
xbox::services::xbox_live_result> result = xblc->presence_service().subscribe_to_device_presence_change(xuid);
@@ -249,8 +248,7 @@ int PresenceServiceUnsubscribeFromDevicePresenceChange_Lua(lua_State* L)
int PresenceServiceSubscribeToTitlePresenceChange_Lua(lua_State* L)
{
#if CPP_TESTS_ENABLED
- string_t xuid{ L"2814639011617876" };
-
+ string_t xuid{ _T("2814639011617876") };
std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext);
xbox::services::xbox_live_result> result = xblc->presence_service().subscribe_to_title_presence_change(xuid, Data()->titleId);
diff --git a/Tests/ApiExplorer/APIs/apis_cpp_social.cpp b/Tests/ApiExplorer/APIs/apis_cpp_social.cpp
index 9f129d4a..b68b18b4 100644
--- a/Tests/ApiExplorer/APIs/apis_cpp_social.cpp
+++ b/Tests/ApiExplorer/APIs/apis_cpp_social.cpp
@@ -202,11 +202,12 @@ int SocialServiceRemoveSocialRelationshipChangedHandler_Lua(lua_State *L)
int ReputationServiceSubmitReputationFeedback_Lua(lua_State* L)
{
#if CPP_TESTS_ENABLED
- string_t xuid = L"2814639011617876";
xbox::services::social::reputation_feedback_type reputationFeedbackType = xbox::services::social::reputation_feedback_type::positive_helpful_player;
- string_t sessionName = L"";
- string_t reasonMessage = L"Helpful player";
- string_t evidenceResourceId = L"";
+
+ string_t xuid = _T("2814639011617876");
+ string_t sessionName = _T("");
+ string_t reasonMessage = _T("Helpful player");
+ string_t evidenceResourceId = _T("");
std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext);
xblc->reputation_service().submit_reputation_feedback(
@@ -233,16 +234,17 @@ int ReputationServiceSubmitBatchReputationFeedback_Lua(lua_State *L)
{
#if CPP_TESTS_ENABLED
std::vector feedbackItems;
+
feedbackItems.push_back(xbox::services::social::reputation_feedback_item
{
- L"2814639011617876",
+ _T("2814639011617876"),
xbox::services::social::reputation_feedback_type::positive_helpful_player,
xbox::services::multiplayer::multiplayer_session_reference(),
- L"Helpful player",
- L""
+ _T("Helpful player"),
+ _T("")
});
- // Add any additional feedback items here
+ // Add any additional feedback items here
std::shared_ptr xblc = std::make_shared(Data()->xboxLiveContext);
xblc->reputation_service().submit_batch_reputation_feedback(feedbackItems).then(
[](xbox::services::xbox_live_result result)
diff --git a/Tests/ApiExplorer/APIs/apis_cpp_social_manager.cpp b/Tests/ApiExplorer/APIs/apis_cpp_social_manager.cpp
index 1516cceb..f50b12cc 100644
--- a/Tests/ApiExplorer/APIs/apis_cpp_social_manager.cpp
+++ b/Tests/ApiExplorer/APIs/apis_cpp_social_manager.cpp
@@ -114,8 +114,8 @@ xbox::services::social::manager::social_manager_extra_detail_level ConvertString
{
xbox::services::social::manager::social_manager_extra_detail_level detailLevel = xbox::services::social::manager::social_manager_extra_detail_level::no_extra_detail;
- if (pal::stricmp(str, "social_manager_extra_detail_level::title_history_level") == 0) detailLevel = xbox::services::social::manager::social_manager_extra_detail_level::title_history_level;
- else if (pal::stricmp(str, "social_manager_extra_detail_level::preferred_color_level") == 0) detailLevel = xbox::services::social::manager::social_manager_extra_detail_level::preferred_color_level;
+ if (pal::stricmp(str, "social_manager_extra_detail_level::title_history_leve_T(") == 0) detailLevel = xbox::services::social::manager::social_manager_extra_detail_level::title_history_level;
+ else if (pal::stricmp(str, "social_manager_extra_detail_level::preferred_color_leve_T(") == 0) detailLevel = xbox::services::social::manager::social_manager_extra_detail_level::preferred_color_level;
return detailLevel;
}
@@ -132,7 +132,7 @@ xbox::services::social::manager::presence_filter ConvertStringToCppPresenceFilte
else if (pal::stricmp(str, "presence_filter::all_online") == 0) filter = xbox::services::social::manager::presence_filter::all_online;
else if (pal::stricmp(str, "presence_filter::all_offline") == 0) filter = xbox::services::social::manager::presence_filter::all_offline;
else if (pal::stricmp(str, "presence_filter::all_title") == 0) filter = xbox::services::social::manager::presence_filter::all_title;
- else if (pal::stricmp(str, "presence_filter::all") == 0) filter = xbox::services::social::manager::presence_filter::all;
+ else if (pal::stricmp(str, "presence_filter::al_T(") == 0) filter = xbox::services::social::manager::presence_filter::all;
return filter;
}
@@ -150,22 +150,20 @@ xbox::services::social::manager::relationship_filter ConvertStringToCppRelations
// Pool of XDKS.1 xuids to create social groups from
std::vector listXuidStrings
{
- L"2814639011617876",L"2814641789541994",L"2814644008675844",L"2814644210052185",L"2814645164579523",L"2814646075485729",L"2814649783195402",L"2814650260879943",
- L"2814652370182940",L"2814652714045777",L"2814654391560620",L"2814654975417728",L"2814656000993855",L"2814660006763195",L"2814666715930430",L"2814667316080600",
- L"2814669550092398",L"2814669684179632",L"2814669733667211",L"2814671180786692",L"2814679901432274",L"2814613501048225",L"2814614352529204",L"2814615856126401",
- L"2814616641363830",L"2814617883586813",L"2814618053453081",L"2814629752527080",L"2814631255161151",L"2814632477267887",L"2814633284389038",L"2814635732495522",
- L"2814635779785472",L"2814635974475208",L"2814636979708499",L"2814618092438397",L"2814618260480530",L"2814618319551907",L"2814619559360314",L"2814620368929739",
- L"2814620769042115",L"2814621007349381",L"2814623088399025",L"2814623825448960",L"2814624220291971",L"2814624961587858",L"2814626394212372",L"2814626639518570",
- L"2814628203722867",L"2814629143923154",L"2814614382301082",L"2814614959737919",L"2814615558140392",L"2814618401629514",L"2814618701087902",L"2814619300882392",
- L"2814623785189962",L"2814623956387698",L"2814625066090704",L"2814625471782204",L"2814626946705530",L"2814627006318591",L"2814628046127456",L"2814631487749991",
- L"2814631517599783",L"2814632798310691",L"2814633582140204",L"2814634204785789",L"2814634895412664",L"2814635439049207",L"2814638609354868",L"2814639589885754",
- L"2814641670947751",L"2814643512602566",L"2814646137630843",L"2814648499394446",L"2814651465227139",L"2814652150012664",L"2814653926747608",L"2814655098938516",
- L"2814655264861214",L"2814655417678099",L"2814655883565306",L"2814656031821923",L"2814656159501072",L"2814656780954834",L"2814660657970845",L"2814661604435490",
- L"2814663444319727",L"2814663818015575",L"2814665274839967",L"2814667273133504",L"2814670761542037",L"2814672762886609",L"2814673772488023",L"2814674096344056",
- L"2814674229538758",L"2814678943953289",L"2814680898042782"
+ _T("2814639011617876"),_T("2814641789541994"),_T("2814644008675844"),_T("2814644210052185"),_T("2814645164579523"),_T("2814646075485729"),_T("2814649783195402"),_T("2814650260879943"),
+ _T("2814652370182940"),_T("2814652714045777"),_T("2814654391560620"),_T("2814654975417728"),_T("2814656000993855"),_T("2814660006763195"),_T("2814666715930430"),_T("2814667316080600"),
+ _T("2814669550092398"),_T("2814669684179632"),_T("2814669733667211"),_T("2814671180786692"),_T("2814679901432274"),_T("2814613501048225"),_T("2814614352529204"),_T("2814615856126401"),
+ _T("2814616641363830"),_T("2814617883586813"),_T("2814618053453081"),_T("2814629752527080"),_T("2814631255161151"),_T("2814632477267887"),_T("2814633284389038"),_T("2814635732495522"),
+ _T("2814635779785472"),_T("2814635974475208"),_T("2814636979708499"),_T("2814618092438397"),_T("2814618260480530"),_T("2814618319551907"),_T("2814619559360314"),_T("2814620368929739"),
+ _T("2814620769042115"),_T("2814621007349381"),_T("2814623088399025"),_T("2814623825448960"),_T("2814624220291971"),_T("2814624961587858"),_T("2814626394212372"),_T("2814626639518570"),
+ _T("2814628203722867"),_T("2814629143923154"),_T("2814614382301082"),_T("2814614959737919"),_T("2814615558140392"),_T("2814618401629514"),_T("2814618701087902"),_T("2814619300882392"),
+ _T("2814623785189962"),_T("2814623956387698"),_T("2814625066090704"),_T("2814625471782204"),_T("2814626946705530"),_T("2814627006318591"),_T("2814628046127456"),_T("2814631487749991"),
+ _T("2814631517599783"),_T("2814632798310691"),_T("2814633582140204"),_T("2814634204785789"),_T("2814634895412664"),_T("2814635439049207"),_T("2814638609354868"),_T("2814639589885754"),
+ _T("2814641670947751"),_T("2814643512602566"),_T("2814646137630843"),_T("2814648499394446"),_T("2814651465227139"),_T("2814652150012664"),_T("2814653926747608"),_T("2814655098938516"),
+ _T("2814655264861214"),_T("2814655417678099"),_T("2814655883565306"),_T("2814656031821923"),_T("2814656159501072"),_T("2814656780954834"),_T("2814660657970845"),_T("2814661604435490"),
+ _T("2814663444319727"),_T("2814663818015575"),_T("2814665274839967"),_T("2814667273133504"),_T("2814670761542037"),_T("2814672762886609"),_T("2814673772488023"),_T("2814674096344056"),
+ _T("2814674229538758"),_T("2814678943953289"),_T("2814680898042782")
};
-
-
#endif
//lua commands
@@ -399,7 +397,7 @@ int SocialManagerAddLocalUserCpp_Lua(lua_State *L)
{
#if CPP_TESTS_ENABLED
xbox::services::social::manager::social_manager_extra_detail_level extraDetailLevel = ConvertStringToCppSocialManagerExtraDetailLevel(
- GetStringFromLua(L, 1, "social_manager_extra_detail_level::no_extra_detail").c_str());
+ GetStringFromLua(L, 1, "social_manager_extra_detail_level::no_extra_detai_T(").c_str());
LogToFile("SocialManagerAddLocalUserCpp: social_manager_extra_detail_level: %d", extraDetailLevel);
xbox_live_user_t user = Data()->xalUser;
@@ -432,7 +430,7 @@ int SocialManagerCreateSocialUserGroupFromFiltersCpp_Lua(lua_State *L)
{
#if CPP_TESTS_ENABLED
xbox_live_user_t user = Data()->xalUser;
- xbox::services::social::manager::presence_filter presenceFilter = ConvertStringToCppPresenceFilter(GetStringFromLua(L, 1, "presence_filter::all").c_str());
+ xbox::services::social::manager::presence_filter presenceFilter = ConvertStringToCppPresenceFilter(GetStringFromLua(L, 1, "presence_filter::al_T(").c_str());
xbox::services::social::manager::relationship_filter relationshipFilter = ConvertStringToCppRelationshipFilter(GetStringFromLua(L, 2, "relationship_filter::friends").c_str());
LogToFile("SocialManagerCreateSocialUserGroupFromFiltersCpp: presence_filter: %d", presenceFilter);
diff --git a/Tests/ApiExplorer/APIs/apis_libhttp.cpp b/Tests/ApiExplorer/APIs/apis_libhttp.cpp
index eb9fa375..d5a44605 100644
--- a/Tests/ApiExplorer/APIs/apis_libhttp.cpp
+++ b/Tests/ApiExplorer/APIs/apis_libhttp.cpp
@@ -4,10 +4,12 @@
int HCInitialize_Lua(lua_State *L)
{
- //HCInitArgs args = {};
-
// CODE SNIPPET START: HCInitialize
+#if HC_PLATFORM == HC_PLATFORM_ANDROID
+ HRESULT hr = HCInitialize(&(Data()->initArgs));
+#else
HRESULT hr = HCInitialize(nullptr);
+#endif
// CODE SNIPPET END
LogToFile("HCInitialize: hr=%s", ConvertHR(hr).c_str());
@@ -366,6 +368,7 @@ int HCWebSocketSetHeader_Lua(lua_State *L)
int HCWebSocketConnectAsync_Lua(lua_State *L)
{
+ //TODO: websocket.org is no longer in service; find a new server
std::string uri = GetStringFromLua(L, 1, "wss://echo.websocket.org");
std::string subProtocol = GetStringFromLua(L, 2, "");
diff --git a/Tests/ApiExplorer/APIs/apis_xal.cpp b/Tests/ApiExplorer/APIs/apis_xal.cpp
index 395bfb54..8653a046 100644
--- a/Tests/ApiExplorer/APIs/apis_xal.cpp
+++ b/Tests/ApiExplorer/APIs/apis_xal.cpp
@@ -1,588 +1,669 @@
-// Copyright (c) Microsoft Corporation
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-#include "pch.h"
-int XalCleanupAsync_Lua(lua_State *L)
-{
- if (!Data()->m_isXalInitialized)
- return LuaReturnHR(L, S_OK);
-
- if (Data()->nsalMockCall != nullptr)
- {
- HCMockRemoveMock(Data()->nsalMockCall);
- Data()->nsalMockCall = nullptr;
-
- if (Data()->libHttpClientInit) // Set on GDK where XAL is just a wrapper around XUser
- {
- HCCleanup();
- Data()->libHttpClientInit = false;
- }
- }
-
-#if CPP_TESTS_ENABLED
- Data()->blobMetadataResultCpp = xbox::services::title_storage::title_storage_blob_metadata_result{};
- Data()->blobMetadataCpp = xbox::services::title_storage::title_storage_blob_metadata{};
-#endif
-
- auto asyncBlock = std::make_unique();
- asyncBlock->queue = Data()->queue;
- asyncBlock->context = nullptr;
- HRESULT hr = XalCleanupAsync(asyncBlock.get());
- if (SUCCEEDED(hr))
- {
- // Synchronously wait for cleanup to complete
- hr = XAsyncGetStatus(asyncBlock.get(), true);
- Data()->m_isXalInitialized = false;
- }
-
- LogToFile("XalCleanup: hr=%s", ConvertHR(hr).c_str());
- return LuaReturnHR(L, hr);
-}
-
-void SetupXalNsalMock()
-{
- // Mock the NSAL call so XAL doesn't 429 due to repeated fast XAL init/cleanup during testing
-
- // GET https://title.mgt.xboxlive.com/titles/current/endpoints HTTP/1.1
- // HTTP/1.1 200 OK
- // {"EndPoints":[{"Protocol":"http","Host":"*","HostType":"wildcard"},{"Protocol":"https","Host":"*","HostType":"wildcard"}]}
-
- if (Data()->nsalMockCall != nullptr)
- {
- HCMockRemoveMock(Data()->nsalMockCall);
- Data()->nsalMockCall = nullptr;
- }
-
- HRESULT hr = HCMockCallCreate(&(Data()->nsalMockCall));
- if (hr == E_HC_NOT_INITIALISED)
- {
- // This happens on GDK where XAL is just a wrapper around XUser
- LogToScreen("Calling HCInitialize()");
- hr = HCInitialize(nullptr);
- LogToScreen("HCInitialize done");
- assert(SUCCEEDED(hr));
- Data()->libHttpClientInit = true;
- hr = HCMockCallCreate(&(Data()->nsalMockCall));
- }
- assert(SUCCEEDED(hr));
-
- hr = HCMockAddMock(Data()->nsalMockCall, "GET", "https://title.mgt.xboxlive.com/titles/current/endpoints", nullptr, 0);
- assert(SUCCEEDED(hr));
-
- hr = HCMockResponseSetStatusCode(Data()->nsalMockCall, 200);
- assert(SUCCEEDED(hr));
-
- std::string responseBodyString = "{\"EndPoints\":[{\"Protocol\":\"http\",\"Host\":\"*\",\"HostType\":\"wildcard\"},{\"Protocol\":\"https\",\"Host\":\"*\",\"HostType\":\"wildcard\"}]}";
- std::vector bodyBytes{ responseBodyString.begin(), responseBodyString.end() };
- hr = HCMockResponseSetResponseBodyBytes(Data()->nsalMockCall, bodyBytes.data(), static_cast(bodyBytes.size()));
- assert(SUCCEEDED(hr));
-}
-
-
-int XalInitialize_Lua(lua_State *L)
-{
- std::string clientId = GetStringFromLua(L, 1, "000000004C26FED0");
- uint32_t titleId = (uint32_t)GetUint64FromLua(L, 2, 1979882317);
- std::string sandbox = GetStringFromLua(L, 3, "XDKS.1");
-
- // Alternate config for XboxLiveE2E Stats 2017
- //std::string clientId = GetStringFromLua(L, 1, "0000000044296E10");
- //uint32_t titleId = (uint32_t)GetUint64FromLua(L, 2, 2025855259);
-
- Data()->titleId = titleId;
-
- CreateQueueIfNeeded();
-
- // CODE SNIPPET START: XalInitialize
-
- XalInitArgs xalInitArgs = {};
-#if HC_PLATFORM == HC_PLATFORM_UWP
-
- xalInitArgs.titleId = titleId;
- xalInitArgs.packageFamilyName = u8"41336MicrosoftATG.XboxLiveE2E_dspnxghe87tn0";
- //xalInitArgs.correlationVector; // optional
- //xalInitArgs.flags; // optional
- //xalInitArgs.launchUser; // optional
- //xalInitArgs.mainWindow; // optional
-
-#elif HC_PLATFORM == HC_PLATFORM_GDK || HC_PLATFORM == HC_PLATFORM_XDK
- // No args on GDK / XDK
-
-#else
-
- // Args for iOS / Android / Win32
- xalInitArgs.clientId = clientId.c_str();
- xalInitArgs.titleId = titleId;
- xalInitArgs.sandbox = sandbox.c_str();
-#if HC_PLATFORM == HC_PLATFORM_IOS
- // Extra args on iOS
- std::string redirectUri{ "ms-xal-" + clientId + "://auth" };
- xalInitArgs.redirectUri = redirectUri.data();
-#endif
-
-#endif
-
- HCTraceSetTraceToDebugger(true);
- HCSettingsSetTraceLevel(HCTraceLevel::Verbose);
-
- HRESULT hr = XalInitialize(&xalInitArgs, Data()->queue);
- // CODE SNIPPET END
- Data()->m_isXalInitialized = true;
- LogToFile("XalInitialize: hr=%s", ConvertHR(hr).c_str());
-
- SetupXalNsalMock();
-
- return LuaReturnHR(L, hr);
-}
-
-int XalTryAddFirstUserSilentlyAsync_Lua(lua_State *L)
-{
- CreateQueueIfNeeded();
-
- // CODE SNIPPET START: XalTryAddFirstUserSilentlyAsync
- auto asyncBlock = std::make_unique();
- asyncBlock->queue = Data()->queue;
- asyncBlock->context = nullptr;
- asyncBlock->callback = [](XAsyncBlock* asyncBlock)
- {
- std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock*
- XalUserHandle newUser = nullptr;
- HRESULT hr = XalTryAddDefaultUserSilentlyResult(asyncBlock, &newUser);
- // TODO: Store and use newUser
- LogToFile("XalTryAddFirstUserSilentlyResult: hr=%s user=0x%0.8x", ConvertHR(hr).c_str(), newUser); // CODE SNIP SKIP
- if (Data()->xalUser != nullptr)
- {
- XalUserCloseHandle(Data()->xalUser);
- Data()->xalUser = nullptr;
- }
- Data()->xalUser = newUser; // CODE SNIP SKIP
- if (Data()->xalUser) // CODE SNIP SKIP
- { // CODE SNIP SKIP
- Data()->gotXalUser = true; // CODE SNIP SKIP
- } // CODE SNIP SKIP
- LuaStopTestIfFailed(hr);
- CallLuaString("common = require 'common'; common.OnXalTryAddFirstUserSilentlyAsync()"); // CODE SNIP SKIP
- };
-
- HRESULT hr = XalTryAddDefaultUserSilentlyAsync(0u, 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();
- }
- // CODE SNIPPET END
- LogToFile("XalTryAddFirstUserSilentlyAsync: hr=%s", ConvertHR(hr).c_str());
- return LuaReturnHR(L, hr);
-}
-
-int XalGetMaxUsers_Lua(lua_State *L)
-{
- // CODE SNIPPET START: XalGetMaxUsers
- uint32_t maxUsers = 0;
- HRESULT hr = XalGetMaxUsers(&maxUsers);
- // CODE SNIPPET END
- LogToFile("XalGetMaxUsers: hr=%s. result=%d", ConvertHR(hr).c_str(), maxUsers); // CODE SNIP SKIP
- return LuaReturnHR(L, hr);
-}
-
-int XalAddUserWithUiAsync_Lua(lua_State *L)
-{
- CreateQueueIfNeeded();
-
- // CODE SNIPPET START: XalAddUserWithUiAsync
- auto asyncBlock = std::make_unique();
- asyncBlock->queue = Data()->queue;
- asyncBlock->context = nullptr;
- asyncBlock->callback = [](XAsyncBlock* asyncBlock)
- {
- std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock*
- XalUserHandle newUser = nullptr;
- HRESULT hr = XalAddUserWithUiResult(asyncBlock, &newUser);
- // TODO: Store and use newUser
- LogToFile("XalAddUserWithUiResult: hr=%s user=0x%0.8x", ConvertHR(hr).c_str(), newUser); // CODE SNIP SKIP
- if (Data()->xalUser != nullptr)
- {
- XalUserCloseHandle(Data()->xalUser);
- Data()->xalUser = nullptr;
- }
- Data()->xalUser = newUser; // CODE SNIP SKIP
- if (Data()->xalUser) // CODE SNIP SKIP
- { // CODE SNIP SKIP
- Data()->gotXalUser = true; // CODE SNIP SKIP
- } // CODE SNIP SKIP
- CallLuaFunctionWithHr(hr, "OnXalAddUserWithUiAsync"); // CODE SNIP SKIP
- };
-
- HRESULT hr = XalAddUserWithUiAsync(0u, 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();
- }
- // CODE SNIPPET END
- LogToFile("XalAddUserWithUiAsync: hr=%s", ConvertHR(hr).c_str());
- return LuaReturnHR(L, hr);
-}
-
-int XalGetDeviceUserIsPresent_Lua(lua_State *L)
-{
- // CODE SNIPPET START: XalGetDeviceUserIsPresent
- bool result = XalGetDeviceUserIsPresent();
- // CODE SNIPPET END
- LogToFile("XalGetDeviceUserIsPresent: result=%d", result);
- return LuaReturnHR(L, S_OK);
-}
-
-int XalGetDeviceUser_Lua(lua_State *L)
-{
- // CODE SNIPPET START: XalGetDeviceUser
- XalUserHandle deviceUser = nullptr;
- HRESULT hr = XalGetDeviceUser(&deviceUser);
- // CODE SNIPPET END
- LogToFile("XalGetDeviceUser: hr=%s", ConvertHR(hr).c_str());
- return LuaReturnHR(L, hr);
-}
-
-int XalSignOutUserAsyncIsPresent_Lua(lua_State *L)
-{
- // CODE SNIPPET START: XalSignOutUserAsyncIsPresent
- bool result = XalSignOutUserAsyncIsPresent();
- // CODE SNIPPET END
- LogToFile("XalSignOutUserAsyncIsPresent: result=%d", result);
- return LuaReturnHR(L, S_OK);
-}
-
-int XalSignOutUserAsync_Lua(lua_State *L)
-{
- CreateQueueIfNeeded();
-
- // CODE SNIPPET START: XalSignOutUserAsync
- auto asyncBlock = std::make_unique();
- asyncBlock->queue = Data()->queue;
- asyncBlock->context = nullptr;
- asyncBlock->callback = [](XAsyncBlock* asyncBlock)
- {
- std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock*
- HRESULT hr = XAsyncGetStatus(asyncBlock, false);
- // TODO: Store and use newUser
- LogToFile("OnXalSignOutUserAsync: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP
- XalUserCloseHandle(Data()->xalUser); // CODE SNIP SKIP
- Data()->xalUser = nullptr; // CODE SNIP SKIP
- CallLuaFunctionWithHr(hr, "OnXalSignOutUserAsync"); // CODE SNIP SKIP
- };
-
- HRESULT hr = XalSignOutUserAsync(Data()->xalUser, 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();
- }
- // CODE SNIPPET END
- LogToFile("XalSignOutUserAsync: hr=%s", ConvertHR(hr).c_str());
- return LuaReturnHR(L, hr);
-}
-
-int XalUserDuplicateHandle_Lua(lua_State *L)
-{
- // CODE SNIPPET START: XalUserDuplicateHandle
- XalUserHandle dupUser = nullptr;
- HRESULT hr = XalUserDuplicateHandle(Data()->xalUser, &dupUser);
- // CODE SNIPPET END
- LogToFile("XalUserDuplicateHandle: hr=%s", ConvertHR(hr).c_str());
- return LuaReturnHR(L, hr);
-}
-
-int XalUserCloseHandle_Lua(lua_State *L)
-{
- XalUserHandle xalUser = Data()->xalUser;
- if (xalUser != nullptr)
- {
- // CODE SNIPPET START: XalUserCloseHandle
- XalUserCloseHandle(xalUser);
- xalUser = nullptr;
- // CODE SNIPPET END
- Data()->xalUser = nullptr;
- LogToFile("XalUserCloseHandle");
- }
- return LuaReturnHR(L, S_OK);
-}
-
-int XalUserGetId_Lua(lua_State *L)
-{
- if (Data()->xalUser == nullptr)
- {
- return LuaReturnHR(L, S_OK);
- }
-
- // CODE SNIPPET START: XalUserGetId
- uint64_t xboxUserId = 0;
- HRESULT hr = XalUserGetId(Data()->xalUser, &xboxUserId);
- // CODE SNIPPET END
- LogToFile("XalUserGetId: hr=%s xboxUserId=%ul", ConvertHR(hr).c_str(), xboxUserId);
- Data()->xboxUserId = xboxUserId;
- return LuaReturnHR(L, hr);
-}
-
-int XalUserIsDevice_Lua(lua_State *L)
-{
- // CODE SNIPPET START: XalUserIsDevice
- bool result = XalUserIsDevice(Data()->xalUser);
- // CODE SNIPPET END
- LogToFile("XalUserIsDevice: result=%d", result);
- return LuaReturnHR(L, S_OK);
-}
-
-int XalUserIsGuest_Lua(lua_State *L)
-{
- // CODE SNIPPET START: XalUserIsGuest
- bool result = XalUserIsGuest(Data()->xalUser);
- // CODE SNIPPET END
- LogToFile("XalUserIsGuest: result=%d", result);
- return LuaReturnHR(L, S_OK);
-}
-
-int XalUserGetState_Lua(lua_State *L)
-{
- // CODE SNIPPET START: XalUserGetState
- XalUserState state = {};
- HRESULT hr = XalUserGetState(Data()->xalUser, &state);
- // CODE SNIPPET END
- LogToFile("XalUserGetState: hr=%s", ConvertHR(hr).c_str());
- return LuaReturnHR(L, hr);
-}
-
-int XalUserGetGamertag_Lua(lua_State *L)
-{
- // CODE SNIPPET START: XalUserGetGamertag
- size_t gamerTagSize = XalUserGetGamertagSize(Data()->xalUser, XalGamertagComponent_Classic);
- std::vector gamerTag(gamerTagSize, '\0');
-
- size_t bufferUsed;
- HRESULT hr = XalUserGetGamertag(Data()->xalUser, XalGamertagComponent_Classic, gamerTagSize, gamerTag.data(), &bufferUsed);
- // CODE SNIPPET END
- LogToFile("XalUserGetGamertag %s: hr=%s gamerTag=%s", gamerTag.data(), ConvertHR(hr).c_str(), gamerTag.data());
-
- Data()->gamertag = std::string(gamerTag.data());
- return LuaReturnHR(L, hr);
-}
-
-int XalUserGetAgeGroup_Lua(lua_State *L)
-{
- // CODE SNIPPET START: XalUserGetAgeGroup
- XalAgeGroup ageGroup = {};
- HRESULT hr = XalUserGetAgeGroup(Data()->xalUser, &ageGroup);
- // CODE SNIPPET END
- LogToFile("XalUserGetAgeGroup: hr=%s", ConvertHR(hr).c_str());
- return LuaReturnHR(L, hr);
-}
-
-int XalUserCheckPrivilege_Lua(lua_State *L)
-{
- // CODE SNIPPET START: XalUserCheckPrivilege
- XalPrivilege privilege = XalPrivilege_Multiplayer;
- bool hasPrivilege = false;
- XalPrivilegeCheckDenyReasons reasons = { };
- HRESULT hr = XalUserCheckPrivilege(Data()->xalUser, privilege, &hasPrivilege, &reasons);
- // CODE SNIPPET END
- LogToFile("XalUserCheckPrivilege: hr=%s", ConvertHR(hr).c_str());
- return LuaReturnHR(L, hr);
-}
-
-int XalUserGetGamerPictureAsync_Lua(lua_State *L)
-{
- CreateQueueIfNeeded();
-
- // CODE SNIPPET START: XalUserGetGamerPictureAsync
- auto asyncBlock = std::make_unique();
- asyncBlock->queue = Data()->queue;
- asyncBlock->context = nullptr;
- asyncBlock->callback = [](XAsyncBlock* asyncBlock)
- {
- std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock*
- size_t bufferSize;
- HRESULT hr = XalUserGetGamerPictureResultSize(asyncBlock, &bufferSize);
- LogToFile("XalUserGetGamerPictureResultSize: hr=%s bufferSize=%d", ConvertHR(hr).c_str(), bufferSize); // CODE SNIP SKIP
- std::vector buffer(bufferSize);
- hr = XalUserGetGamerPictureResult(asyncBlock, bufferSize, buffer.data());
- LogToFile("XalUserGetGamerPictureResult: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP
- CallLuaFunctionWithHr(hr, "OnXalUserGetGamerPictureAsync"); // CODE SNIP SKIP
- };
-
- HRESULT hr = XalUserGetGamerPictureAsync(Data()->xalUser, XalGamerPictureSize_Small, 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();
- }
- // CODE SNIPPET END
- LogToFile("XalUserGetGamerPictureAsync: hr=%s", ConvertHR(hr).c_str());
- return LuaReturnHR(L, hr);
-}
-
-int XalUserGetTokenAndSignatureSilentlyAsync_Lua(lua_State *L)
-{
- CreateQueueIfNeeded();
-
- std::string method = GetStringFromLua(L, 1, "");
- std::string url = GetStringFromLua(L, 2, "");
-
- // CODE SNIPPET START: XalUserGetTokenAndSignatureSilentlyAsync
- auto asyncBlock = std::make_unique();
- asyncBlock->queue = Data()->queue;
- asyncBlock->context = nullptr;
- asyncBlock->callback = [](XAsyncBlock* asyncBlock)
- {
- std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock*
- size_t bufferSize;
- HRESULT hr = XalUserGetTokenAndSignatureSilentlyResultSize(asyncBlock, &bufferSize);
- LogToFile("XalUserGetTokenAndSignatureSilentlyResultSize: hr=%s bufferSize=%d", ConvertHR(hr).c_str(), bufferSize); // CODE SNIP SKIP
- std::vector buffer(bufferSize);
- XalUserGetTokenAndSignatureData* result;
- hr = XalUserGetTokenAndSignatureSilentlyResult(asyncBlock, bufferSize, buffer.data(), &result, nullptr);
- LogToFile("XalUserGetTokenAndSignatureSilentlyResult: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP
- CallLuaFunctionWithHr(hr, "OnXalUserGetTokenAndSignatureSilentlyAsync"); // CODE SNIP SKIP
- };
-
- XalUserGetTokenAndSignatureArgs args{};
- args.method = method.data();
- args.url = url.data();
-
- HRESULT hr = XalUserGetTokenAndSignatureSilentlyAsync(Data()->xalUser, &args, 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();
- }
- // CODE SNIPPET END
- LogToFile("XalUserCheckPrivilegesWithUiAsync: hr=%s", ConvertHR(hr).c_str());
- return LuaReturnHR(L, hr);
-}
-
-int XalUserResolveIssueWithUiAsync_Lua(lua_State *L)
-{
- CreateQueueIfNeeded();
-
- // CODE SNIPPET START: XalUserResolveIssueWithUiAsync
- auto asyncBlock = std::make_unique();
- asyncBlock->queue = Data()->queue;
- asyncBlock->context = nullptr;
- asyncBlock->callback = [](XAsyncBlock* asyncBlock)
- {
- std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock*
- HRESULT hr = XAsyncGetStatus(asyncBlock, false);
- LogToFile("XAsyncGetStatus: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP
- CallLuaFunctionWithHr(hr, "OnXalUserResolveIssueWithUiAsync"); // CODE SNIP SKIP
- };
-
- HRESULT hr = XalUserResolveIssueWithUiAsync(Data()->xalUser, "https://www.xboxlive.com", 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();
- }
- // CODE SNIPPET END
- LogToFile("XalUserResolveIssueWithUiAsync: hr=%s", ConvertHR(hr).c_str());
- return LuaReturnHR(L, hr);
-}
-
-// CODE SNIPPET START: XalUserRegisterChangeEventHandler
-void OnXalUserChangeEventHandler(
- _In_opt_ void* context,
- _In_ XalUserLocalId userId,
- _In_ XalUserChangeType change)
-{
- UNREFERENCED_PARAMETER(context);
- UNREFERENCED_PARAMETER(userId);
- UNREFERENCED_PARAMETER(change);
-}
-
-#if HC_PLATFORM == HC_PLATFORM_GDK
-void CALLBACK OnXalUserChangeEventHandler_GDK(
- _In_opt_ void* context,
- _In_ XUserLocalId userLocalId,
- _In_ XUserChangeEvent event)
-{
- UNREFERENCED_PARAMETER(context);
- UNREFERENCED_PARAMETER(userLocalId);
- UNREFERENCED_PARAMETER(event);
-}
-#endif
-// CODE SNIPPET END
-
-int XalUserRegisterChangeEventHandler_Lua(lua_State *L)
-{
- CreateQueueIfNeeded();
-
- void* context = nullptr;
- XalRegistrationToken token = { 0 };
-
- // CODE SNIPPET START: XalUserRegisterChangeEventHandler
-#if HC_PLATFORM == HC_PLATFORM_GDK
- HRESULT hr = XalUserRegisterChangeEventHandler(Data()->queue, context, OnXalUserChangeEventHandler_GDK, &token);
-#else
- HRESULT hr = XalUserRegisterChangeEventHandler(Data()->queue, context, OnXalUserChangeEventHandler, &token);
-#endif
- // CODE SNIPPET END
- LogToFile("XalUserRegisterChangeEventHandler: hr=%s", ConvertHR(hr).c_str());
- return LuaReturnHR(L, hr);
-}
-
-int XalUserUnregisterChangeEventHandler_Lua(lua_State *L)
-{
- XalRegistrationToken token{0};
-
- // CODE SNIPPET START: XalUserUnregisterChangeEventHandler
- XalUserUnregisterChangeEventHandler(token);
- // CODE SNIPPET END
- LogToFile("XalUserUnregisterChangeEventHandler");
- return LuaReturnHR(L, S_OK);
-}
-
-
-#if HC_PLATFORM == HC_PLATFORM_GDK || HC_PLATFORM == HC_PLATFORM_IOS || HC_PLATFORM == HC_PLATFORM_UWP || HC_PLATFORM == HC_PLATFORM_XDK
-int XalPlatformWebSetEventHandler_Lua(lua_State *L)
-{
- return LuaReturnHR(L, S_OK);
-}
-
-int XalPlatformStorageSetEventHandlers_Lua(lua_State *L)
-{
- return LuaReturnHR(L, S_OK);
-}
-#endif
-
-void SetupAPIs_Xal()
-{
- lua_register(Data()->L, "XalInitialize", XalInitialize_Lua);
- lua_register(Data()->L, "XalCleanupAsync", XalCleanupAsync_Lua);
- lua_register(Data()->L, "XalPlatformWebSetEventHandler", XalPlatformWebSetEventHandler_Lua);
- lua_register(Data()->L, "XalPlatformStorageSetEventHandlers", XalPlatformStorageSetEventHandlers_Lua);
- lua_register(Data()->L, "XalGetMaxUsers", XalGetMaxUsers_Lua);
-
- lua_register(Data()->L, "XalTryAddFirstUserSilentlyAsync", XalTryAddFirstUserSilentlyAsync_Lua);
- lua_register(Data()->L, "XalAddUserWithUiAsync", XalAddUserWithUiAsync_Lua);
- lua_register(Data()->L, "XalGetDeviceUserIsPresent", XalGetDeviceUserIsPresent_Lua);
- lua_register(Data()->L, "XalGetDeviceUser", XalGetDeviceUser_Lua);
- lua_register(Data()->L, "XalSignOutUserAsyncIsPresent", XalSignOutUserAsyncIsPresent_Lua);
- lua_register(Data()->L, "XalSignOutUserAsync", XalSignOutUserAsync_Lua);
-
- lua_register(Data()->L, "XalUserDuplicateHandle", XalUserDuplicateHandle_Lua);
- lua_register(Data()->L, "XalUserCloseHandle", XalUserCloseHandle_Lua);
- lua_register(Data()->L, "XalUserGetId", XalUserGetId_Lua);
- lua_register(Data()->L, "XalUserIsDevice", XalUserIsDevice_Lua);
- lua_register(Data()->L, "XalUserIsGuest", XalUserIsGuest_Lua);
- lua_register(Data()->L, "XalUserGetState", XalUserGetState_Lua);
- lua_register(Data()->L, "XalUserGetGamertag", XalUserGetGamertag_Lua);
- lua_register(Data()->L, "XalUserGetGamerPictureAsync", XalUserGetGamerPictureAsync_Lua);
- lua_register(Data()->L, "XalUserGetAgeGroup", XalUserGetAgeGroup_Lua);
- lua_register(Data()->L, "XalUserCheckPrivilege", XalUserCheckPrivilege_Lua);
- lua_register(Data()->L, "XalUserGetTokenAndSignatureSilentlyAsync", XalUserGetTokenAndSignatureSilentlyAsync_Lua);
- lua_register(Data()->L, "XalUserResolveIssueWithUiAsync", XalUserResolveIssueWithUiAsync_Lua);
- lua_register(Data()->L, "XalUserRegisterChangeEventHandler", XalUserRegisterChangeEventHandler_Lua);
- lua_register(Data()->L, "XalUserUnregisterChangeEventHandler", XalUserUnregisterChangeEventHandler_Lua);
-}
+// Copyright (c) Microsoft Corporation
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+#include "pch.h"
+int XalCleanupAsync_Lua(lua_State *L)
+{
+ if (!Data()->m_isXalInitialized)
+ return LuaReturnHR(L, S_OK);
+
+ if (Data()->nsalMockCall != nullptr)
+ {
+ HCMockRemoveMock(Data()->nsalMockCall);
+ Data()->nsalMockCall = nullptr;
+
+ if (Data()->libHttpClientInit) // Set on GDK where XAL is just a wrapper around XUser
+ {
+ HCCleanup();
+ Data()->libHttpClientInit = false;
+ }
+ }
+
+#if CPP_TESTS_ENABLED
+ Data()->blobMetadataResultCpp = xbox::services::title_storage::title_storage_blob_metadata_result{};
+ Data()->blobMetadataCpp = xbox::services::title_storage::title_storage_blob_metadata{};
+#endif
+
+ auto asyncBlock = std::make_unique();
+ asyncBlock->queue = Data()->queue;
+ asyncBlock->context = nullptr;
+ HRESULT hr = XalCleanupAsync(asyncBlock.get());
+ if (SUCCEEDED(hr))
+ {
+ // Synchronously wait for cleanup to complete
+ hr = XAsyncGetStatus(asyncBlock.get(), true);
+ Data()->m_isXalInitialized = false;
+ }
+
+ LogToFile("XalCleanup: hr=%s", ConvertHR(hr).c_str());
+ return LuaReturnHR(L, hr);
+}
+
+void SetupXalNsalMock()
+{
+ // Mock the NSAL call so XAL doesn't 429 due to repeated fast XAL init/cleanup during testing
+
+ // GET https://title.mgt.xboxlive.com/titles/current/endpoints HTTP/1.1
+ // HTTP/1.1 200 OK
+ // {"EndPoints":[{"Protocol":"http","Host":"*","HostType":"wildcard"},{"Protocol":"https","Host":"*","HostType":"wildcard"}]}
+
+ if (Data()->nsalMockCall != nullptr)
+ {
+ HCMockRemoveMock(Data()->nsalMockCall);
+ Data()->nsalMockCall = nullptr;
+ }
+
+ HRESULT hr = HCMockCallCreate(&(Data()->nsalMockCall));
+ if (hr == E_HC_NOT_INITIALISED)
+ {
+ // This happens on GDK where XAL is just a wrapper around XUser
+ LogToScreen("Calling HCInitialize()");
+
+#if HC_PLATFORM == HC_PLATFORM_ANDROID
+ hr = HCInitialize(&(Data()->initArgs));
+#else
+ hr = HCInitialize(nullptr);
+#endif
+ LogToScreen("HCInitialize done");
+ assert(SUCCEEDED(hr));
+ Data()->libHttpClientInit = true;
+ hr = HCMockCallCreate(&(Data()->nsalMockCall));
+ }
+ assert(SUCCEEDED(hr));
+
+ hr = HCMockAddMock(Data()->nsalMockCall, "GET", "https://title.mgt.xboxlive.com/titles/current/endpoints", nullptr, 0);
+ assert(SUCCEEDED(hr));
+
+ hr = HCMockResponseSetStatusCode(Data()->nsalMockCall, 200);
+ assert(SUCCEEDED(hr));
+
+ std::string responseBodyString = "{\"EndPoints\":[{\"Protocol\":\"http\",\"Host\":\"*\",\"HostType\":\"wildcard\"},{\"Protocol\":\"https\",\"Host\":\"*\",\"HostType\":\"wildcard\"}]}";
+ std::vector bodyBytes{ responseBodyString.begin(), responseBodyString.end() };
+ hr = HCMockResponseSetResponseBodyBytes(Data()->nsalMockCall, bodyBytes.data(), static_cast(bodyBytes.size()));
+ assert(SUCCEEDED(hr));
+}
+
+
+int XalInitialize_Lua(lua_State *L)
+{
+ std::string clientId = GetStringFromLua(L, 1, "000000004C26FED0");
+ uint32_t titleId = (uint32_t)GetUint64FromLua(L, 2, 1979882317);
+ std::string sandbox = GetStringFromLua(L, 3, "XDKS.1");
+
+ // Alternate config for XboxLiveE2E Stats 2017
+ //std::string clientId = GetStringFromLua(L, 1, "0000000044296E10");
+ //uint32_t titleId = (uint32_t)GetUint64FromLua(L, 2, 2025855259);
+
+ Data()->titleId = titleId;
+
+ CreateQueueIfNeeded();
+
+ // CODE SNIPPET START: XalInitialize
+
+ XalInitArgs xalInitArgs = {};
+#if HC_PLATFORM == HC_PLATFORM_UWP
+
+ xalInitArgs.titleId = titleId;
+ xalInitArgs.packageFamilyName = u8"41336MicrosoftATG.XboxLiveE2E_dspnxghe87tn0";
+ //xalInitArgs.correlationVector; // optional
+ //xalInitArgs.flags; // optional
+ //xalInitArgs.launchUser; // optional
+ //xalInitArgs.mainWindow; // optional
+
+#elif HC_PLATFORM == HC_PLATFORM_GDK || HC_PLATFORM == HC_PLATFORM_XDK
+ // No args on GDK / XDK
+
+#else
+
+ // Args for iOS / Android / Win32
+ xalInitArgs.clientId = clientId.c_str();
+ xalInitArgs.titleId = titleId;
+ xalInitArgs.sandbox = sandbox.c_str();
+#if HC_PLATFORM == HC_PLATFORM_IOS || HC_PLATFORM == HC_PLATFORM_ANDROID
+ // Extra args on Mobile
+ std::string redirectUri{ "ms-xal-" + clientId + "://auth" };
+ xalInitArgs.redirectUri = redirectUri.data();
+#if HC_PLATFORM == HC_PLATFORM_ANDROID
+ // Extra args on Android
+ xalInitArgs.disableDiagnosticTelemetry = false;
+
+ JNIEnv* jniEnv = nullptr;
+ jint result = Data()->javaVM->GetEnv(reinterpret_cast(&jniEnv), JNI_VERSION_1_6);
+ if (result != JNI_OK)
+ {
+ LogToScreen("Failed to retrieve the JNIEnv from the JavaVM.");
+ }
+
+ assert(jniEnv != nullptr);
+
+ jobject res = jniEnv->CallObjectMethod(Data()->m_mainActivityClassInstance, Data()->m_getApplicationContext);
+
+ xalInitArgs.javaVM = Data()->javaVM;
+ xalInitArgs.appContext = res;
+#endif
+#endif
+#endif
+
+ HCTraceSetTraceToDebugger(true);
+ HCSettingsSetTraceLevel(HCTraceLevel::Verbose);
+
+ HRESULT hr = XalInitialize(&xalInitArgs, Data()->queue);
+ // CODE SNIPPET END
+ Data()->m_isXalInitialized = true;
+
+ LogToScreen("XalInitialize: hr=%s", ConvertHR(hr).c_str());
+ LogToFile("XalInitialize: hr=%s", ConvertHR(hr).c_str());
+
+ SetupXalNsalMock();
+
+ return LuaReturnHR(L, hr);
+}
+
+int XalTryAddFirstUserSilentlyAsync_Lua(lua_State *L)
+{
+ CreateQueueIfNeeded();
+
+ // CODE SNIPPET START: XalTryAddFirstUserSilentlyAsync
+ auto asyncBlock = std::make_unique();
+ asyncBlock->queue = Data()->queue;
+ asyncBlock->context = nullptr;
+ asyncBlock->callback = [](XAsyncBlock* asyncBlock)
+ {
+ std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock*
+ XalUserHandle newUser = nullptr;
+ HRESULT hr = XalTryAddDefaultUserSilentlyResult(asyncBlock, &newUser);
+
+ // TODO: Store and use newUser
+ LogToScreen("XalTryAddFirstUserSilentlyResult: hr=%s user=0x%0.8x", ConvertHR(hr).c_str(), newUser);
+ LogToFile("XalTryAddFirstUserSilentlyResult: hr=%s user=0x%0.8x", ConvertHR(hr).c_str(), newUser); // CODE SNIP SKIP
+ if (Data()->xalUser != nullptr)
+ {
+ XalUserCloseHandle(Data()->xalUser);
+ Data()->xalUser = nullptr;
+ }
+ Data()->xalUser = newUser; // CODE SNIP SKIP
+ if (Data()->xalUser) // CODE SNIP SKIP
+ { // CODE SNIP SKIP
+ Data()->gotXalUser = true; // CODE SNIP SKIP
+ } // CODE SNIP SKIP
+ LuaStopTestIfFailed(hr);
+ CallLuaString("common = require 'common'; common.OnXalTryAddFirstUserSilentlyAsync()"); // CODE SNIP SKIP
+ };
+
+ HRESULT hr = XalTryAddDefaultUserSilentlyAsync(0u, 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();
+ }
+ // CODE SNIPPET END
+ LogToScreen("XalTryAddFirstUserSilentlyAsync: hr=%s", ConvertHR(hr).c_str());
+ LogToFile("XalTryAddFirstUserSilentlyAsync: hr=%s", ConvertHR(hr).c_str());
+ return LuaReturnHR(L, hr);
+}
+
+int XalGetMaxUsers_Lua(lua_State *L)
+{
+ // CODE SNIPPET START: XalGetMaxUsers
+ uint32_t maxUsers = 0;
+ HRESULT hr = XalGetMaxUsers(&maxUsers);
+ // CODE SNIPPET END
+
+ LogToScreen("XalGetMaxUsers: hr=%s. result=%d", ConvertHR(hr).c_str(), maxUsers);
+ LogToFile("XalGetMaxUsers: hr=%s. result=%d", ConvertHR(hr).c_str(), maxUsers); // CODE SNIP SKIP
+ return LuaReturnHR(L, hr);
+}
+
+int XalAddUserWithUiAsync_Lua(lua_State *L)
+{
+ CreateQueueIfNeeded();
+
+ // CODE SNIPPET START: XalAddUserWithUiAsync
+ auto asyncBlock = std::make_unique();
+ asyncBlock->queue = Data()->queue;
+ asyncBlock->context = nullptr;
+ asyncBlock->callback = [](XAsyncBlock* asyncBlock)
+ {
+ std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock*
+ XalUserHandle newUser = nullptr;
+ HRESULT hr = XalAddUserWithUiResult(asyncBlock, &newUser);
+
+ // TODO: Store and use newUser
+ LogToScreen("XalAddUserWithUiResult: hr=%s user=0x%0.8x", ConvertHR(hr).c_str(), newUser);
+ LogToFile("XalAddUserWithUiResult: hr=%s user=0x%0.8x", ConvertHR(hr).c_str(), newUser); // CODE SNIP SKIP
+ if (Data()->xalUser != nullptr)
+ {
+ XalUserCloseHandle(Data()->xalUser);
+ Data()->xalUser = nullptr;
+ }
+ Data()->xalUser = newUser; // CODE SNIP SKIP
+ if (Data()->xalUser) // CODE SNIP SKIP
+ { // CODE SNIP SKIP
+ Data()->gotXalUser = true; // CODE SNIP SKIP
+ } // CODE SNIP SKIP
+ CallLuaFunctionWithHr(hr, "OnXalAddUserWithUiAsync"); // CODE SNIP SKIP
+ };
+
+ HRESULT hr = XalAddUserWithUiAsync(0u, 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();
+ }
+ // CODE SNIPPET END
+
+ LogToScreen("XalAddUserWithUiAsync: hr=%s", ConvertHR(hr).c_str());
+ LogToFile("XalAddUserWithUiAsync: hr=%s", ConvertHR(hr).c_str());
+ return LuaReturnHR(L, hr);
+}
+
+int XalGetDeviceUserIsPresent_Lua(lua_State *L)
+{
+ // CODE SNIPPET START: XalGetDeviceUserIsPresent
+ bool result = XalGetDeviceUserIsPresent();
+ // CODE SNIPPET END
+
+ LogToScreen("XalGetDeviceUserIsPresent: result=%d", result);
+ LogToFile("XalGetDeviceUserIsPresent: result=%d", result);
+ return LuaReturnHR(L, S_OK);
+}
+
+int XalGetDeviceUser_Lua(lua_State *L)
+{
+ // CODE SNIPPET START: XalGetDeviceUser
+ XalUserHandle deviceUser = nullptr;
+ HRESULT hr = XalGetDeviceUser(&deviceUser);
+ // CODE SNIPPET END
+
+ LogToScreen("XalGetDeviceUser: hr=%s", ConvertHR(hr).c_str());
+ LogToFile("XalGetDeviceUser: hr=%s", ConvertHR(hr).c_str());
+ return LuaReturnHR(L, hr);
+}
+
+int XalSignOutUserAsyncIsPresent_Lua(lua_State *L)
+{
+ // CODE SNIPPET START: XalSignOutUserAsyncIsPresent
+ bool result = XalSignOutUserAsyncIsPresent();
+ // CODE SNIPPET END
+
+ LogToScreen("XalSignOutUserAsyncIsPresent: result=%d", result);
+ LogToFile("XalSignOutUserAsyncIsPresent: result=%d", result);
+ return LuaReturnHR(L, S_OK);
+}
+
+int XalSignOutUserAsync_Lua(lua_State *L)
+{
+ CreateQueueIfNeeded();
+
+ // CODE SNIPPET START: XalSignOutUserAsync
+ auto asyncBlock = std::make_unique();
+ asyncBlock->queue = Data()->queue;
+ asyncBlock->context = nullptr;
+ asyncBlock->callback = [](XAsyncBlock* asyncBlock)
+ {
+ std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock*
+ HRESULT hr = XAsyncGetStatus(asyncBlock, false);
+
+ // TODO: Store and use newUser
+ LogToScreen("OnXalSignOutUserAsync: hr=%s", ConvertHR(hr).c_str());
+ LogToFile("OnXalSignOutUserAsync: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP
+ XalUserCloseHandle(Data()->xalUser); // CODE SNIP SKIP
+ Data()->xalUser = nullptr; // CODE SNIP SKIP
+ CallLuaFunctionWithHr(hr, "OnXalSignOutUserAsync"); // CODE SNIP SKIP
+ };
+
+ HRESULT hr = XalSignOutUserAsync(Data()->xalUser, 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();
+ }
+ // CODE SNIPPET END
+
+ LogToScreen("XalSignOutUserAsync: hr=%s", ConvertHR(hr).c_str());
+ LogToFile("XalSignOutUserAsync: hr=%s", ConvertHR(hr).c_str());
+ return LuaReturnHR(L, hr);
+}
+
+int XalUserDuplicateHandle_Lua(lua_State *L)
+{
+ // CODE SNIPPET START: XalUserDuplicateHandle
+ XalUserHandle dupUser = nullptr;
+ HRESULT hr = XalUserDuplicateHandle(Data()->xalUser, &dupUser);
+ // CODE SNIPPET END
+
+ LogToScreen("XalUserDuplicateHandle: hr=%s", ConvertHR(hr).c_str());
+ LogToFile("XalUserDuplicateHandle: hr=%s", ConvertHR(hr).c_str());
+ return LuaReturnHR(L, hr);
+}
+
+int XalUserCloseHandle_Lua(lua_State *L)
+{
+ XalUserHandle xalUser = Data()->xalUser;
+ if (xalUser != nullptr)
+ {
+ // CODE SNIPPET START: XalUserCloseHandle
+ XalUserCloseHandle(xalUser);
+ xalUser = nullptr;
+ // CODE SNIPPET END
+ Data()->xalUser = nullptr;
+
+ LogToScreen("XalUserCloseHandle");
+ LogToFile("XalUserCloseHandle");
+ }
+ return LuaReturnHR(L, S_OK);
+}
+
+int XalUserGetId_Lua(lua_State *L)
+{
+ if (Data()->xalUser == nullptr)
+ {
+ return LuaReturnHR(L, S_OK);
+ }
+
+ // CODE SNIPPET START: XalUserGetId
+ uint64_t xboxUserId = 0;
+ HRESULT hr = XalUserGetId(Data()->xalUser, &xboxUserId);
+ // CODE SNIPPET END
+
+ LogToScreen("XalUserGetId: hr=%s xboxUserId=%ul", ConvertHR(hr).c_str(), xboxUserId);
+ LogToFile("XalUserGetId: hr=%s xboxUserId=%ul", ConvertHR(hr).c_str(), xboxUserId);
+ Data()->xboxUserId = xboxUserId;
+ return LuaReturnHR(L, hr);
+}
+
+int XalUserIsDevice_Lua(lua_State *L)
+{
+ // CODE SNIPPET START: XalUserIsDevice
+ bool result = XalUserIsDevice(Data()->xalUser);
+ // CODE SNIPPET END
+
+ LogToScreen("XalUserIsDevice: result=%d", result);
+ LogToFile("XalUserIsDevice: result=%d", result);
+ return LuaReturnHR(L, S_OK);
+}
+
+int XalUserIsGuest_Lua(lua_State *L)
+{
+ // CODE SNIPPET START: XalUserIsGuest
+ bool result = XalUserIsGuest(Data()->xalUser);
+ // CODE SNIPPET END
+
+ LogToScreen("XalUserIsGuest: result=%d", result);
+ LogToFile("XalUserIsGuest: result=%d", result);
+ return LuaReturnHR(L, S_OK);
+}
+
+int XalUserGetState_Lua(lua_State *L)
+{
+ // CODE SNIPPET START: XalUserGetState
+ XalUserState state = {};
+ HRESULT hr = XalUserGetState(Data()->xalUser, &state);
+ // CODE SNIPPET END
+
+ LogToScreen("XalUserGetState: hr=%s", ConvertHR(hr).c_str());
+ LogToFile("XalUserGetState: hr=%s", ConvertHR(hr).c_str());
+ return LuaReturnHR(L, hr);
+}
+
+int XalUserGetGamertag_Lua(lua_State *L)
+{
+ // CODE SNIPPET START: XalUserGetGamertag
+ size_t gamerTagSize = XalUserGetGamertagSize(Data()->xalUser, XalGamertagComponent_Classic);
+ std::vector gamerTag(gamerTagSize, '\0');
+
+ size_t bufferUsed;
+ HRESULT hr = XalUserGetGamertag(Data()->xalUser, XalGamertagComponent_Classic, gamerTagSize, gamerTag.data(), &bufferUsed);
+ // CODE SNIPPET END
+
+ LogToScreen("XalUserGetGamertag %s: hr=%s gamerTag=%s", gamerTag.data(), ConvertHR(hr).c_str(), gamerTag.data());
+ LogToFile("XalUserGetGamertag %s: hr=%s gamerTag=%s", gamerTag.data(), ConvertHR(hr).c_str(), gamerTag.data());
+
+ Data()->gamertag = std::string(gamerTag.data());
+ return LuaReturnHR(L, hr);
+}
+
+int XalUserGetAgeGroup_Lua(lua_State *L)
+{
+ // CODE SNIPPET START: XalUserGetAgeGroup
+ XalAgeGroup ageGroup = {};
+ HRESULT hr = XalUserGetAgeGroup(Data()->xalUser, &ageGroup);
+ // CODE SNIPPET END
+
+ LogToScreen("XalUserGetAgeGroup: hr=%s", ConvertHR(hr).c_str());
+ LogToFile("XalUserGetAgeGroup: hr=%s", ConvertHR(hr).c_str());
+ return LuaReturnHR(L, hr);
+}
+
+int XalUserCheckPrivilege_Lua(lua_State *L)
+{
+ // CODE SNIPPET START: XalUserCheckPrivilege
+ XalPrivilege privilege = XalPrivilege_Multiplayer;
+ bool hasPrivilege = false;
+ XalPrivilegeCheckDenyReasons reasons = { };
+ HRESULT hr = XalUserCheckPrivilege(Data()->xalUser, privilege, &hasPrivilege, &reasons);
+ // CODE SNIPPET END
+
+ LogToScreen("XalUserCheckPrivilege: hr=%s", ConvertHR(hr).c_str());
+ LogToFile("XalUserCheckPrivilege: hr=%s", ConvertHR(hr).c_str());
+ return LuaReturnHR(L, hr);
+}
+
+int XalUserGetGamerPictureAsync_Lua(lua_State *L)
+{
+ CreateQueueIfNeeded();
+
+ // CODE SNIPPET START: XalUserGetGamerPictureAsync
+ auto asyncBlock = std::make_unique();
+ asyncBlock->queue = Data()->queue;
+ asyncBlock->context = nullptr;
+ asyncBlock->callback = [](XAsyncBlock* asyncBlock)
+ {
+ std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock*
+ size_t bufferSize;
+ HRESULT hr = XalUserGetGamerPictureResultSize(asyncBlock, &bufferSize);
+ LogToScreen("XalUserGetGamerPictureResultSize: hr=%s bufferSize=%d", ConvertHR(hr).c_str(), bufferSize);
+ LogToFile("XalUserGetGamerPictureResultSize: hr=%s bufferSize=%d", ConvertHR(hr).c_str(), bufferSize); // CODE SNIP SKIP
+
+ std::vector buffer(bufferSize);
+ hr = XalUserGetGamerPictureResult(asyncBlock, bufferSize, buffer.data());
+ LogToScreen("XalUserGetGamerPictureResult: hr=%s", ConvertHR(hr).c_str());
+ LogToFile("XalUserGetGamerPictureResult: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP
+ CallLuaFunctionWithHr(hr, "OnXalUserGetGamerPictureAsync"); // CODE SNIP SKIP
+ };
+
+ HRESULT hr = XalUserGetGamerPictureAsync(Data()->xalUser, XalGamerPictureSize_Small, 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();
+ }
+ // CODE SNIPPET END
+
+ LogToScreen("XalUserGetGamerPictureAsync: hr=%s", ConvertHR(hr).c_str());
+ LogToFile("XalUserGetGamerPictureAsync: hr=%s", ConvertHR(hr).c_str());
+ return LuaReturnHR(L, hr);
+}
+
+int XalUserGetTokenAndSignatureSilentlyAsync_Lua(lua_State *L)
+{
+ CreateQueueIfNeeded();
+
+ std::string method = GetStringFromLua(L, 1, "");
+ std::string url = GetStringFromLua(L, 2, "");
+
+ // CODE SNIPPET START: XalUserGetTokenAndSignatureSilentlyAsync
+ auto asyncBlock = std::make_unique();
+ asyncBlock->queue = Data()->queue;
+ asyncBlock->context = nullptr;
+ asyncBlock->callback = [](XAsyncBlock* asyncBlock)
+ {
+ std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock*
+ size_t bufferSize;
+ HRESULT hr = XalUserGetTokenAndSignatureSilentlyResultSize(asyncBlock, &bufferSize);
+ LogToScreen("XalUserGetTokenAndSignatureSilentlyResultSize: hr=%s bufferSize=%d", ConvertHR(hr).c_str(), bufferSize);
+ LogToFile("XalUserGetTokenAndSignatureSilentlyResultSize: hr=%s bufferSize=%d", ConvertHR(hr).c_str(), bufferSize); // CODE SNIP SKIP
+
+ std::vector buffer(bufferSize);
+ XalUserGetTokenAndSignatureData* result;
+ hr = XalUserGetTokenAndSignatureSilentlyResult(asyncBlock, bufferSize, buffer.data(), &result, nullptr);
+ LogToScreen("XalUserGetTokenAndSignatureSilentlyResult: hr=%s", ConvertHR(hr).c_str());
+ LogToFile("XalUserGetTokenAndSignatureSilentlyResult: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP
+ CallLuaFunctionWithHr(hr, "OnXalUserGetTokenAndSignatureSilentlyAsync"); // CODE SNIP SKIP
+ };
+
+ XalUserGetTokenAndSignatureArgs args{};
+ args.method = method.data();
+ args.url = url.data();
+
+ HRESULT hr = XalUserGetTokenAndSignatureSilentlyAsync(Data()->xalUser, &args, 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();
+ }
+ // CODE SNIPPET END
+
+ LogToScreen("XalUserCheckPrivilegesWithUiAsync: hr=%s", ConvertHR(hr).c_str());
+ LogToFile("XalUserCheckPrivilegesWithUiAsync: hr=%s", ConvertHR(hr).c_str());
+ return LuaReturnHR(L, hr);
+}
+
+int XalUserResolveIssueWithUiAsync_Lua(lua_State *L)
+{
+ CreateQueueIfNeeded();
+
+ // CODE SNIPPET START: XalUserResolveIssueWithUiAsync
+ auto asyncBlock = std::make_unique();
+ asyncBlock->queue = Data()->queue;
+ asyncBlock->context = nullptr;
+ asyncBlock->callback = [](XAsyncBlock* asyncBlock)
+ {
+ std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock*
+ HRESULT hr = XAsyncGetStatus(asyncBlock, false);
+ LogToScreen("XAsyncGetStatus: hr=%s", ConvertHR(hr).c_str());
+ LogToFile("XAsyncGetStatus: hr=%s", ConvertHR(hr).c_str()); // CODE SNIP SKIP
+ CallLuaFunctionWithHr(hr, "OnXalUserResolveIssueWithUiAsync"); // CODE SNIP SKIP
+ };
+
+ HRESULT hr = XalUserResolveIssueWithUiAsync(Data()->xalUser, "https://www.xboxlive.com", 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();
+ }
+ // CODE SNIPPET END
+
+ LogToScreen("XalUserResolveIssueWithUiAsync: hr=%s", ConvertHR(hr).c_str());
+ LogToFile("XalUserResolveIssueWithUiAsync: hr=%s", ConvertHR(hr).c_str());
+ return LuaReturnHR(L, hr);
+}
+
+// CODE SNIPPET START: XalUserRegisterChangeEventHandler
+void OnXalUserChangeEventHandler(
+ _In_opt_ void* context,
+ _In_ XalUserLocalId userId,
+ _In_ XalUserChangeType change)
+{
+ UNREFERENCED_PARAMETER(context);
+ UNREFERENCED_PARAMETER(userId);
+ UNREFERENCED_PARAMETER(change);
+}
+
+#if HC_PLATFORM == HC_PLATFORM_GDK
+void CALLBACK OnXalUserChangeEventHandler_GDK(
+ _In_opt_ void* context,
+ _In_ XUserLocalId userLocalId,
+ _In_ XUserChangeEvent event)
+{
+ UNREFERENCED_PARAMETER(context);
+ UNREFERENCED_PARAMETER(userLocalId);
+ UNREFERENCED_PARAMETER(event);
+}
+#endif
+// CODE SNIPPET END
+
+int XalUserRegisterChangeEventHandler_Lua(lua_State *L)
+{
+ CreateQueueIfNeeded();
+
+ void* context = nullptr;
+ XalRegistrationToken token = { 0 };
+
+ // CODE SNIPPET START: XalUserRegisterChangeEventHandler
+#if HC_PLATFORM == HC_PLATFORM_GDK
+ HRESULT hr = XalUserRegisterChangeEventHandler(Data()->queue, context, OnXalUserChangeEventHandler_GDK, &token);
+#else
+ HRESULT hr = XalUserRegisterChangeEventHandler(Data()->queue, context, OnXalUserChangeEventHandler, &token);
+#endif
+ // CODE SNIPPET END
+
+ LogToScreen("XalUserRegisterChangeEventHandler: hr=%s", ConvertHR(hr).c_str());
+ LogToFile("XalUserRegisterChangeEventHandler: hr=%s", ConvertHR(hr).c_str());
+ return LuaReturnHR(L, hr);
+}
+
+int XalUserUnregisterChangeEventHandler_Lua(lua_State *L)
+{
+ XalRegistrationToken token{0};
+
+ // CODE SNIPPET START: XalUserUnregisterChangeEventHandler
+ XalUserUnregisterChangeEventHandler(token);
+ // CODE SNIPPET END
+
+ LogToScreen("XalUserUnregisterChangeEventHandler");
+ LogToFile("XalUserUnregisterChangeEventHandler");
+ return LuaReturnHR(L, S_OK);
+}
+
+
+#if HC_PLATFORM == HC_PLATFORM_GDK || HC_PLATFORM == HC_PLATFORM_IOS || HC_PLATFORM == HC_PLATFORM_UWP || HC_PLATFORM == HC_PLATFORM_XDK || HC_PLATFORM == HC_PLATFORM_ANDROID
+int XalPlatformWebSetEventHandler_Lua(lua_State *L)
+{
+ return LuaReturnHR(L, S_OK);
+}
+
+int XalPlatformStorageSetEventHandlers_Lua(lua_State *L)
+{
+ return LuaReturnHR(L, S_OK);
+}
+#endif
+
+void SetupAPIs_Xal()
+{
+ lua_register(Data()->L, "XalInitialize", XalInitialize_Lua);
+ lua_register(Data()->L, "XalCleanupAsync", XalCleanupAsync_Lua);
+
+ lua_register(Data()->L, "XalPlatformWebSetEventHandler", XalPlatformWebSetEventHandler_Lua);
+ lua_register(Data()->L, "XalPlatformStorageSetEventHandlers", XalPlatformStorageSetEventHandlers_Lua);
+
+ lua_register(Data()->L, "XalGetMaxUsers", XalGetMaxUsers_Lua);
+
+ lua_register(Data()->L, "XalTryAddFirstUserSilentlyAsync", XalTryAddFirstUserSilentlyAsync_Lua);
+ lua_register(Data()->L, "XalAddUserWithUiAsync", XalAddUserWithUiAsync_Lua);
+ lua_register(Data()->L, "XalGetDeviceUserIsPresent", XalGetDeviceUserIsPresent_Lua);
+ lua_register(Data()->L, "XalGetDeviceUser", XalGetDeviceUser_Lua);
+ lua_register(Data()->L, "XalSignOutUserAsyncIsPresent", XalSignOutUserAsyncIsPresent_Lua);
+ lua_register(Data()->L, "XalSignOutUserAsync", XalSignOutUserAsync_Lua);
+
+ lua_register(Data()->L, "XalUserDuplicateHandle", XalUserDuplicateHandle_Lua);
+ lua_register(Data()->L, "XalUserCloseHandle", XalUserCloseHandle_Lua);
+ lua_register(Data()->L, "XalUserGetId", XalUserGetId_Lua);
+ lua_register(Data()->L, "XalUserIsDevice", XalUserIsDevice_Lua);
+ lua_register(Data()->L, "XalUserIsGuest", XalUserIsGuest_Lua);
+ lua_register(Data()->L, "XalUserGetState", XalUserGetState_Lua);
+ lua_register(Data()->L, "XalUserGetGamertag", XalUserGetGamertag_Lua);
+ lua_register(Data()->L, "XalUserGetGamerPictureAsync", XalUserGetGamerPictureAsync_Lua);
+ lua_register(Data()->L, "XalUserGetAgeGroup", XalUserGetAgeGroup_Lua);
+ lua_register(Data()->L, "XalUserCheckPrivilege", XalUserCheckPrivilege_Lua);
+ lua_register(Data()->L, "XalUserGetTokenAndSignatureSilentlyAsync", XalUserGetTokenAndSignatureSilentlyAsync_Lua);
+ lua_register(Data()->L, "XalUserResolveIssueWithUiAsync", XalUserResolveIssueWithUiAsync_Lua);
+ lua_register(Data()->L, "XalUserRegisterChangeEventHandler", XalUserRegisterChangeEventHandler_Lua);
+ lua_register(Data()->L, "XalUserUnregisterChangeEventHandler", XalUserUnregisterChangeEventHandler_Lua);
+}
+
\ No newline at end of file
diff --git a/Tests/ApiExplorer/APIs/apis_xblc.cpp b/Tests/ApiExplorer/APIs/apis_xblc.cpp
index a9e12f5b..849a1f94 100644
--- a/Tests/ApiExplorer/APIs/apis_xblc.cpp
+++ b/Tests/ApiExplorer/APIs/apis_xblc.cpp
@@ -10,6 +10,9 @@ void StopSocialManagerDoWorkHelperCpp();
int XblInitialize_Lua(lua_State *L)
{
+ // Ensure we properly handle a null process queue
+ XTaskQueueSetCurrentProcessTaskQueue(nullptr);
+
CreateQueueIfNeeded();
// CODE SNIPPET START: XblInitialize
@@ -26,8 +29,9 @@ int XblInitialize_Lua(lua_State *L)
auto pathString = std::string{ pathArray } + '\\';
args.localStoragePath = pathString.data();
#endif
-#if HC_PLATFORM == HC_PLATFORM_IOS
-
+#if HC_PLATFORM == HC_PLATFORM_ANDROID
+ args.applicationContext = Data()->applicationContext;
+ args.javaVM = Data()->javaVM;
#endif
HRESULT hr = XblInitialize(&args);
// CODE SNIPPET END
diff --git a/Tests/ApiExplorer/APIs/apis_xblc_achievements_manager.cpp b/Tests/ApiExplorer/APIs/apis_xblc_achievements_manager.cpp
index 9e8a75c7..d402ff8d 100644
--- a/Tests/ApiExplorer/APIs/apis_xblc_achievements_manager.cpp
+++ b/Tests/ApiExplorer/APIs/apis_xblc_achievements_manager.cpp
@@ -77,6 +77,14 @@ int StartAchievementsManagerDoWorkLoop_Lua(lua_State* L)
std::lock_guard lock(Data()->m_luaLock);
achievementsState.doWorkThread = std::thread([]()
{
+#if HC_PLATFORM == HC_PLATFORM_ANDROID
+ JNIEnv* jniEnv = nullptr;
+
+ // This should be a background thread that MUST attach a new java thread.
+ Data()->javaVM->GetEnv(reinterpret_cast(&jniEnv), JNI_VERSION_1_6);
+ Data()->javaVM->AttachCurrentThread(&jniEnv, nullptr);
+
+#endif
Data()->m_achievementsDoWorkDone = false;
while (achievementsState.doWork && !Data()->m_quit)
{
@@ -88,6 +96,10 @@ int StartAchievementsManagerDoWorkLoop_Lua(lua_State* L)
}
Data()->m_achievementsDoWorkDone = true;
LogToScreen("Exiting do work thread");
+
+#if HC_PLATFORM == HC_PLATFORM_ANDROID
+ Data()->javaVM->DetachCurrentThread();
+#endif
});
return LuaReturnHR(L, S_OK);
}
@@ -112,7 +124,11 @@ void StopAchievementsManagerDoWorkHelper()
int SetupAchievementsManagerPerformanceTestMock_Lua(lua_State *L)
{
+#if HC_PLATFORM == HC_PLATFORM_ANDROID
+ std::string addUserMockResponse = ReadFile("PerformanceTestMockResponse.json");
+#else
std::string addUserMockResponse = ReadFile("achievements\\PerformanceTestMockResponse.json");
+#endif
assert(!addUserMockResponse.empty());
auto hr = HCMockCallCreate(&achievementsState.mockHandle);
diff --git a/Tests/ApiExplorer/APIs/apis_xblc_gameinvite_notifications.cpp b/Tests/ApiExplorer/APIs/apis_xblc_gameinvite_notifications.cpp
index 5727286e..9247a94b 100644
--- a/Tests/ApiExplorer/APIs/apis_xblc_gameinvite_notifications.cpp
+++ b/Tests/ApiExplorer/APIs/apis_xblc_gameinvite_notifications.cpp
@@ -22,6 +22,8 @@ int XblGameInviteAddNotificationHandler_Lua(lua_State *L)
LogToScreen(args->inviteHandleId);
LogToScreen("Invite Protocol:");
LogToScreen(args->inviteProtocol);
+ LogToScreen("Invite Context:");
+ LogToScreen(args->inviteContext);
LogToScreen("Sender Gamertag:");
LogToScreen(args->senderGamertag);
LogToScreen("Modern Gamertag:");
diff --git a/Tests/ApiExplorer/APIs/apis_xblc_grts.cpp b/Tests/ApiExplorer/APIs/apis_xblc_grts.cpp
index 6569717a..a257d424 100644
--- a/Tests/ApiExplorer/APIs/apis_xblc_grts.cpp
+++ b/Tests/ApiExplorer/APIs/apis_xblc_grts.cpp
@@ -49,7 +49,7 @@ HRESULT XGameUiShowSendGameInviteAsyncHelper(
int XGameUiShowSendGameInviteAsyncToMPMLobby_Lua(lua_State* L)
{
- std::string inviteText = GetStringFromLua(L, 1, "Join Me In My Game!");
+ std::string inviteText = GetStringFromLua(L, 1, "//MPSD/custominvitestrings_JoinMyGame");
std::string customActivationContext = GetStringFromLua(L, 2, "MyCustomActivationContext");
XblMultiplayerSessionReference sessionReference{};
diff --git a/Tests/ApiExplorer/APIs/apis_xblc_multiplayer_manager.cpp b/Tests/ApiExplorer/APIs/apis_xblc_multiplayer_manager.cpp
index fdd8dfe4..19a01299 100644
--- a/Tests/ApiExplorer/APIs/apis_xblc_multiplayer_manager.cpp
+++ b/Tests/ApiExplorer/APIs/apis_xblc_multiplayer_manager.cpp
@@ -594,7 +594,7 @@ int XblMultiplayerManagerLobbySessionSetLocalMemberProperties_Lua(lua_State* L)
int XblMultiplayerManagerLobbySessionInviteFriends_Lua(lua_State* L)
{
#if HC_PLATFORM == HC_PLATFORM_WIN32
- HRESULT hr = XblMultiplayerManagerLobbySessionInviteFriends(Data()->xalUser, nullptr, "Join my game!");
+ HRESULT hr = XblMultiplayerManagerLobbySessionInviteFriends(Data()->xalUser, nullptr, "//MPSD/custominvitestrings_JoinMyGame");
#else
HRESULT hr = S_OK;
#endif
diff --git a/Tests/ApiExplorer/APIs/apis_xblc_social.cpp b/Tests/ApiExplorer/APIs/apis_xblc_social.cpp
index 2e8dd9df..1ebb7c6d 100644
--- a/Tests/ApiExplorer/APIs/apis_xblc_social.cpp
+++ b/Tests/ApiExplorer/APIs/apis_xblc_social.cpp
@@ -323,6 +323,105 @@ int profile_service_get_user_profile_Lua(lua_State *L)
return LuaReturnHR(L, hr);
}
+int UnsubscribeToTitleAndDevicePresenceChangeForFriends_Lua(lua_State *L)
+{
+ HRESULT hr = S_OK;
+
+ for (XblRealTimeActivitySubscriptionHandle subscriptionHandleDevice : Data()->subscriptionHandleDeviceList)
+ {
+ hr = XblPresenceUnsubscribeFromDevicePresenceChange(
+ Data()->xboxLiveContext,
+ subscriptionHandleDevice
+ );
+ LuaStopTestIfFailed(hr);
+ assert(SUCCEEDED(hr));
+ }
+
+ for (XblRealTimeActivitySubscriptionHandle subscriptionHandleTitle : Data()->subscriptionHandleTitleList)
+ {
+ hr = XblPresenceUnsubscribeFromTitlePresenceChange(
+ Data()->xboxLiveContext,
+ subscriptionHandleTitle
+ );
+ }
+
+ Data()->subscriptionHandleTitleList.clear();
+ Data()->subscriptionHandleDeviceList.clear();
+
+ LogToScreen("UnsubscribeToTitleAndDevicePresenceChangeForFriends: hr=%s", ConvertHR(hr).c_str());
+ return LuaReturnHR(L, hr);
+}
+
+int SubscribeToTitleAndDevicePresenceChangeForFriends_Lua(lua_State *L)
+{
+ auto asyncBlock = std::make_unique();
+ asyncBlock->queue = Data()->queue;
+ asyncBlock->context = nullptr;
+ asyncBlock->callback = [](XAsyncBlock* asyncBlock)
+ {
+ std::unique_ptr asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock*
+ HRESULT hr = XblSocialGetSocialRelationshipsResult(asyncBlock, &state.socialResultHandle);
+
+ const XblSocialRelationship* relationships = nullptr;
+ size_t relationshipsCount = 0;
+ hr = XblSocialRelationshipResultGetRelationships(state.socialResultHandle, &relationships, &relationshipsCount);
+
+ Data()->subscriptionHandleTitleList.clear();
+ Data()->subscriptionHandleDeviceList.clear();
+
+ XblRealTimeActivitySubscriptionHandle subscriptionHandleDevice;
+ XblRealTimeActivitySubscriptionHandle subscriptionHandleTitle;
+ LogToScreen("Got %u SocialRelationships:", relationshipsCount);
+ for (size_t i = 0; i < relationshipsCount; ++i)
+ {
+ if (i % 100 == 0)
+ {
+ LogToScreen("Sub'ing to friend %d", i);
+ }
+
+ hr = XblPresenceSubscribeToDevicePresenceChange(
+ Data()->xboxLiveContext,
+ relationships[i].xboxUserId,
+ &subscriptionHandleDevice);
+ LuaStopTestIfFailed(hr);
+ assert(SUCCEEDED(hr));
+
+ hr = XblPresenceSubscribeToTitlePresenceChange(
+ Data()->xboxLiveContext,
+ relationships[i].xboxUserId,
+ Data()->titleId,
+ &subscriptionHandleTitle);
+ LuaStopTestIfFailed(hr);
+ assert(SUCCEEDED(hr));
+
+ Data()->subscriptionHandleDeviceList.push_back(subscriptionHandleDevice);
+ Data()->subscriptionHandleTitleList.push_back(subscriptionHandleTitle);
+ }
+
+ LogToScreen("SubscribeToTitleAndDevicePresenceChangeForFriends: hr=%s", ConvertHR(hr).c_str());
+ CallLuaFunctionWithHr(hr, "OnSubscribeToTitleAndDevicePresenceChangeForFriends");
+ };
+
+ HRESULT hr = XblSocialGetSocialRelationshipsAsync(
+ Data()->xboxLiveContext,
+ Data()->xboxUserId,
+ XblSocialRelationshipFilter::All,
+ 0,
+ 0,
+ 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();
+ }
+
+ LogToFile("SubscribeToTitleAndDevicePresenceChangeForFriends: hr=%s", ConvertHR(hr).c_str());
+ return LuaReturnHR(L, hr);
+}
+
void SetupAPIs_XblSocial()
{
lua_register(Data()->L, "XblSocialGetSocialRelationshipsAsync", XblSocialGetSocialRelationshipsAsync_Lua);
@@ -340,5 +439,8 @@ void SetupAPIs_XblSocial()
lua_register(Data()->L, "XblSocialSubmitReputationFeedbackAsync", XblSocialSubmitReputationFeedbackAsync_Lua);
lua_register(Data()->L, "XblSocialSubmitBatchReputationFeedbackAsync", XblSocialSubmitBatchReputationFeedbackAsync_Lua);
+ lua_register(Data()->L, "SubscribeToTitleAndDevicePresenceChangeForFriends", SubscribeToTitleAndDevicePresenceChangeForFriends_Lua);
+ lua_register(Data()->L, "UnsubscribeToTitleAndDevicePresenceChangeForFriends", UnsubscribeToTitleAndDevicePresenceChangeForFriends_Lua);
+
lua_register(Data()->L, "profile_service_get_user_profile", profile_service_get_user_profile_Lua);
}
diff --git a/Tests/ApiExplorer/Android/pch.h b/Tests/ApiExplorer/Android/pch.h
new file mode 100644
index 00000000..654ab720
--- /dev/null
+++ b/Tests/ApiExplorer/Android/pch.h
@@ -0,0 +1,11 @@
+// Copyright (c) Microsoft Corporation
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+#pragma once
+
+#include "pch_common.h"
+
+#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "AndroidAPIExplorerJni", __VA_ARGS__))
+#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "AndroidAPIExplorerJni", __VA_ARGS__))
+#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "AndroidAPIExplorerJni", __VA_ARGS__))
+#define UNREFERENCED_PARAMETER(P) (P)
+#define RETURN_STRING_FROM_ENUM(e) case(e): return #e;
\ No newline at end of file
diff --git a/Tests/ApiExplorer/Include/api_explorer.h b/Tests/ApiExplorer/Include/api_explorer.h
index 0dc00de3..c2c10505 100644
--- a/Tests/ApiExplorer/Include/api_explorer.h
+++ b/Tests/ApiExplorer/Include/api_explorer.h
@@ -1,6 +1,9 @@
// Copyright (c) Microsoft Corporation
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#pragma once
+#if HC_PLATFORM == HC_PLATFORM_ANDROID
+#include "multidevice.h"
+#endif
#include "lua.h"
#include "lualib.h"
@@ -83,6 +86,8 @@ struct ApiExplorerData
bool gotXalUser{ false };
XTaskQueueHandle queue{ nullptr };
XblContextHandle xboxLiveContext{ nullptr };
+ std::vector subscriptionHandleDeviceList;
+ std::vector subscriptionHandleTitleList;
std::string gamertag;
uint64_t xboxUserId{ 0 };
uint32_t titleId{ 0 };
@@ -91,6 +96,18 @@ struct ApiExplorerData
HCMockCallHandle nsalMockCall{ nullptr };
bool libHttpClientInit{ false };
+ // Android Specific Data
+#if HC_PLATFORM == HC_PLATFORM_ANDROID
+ JavaVM *javaVM;
+ jobject applicationContext;
+ HCInitArgs initArgs;
+
+ /// The Java Application Context.
+ jclass m_mainActivityClass;
+ jobject m_mainActivityClassInstance;
+ jmethodID m_getApplicationContext;
+#endif
+
// Achievements Data
XblAchievementsResultHandle achievementsResult{ nullptr };
#if CPP_TESTS_ENABLED
diff --git a/Tests/ApiExplorer/Include/runner.h b/Tests/ApiExplorer/Include/runner.h
index 941d1366..c85f6681 100644
--- a/Tests/ApiExplorer/Include/runner.h
+++ b/Tests/ApiExplorer/Include/runner.h
@@ -30,3 +30,8 @@ void LogToScreen(_Printf_format_string_ char const* format, ...);
#if HC_PLATFORM == HC_PLATFORM_IOS
void SetupAPNSRegistrationToken(std::string registrationToken);
#endif
+
+// These functions are specific to APIExplorer on Android
+#if HC_PLATFORM == HC_PLATFORM_ANDROID
+void SetupAndroidContext( JavaVM *javaVM, jobject context, jclass mainActivityClass, jobject mainActivityInstance, jmethodID getApplicationContext);
+#endif
diff --git a/Tests/ApiExplorer/Shared/commands.cpp b/Tests/ApiExplorer/Shared/commands.cpp
index 18726143..b31b22a1 100644
--- a/Tests/ApiExplorer/Shared/commands.cpp
+++ b/Tests/ApiExplorer/Shared/commands.cpp
@@ -2,6 +2,11 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "pch.h"
#include "utils.h"
+#if HC_PLATFORM == HC_PLATFORM_ANDROID
+#include "api_explorer.h"
+#include "pal.h"
+#include "runner.h"
+#endif
#include "cpprest/json.h"
#include "mem_hook.h"
@@ -9,7 +14,7 @@
#include
-#if HC_PLATFORM == HC_PLATFORM_IOS
+#if HC_PLATFORM == HC_PLATFORM_IOS || HC_PLATFORM == HC_PLATFORM_ANDROID
#define E_NOT_VALID_STATE E_FAIL
#endif
@@ -43,8 +48,14 @@ void InitApiExplorerData()
if (g_data->m_testsPath.empty())
{
+#if HC_PLATFORM != HC_PLATFORM_ANDROID
auto rootFile = pal::FindFile("tests/tests.root");
+#else
+ auto rootFile = pal::FindFile("tests.root");
+#endif
+ assert(!rootFile.empty());
g_data->m_testsPath = rootFile.substr(0, rootFile.find_last_of("/"));
+
}
}
@@ -94,6 +105,7 @@ void Log(_Printf_format_string_ char const* format, ...)
pal::vsprintf(message, 4096, format, varArgs);
va_end(varArgs);
+ LogToScreen(message);
LogToFile(message);
}
#endif
@@ -296,7 +308,7 @@ bool LoadFile(const std::string& fileNameInput)
{
LogToFile("Couldn't find %s", fileNameInput.c_str());
}
-
+
return false;
}
@@ -327,39 +339,6 @@ void APIRunner_CleanupLeakCheck()
{
auto memHook = GetApiRunnerMemHook();
memHook->LogStats("CleanupLeakCheck");
- if (Data()->xboxLiveContext)
- {
- XblContextCloseHandle(Data()->xboxLiveContext);
- Data()->xboxLiveContext = nullptr;
- }
- memHook->LogStats("CleanupLeakCheck post-XblContextCloseHandle");
- if (Data()->xalUser)
- {
- XalUserCloseHandle(Data()->xalUser);
- Data()->xalUser = nullptr;
- }
- memHook->LogStats("CleanupLeakCheck post-XalUserCloseHandle");
-
- XAsyncBlock block{};
- HRESULT hr = XblCleanupAsync(&block);
- if (SUCCEEDED(hr))
- {
- hr = XAsyncGetStatus(&block, true);
- }
- memHook->LogStats("CleanupLeakCheck post-XblCleanupAsync");
-
- hr = XalCleanupAsync(&block);
- if (SUCCEEDED(hr))
- {
- hr = XAsyncGetStatus(&block, true);
- }
-
- if (Data()->queue != nullptr)
- {
- XTaskQueueCloseHandle(Data()->queue);
- Data()->queue = nullptr;
- }
- memHook->LogStats("CleanupLeakCheck post-XalCleanupAsync");
memHook->LogLeaks();
}
@@ -370,8 +349,11 @@ HRESULT RunTestWithoutCleanup(const std::string& scriptName)
bool testLoaded = LoadFile(scriptName);
if (!testLoaded)
{
+ std::string scriptFailure = "Failed to load " + scriptName;
+ Log(scriptFailure.c_str());
return E_FAIL;
}
+
return WaitForTestResult();
}
@@ -412,10 +394,14 @@ HRESULT ApiRunnerRunTest(std::string testName)
HRESULT RunSetupScript()
{
- std::string sharedFolder = "_luasetup_\\xal";
-
char message[4096] = {};
+#if HC_PLATFORM != HC_PLATFORM_ANDROID
+ std::string sharedFolder = "_luasetup_\\xal";
sprintf_s(message, "tests\\%s\\setup.lua", sharedFolder.c_str());
+#else
+ std::string sharedFolder = "xal";
+ sprintf_s(message, "%s/setup.lua", sharedFolder.c_str());
+#endif
std::string strFilePath = pal::FindFile(message);
if (strFilePath.empty() || strFilePath.length() < 5)
{
@@ -423,7 +409,7 @@ HRESULT RunSetupScript()
return E_NOINTERFACE;
}
- return RunTestInternal(strFilePath, true);
+ return RunTestInternal(message, true);
}
void OnCmdRepeatRunTest(const std::vector& cmdLineTokens)
@@ -539,10 +525,12 @@ HRESULT RunTestsHelper(TestSet set)
HRESULT hr = RunSetupScript();
if (FAILED(hr))
{
+ LogToScreen("RunSetupScript() failed with HR = %s", ConvertHR(hr).c_str());
return hr;
}
if (!Data()->gotXalUser)
{
+ LogToScreen("Data()->GotXalUser was invalid");
return E_NOT_VALID_STATE;
}
@@ -623,6 +611,7 @@ HRESULT RunTestsHelper(TestSet set)
{
auto memHook = GetApiRunnerMemHook();
memHook->LogUnhookedStats();
+ APIRunner_CleanupLeakCheck();
}
CallLuaString("test.summary()");
@@ -800,7 +789,17 @@ web::json::array extract_json_array(
std::string ApiRunnerReadFile(std::string fileName)
{
assert(Data() != nullptr); // call ApiRunnerSetupApiExplorer() first
+
+#if HC_PLATFORM == HC_PLATFORM_ANDROID
+ // In Android, the test files are considered assets.
+ // These files are copied from the assets folder into the external storage
+ // when the app is installed and launched on a device.
+ // Searching for the file is then done using the filename and doesn't use paths like other platforms
+ std::string fileNameEdited = fileName.substr(0, fileName.find_last_of("\\"));
+ std::string strFilePath = pal::FindFile(fileNameEdited);
+#else
std::string strFilePath = pal::FindFile(fileName);
+#endif
if (strFilePath.empty())
{
std::string testPath = "Tests\\" + fileName;
@@ -893,14 +892,14 @@ HRESULT ApiRunnerRunTestWithSetup(std::string testName, bool repeat)
return E_UNEXPECTED;
}
+
+ Data()->m_runningTests = true;
+ SetupLua();
if (Data()->m_trackUnhookedMemory)
{
auto memHook = GetApiRunnerMemHook();
memHook->StartMemTracking();
}
-
- Data()->m_runningTests = true;
- SetupLua();
HRESULT hr = RunSetupScript();
if (SUCCEEDED(hr))
{
@@ -930,6 +929,7 @@ HRESULT ApiRunnerRunTestWithSetup(std::string testName, bool repeat)
{
auto memHook = GetApiRunnerMemHook();
memHook->LogUnhookedStats();
+ APIRunner_CleanupLeakCheck();
}
CleanupLua();
@@ -1266,3 +1266,18 @@ void SetupAPNSRegistrationToken(std::string registrationToken)
Data()->apnsToken = registrationToken;
}
#endif
+
+#if HC_PLATFORM == HC_PLATFORM_ANDROID
+void SetupAndroidContext( JavaVM *javaVM, jobject context, jclass mainActivityClass, jobject mainActivityInstance, jmethodID getApplicationContext)
+{
+ Data()->javaVM = javaVM;
+ Data()->m_mainActivityClass = mainActivityClass;
+ Data()->m_getApplicationContext = getApplicationContext;
+ Data()->m_mainActivityClassInstance = mainActivityInstance;
+ Data()->applicationContext = context;
+ Data()->initArgs = {};
+ Data()->initArgs.applicationContext = context;
+ Data()->initArgs.javaVM = javaVM;
+}
+
+#endif
diff --git a/Tests/ApiExplorer/Shared/log.cpp b/Tests/ApiExplorer/Shared/log.cpp
index 7406e9a0..9b7762f8 100644
--- a/Tests/ApiExplorer/Shared/log.cpp
+++ b/Tests/ApiExplorer/Shared/log.cpp
@@ -3,6 +3,10 @@
#if HC_PLATFORM == HC_PLATFORM_WIN32
#include
#endif
+#if HC_PLATFORM == HC_PLATFORM_ANDROID
+#include "pal.h"
+#endif
+
#include
std::string g_logFilePath;
@@ -56,7 +60,7 @@ void LogToFile(_Printf_format_string_ char const* format, ...)
#endif
}
-#if HC_PLATFORM != HC_PLATFORM_UWP && HC_PLATFORM != HC_PLATFORM_WIN32 && HC_PLATFORM != HC_PLATFORM_GDK && HC_PLATFORM != HC_PLATFORM_XDK && HC_PLATFORM != HC_PLATFORM_IOS
+#if HC_PLATFORM != HC_PLATFORM_UWP && HC_PLATFORM != HC_PLATFORM_WIN32 && HC_PLATFORM != HC_PLATFORM_GDK && HC_PLATFORM != HC_PLATFORM_XDK && HC_PLATFORM != HC_PLATFORM_IOS && HC_PLATFORM != HC_PLATFORM_ANDROID
void LogToScreen(_Printf_format_string_ char const* format, ...)
{
diff --git a/Tests/ApiExplorer/Shared/lua-include.cpp b/Tests/ApiExplorer/Shared/lua-include.cpp
index 83c60884..62b311c1 100644
--- a/Tests/ApiExplorer/Shared/lua-include.cpp
+++ b/Tests/ApiExplorer/Shared/lua-include.cpp
@@ -14,7 +14,9 @@
#include "lapi.c"
#include "lauxlib.c"
#include "lbaselib.c"
+#if HC_PLATFORM != HC_PLATFORM_ANDROID
#include "lbitlib.c"
+#endif
#include "lcode.c"
#include "lcorolib.c"
#include "lctype.c"
diff --git a/Tests/ApiExplorer/Shared/mem_hook.cpp b/Tests/ApiExplorer/Shared/mem_hook.cpp
index 2b44ae7b..446bd788 100644
--- a/Tests/ApiExplorer/Shared/mem_hook.cpp
+++ b/Tests/ApiExplorer/Shared/mem_hook.cpp
@@ -15,6 +15,10 @@
#define TRACE_MAX_FUNCTION_NAME_LENGTH 1024
#endif
+#if HC_PLATFORM == HC_PLATFORM_ANDROID
+#include "runner.h"
+#endif
+
ApiRunerMemHook* g_mem = new ApiRunerMemHook();
bool g_rawMemHookInitTracking{ false };
@@ -219,21 +223,47 @@ void ApiRunerMemHook::ResetStats()
m_mapStackLog.clear();
}
+void MemHookLog(_Printf_format_string_ char const* format, ...)
+{
+ // Stock impl that just logs to file
+ // Hook up UI logs for each platform based on platform specific UI display calls
+
+#if HC_PLATFORM_IS_MICROSOFT
+ char message[8000] = {};
+
+ va_list varArgs{};
+ va_start(varArgs, format);
+ pal::vsprintf(message, 4096, format, varArgs);
+ va_end(varArgs);
+
+ OutputDebugStringA(message);
+ OutputDebugStringA("\n");
+#else
+ UNREFERENCED_PARAMETER(format);
+#endif
+}
+
void ApiRunerMemHook::LogLeaks()
{
std::lock_guard guard(m_lock);
- LogToScreen("Leaks: -- START --");
+ LogToScreen("Leaks: %d mem leaks found", m_allocSizeMap.size());
+
+ // Using MemHookLog since long LogToScreen lines can causes mem allocations and thus doesn't work well in this module
+ MemHookLog("Leaks: -- START --");
for (auto& it : m_allocSizeMap)
{
void* ptr = it.first;
uint64_t size = it.second;
auto& stackLog = m_mapStackLog[ptr];
auto& id = m_allocIdMap[ptr];
- LogToScreen("[%d] %0.8x: %d from %s", id, ptr, size, stackLog[4].c_str());
+ if (stackLog.size() >= 4)
+ MemHookLog("[%d] %0.8x: %d from %s", id, ptr, size, stackLog[4].c_str());
+ else
+ MemHookLog("[%d] %0.8x: %d", id, ptr, size);
}
- LogToScreen("Leaks: -- END --");
+ MemHookLog("Leaks: -- END --");
- LogToFile("== Leak CSV Start ==");
+ MemHookLog("== Leak CSV Start ==");
for (auto& it : m_allocSizeMap)
{
void* ptr = it.first;
@@ -244,17 +274,17 @@ void ApiRunerMemHook::LogLeaks()
for (auto& stackLine : stackLog)
{
stackLineId++;
- LogToFile("%d,%0.8x,%d,%d,%s", id, ptr, size, stackLineId, stackLine.c_str());
+ MemHookLog("%d,%0.8x,%d,%d,%s", id, ptr, size, stackLineId, stackLine.c_str());
}
}
- LogToFile("== Leak CSV End ==");
+ MemHookLog("== Leak CSV End ==");
}
void ApiRunerMemHook::LogStats(const std::string& name)
{
std::lock_guard guard(m_lock);
- LogToScreen("%s mem: %u outstanding alloc, (%u total / %u deleted)", name.c_str(), m_allocated - m_allocDeleted, m_allocated, m_allocDeleted);
+ MemHookLog("%s mem: %u outstanding alloc, (%u total / %u deleted)", name.c_str(), m_allocated - m_allocDeleted, m_allocated, m_allocDeleted);
}
// IsStackInsideCallback() needs non-allocating case insenstive string compare so using manual impl
diff --git a/Tests/ApiExplorer/Shared/multidevice.cpp b/Tests/ApiExplorer/Shared/multidevice.cpp
index d5d56d5e..a28e9b18 100644
--- a/Tests/ApiExplorer/Shared/multidevice.cpp
+++ b/Tests/ApiExplorer/Shared/multidevice.cpp
@@ -11,7 +11,13 @@
#if HC_PLATFORM_IS_MICROSOFT
#pragma warning( pop )
#endif
-
+#if HC_PLATFORM == HC_PLATFORM_ANDROID
+#include
+#include "multidevice.h"
+#include "api_explorer.h"
+#include "runner.h"
+#include "pal.h"
+#endif
#if __has_include("apirunnercloudfns.h")
#include "apirunnercloudfns.h"
#else
@@ -571,6 +577,13 @@ HRESULT ApiRunnerMultiDeviceManager::MakeCall(
auto asyncBlock = std::make_unique();
asyncBlock->context = contextPtr.get();
+ XTaskQueueHandle queue = nullptr;
+ hr = XTaskQueueCreate(
+ XTaskQueueDispatchMode::ThreadPool,
+ XTaskQueueDispatchMode::ThreadPool,
+ &queue);
+ RETURN_HR_IF_FAILED(hr);
+ asyncBlock->queue = queue;
asyncBlock->callback = [](XAsyncBlock* asyncBlock)
{
std::string responseString;
diff --git a/Tests/ApiExplorer/Shared/pal.h b/Tests/ApiExplorer/Shared/pal.h
index ffc1e6e2..ead856af 100644
--- a/Tests/ApiExplorer/Shared/pal.h
+++ b/Tests/ApiExplorer/Shared/pal.h
@@ -21,18 +21,31 @@ constexpr auto sprintf_s = sprintf;
#endif
+
+#if HC_PLATFORM == HC_PLATFORM_ANDROID
+typedef unsigned long DWORD, *PDWORD, *LPDWORD;
+#endif
+
+
namespace pal
{
void strcpy(char * dst, size_t size, const char* src);
int stricmp(const char* left, const char* right);
int vsprintf(char* message, size_t size,char const* format, va_list varArgs);
char* strtok(char* str, char const* delimiter, char** context);
-
+
+ // Due to java not supporting unsigned numeric values, we have to do regular long on Android
+#if HC_PLATFORM != HC_PLATFORM_ANDROID
void Sleep(unsigned long duration);
-
+#else
+ void Sleep(long duration);
+#endif
+
bool FileExists(std::string fileName); // TODO might only be used on win32
std::vector EnumFilesInFolder(std::string folder, std::string spec);
std::string FindFile(std::string fileName);
-
std::string GetLuaPath();
+#if HC_PLATFORM == HC_PLATFORM_ANDROID
+ std::string OpenFile(std::string filePath);
+#endif
}
diff --git a/Tests/ApiExplorer/Shared/utils.cpp b/Tests/ApiExplorer/Shared/utils.cpp
index a12b4e64..bc66784e 100644
--- a/Tests/ApiExplorer/Shared/utils.cpp
+++ b/Tests/ApiExplorer/Shared/utils.cpp
@@ -3,6 +3,12 @@
#include "pch.h"
#include "utils.h"
+#if HC_PLATFORM == HC_PLATFORM_ANDROID
+#include "runner.h"
+#include "pal.h"
+#include "api_explorer.h"
+#endif
+
#if HC_PLATFORM == HC_PLATFORM_GDK
#include
#endif
@@ -168,6 +174,7 @@ std::string ConvertHR(HRESULT hr)
std::string ReadFile(std::string fileName)
{
+
std::string filePath = pal::FindFile(fileName);
if (filePath.empty())
{
diff --git a/Tests/ApiExplorer/Tests/_luasetup_/xal/common.lua b/Tests/ApiExplorer/Tests/_luasetup_/xal/common.lua
index 3cb2199c..65255b19 100644
--- a/Tests/ApiExplorer/Tests/_luasetup_/xal/common.lua
+++ b/Tests/ApiExplorer/Tests/_luasetup_/xal/common.lua
@@ -15,6 +15,7 @@ function common.cleanup()
XalUserCloseHandle()
XblCleanupAsync()
XalCleanupAsync()
+ XTaskQueueCloseHandle()
end
function common.OnXalTryAddFirstUserSilentlyAsync()
diff --git a/Tests/ApiExplorer/Tests/achievements/achievements_progress_notification.lua b/Tests/ApiExplorer/Tests/achievements/achievements_progress_notification.lua
index 6405998c..ee9da20b 100644
--- a/Tests/ApiExplorer/Tests/achievements/achievements_progress_notification.lua
+++ b/Tests/ApiExplorer/Tests/achievements/achievements_progress_notification.lua
@@ -4,6 +4,8 @@ common = require 'common'
function AchievementProgressNotifications_Handler()
print ('AchievementProgressNotifications_Handler')
XblAchievementsAddAchievementProgressChangeHandler()
+ SetCheckHR(0)
+ Sleep(5000)
XblAchievementsUpdateAchievementAsync("2", 20)
end
@@ -17,12 +19,17 @@ function OnXblAchievementsUpdateAchievementAsync()
print ("OnXblAchievementsUpdateAchievementAsync")
local hr = GetLastError()
SetCheckHR(1)
- if hr ~= 0 and hr ~= -2145844944 then -- -2145844944 == 0x80190130 == HTTP_E_STATUS_NOT_MODIFIED
+ if hr == -2145844944 then -- -2145844944 == 0x80190130 == HTTP_E_STATUS_NOT_MODIFIED
+ print("Achievement not modified.")
+ XblAchievementsRemoveAchievementProgressChangeHandler()
+ test.stopTest()
+ elseif hr ~= 0 then
test.equal(hr, 0);
print("Failure. hr=" .. hr)
test.stopTest()
- end
- print ("Waiting to receive notification.")
+ else
+ print ("Waiting to receive notification.")
+ end
end
test.skip = true;
diff --git a/Tests/ApiExplorer/Tests/libHttp/websocket_closehandle_while_connected.lua b/Tests/ApiExplorer/Tests/libHttp/websocket_closehandle_while_connected.lua
index 241b8363..67ee3228 100644
--- a/Tests/ApiExplorer/Tests/libHttp/websocket_closehandle_while_connected.lua
+++ b/Tests/ApiExplorer/Tests/libHttp/websocket_closehandle_while_connected.lua
@@ -2,6 +2,8 @@
test = require 'u-test'
common = require 'common'
+--Bug 35915673 Disabling websocket tests due to websocket.org no longer in service
+test.skip = true
test.testWebsocketCloseHandleWhileConnected = function()
print("testWebsocketCloseHandleWhileConnected")
diff --git a/Tests/ApiExplorer/Tests/libHttp/websocket_disconnect_while_connecting.lua b/Tests/ApiExplorer/Tests/libHttp/websocket_disconnect_while_connecting.lua
index 5979a366..c33880ac 100644
--- a/Tests/ApiExplorer/Tests/libHttp/websocket_disconnect_while_connecting.lua
+++ b/Tests/ApiExplorer/Tests/libHttp/websocket_disconnect_while_connecting.lua
@@ -2,6 +2,8 @@
test = require 'u-test'
common = require 'common'
+--Bug 35915673 Disabling websocket tests due to websocket.org no longer in service
+test.skip = true
test.testWebsocketDisconnectWhileConnecting = function()
print("testWebsocketDisconnectWhileConnecting")
diff --git a/Tests/ApiExplorer/Tests/libHttp/websocket_send.lua b/Tests/ApiExplorer/Tests/libHttp/websocket_send.lua
index 48030e62..a4972aa9 100644
--- a/Tests/ApiExplorer/Tests/libHttp/websocket_send.lua
+++ b/Tests/ApiExplorer/Tests/libHttp/websocket_send.lua
@@ -2,6 +2,8 @@
test = require 'u-test'
common = require 'common'
+--Bug 35915673 Disabling websocket tests due to websocket.org no longer in service
+test.skip = true
test.testWebsocketSend = function()
print("testWebsocketSend")
diff --git a/Tests/ApiExplorer/Tests/rta/RTA_activation.lua b/Tests/ApiExplorer/Tests/rta/RTA_activation.lua
index d7eefd40..193c41f1 100644
--- a/Tests/ApiExplorer/Tests/rta/RTA_activation.lua
+++ b/Tests/ApiExplorer/Tests/rta/RTA_activation.lua
@@ -5,9 +5,17 @@ common = require 'common'
function RTAActivation_Handler()
print("RTAActivation_Handler")
- StartSocialManagerDoWorkLoop()
- XblRealTimeActivityAddConnectionStateChangeHandler()
- XblSocialAddSocialRelationshipChangedHandler()
+
+ -- Test isn't valid on all platforms (i.e. on Win32 we set up a notification service subscription by default so the RTA connection
+ -- won't be torn down just by removing the social relationship changed handler).
+ isGdk = IsGDKPlatform()
+ if isGdk then
+ StartSocialManagerDoWorkLoop()
+ XblRealTimeActivityAddConnectionStateChangeHandler()
+ XblSocialAddSocialRelationshipChangedHandler()
+ else
+ test.stopTest()
+ end
end
function OnXblRealTimeActivityAddConnectionStateChangeHandler_Connecting()
diff --git a/Tests/ApiExplorer/Tests/rta/simpleRTA-cpp.lua b/Tests/ApiExplorer/Tests/rta/simpleRTA-cpp.lua
index 39ab3f36..c8911399 100644
--- a/Tests/ApiExplorer/Tests/rta/simpleRTA-cpp.lua
+++ b/Tests/ApiExplorer/Tests/rta/simpleRTA-cpp.lua
@@ -3,8 +3,16 @@ common = require 'common'
function RtaActivateDeactivateCpp()
print("RtaActivateDeactivateCpp");
- RealTimeActivityServiceAddConnectionStateChangeHandler();
- RealTimeActivityServiceActivate();
+
+ -- Test isn't valid on all platforms (i.e. on Win32 we set up a notification service subscription by default so the RTA connection
+ -- won't be torn down just by removing the social relationship changed handler).
+ isGdk = IsGDKPlatform()
+ if isGdk then
+ RealTimeActivityServiceAddConnectionStateChangeHandler();
+ RealTimeActivityServiceActivate();
+ else
+ test.stopTest()
+ end
end
function OnRealTimeActivityServiceAddConnectionStateChangeHandler_Connected()
diff --git a/Tests/ApiExplorer/Tests/rta/simpleRTA.lua b/Tests/ApiExplorer/Tests/rta/simpleRTA.lua
index f46625e3..841e282a 100644
--- a/Tests/ApiExplorer/Tests/rta/simpleRTA.lua
+++ b/Tests/ApiExplorer/Tests/rta/simpleRTA.lua
@@ -3,9 +3,17 @@ common = require 'common'
function SimpleRTA_Handler()
print("SimpleRTA");
- XblRealTimeActivityAddConnectionStateChangeHandler();
- -- Add a real-time handler to force RTA connection
- XblSocialAddSocialRelationshipChangedHandler();
+
+ -- Test isn't valid on all platforms (i.e. on Win32 we set up a notification service subscription by default so the RTA connection
+ -- won't be torn down just by removing the social relationship changed handler).
+ isGdk = IsGDKPlatform()
+ if isGdk then
+ XblRealTimeActivityAddConnectionStateChangeHandler();
+ -- Add a real-time handler to force RTA connection
+ XblSocialAddSocialRelationshipChangedHandler();
+ else
+ test.stopTest()
+ end
end
function OnXblRealTimeActivityAddConnectionStateChangeHandler_Connected()
diff --git a/Tests/ApiExplorer/Tests/rta/simpleRTA_legacy.lua b/Tests/ApiExplorer/Tests/rta/simpleRTA_legacy.lua
index a682ccd2..b5ff264c 100644
--- a/Tests/ApiExplorer/Tests/rta/simpleRTA_legacy.lua
+++ b/Tests/ApiExplorer/Tests/rta/simpleRTA_legacy.lua
@@ -5,8 +5,16 @@ common = require 'common'
function SimpleRTALegacy_Handler()
print("SimpleRTALegacy_Handler");
- XblRealTimeActivityAddConnectionStateChangeHandler();
- XblRealTimeActivityActivate();
+
+ -- Test isn't valid on all platforms (i.e. on Win32 we set up a notification service subscription by default so the RTA connection
+ -- won't be torn down just by removing the social relationship changed handler).
+ isGdk = IsGDKPlatform()
+ if isGdk then
+ XblRealTimeActivityAddConnectionStateChangeHandler();
+ XblRealTimeActivityActivate();
+ else
+ test.stopTest()
+ end
end
function OnXblRealTimeActivityAddConnectionStateChangeHandler_Connected()
diff --git a/Tests/ApiExplorer/Tests/social/social_sub_unsub.lua b/Tests/ApiExplorer/Tests/social/social_sub_unsub.lua
new file mode 100644
index 00000000..779c89d1
--- /dev/null
+++ b/Tests/ApiExplorer/Tests/social/social_sub_unsub.lua
@@ -0,0 +1,45 @@
+test = require 'u-test'
+common = require 'common'
+
+function SocialSubUnSub_Handler()
+ XblRealTimeActivityAddConnectionStateChangeHandler();
+ XblPresenceAddDevicePresenceChangedHandler()
+ XblPresenceAddTitlePresenceChangedHandler()
+ XblRealTimeActivityActivate();
+end
+
+function OnXblRealTimeActivityAddConnectionStateChangeHandler_Connected()
+ print("RTA connection connected");
+ TestLoop()
+end
+
+function OnDevicePresenceChanged()
+end
+
+function OnTitlePresenceChanged()
+end
+
+function TestLoop()
+ SubscribeToTitleAndDevicePresenceChangeForFriends();
+end
+
+loopCount = 0
+function OnSubscribeToTitleAndDevicePresenceChangeForFriends()
+ loopCount = loopCount + 1
+ if loopCount == 3 then
+ Sleep(500);
+ UnsubscribeToTitleAndDevicePresenceChangeForFriends();
+ Sleep(500);
+ test.stopTest();
+ else
+ print("OnSubscribeToTitleAndDevicePresenceChangeForFriends")
+ Sleep(500);
+ UnsubscribeToTitleAndDevicePresenceChangeForFriends();
+ Sleep(500);
+ TestLoop()
+ end
+end
+
+test.SocialSubUnSub = function()
+ common.init(SocialSubUnSub_Handler)
+end
diff --git a/Tests/GDK/APIRunner.GDK/APIRunner.GDK.Bin.vcxproj b/Tests/GDK/APIRunner.GDK/APIRunner.GDK.Bin.vcxproj
index f7368e59..ef7e3d5f 100644
--- a/Tests/GDK/APIRunner.GDK/APIRunner.GDK.Bin.vcxproj
+++ b/Tests/GDK/APIRunner.GDK/APIRunner.GDK.Bin.vcxproj
@@ -458,7 +458,7 @@
Windowstrue
- uuid.lib;$(Console_Libs);%(AdditionalDependencies)
+ dbghelp.lib;uuid.lib;$(Console_Libs);%(AdditionalDependencies)pch.h
diff --git a/xsapi.staticlib.props b/xsapi.staticlib.props
index d4e0c5f8..a8c3d1c9 100644
--- a/xsapi.staticlib.props
+++ b/xsapi.staticlib.props
@@ -17,7 +17,6 @@
UWPWin32XDK
- Android
@@ -28,7 +27,6 @@
142141Microsoft.Xbox.Services.$(XsapiToolset).$(XsapiPlatform).Cpp
- Microsoft.Xbox.Services.$(XsapiToolset).$(XsapiPlatform)Microsoft.Xbox.Services.$(XsapiToolset).GDK.C
@@ -49,15 +47,7 @@
true
-
-
- true
- true
- true
- true
- true
-
-
+
truetrue
@@ -81,7 +71,6 @@
_NO_ASYNCRTIMP;_NO_PPLXIMP;_NO_XSAPIIMP;XBL_API_EXPORT;%(PreprocessorDefinitions)HC_WINHTTP_WEBSOCKETS;HC_PLATFORM_IS_MICROSOFT=1;HC_PLATFORM_GDK=4;HC_PLATFORM=HC_PLATFORM_GDK;HC_DATAMODEL=HC_DATAMODEL_LLP64;%(PreprocessorDefinitions)
- __STDC_WANT_LIB_EXT1__=1;ASIO_STANDALONE;%(PreprocessorDefinitions)XSAPI_BUILT_FROM_SOURCE=1;%(PreprocessorDefinitions);$(XsapiInclude);%(AdditionalIncludeDirectories)
@@ -98,7 +87,6 @@
{AB77D282-496D-413F-9A51-F78DF740A82A}{8A112040-CDA1-4490-B518-62DFCC451FA7}{60139f62-bf37-4f11-bd93-5fbf4e92100c}
- {5fa18992-3e85-4090-b21e-6ef7fb613a44}{B0D02E4C-DB36-416C-8F1E-8666B3FAF876}
@@ -188,12 +176,10 @@
{7333335C-CD75-4802-8F56-702C0BE2563F}{7326b85d-b8df-4a59-a2c7-c721a7a1b8cb}
- {554cba60-e373-40c9-9284-b93e0fc27362}{F321E346-9F67-4291-9A71-D219F892BDEC}{9E8E68BA-6CBE-408E-8738-320474546551}
- {2f1f7c6a-1fda-4cce-935d-0e9324d5db92}