> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Migración de MPSD a PlayFab Multiplayer y MPA

> Guía de migración para mover títulos de XBOX de MPSD a las salas (lobbies) de PlayFab Multiplayer y Multiplayer Activity (MPA) para invitaciones, emparejamiento y jugadores recientes.

## Introducción

Este documento está dirigido a los desarrolladores de juegos que actualmente usan MPSD y quieren pasar a usar PlayFab Multiplayer y MPA para sus juegos multijugador. El documento cubre los escenarios multijugador más comunes y proporciona fragmentos de código que muestran cómo usar PlayFab Multiplayer con MPA.

### Información general de Multiplayer Session Directory (MPSD)

* Servicio de sesiones con todas las características para compartir la información necesaria para conectar a un grupo de usuarios
* Integrado con la interfaz de usuario de XBOX para las funciones de invitación y unión
* Se integra totalmente con el emparejamiento de SmartMatch
* Las sesiones se derivan de plantillas de sesión predefinidas
* Funcionalidad integrada para la detección de conectividad y el flujo de sesiones
* Disponible como servicio a servicio

### Información general de Multiplayer Activity Service (MPA)

* Servicio ligero para simplificar la integración de XBOX Live para actividades de jugadores, invitaciones y jugadores recientes
* Se coordina con el shell y el sistema operativo de la consola al enviar/aceptar invitaciones y al recibir uniones
* Sin administración de sesiones ni emparejamiento
* Disponible como servicio a servicio

### Información general de PlayFab Multiplayer

* Servicio completo de salas multijugador que incluye funcionalidad de búsqueda y exploración de salas
* Notificaciones de servicio multiplataforma en tiempo real con integración transparente de API
* Servicio completo de emparejamiento que admite notificaciones en tiempo real

## Inicialización

En la tabla siguiente se muestra una lista de funciones comparables que usan MPSD y PlayFab Multiplayer para la inicialización.

| MPSD                                          | PlayFab Multiplayer           |
| --------------------------------------------- | ----------------------------- |
| `XblMultiplayerAddSubscriptionLostHandler`    | `PFMultiplayerInitialize`     |
| `XblMultiplayerAddConnectionIdChangedHandler` | `PFMultiplayerSetEntityToken` |
| `XblMultiplayerSetSubscriptionsEnabled`       |                               |
| `XblMultiplayerSessionCurrentUserSetStatus`   |                               |

### Inicialización: código de ejemplo

Inicialice la biblioteca con su titleID de PlayFab y establezca el token de entidad que se recibió durante el inicio de sesión en el servicio de PlayFab.

```cpp theme={null}
PFMultiplayerHandle pfmHandle{};
HRESULT hr = PFMultiplayerInitialize(pfTitleId, &pfmHandle);
if (FAILED(hr))
{
    //...
}

hr = PFMultiplayerSetEntityToken(pfmHandle, &entityKey, entityToken);
if (FAILED(hr))
{
    //...
}
```

## Cambios de estado de la sala

En la tabla siguiente se muestra una lista de funciones comparables que usan MPSD y PlayFab Multiplayer para controlar los eventos relacionados con las sesiones/salas.

| MPSD                                           | PlayFab Multiplayer                              |
| ---------------------------------------------- | ------------------------------------------------ |
| `XblMultiplayerSessionSubscribedChangeTypes`   | `PFMultiplayerStartProcessingLobbyStateChanges`  |
| `XblMultiplayerSessionChangedHandler`          | `PFMultiplayerFinishProcessingLobbyStateChanges` |
| `XblMultiplayerSessionSubscriptionLostHandler` |                                                  |

### Cambios de estado de la sala: código de ejemplo

Notifique a la biblioteca que va a empezar a procesar los cambios de estado. Controle cada cambio de estado en cola y, a continuación, notifique que ha terminado de procesar los cambios de estado.

