Xbox Live Helper library overview
The Xbox Live Helper library for PlayFab Party is designed to help games using PlayFab Party meet Xbox Live policies related to communication (XR-015 and XR-045). The Xbox Live Helper library is available on Nuget.org.
Compatibility with the PlayFab Party library
While we strive to minimize breaking changes in our APIs, some changes made to the PlayFab Party API might cause the Xbox Live Helper library to return erroneous values. Refer to the below table to ensure that your libraries' version are compatible.
Xbox Live Helper Library version |
PlayFab Party Version 1.0.1 |
PlayFab Party Version 1.3.0+ |
---|---|---|
1.0.1 | ✔ | |
1.1.0 | ✔ | |
1.2.0 | ✔ | |
1.2.5 | ✔ |
Keeping Track of Xbox Live Users
The PlayFab Party Xbox Live Helper library must be explicitly informed of the Xbox Live users currently participating in the Party session. It is recommended that titles inform the library by listening for changes to their multiplayer session document and reflecting that roster in the Xbox Live Helper library via PartyXblManager::CreateLocalChatUser
and PartyXblManager::CreateRemoteChatUser
.
For local users:
void
OnLocalXboxUserAddedToMPSD(
uint64_t xboxUserId
)
{
PartyXblLocalChatUser* localChatUser;
PartyError err = PartyXblManager::GetSingleton().CreateLocalChatUser(xboxUserId, nullptr, &localChatUser);
if (PARTY_FAILED(err))
{
DEBUGLOG("CreateLocalChatUser failed: %s\n", PartyXblManager::GetErrorMessage(err));
return;
}
}
At this point, if a PartyLocalChatControl already exists for the corresponding local Xbox user, you can associate it with this PartyXblLocalChatUser via the SetCustomContext methods.
localChatControl->SetCustomContext(localChatUser);
localChatUser->SetCustomContext(localChatControl);
Otherwise you can use this new PartyXblLocalChatUser to generate the chat control and associate them then. See Creating PartyLocalChatControls from PartyXblLocalChatUsers for more information.
For remote users:
void
OnRemoteXboxUserAddedToMPSD(
uint64_t xboxUserId
)
{
PartyXblChatUser* remoteChatUser;
PartyError err = PartyXblManager::GetSingleton().CreateRemoteChatUser(remoteXboxUserId, &remoteChatUser);
if (PARTY_FAILED(err))
{
DEBUGLOG("CreateRemoteChatUser failed: %s\n", PartyXblManager::GetErrorMessage(err));
return;
}
At this point, if a PartyChatControl already exists for the corresponding remote Xbox user, you can associate it with this PartyXblChatUser via the SetCustomContext methods.
remoteChatControl->SetCustomContext(remoteChatUser);
remoteChatUser->SetCustomContext(remoteChatControl);
Bear in mind that updates to the session document and updates to the list of remote chat controls may not be ordered, and you may require similar association logic when processing PartyChatControlCreatedStateChange
updates for remote chat controls.
For both local and remote chat users it's important to keep in mind that the core Party library identifies users and chat controls via PlayFab Entity IDs where as the Xbox Live helper library identifies chat users with Xbox User IDs. Therefore translating between the two is often necessary. Refer to Mapping between Xbox Live User IDs and PlayFab Entity IDs for more information.
Creating PartyLocalChatControls from PartyXblLocalChatUsers
PartyXblLocalChatUser
objects are often only useful when associated with PartyLocalUser
and PartyLocalChatControl
objects in the Party library. Generating PartyLocalUser
and PartyLocalChatControl
objects, requires titles to log in their users to PlayFab and retrieve their user's entityId
and titlePlayerEntityToken
. Login can be performed via the PlayFab CPP SDK, but if a title intends to use Xbox Live credentials to log into PlayFab, they may use PartyXblManager::LoginToPlayFab
to avoid pulling in an extra dependency.
The following sample shows how the Xbox Live Helper library can help you create PartyLocalUser
and PartyLocalChatControl
objects, from PartyXblLocalChatUser
objects. For more information on creating PartyXblLocalChatUser
objects, see Keeping Track of Xbox Live Users.
err = PartyXblManager::GetSingleton().LoginToPlayFab(localChatUser, nullptr);
if (PARTY_FAILED(err))
{
DEBUGLOG("LoginToPlayFab failed: %s\n", PartyXblManager::GetErrorMessage(err));
return;
}
Shortly after calling PartyXblManager::LoginToPlayFab
you will receive a PartyXblLoginToPlayFabCompletedStateChange
containing the result of the login operation.
PartyLocalUser* partyLocalUser;
if (stateChange->stateChangeType == PartyXblStateChangeType::LoginToPlayFabCompleted)
{
auto loginToPlayFabCompleted = static_cast<PartyXblLoginToPlayFabCompletedStateChange*>(stateChange);
if (loginToPlayFabCompleted->result == PartyXblStateChangeResult::Succeeded)
{
err = PartyManager::GetSingleton().CreateLocalUser(
loginToPlayFabCompleted->entityId,
loginToPlayFabCompleted->titlePlayerEntityToken,
partyLocalUser));
if (PARTY_FAILED(err))
{
DEBUGLOG("CreateLocalUser failed: %s\n", PartyManager::GetErrorMessage(err));
return;
}
}
}
PartyLocalDevice* localDevice;
err = PartyManager::GetSingleton().GetLocalDevice(&localDevice);
if (PARTY_FAILED(err))
{
DEBUGLOG("GetLocalDevice failed: %s\n", PartyManager::GetErrorMessage(err));
return;
}
PartyLocalChatControl* localChatControl;
err = localDevice->CreateChatControl(partyLocalUser, nullptr, nullptr, &localChatControl);
if (PARTY_FAILED(err))
{
DEBUGLOG("CreateChatControl failed: %s\n", PartyManager::GetErrorMessage(err));
return;
}
// We can use the custom context on the PartyXblLocalChatUser to store the PartyLocalChatControl for easy access
// in the future.
localChatUser->SetCustomContext(localChatControl);
Respecting an Xbox Live user's accessibility preferences
The PartyXblLocalChatUser
object exposes some of the accessibility preferences of the Xbox Live user which are relevant to Party chat sessions. Titles can use this information to provide a better experience to their players by enabling some of Party's accessibility features right away.
PartyXblAccessibilitySettings accessibilitySettings;
err = localChatUser->GetAccessibilitySettings(&accessibilitySettings);
if (PARTY_FAILED(err))
{
DEBUGLOG("GetAccessibilitySettings failed: %s\n", PartyXblManager::GetErrorMessage(err));
return;
}
if (accessibilitySettings.speechToTextEnabled)
{
PartyVoiceChatTranscriptionOptions option = PartyVoiceChatTranscriptionOptions::TranscribeOtherChatControlsWithMatchingLanguages;
m_localChatControl->SetTranscriptionOptions(option, nullptr);
}
Respecting an Xbox Live user's privacy settings and permissions
Per Xbox Live policies, titles must not allow communication over Xbox Live when the user's privacy or permissions do not allow it. The Xbox Live Helper library helps you achieve that by allowing you to query the most restrictive PartyChatPermissionOptions
between two users allowed by Xbox Live policies. Anytime this value changes, a PartyXblRequiredChatPermissionInfoChangedStateChange
will be generated by the library. The updated PartyChatPermissionOptions
can be obtained by a call to PartyXblLocalChatUser::GetRequiredChatPermissionInfo()
.
PartyXblChatUser* remoteChatUser;
PartyError err = PartyXblManager::GetSingleton().CreateRemoteChatUser(remoteXboxUserId, &remoteChatUser);
if (PARTY_FAILED(err))
{
DEBUGLOG("CreateRemoteChatUser failed: %s\n", PartyXblManager::GetErrorMessage(err));
return;
}
// Once the chat control representing this remote Xbox Live user joins a network, we can use the custom context
// on the PartyXblChatUser to store the chat control object for quick access in the future.
remoteChatUser->SetCustomContext(m_remotePartyChatControl);
The Xbox Live helper library tracks the privacy and privilege settings for each remote chat user in relation to each local chat user by communicating with Xbox Live privacy services. Additionally, the library will listen for changes to these settings by subscribing to Real-Time Activity updates. When new remote chat users are added or when the privacy and privilege relationship between a local chat user and an existing remote chat user changes, a PartyXblRequiredChatPermissionInfoChangedStateChange
will be generated to notify you that an updated PartyChatPermissionOptions
value is now available.
// Wait for PartyXblRequiredChatPermissionInfoChangedStateChange
if (stateChange->stateChangeType == PartyXblStateChangeType::RequiredChatPermissionInfoChanged)
{
auto chatPermissionChanged = static_cast<PartyXblRequiredChatPermissionInfoChangedStateChange*>(stateChange);
PartyXblLocalChatUser* localChatUser = chatPermissionChanged->localChatUser;
PartyXblChatUser* targetChatUser = chatPermissionChanged->targetChatUser;
PartyXblChatPermissionInfo chatPermissionInfo;
PartyError err = localChatUser->GetRequiredChatPermissionInfo(targetChatUser, &chatPermissionInfo);
if (PARTY_FAILED(err))
{
DEBUGLOG("GetRequiredChatPermissionInfo failed: %s\n", PartyXblManager::GetErrorMessage(err));
return;
}
PartyLocalChatControl* localChatControl;
localChatUser->GetCustomContext(reinterpret_cast<void**>(&localChatControl));
if (PARTY_FAILED(err))
{
DEBUGLOG("GetCustomContext failed: %s\n", PartyXblManager::GetErrorMessage(err));
return;
}
PartyChatControl* targetChatControl;
targetChatUser->GetCustomContext(reinterpret_cast<void**>(&targetChatControl));
if (PARTY_FAILED(err))
{
DEBUGLOG("GetCustomContext failed: %s\n", PartyXblManager::GetErrorMessage(err));
return;
}
localChatControl->SetPermission(targetChatControl, chatPermissionInfo.chatPermissionMask);
if (PARTY_FAILED(err))
{
DEBUGLOG("SetPermission failed: %s\n", PartyXblManager::GetErrorMessage(err));
return;
}
}
The PartyXblChatPermissionInfo
structure contains two pieces of information:
- A
PartyChatPermissionOptions
mask that can be either passed as-is toPartyLocalChatControl::SetPermission()
or that can be used as a binary mask if you already have aPartyChatPermissionOptions
value that you would like to use but want to make sure that you are respecting Xbox Live policies. - A
PartyXblChatPermissionMaskReason
value that provides extra information regarding the value ofPartyXblChatPermissionInfo::chatPermissionMask
PartyXblChatPermissionMaskReason | Explanation |
---|---|
NoRestriction | There is no restriction currently applying to this chat permission. |
Determining | Communication is restricted while the local user's communication privilege and privacy settings are being determined |
Privilege | Communication is restricted due to the local user's communication privilege. |
Privacy | Communication is restricted due to the local user's privacy settings in relation to the target chat user. |
InvalidTargetUser | Communication is restricted because the target user was not recognized as valid by Xbox Live services. |
XboxLiveServiceError | The required chat permission could not be successfully determined due to issues with Xbox Live services. |
UnknownError | The required chat permission could not be successfully determined due to an unknown internal error. |
Respecting cross-network communication permissions
Titles that support cross-network play and communication between Xbox Live and non-Xbox Live players need to check communication permissions prior to allowing communication between those players. The Xbox Live Helper library provides this information through PartyXblLocalChatUser::GetCrossNetworkCommunicationPrivacySetting()
. This method returns a PartyXblCrossNetworkCommunicationPrivacySetting
enum with three possible value:
PartyXblCrossNetworkCommunicationPrivacySetting | Explanation |
---|---|
Allowed | This Xbox Live user's permissions are set to allow communication with all cross-network players. |
FriendsOnly | This Xbox Live user's permissions are set to allow communication with cross-network friend only. |
Disallowed | This Xbox Live user's permissions do not allow any communication with cross-network players. |
PartyXblCrossNetworkCommunicationPrivacySetting crossNetworkSetting;
localChatUser->GetCrossNetworkCommunicationPrivacySetting(&crossNetworkSetting);
if (crossNetworkSetting == PartyXblCrossNetworkCommunicationPrivacySetting::Disallowed)
{
m_localChatControl->SetPermissions(crossNetworkChatControl, PartyChatPermissionOptions::None);
}
More information on XR-015 and how it pertains to cross-network play and communication can be found here
Mapping between Xbox Live User IDs and PlayFab Entity IDs
Many Xbox Live titles using PlayFab Party need to translate between Xbox Live Users IDs (used throughout the Xbox Live ecosystem) and PlayFab Entity IDs (used by PlayFab Party). Using PartyXblManager::GetEntityIdsFromXboxLiveUserIds
, titles can retrieve a list of PlayFab Entity IDs corresponding to a given list of Xbox Live User IDs. Titles are expected to already have a list of Xbox Live User IDs through the use of an external roster service, like the Multiplayer Session Directory. By associating the Xbox Live User IDs from the roster with their PlayFab Entity IDs, we can construct a mapping of all PlayFab Entity IDs corresponding to your game session's roster. This mapping can then be used to associate PartyEndpoint
and PartyChatControl
objects with their corresponding Xbox Live users.
Note
Each Xbox Live User ID will only map to a PlayFab Entity ID if this Xbox Live user has already been linked to a PlayFab account. A PlayFab account is automatically created and linked the first time PartyXblManager::LoginToPlayFab
is called for a given Xbox user. Alternatively, consumers of the PlayFab SDK can use the LoginWithXbox API to achieve the same results.
The local PartyXblLocalChatUser
will be used to authenticate with PlayFab. If the user was not previously logged in to PlayFab with a call to PartyXblManager::LoginToPlayFab
, the Xbox Live Helper library will need to authenticate the user in the background.
uint32_t userCount;
PartyXblChatUserArray chatUsers;
PartyError err = PartyXblManager::GetSingleton().GetChatUsers(&userCount, &users);
if (PARTY_FAILED(err))
{
DEBUGLOG("GetChatUsers failed: %s\n", PartyXblManager::GetErrorMessage(err));
return;
}
// The list of remote Xbox Live User IDs. This can be populated with arbitrary IDs
// std::vector<uint64_t> remoteXboxLiveUserIds = {2533274792693551, 2814659110958830};
//
// but can also be pulled from the list of remote chat users
std::vector<uint64_t> remoteXboxLiveUserIds;
for (uint32_t i = 0; i < userCount; ++i)
{
PartyXblChatUser* chatUser = users[i];
PartyXblLocalChatUser* localChatUser;
err = chatUser->GetLocal(&localChatUser);
if (PARTY_FAILED(err))
{
DEBUGLOG("PartyChatUser(0x%p)::GetLocal failed: %s\n", chatUser, PartyXblManager::GetErrorMessage(err));
return;
}
if (localChatUser != nullptr)
{
continue; // ignore local users
}
uint64_t userId;
err = chatUser->GetXboxUserId(&userId);
if (PARTY_FAILED(err))
{
DEBUGLOG("PartyChatUser(0x%p)::GetXboxUserId failed: %s\n", chatUser, PartyXblManager::GetErrorMessage(err));
return;
}
remoteXboxLiveUserIds.push_back(userId);
}
err = PartyXblManager::GetSingleton().GetEntityIdsFromXboxLiveUserIds(
remoteXboxLiveUserIds.size(),
remoteXboxLiveUserIds.data(),
localChatUser,
nullptr);
if (PARTY_FAILED(err))
{
DEBUGLOG("GetEntityIdsFromXboxLiveUserIds failed: %s\n", PartyXblManager::GetErrorMessage(err));
return;
}
Shortly after calling PartyXblManager::GetEntityIdsFromXboxLiveUserIds
you will receive a PartyXblGetEntityIdsFromXboxLiveUserIdsCompletedStateChange
containing the result of the operation. This result can be used to construct or update your mappings.
// Wait for PartyXblGetEntityIdsFromXboxLiveUserIdsCompletedStateChange
if (stateChange->stateChangeType == PartyXblStateChangeType::GetEntityIdsFromXboxLiveUserIdsCompleted)
{
std::vector<std::pair<uint64_t, std::string>> cachedXboxUserIdToPlayFabEntityIdMap;
auto getEntityIdsFromXboxLiveUserIdsResult = static_cast<PartyXblGetEntityIdsFromXboxLiveUserIdsCompletedStateChange*>(stateChange);
for (uint32_t i = 0; i < getEntityIdsFromXboxLiveUserIdsResult.entityIdMappingCount; ++i)
{
const PartyXblXboxUserIdToPlayFabEntityIdMapping& idMapping = getEntityIdsFromXboxLiveUserIdsResult.entityIdMappings[i];
Log(" Xbox Live User ID: %llu", idMapping.xboxLiveUserId);
if (strlen(idMapping.playfabEntityId)) != 0)
{
Log(" PlayFab Entity ID: %s", idMapping.playfabEntityId);
cachedXboxUserIdToPlayFabEntityIdMap.emplace_back(idMapping.xboxLiveUserId, idMapping.playfabEntityId);
}
else
{
// This Xbox Live User did not have a linked PlayFab Account.
Log(" PlayFab Entity ID: NOT FOUND");
}
}
m_cachedXboxUserIdToPlayFabEntityIdMap = std::move(cachedXboxUserIdToPlayFabEntityIdMap);
With such a mapping, titles can recognize when a Party object represents an Xbox Live user.
uint64_t
GetXboxUserIdFromPlayFabEntityId(
PartyString entityId
)
{
for (const std::pair<uint64_t, std::string>& idMapping : m_cachedXboxUserIdToPlayFabEntityIdMap)
{
const std::string& entityIdForXboxUserId = idMapping.second;
if (entityIdForXboxUserId == entityId)
{
return idMapping.first;
}
}
// Failed to find a matching Xbox User ID. This Entity ID does not represent an Xbox Live user.
return 0;
}
Special considerations for Windows
On Windows, the Xbox Live Helper library needs helps from the title to obtain Xbox Live tokens. The library will request tokens by generating PartyXblTokenAndSignatureRequestedStateChange
. The title can use the Xbox Authentication Library (XAL) to fulfill these requests. Offloading this work to the title ensures that it remains in full control of any UI handling and consent prompt that is normally associated with user authentication.
if (stateChange->stateChangeType == PartyXblStateChangeType::TokenAndSignatureRequested)
{
auto tokenAndSignatureRequested = static_cast<PartyXblTokenAndSignatureRequestedStateChange*>(stateChange);
// Convert the headers to the format XAL expect
std::vector<XalHttpHeader> xalHeaders;
for (uint32_t i = 0; i < stateChange.headerCount; ++i)
{
xalHeaders.push_back({stateChange.headers[i].name, stateChange.headers[i].value});
}
XalUserGetTokenAndSignatureArgs args = {};
args.method = stateChange.method;
args.url = stateChange.url;
args.headerCount = static_cast<uint32_t>(xalHeaders.size());
args.headers = xalHeaders.data();
args.bodySize = stateChange.bodySize;
args.body = static_cast<const uint8_t*>(stateChange.body);
args.forceRefresh = stateChange.forceRefresh != 0;
args.allUsers = stateChange.allUsers != 0;
XAsyncBlock* asyncBlock = new XAsyncBlock;
asyncBlock->queue = m_queue;
HRESULT hr = XalUserGetTokenAndSignatureSilentlyAsync(m_userHandle, &args, asyncBlock);
}
For more guidance on how to retrieve token and signature using XAL, see the Xbox Authentication Library documentation.
Once you have the token and signature, they can be provided to the Xbox Live Helper library by calling PartyXblManager::CompleteGetTokenAndSignatureRequest()
with the same correlationId
that was provided to the title through the state change.