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.0 10.0.19041.0 false - Microsoft_Xbox_Services_141_GDK_C_Thunks + Microsoft.Xbox.Services.141.GDK.C.Thunks Microsoft.Xbox.Services GDK 1 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 @@ Windows true - 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 @@ UWP Win32 XDK - Android @@ -28,7 +27,6 @@ 142 141 Microsoft.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 - - + true true @@ -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}