```cpp theme={null}
HRESULT hr = PFMultiplayerStartProcessingLobbyStateChanges(MultiplayerHandle, &StateChangeCount, &StateChanges);
if (FAILED(hr))
{
    //...
}

for (uint32_t i = 0; i < StateChangeCount; ++i)
{
    const PFLobbyStateChange& Change = *StateChanges[i];

    switch (Change.stateChangeType)
    {
    case PFLobbyStateChangeType::CreateAndJoinLobbyCompleted:        /*...*/ break;
    case PFLobbyStateChangeType::JoinLobbyCompleted:                 /*...*/ break;
    case PFLobbyStateChangeType::MemberAdded:                        /*...*/ break;
    case PFLobbyStateChangeType::AddMemberCompleted:                 /*...*/ break;
    case PFLobbyStateChangeType::MemberRemoved:                      /*...*/ break;
    case PFLobbyStateChangeType::ForceRemoveMemberCompleted:         /*...*/ break;
    case PFLobbyStateChangeType::LeaveLobbyCompleted:                /*...*/ break;
    case PFLobbyStateChangeType::Updated:                            /*...*/ break;
    case PFLobbyStateChangeType::PostUpdateCompleted:                /*...*/ break;
    case PFLobbyStateChangeType::Disconnecting:                      /*...*/ break;
    case PFLobbyStateChangeType::Disconnected:                       /*...*/ break;
    case PFLobbyStateChangeType::JoinArrangedLobbyCompleted:         /*...*/ break;
    case PFLobbyStateChangeType::FindLobbiesCompleted:               /*...*/ break;
    case PFLobbyStateChangeType::InviteReceived:                     /*...*/ break;
    case PFLobbyStateChangeType::InviteListenerStatusChanged:        /*...*/ break;
    case PFLobbyStateChangeType::SendInviteCompleted:                /*...*/ break;
    case PFLobbyStateChangeType::CreateAndClaimServerLobbyCompleted: /*...*/ break;
    case PFLobbyStateChangeType::ClaimServerLobbyCompleted:          /*...*/ break;
    case PFLobbyStateChangeType::ServerPostUpdateCompleted:          /*...*/ break;
    case PFLobbyStateChangeType::ServerDeleteLobbyCompleted:         /*...*/ break;
    }
}

hr = PFMultiplayerFinishProcessingLobbyStateChanges(MultiplayerHandle, StateChangeCount, StateChanges);
if (FAILED(hr))
{
    //...
}
```

## Creación de una sala

En la tabla siguiente se muestra una lista de funciones comparables que usan MPSD y PlayFab Multiplayer para crear una sesión/sala y unirse a ella.

| MPSD                                                | PlayFab Multiplayer               |
| --------------------------------------------------- | --------------------------------- |
| `XblMultiplayerSessionReferenceCreate`              | `PFMultiplayerCreateAndJoinLobby` |
| `XblMultiplayerSessionCreateHandle`                 |                                   |
| `XblMultiplayerSessionJoin`                         |                                   |
| `XblMultiplayerAddSessionChangedHandler`            |                                   |
| `XblMultiplayerSessionSetSessionChangeSubscription` |                                   |
| `XblMultiplayerSessionSetHostDeviceToken`           |                                   |
| `XblMultiplayerWriteSessionAsync`                   |                                   |

<Note>No se requiere ninguna instalación ni configuración adicional en PlayFab Game Manager para crear una sala. Toda la configuración se puede hacer en el código.</Note>

### Creación de una sala: código de ejemplo

Configure la sala y establezca las propiedades iniciales de la sala o de los miembros; a continuación, cree la sala y únase a ella.

```cpp theme={null}
PFLobbyCreateConfiguration createConfig{};
createConfig.maxMemberCount = 4;
createConfig.ownerMigrationPolicy = PFLobbyOwnerMigrationPolicy::Automatic;
createConfig.accessPolicy = PFLobbyAccessPolicy::Public;

const char* memberPropertyKeys[] { "favoriteColor" };
const char* memberPropertyValues[] { "blue" };

PFLobbyJoinConfiguration joinConfig{};
joinConfig.memberPropertyCount = 1;
joinConfig.memberPropertyKeys = memberPropertyKeys;
joinConfig.memberPropertyValues = memberPropertyValues;

PFLobbyHandle myLobby{};

HRESULT hr = PFMultiplayerCreateAndJoinLobby(
    pfmHandle,           // PFMultiplayerHandle
    &localUserEntityKey, // local user 
    &createConfig,       // create config
    &joinConfig,         // join config
    nullptr,             // async context (optional)   
    &myLobby);           // lobby handle

if (FAILED(hr))
{
    //...
}
```

## Búsqueda de salas

En la tabla siguiente se muestra una lista de funciones comparables que usan MPSD y PlayFab Multiplayer para buscar sesiones/salas.

| MPSD                                                       | PlayFab Multiplayer        |
| ---------------------------------------------------------- | -------------------------- |
| `XblMultiplayerGetSearchHandlesAsync`                      | `PFMultiplayerFindLobbies` |
| `XblMultiplayerSearchHandleGetId`                          |                            |
| `XblMultiplayerSearchHandleGetCustomSessionPropertiesJson` |                            |
| `XblMultiplayerSearchHandleGetMemberCounts`                |                            |
| `XblMultiplayerSearchHandleGetSessionClosed`               |                            |

### Búsqueda de salas: código de ejemplo

Establezca la configuración de búsqueda y, a continuación, busque salas.

```cpp theme={null}
PFLobbySearchConfiguration searchConfiguration{};
searchConfiguration.filterString = filterString.c_str(); // filtering
searchConfiguration.sortString = sortString.c_str();     // sorting
searchConfiguration.clientSearchResultCount = 50;        // limits the number of results
searchConfiguration.friendsFilter;                       // return only lobbies with friends in them

HRESULT hr = PFMultiplayerFindLobbies(
    pfmHandle,          // PFMultiplayerHandle
    &localUserEntityKey,  // local user
    &searchConfiguration, // search config
    nullptr);             // async context (optional)

if (FAILED(hr))
{
    //...
} 
```

A continuación, una vez que se devuelva el cambio de estado del evento, procese los resultados de la búsqueda.

```cpp theme={null}
const auto& stateChange = static_cast<const PFLobbyFindLobbiesCompletedStateChange&>(change);
if (SUCCEEDED(stateChange.result))
{
    for (uint32_t i = 0; i < stateChange.searchResultCount; ++i)
    {
        const PFLobbySearchResult& searchResult = stateChange.searchResults[i];
        searchResult.lobbyId;            // lobby id
        searchResult.connectionString;   // connection string
        searchResult.ownerEntity;        // lobby host
        searchResult.maxMemberCount;     // lobby size
        searchResult.currentMemberCount; // players in lobby
        
        for (uint32_t j = 0; j < searchResult.searchPropertyCount; ++j) 
        {
            const char* searchPropertyKey = searchResult.searchPropertyKeys[j];
            const char* searchPropertyValue = searchResult.searchPropertyValues[j];
            /*...*/
        }
        
        for (uint32_t k = 0; k < searchResult.friendCount; ++k) 
        {
            PFEntityKey friendEntityKey = searchResult.friends[k];
            /*...*/
        }
    }
}
else
{
    //...
}
```

## Claves de búsqueda de salas

Solo se permite usar un conjunto restringido de claves al definir propiedades de búsqueda personalizadas.

* Para las propiedades de cadena, se admiten las claves siguientes: string\_key1, string\_key2, \[...] string\_key30
* Para las propiedades numéricas, se admiten las claves siguientes: number\_key1, number\_key2, \[...] number\_key30

## Operadores de búsqueda de salas

Las cadenas de consulta de las API de **FindLobbies** están estructuradas con una sintaxis de tipo OData. El tamaño máximo de la cadena de filtro es de 600 caracteres.

Estos operadores de OData se pueden usar para componer cadenas de consulta. Los operadores distinguen mayúsculas de minúsculas.

| Operadores | Significado       | Ejemplo                                                 |
| ---------- | ----------------- | ------------------------------------------------------- |
| eq         | igual a           | string\_key1 eq 'CaptureTheFlag'                        |
| lt         | menor que         | number\_key2 lt 10                                      |
| le         | menor o igual que | number\_key2 le 10                                      |
| gt         | mayor que         | number\_key3 gt 100                                     |
| ge         | mayor o igual que | number\_key3 ge 100                                     |
| ne         | distinto de       | string\_key1 ne 'CaptureTheFlag'                        |
| and        | y                 | string\_key1 eq 'CaptureTheFlag' and number\_key2 lt 10 |

<Note>Al comparar propiedades de cadena, asegúrese de encerrar el valor comparado entre comillas simples. Por ejemplo, "string\_key1 eq **'SOME STRING VALUE'**". Las propiedades numéricas no necesitan encerrarse.</Note>

También hay operadores predefinidos disponibles para su uso. Deben ir precedidos del prefijo "lobby/" al especificarlos.

| Operadores           | Significado                                                                            | Ejemplo                            |
| -------------------- | -------------------------------------------------------------------------------------- | ---------------------------------- |
| memberCount          | número de jugadores en una sala                                                        | lobby/memberCount eq 5             |
| maxMemberCount       | número máximo de jugadores permitidos en una sala                                      | lobby/maxMemberCount gt 10         |
| memberCountRemaining | número restante de jugadores que pueden unirse a la sala                               | lobby/memberCountRemaining gt 0    |
| membershipLock       | estado de bloqueo de las salas; debe ser igual a 'Unlocked' o 'Locked'                 | lobby/membershipLock eq 'Unlocked' |
| amOwner              | salas de las que es propietario; debe ser igual a 'true'                               | lobby/amOwner eq 'true'            |
| amMember             | salas de las que es miembro; debe ser igual a 'true'                                   | lobby/amMember eq 'true'           |
| amServer             | salas propiedad del cliente a las que se ha unido el servidor; debe ser igual a 'true' | lobby/amServer eq 'true'           |

## Ordenación de los resultados de la búsqueda

Cadena de estilo OData que contiene la ordenación de esta consulta en orden ascendente ("asc") o descendente ("desc"). Las cláusulas OrderBy se pueden usar con cualquiera de las claves numéricas de búsqueda o con las claves de búsqueda predefinidas que sean numéricas. Para ordenar por proximidad a un número, se puede usar un moniker distance para ordenar por distancia respecto a la clave de búsqueda numérica dada. No puede usar ascendente ni descendente con la ordenación por distancia. Este campo solo admite una cláusula de ordenación o una cláusula de distancia. Si no se proporciona ninguna ordenación, o si se necesita un criterio de desempate para la ordenación dada, la ordenación predeterminada será descendente según la hora de creación.

| Ejemplo                    | Significado                                        |
| -------------------------- | -------------------------------------------------- |
| number\_key1 asc           | ordenar por clave numérica de búsqueda ascendente  |
| lobby/memberCount desc     | ordenar por clave numérica de búsqueda descendente |
| distance(number\_key1 = 5) | ordenar por distancia respecto al número dado      |
|                            | ordenar por hora de creación descendente           |

### Ordenación y filtrado de los resultados de la búsqueda: código de ejemplo

```cpp theme={null}
PFLobbySearchConfiguration searchConfiguration{};​
​
PFLobbySearchFriendsFilter friendsFilter{};    ​
friendsFilter.includeXboxFriendsToken = MyGame::GetLocalUserXboxToken();​
searchConfiguration.friendsFilter = &friendsFilter;​

// Create filter string for ranked deathmatch with skill between 10-20​
std::string filterString;​
filterString +=  "string_key1 eq DeathMatch and ";​ 
filterString +=  "string_key2 eq Ranked and ";​
filterString +=  "number_key1 -ge 10 and ";​
filterString +=  "number_key1 -le 20";​
​
// Create sort string based on skill level​
std::string sortString;​
sortString += std::string("distance{number_key1=" + std::to_string(playerSkill.c_str()) + "}";​

searchConfiguration.filterString = filterString.c_str();​
searchConfiguration.sortString = sortString.c_str();​
searchConfiguration.clientSearchResultCount = 10;        // limits the number of results​
```

## Unión a una sala

En la tabla siguiente se muestra una lista de funciones comparables que usan MPSD y PlayFab Multiplayer para unirse a sesiones/salas.

| MPSD                                                | PlayFab Multiplayer      |
| --------------------------------------------------- | ------------------------ |
| `XblMultiplayerGetSessionByHandleAsync`             | `PFMultiplayerJoinLobby` |
| `XblMultiplayerSessionJoin`                         |                          |
| `XblMultiplayerAddSessionChangedHandler`            |                          |
| `XblMultiplayerSessionSetSessionChangeSubscription` |                          |
| `XblMultiplayerSessionCurrentUserSetStatus`         |                          |
| `XblMultiplayerWriteSessionByHandleAsync`           |                          |
| `XblMultiplayerSessionCloseHandle`                  |                          |

<Note>Para unirse a una sala se requiere una cadena de conexión. Normalmente, el anfitrión de la sala establecerá esta cadena de conexión en su actividad o la enviará mediante una invitación. Para obtener la cadena de conexión, tiene que llamar a `PFLobbyGetConnectionString`.</Note>

### Unión a una sala: código de ejemplo

Establezca la configuración de unión inicial y, a continuación, únase a la sala.

```cpp theme={null}
const char* memberPropertyKeys[] { "number", "name"};
const char* memberPropertyValues[] { "8675309", "Jenny"};

PFLobbyJoinConfiguration joinConfig{};
joinConfig.memberPropertyCount = 2;
joinConfig.memberPropertyKeys = memberPropertyKeys;
joinConfig.memberPropertyValues = memberPropertyValues;

PFLobbyHandle myLobby{};

HRESULT hr = PFMultiplayerJoinLobby(
    pfmHandle,             // PFMultiplayerHandle
    &localUserEntityKey,   // local user
    lobbyConnectionString, // connection string
    &joinConfig,           // join config
    nullptr,               // async context (optional)
    &myLobby);             // handle to the lobby

if (FAILED(hr))
{
    //...
} 
```

## Actualización de una sala

En la tabla siguiente se muestra una lista de funciones comparables que usan MPSD y PlayFab Multiplayer para actualizar una sesión/sala.

| MPSD                                      | PlayFab Multiplayer |
| ----------------------------------------- | ------------------- |
| `XblMultiplayerGetSessionByHandleAsync`   | `PFLobbyPostUpdate` |
| `XblMultiplayerWriteSessionByHandleAsync` |                     |
| `XblMultiplayerSessionCloseHandle`        |                     |

<Note>`PFLobbyPostUpdate` se puede usar para actualizar tanto las propiedades de la sala como las propiedades de los miembros. Puede actualizar uno o ambos tipos de propiedades con una sola llamada a la función.</Note>

### Actualización de una sala: código de ejemplo (propiedades de la sala)

```cpp theme={null}
const char* lobbyPropertyKeys[] { "exampleKey_1", "exampleKey_2" };
const char* lobbyPropertyValues[] { "exampleValue_1234", "exampleValue_ABCD" };

PFLobbyDataUpdate lobbyUpdateData{};
lobbyUpdateData.lobbyPropertyCount = 2;
lobbyUpdateData.lobbyPropertyKeys = lobbyPropertyKeys;
lobbyUpdateData.lobbyPropertyValues = lobbyPropertyValues;

HRESULT hr = PFLobbyPostUpdate(
    myLobby,             // handle to the lobby
    &localUserEntityKey, // local user
    &lobbyUpdateData,    // update data for the lobby
    nullptr,             // update data for a member
    nullptr);            // async context (optional)

if (FAILED(hr))
{
    //...
} 
```

### Actualización de una sala: código de ejemplo (propiedades de los miembros)

```cpp theme={null}
const char* memberPropertyKeys[] { "favoriteColor" };
const char* memberPropertyValues[] { "yellow" };

PFLobbyMemberDataUpdate memberUpdateData{};
memberUpdateData.lobbyPropertyCount = 1;
memberUpdateData.lobbyPropertyKeys = memberPropertyKeys;
memberUpdateData.lobbyPropertyValues = memberPropertyKeys;

HRESULT hr = PFLobbyPostUpdate(
    myLobby,             // handle to the lobby
    &localUserEntityKey, // local user
    nullptr,             // update data for the lobby
    & memberUpdateData   // update data for a member
    nullptr);            // async context (optional)

if (FAILED(hr))
{
    //...
}
```

## Emparejamiento

Las API de emparejamiento de PlayFab Multiplayer son relativamente similares a las API de emparejamiento de MPSD.

| MPSD                                                        | PlayFab Multiplayer                                            |
| ----------------------------------------------------------- | -------------------------------------------------------------- |
| Configuración mediante hoppers de Smartmatch                | Basado en colas de emparejamiento                              |
| El hopper está enlazado a una plantilla de sesión de MPSD   | Las reglas de emparejamiento se aplican en la cola             |
| Las reglas de emparejamiento se aplican en el hopper        | El emparejamiento se inicia mediante un vale de emparejamiento |
| El emparejamiento requiere una sesión de MPSD existente     | El resultado del emparejamiento es una nueva sala de PlayFab   |
| El resultado del emparejamiento es una nueva sesión de MPSD | Admite la asignación de servidores de PlayFab Multiplayer      |

<Note>Las colas de emparejamiento deben configurarse mediante PlayFab Game Manager.</Note>

## Cambios de estado del emparejamiento

En la tabla siguiente se muestra una lista de funciones comparables que usan MPSD y PlayFab Multiplayer para controlar los eventos relacionados con el emparejamiento.

| MPSD                                           | PlayFab Multiplayer                                    |
| ---------------------------------------------- | ------------------------------------------------------ |
| `XblMultiplayerSessionSubscribedChangeTypes`   | `PFMultiplayerStartProcessingMatchmakingStateChanges`  |
| `XblMultiplayerSessionChangedHandler`          | `PFMultiplayerFinishProcessingMatchmakingStateChanges` |
| `XblMultiplayerSessionSubscriptionLostHandler` |                                                        |

### Cambios de estado del emparejamiento: código de ejemplo

Notifique a la biblioteca que va a empezar a procesar los cambios de estado. Controle cada cambio de estado en cola y, a continuación, notifique que ha terminado de procesar los cambios de estado.

```cpp theme={null}
uint32_t stateChangeCount = 0;
const PFMatchmakingStateChange* const* stateChanges = nullptr;

HRESULT hr = PFMultiplayerStartProcessingMatchmakingStateChanges(pfmHandle, &stateChangeCount, &stateChanges);
if (FAILED(hr))
{
    //...
}

for (uint32 i = 0; i < stateChangeCount; ++i)
{
    const PFMatchmakingStateChange& change = *stateChanges[i];
    
    switch (change.stateChangeType)
    {
    case PFMatchmakingStateChangeType::TicketStatusChanged: /*...*/ break;
    case PFMatchmakingStateChangeType::TicketCompleted:     /*...*/ break;
    }
}

hr = PFMultiplayerFinishProcessingMatchmakingStateChanges(pfmHandle, stateChangeCount, stateChanges);
if (FAILED(hr))
{
    //...
}
```

## Inicio del emparejamiento

| MPSD                                      | PlayFab Multiplayer                        |
| ----------------------------------------- | ------------------------------------------ |
| Llamadas de creación de sesión de MPSD... | `PFMultiplayerCreateMatchmakingTicket`     |
| `XblMatchmakingCreateMatchTicketAsync`    | `PFMultiplayerJoinMatchmakingTicketFromId` |
| `XblMatchmakingCreateMatchTicketResult`   | `PFMatchmakingTicketGetStatus`             |
| `XblMultiplayerSessionMatchmakingServer`  | `PFMatchmakingTicketGetMatch`              |
| Llamadas de unión a sesión de MPSD...     | `PFMultiplayerJoinArrangedLobby`           |

### Inicio del emparejamiento: código de ejemplo

```cpp theme={null}
PFMatchmakingTicketConfiguration matchTicketConfig{};
matchTicketConfig.timeoutInSeconds;        // how long to attempt matchmaking
matchTicketConfig.queueName;               // matchmaking queue name
matchTicketConfig.membersToMatchWithCount; // num remote players to go into matchmaking with
matchTicketConfig.membersToMatchWith;      // remote players to go into matchmaking with

HRESULT hr = PFMultiplayerCreateMatchmakingTicket(
    pfmHandle,                   // PFMultiplayerHandle
    1,                           // local user count
    &currentUserEntityKey,       // local users
    nullptr,                     // local user attributes (optional)
    &ticketConfig,               // ticket config
    nullptr,                     // async context (optional)
    &m_activeMatchmakingTicket); // matchmaking ticket

if (FAILED(hr))
{
    //...
} 

hr = PFMultiplayerJoinMatchmakingTicketFromId(
    pfmHandle,                   // PFMultiplayerHandle
    1,                           // local user count
    &currentUserEntityKey,       // local users
    nullptr,                     // local user attributes (optional)
    ticketId,                    // matchmaking ticket to join
    queueName,                   // matchmaking queue name
    nullptr,                     // async context (optional)
    &m_activeMatchmakingTicket); // matchmaking ticket

if (FAILED(hr))
{
    //...
}
```

<Note>El emparejamiento no comenzará hasta que todos los especificados en el campo `membersToMatchWith` se hayan unido.</Note>

A continuación, una vez que se haya encontrado una coincidencia y se devuelva el cambio de estado, únase a la sala organizada.

```cpp theme={null}
const auto& stateChange = static_cast<const PFMatchmakingTicketCompletedStateChange&>(change);
if (SUCCEEDED(stateChange.result))
{
    PFMatchmakingTicketStatus status{};
    HRESULT hr = PFMatchmakingTicketGetStatus(stateChange.ticket, &status);
    if (SUCCEEDED(hr))
    {
        if (status == PFMatchmakingTicketStatus::Matched)
        {
            const PFMatchmakingMatchDetails* matchDetails = nullptr;
            hr = PFMatchmakingTicketGetMatch(stateChange.ticket, &matchDetails);
            if (SUCCEEDED(hr))
            {
                const char* memberPropertyKeys[] { "favoriteCheese" };
                const char* memberPropertyValues[] { "Wensleydale" };

                PFLobbyArrangedJoinConfiguration joinConfig{};
                joinConfig.accessPolicy = PFLobbyAccessPolicy::Private;
                joinConfig.maxMemberCount = 4;
                joinConfig.ownerMigrationPolicy = PFLobbyOwnerMigrationPolicy::Automatic;
                joinConfig.memberPropertyCount = 1;
                joinConfig.memberPropertyKeys = memberPropertyKeys;
                joinConfig.memberPropertyValues = memberPropertyValues;
                
                PFLobbyHandle myLobby{};

                hr = PFMultiplayerJoinArrangedLobby(
                    pfmHandle,                            // PFMultiplayerHandle
                    &localUserEntityKey,                  // local user
                    matchDetails->lobbyArrangementString, // connection string
                    &config,                              // join config
                    nullptr,                              // async context (optional)
                    &myLobby);                            // handle to the lobby

                if (FAILED(hr))
                {
                    //...
                }
            }
        }
    }
}
```

## Limpieza

En la tabla siguiente se muestra una lista de funciones comparables que usan MPSD y PlayFab Multiplayer para la limpieza y el cierre.

| MPSD                                             | MPA                         |
| ------------------------------------------------ | --------------------------- |
| `XblMultiplayerRemoveSubscriptionLostHandler`    | `PFMultiplayerUninitialize` |
| `XblMultiplayerRemoveConnectionIdChangedHandler` |                             |
| `XblMultiplayerSetSubscriptionsEnabled`          |                             |

<Note>Asegúrese de salir de todas las salas activas y de destruir los vales de emparejamiento en curso antes de llamar a `PFMultiplayerUninitialize`.</Note>

### Limpieza: código de ejemplo

```cpp theme={null}
HRESULT hr = PFMultiplayerUninitialize(pfmHandle);
if (FAILED(hr))
{
    //...
}
```

## Actividades

En la tabla siguiente se muestra una lista de funciones comparables que usan MPSD y MPA para administrar actividades.

| MPSD                               | MPA                                           |
| ---------------------------------- | --------------------------------------------- |
| `XblMultiplayerSetActivityAsync`   | `XblMultiplayerActivitySetActivityAsync`      |
| `XblMultiplayerClearActivityAsync` | `XblMultiplayerActivityDeleteActivityAsync`   |
|                                    | `XblMultiplayerActivityGetActivityAsync`      |
|                                    | `XblMultiplayerActivityGetActivityResultSize` |
|                                    | `XblMultiplayerActivityGetActivityResult`     |

<Note>Al establecer una actividad o enviar una invitación, asegúrese de usar la cadena de conexión que devuelve `PFLobbyGetConnectionString`.</Note>

### Actividades: código de ejemplo

```cpp theme={null}
const char* connectionString;
HRESULT hr = PFLobbyGetConnectionString(myLobby, &connectionString);
if (FAILED(hr))
{
    //...
}

uint32_t maxPlayerCount;
hr = PFLobbyGetMaxMemberCount(myLobby, &maxPlayerCount);
if (FAILED(hr))
{
    //...
}

uint32_t lobbyMemberCount;
const PFEntityKey* lobbyMembers;
hr = PFLobbyGetMembers(myLobby, &lobbyMemberCount, &lobbyMembers);
if (FAILED(hr))
{
    //...
}

const char* lobbyId;
hr = PFLobbyGetLobbyId(myLobby, &lobbyId);
if (FAILED(hr))
{
    //...
}

PFLobbyAccessPolicy pfAccessPolicy;
hr = PFLobbyGetAccessPolicy(LobbyHandle, &pfAccessPolicy);
if (FAILED(hr))
{
    //...
}

XblMultiplayerActivityJoinRestriction joinRestriction = XblMultiplayerActivityJoinRestriction::InviteOnly;

switch (pfAccessPolicy)
{
case PFLobbyAccessPolicy::Public:  joinRestriction = XblMultiplayerActivityJoinRestriction::Public; break;
case PFLobbyAccessPolicy::Friends: joinRestriction = XblMultiplayerActivityJoinRestriction::Followed; break;
case PFLobbyAccessPolicy::Private: joinRestriction = XblMultiplayerActivityJoinRestriction::InviteOnly; break;
}

XblMultiplayerActivityInfo info{};
info.connectionString = connectionString;
info.joinRestriction = joinRestriction;
info.maxPlayers = maxPlayerCount;
info.currentPlayers = lobbyMemberCount;
info.groupId = lobbyId;
info.xuid = myXuid;

auto async = std::make_unique<XAsyncBlock>();
async->queue = queue;
async->callback = [](XAsyncBlock* async) 
{
    std::unique_ptr<XAsyncBlock> asyncBlockPtr{ async };
    HRESULT hr = XAsyncGetStatus(async, false);
    if(FAILED(hr))
    {
        //...
    }
};

HRESULT hr = XblMultiplayerActivitySetActivityAsync(
    xblContext,    // XblContextHandle
    &info,         // XblMultiplayerActivityInfo
    false,         // Allow cross-platform joins
    async.get()    // XAsyncBlock
);

if (SUCCEEDED(hr)) 
{
    async.release();
}
else
{
    //...
}
```

## Invitaciones

En la tabla siguiente se muestra una lista de funciones comparables que usan MPSD y MPA para enviar y recibir invitaciones.

| MPSD                             | MPA                                             |
| -------------------------------- | ----------------------------------------------- |
| `XGameInviteRegisterForEvent`    | `XGameInviteRegisterForEvent`                   |
| `XGameInviteUnregisterForEvent`  | `XGameInviteUnregisterForEvent`                 |
| `XblMultiplayerSendInvitesAsync` | `XblMultiplayerActivitySendInvitesAsync`        |
| `XGameUiShowSendGameInviteAsync` | `XGameUiShowMultiplayerActivityGameInviteAsync` |

### Invitaciones: código de ejemplo (interfaz de usuario del título)

```cpp theme={null}
const char* connectionString;
HRESULT hr = PFLobbyGetConnectionString(myLobby, &connectionString);
if (SUCCEEDED(hr))
{
    auto async = std::make_unique<XAsyncBlock>();
    async->queue = queue;
    async->callback = [](XAsyncBlock* async)
    {
        std::unique_ptr<XAsyncBlock> asyncBlockPtr{ async };
        HRESULT hr = XAsyncGetStatus(async, false);
        if(FAILED(hr))
        {
            //...   
        }
    };
    
    HRESULT hr = XblMultiplayerActivitySendInvitesAsync(
        xblContext,       // XblContextHandle 
        &xuid,            // recipient
        1,                // number of invited XUIDs
        true,             // allow cross-platform joins
        connectionString, // use lobby connection string
        async.get());

    if (SUCCEEDED(hr))
    {
        async.release();
    }
    else
    {
        //...
    }
}
else
{
    //...
}
```

### Invitaciones: código de ejemplo (interfaz de usuario de XBOX)

```cpp theme={null}
auto async = std::make_unique<XAsyncBlock>();
async->queue = queue;
async->callback = [](XAsyncBlock* async)
{
    std::unique_ptr<XAsyncBlock> async{ async };
    HRESULT hr = XGameUiShowMultiplayerActivityGameInviteResult(async);
    if(FAILED(hr))
    {
        //...   
    }
};

HRESULT hr = XGameUiShowMultiplayerActivityGameInviteAsync(
    async.get(), // XAsyncBlock
    user.get()   // XUserHandle that is sending the invite
);

if (SUCCEEDED(hr))
{
    async.release();
}
else
{
    //...
}
```

<Note>`XGameUiShowMultiplayerActivityGameInviteResult` usa la actividad establecida actualmente. Debe establecer una actividad mediante `XblMultiplayerActivitySetActivityAsync` antes de usar esta función.</Note>

## Jugadores recientes

En la tabla siguiente se muestra cómo se administra la lista de jugadores recientes al usar MPSD y MPA.

| MPSD                                                          | MPA                                             |
| ------------------------------------------------------------- | ----------------------------------------------- |
| Los jugadores deben estar en la misma sesión de MPSD          | `XblMultiplayerActivityUpdateRecentPlayers`     |
| La sesión tiene la propiedad `gameplay` establecida en `true` | `XblMultiplayerActivityFlushRecentPlayersAsync` |
| Ambos jugadores están marcados como activos                   |                                                 |

<Note>Para evitar la limitación, el procedimiento recomendado es procesar por lotes las llamadas a `XblMultiplayerActivityUpdateRecentPlayers`.</Note>

### Jugadores recientes: código de ejemplo

```cpp theme={null}
XblMultiplayerActivityRecentPlayerUpdate update{};
update.xuid = metPlayerXuid;
update.encounterType = XblMultiplayerActivityEncounterType::Default;

HRESULT hr = XblMultiplayerActivityUpdateRecentPlayers(xblContext, &update, 1);
if (FAILED(hr))
{
    //...
}
```


## Related topics

- [Mpa](/es/services/xbox-services/multiplayer/mpa/index.md)
- [Migración de un árbitro](/es/services/xbox-services/multiplayer/concepts/live-migrating-an-arbiter.md)
- [Mpsd](/es/services/xbox-services/multiplayer/mpsd/index.md)
- [Guía de diseño multijugador para XBOX y PC](/es/services/xbox-services/multiplayer/overviews/multiplayer-design-guidance-xbox-pc-gdk.md)
- [Conceptos](/es/services/xbox-services/multiplayer/mpsd/concepts/index.md)
