Freigeben über


Asynchrone PlayFab Party-Vorgänge und -Benachrichtigungen

Für Vorgänge, die langsam oder rechenintensiv sein können, macht PlayFab Party asynchrone APIs verfügbar. Asynchrone APIs bieten Titeln die Möglichkeit, teure oder langsame Vorgänge über ihre Standard Threads zu starten und den Abschluss dieser Vorgänge in einem Thread ihrer Wahl abzufragen. Derselbe Abrufmechanismus wird auch verwendet, um asynchrone Benachrichtigungen über Aktualisierungen der PlayFab-Party-Bibliothek an den Titel zu übermitteln. Diese Seite bietet eine Übersicht über die asynchronen API-Muster von PlayFab Party und bewährte Methoden für die Programmierung für diese.

Grundlegende API-Muster

Es gibt zwei Arten von asynchronen API-Mustern, die in PlayFab Party beachtet werden müssen:

  1. Asynchrone Vorgänge
  2. Asynchrone Benachrichtigungen

Asynchrone Vorgänge

Es ist einfach, die asynchronen APIs von PlayFab Party zu verwenden. Das allgemeine Muster zum Starten und Abschließen asynchroner Vorgänge sieht wie folgt aus:

  1. Führen Sie einen regulären Methodenaufruf an die entsprechende asynchrone API Ihrer Wahl aus. Zu den gängigen asynchronen Partyvorgängen, von denen Sie wahrscheinlich gebrauchen werden, gehören:

  2. Überprüfen Sie den PartyError-Rückgabewert der API mit den Makros PARTY_SUCCEEDED() oder PARTY_FAILED(). Dieser synchron zurückgegebene Wert gibt An, ob der Vorgang erfolgreich gestartet wurde.

Warnung

Der synchrone Rückgabewert eines asynchronen Party-API-Aufrufs informiert Sie NICHT darüber, ob der Vorgang erfolgreich abgeschlossen wurde. Weitere Informationen zu synchronen und asynchronen Fehlern finden Sie in einem späteren Abschnitt.

  1. Rufen Sie den Abschluss des asynchronen Vorgangs ab, indem Sie nach der "Abschlusszustandsänderung" des zugeordneten Vorgangs suchen, die von PartyManager::StartProcessingStateChanges()bereitgestellt wird. Ein Beispiel für die zugeordnete "Änderung des Abschlusszustands" für PartyManager::CreateNewNetwork() ist PartyCreateNewNetworkCompletedStateChange. Ausführlichere Informationen dazu, was "Zustandsänderungen" sind und wie sie funktionieren, finden Sie unten im Abschnitt Zustandsänderungen .

  2. Überprüfen Sie das Ergebnis der Abschlusszustandsänderung und den ErrorDetail-Wert , um zu ermitteln, ob der Vorgang erfolgreich war oder fehlgeschlagen ist. Ausführlichere Informationen zu diesen Fehlerwerten finden Sie weiter unten im Abschnitt synchrone und asynchrone Fehler .

Asynchrone Benachrichtigungen

Einige Features senden den Titel asynchrone Benachrichtigungen über Änderungen an die PlayFab Party-Bibliothek.

Zu den gängigen Benachrichtigungen gehören:

  1. EndpointCreated , wenn neue Endpunkte in Partynetzwerken erstellt werden.
  2. ChatControlJoinedNetwork , wenn Chatsteuerelemente Parteinetzwerke beitreten.
  3. LocalChatAudioInputChanged , wenn Party registriert, dass sich die Audioeingabe eines lokalen Chatsteuerelements irgendwie geändert hat.

Diese asynchronen Benachrichtigungen werden dem Titel von PlayFab Party als "Zustandsänderungen" über PartyManager::StartProcessingStateChanges() bereitgestellt.

Ausführlichere Informationen dazu, was "Zustandsänderungen" sind und wie sie funktionieren, finden Sie unten im Abschnitt Zustandsänderungen .

Zustandsänderungen

Das asynchrone API-Modell von PlayFab Party basiert auf der Struktur PartyStateChange.

Diese "Zustandsänderungen" sind asynchrone Benachrichtigungen von Ereignissen aus der PlayFab Party-Bibliothek. Diese Benachrichtigungen werden intern in die Warteschlange eingereiht, und der Titel verarbeitet sie durch Aufrufen von PartyManager::StartProcessingStateChanges(). StartProcessingStateChanges() gibt alle Statusänderungen in der Warteschlange als Liste zurück, die der Titel durchlaufen und einzeln verarbeiten kann. Jede Zustandsänderung verfügt über ein PartyStateChange::stateChangeType-Feld , das überprüft werden kann, um zu bestimmen, über welche spezifische Zustandsänderung der Titel benachrichtigt wird. Sobald der Titel weiß, welche Zustandsänderung er erhalten hat, kann er die generische PartyStateChange-Struktur in einen spezifischeren Typ von Zustandsänderungsstruktur umwandeln, um die spezifischen Daten dieses Ereignisses zu überprüfen.

In der Regel wird die Verarbeitung von Zustandsänderungen als einfache Switch-Anweisung implementiert, die jede Zustandsänderung an einen Handler delegiert.

Nachdem die Liste der Zustandsänderungen verarbeitet wurde, muss sie an PartyManager::FinishProcessingStateChanges() zurückgegeben werden.

uint32_t stateChangeCount;
Party::PartyStateChangeArray stateChanges;
PartyError error = Party::PartyManager::GetSingleton().StartProcessingStateChanges(&stateChangeCount, &stateChanges);
if (PARTY_FAILED(error))
{
    return error;
}

for (uin32_t i = 0; i < stateChangeCount; ++i)
{
    const Party::PartyStateChange* stateChange = stateChanges[i];
    switch (stateChange->stateChangeType)
    {
        case Party::PartyStateChangeType::CreateNewNetworkCompleted:
        {
            auto createNewNetworkStateChange = static_cast<const Party::PartyCreateNewNetworkCompletedStateChange*>(stateChange);
            HandlePartyCreateNewNetworkCompleted(createNewNetworkStateChange);
            break;
        }
        // add other state change handlers here.
    }
}

error = Party::PartyManager::GetSingleton().FinishProcessingStateChanges(stateChangeCount, stateChanges);
if (PARTY_FAILED(error))
{
    return error;
}

Notiz

Bei den meisten Titeln wird empfohlen, alle Zustandsänderungen sofort zu verarbeiten und gleichzeitig an FinishProcessingStateChanges() zurückzugeben. Für erweiterte Szenarien kann es sinnvoll sein, einige Zustandsänderungen für einen längeren Zeitraum zu behalten und später zurückzugeben. Dies wird im Folgenden unter Festhalten an Zustandsänderungen ausführlicher erläutert.

Synchrone und asynchrone Fehler

Wenn Sie asynchrone PlayFab-Party-APIs verwenden, ist es wichtig zu beachten, dass es zwei Arten von Fehlern gibt, die Sie behandeln müssen:

  1. Synchrone Fehler
  2. asynchrone Fehler

"Synchrone Fehler" sind Fehler, die als Rückgabewert eines asynchronen API-Aufrufs bereitgestellt werden und Fehler beim Starten eines asynchronen Vorgangs darstellen. Diese Fehler sind im Allgemeinen darauf zurückzuführen, dass die API mit falschen Parametern aufgerufen wird, die API aufgerufen wird, während sich die Bibliothek in einem ungültigen Zustand befindet, oder aufgrund von Fehlern der internen Bibliothek beim Zuweisen von Arbeitsspeicher.

"Asynchrone Fehler" werden als Daten in der Zustandsänderung bereitgestellt, die mit dem asynchronen Abschluss der einzelnen Vorgänge verbunden ist. Für Zustandsänderungsstrukturen gibt es zwei relevante Felder, die für die Fehlerbehandlung relevant sind.

  1. result – ein PartyStateChangeResult Enumerationswert, der für alle Änderungen des Abschlusszustands verfügbar ist. Der Zweck dieses Werts besteht darin, Titeln eine umfassende Beschreibung zu geben, warum ein Vorgang fehlgeschlagen ist. Dieser Wert kann von Titeln verwendet werden, um programmgesteuert auf asynchrone Fehler zu reagieren und diese zu behandeln.
  2. errorDetail – ein PartyError Wert, der für alle Änderungen des Abschlusszustands und einige Änderungen des Benachrichtigungszustands verfügbar ist. Diese Fehlerdetailwerte sind weitgehend undurchsichtig und sollten nicht programmiert werden. Sie sollen in erster Linie in Diagnose (z. B. Telemetrie- oder Fehlerprotokolle) geschrieben werden, um ein detaillierteres Verständnis dafür zu erhalten, warum ein Vorgang fehlgeschlagen ist. Diese Fehlerdetails können durch Aufrufen von PartyManager::GetErrorMessage() in ein lesbares Format konvertiert werden. Diese Fehlermeldungen sollen nur von Entwicklern angezeigt werden. Sie sind nicht lokalisiert oder für die Nutzung durch Endbenutzer vorgesehen. Darüber hinaus finden Sie eine Liste der Fehlercodes des PlayFab Party SDK mit ihren Fehlermeldungen in unserem PlayFab Party Error Codes-Dokument .

Wichtig

Beim Erfassen von Zustandsänderungsfehlern (z. B. Entwicklerprotokolle, Telemetriedaten, PlayFab Entwicklercommunity Fehlerberichten usw.) wird dringend empfohlen, dass Sie sowohl die Werte als auch die resulterrorDetail Werte aller relevanten Zustandsänderungen als Teil Ihrer Diagnose erfassen. Die beiden Werte erfassen zusammen mehr Informationen als beide Werte isoliert.

Bezeichner für asynchrone Vorgänge

Jede asynchrone API enthält einen void* asyncIdentifer Parameter. Dieser Wert ist ein Pass-Through-Parameter, der bei der zugehörigen Abschlusszustandsänderung dieses API-Aufrufs festgelegt wird, sobald er von StartProcessingStateChanges() bereitgestellt wird.

Der Zweck dieses Werts besteht darin, Titeln einen Mechanismus zum Anfügen beliebiger Kontexte mit Zeigergröße an ihre asynchronen API-Aufrufe zu geben. Diese Kontexte können in vielen Szenarien verwendet werden, darunter:

  1. Zuordnen von titelspezifischen Daten zu einem Aufruf der PlayFab-Party-API
  2. Verknüpfen mehrerer asynchroner Vorgänge mit einem freigegebenen Bezeichner

Diese asynchronen Bezeichner sind für die Verwendung von PlayFab Party nicht erforderlich, können jedoch das Schreiben von Titellogik vereinfachen.

Vorgangswarteschlange

Bei der Arbeit mit asynchronen APIs müssen häufig mehrere asynchrone Vorgänge nacheinander als Teil eines größeren asynchronen Flows ausgeführt werden.

In PlayFab Party wäre ein Beispiel der Beitritt zu einem Partynetzwerk und das Verbinden eines Chatsteuerelements mit diesem. Serialisiert, würde dieser Flow wie folgt aussehen:

  1. Rufen Sie PartyManager::ConnectToNetwork() auf, um Ihr Gerät mit dem Partynetzwerk zu verbinden.
  2. Warten Sie, bis partyConnectToNetworkCompletedStateChange angibt, dass die Verbindung erfolgreich war.
  3. Rufen Sie PartyNetwork::AuthenticateLocalUser() auf, um ihren Benutzer zu authentifizieren und in das Partynetzwerk einzubinden.
  4. Warten Sie, bis partyAuthenticateLocalUserCompletedStateChange angibt, dass die Authentifizierung erfolgreich war.
  5. Rufen Sie PartyNetwork::ConnectChatControl() auf, um Ihr Chat-Steuerelement mit dem Partynetzwerk zu verbinden.
  6. Warten Sie, bis partyConnectChatControlCompletedStateChange angibt, dass die Verbindung mit der Chatsteuerung erfolgreich war.

Für komplexere Flows und Titellogik kann dieses serialisierte Muster geeignet sein. Für einfachere Flows bietet PlayFab Party jedoch eine Alternative, die den Titelcode vereinfachen soll:

Viele PlayFab-Party-APIs, die asynchron sind, unterstützen das Warteschlangen von abhängigen Vorgängen, bevor ein vorheriger Vorgang vollständig abgeschlossen wurde. Im vorherigen Beispiel können Sie mit der Authentifizierung Ihres lokalen Benutzers in einem Partynetzwerk beginnen, bevor Sie die Verbindung erfolgreich abgeschlossen haben , und mit dem Verbinden Ihres Chatsteuerelements beginnen, bevor der zugeordnete lokale Benutzer die Authentifizierung abgeschlossen hat.

Durch Warteschlangen können Sie effektiv eine Sammlung asynchroner Vorgänge bündeln, alle gleichzeitig starten und die Fehlerbehandlung zu einem einzelnen Fehlerpunkt zusammenfassen.

Party::PartyNetwork* newPartyNetwork;
PartyError err = PartyManager::GetSingleton().ConnectToNetwork(networkDescriptor, nullptr, &newPartyNetwork);
if (PARTY_SUCCEEDED(err))
{
    err = newPartyNetwork->AuthenticateLocalUser(m_localUser, networkInvitation, nullptr);
    if (PARTY_SUCCEEDED(err))
    {
        err = newPartyNetwork->ConnectChatControl(m_chatControl, nullptr);
        if (PARTY_SUCCEEDED(err))
        {
            Log("Connecting chat control to new party network!");
            m_network = newPartyNetwork;

            // After this point, we should log any failures reported in PartyConnectToNetworkCompletedStateChange,
            // PartyAuthenticateLocalUserCompletedStateChange, and PartyConnectChatControlCompletedStateChange for
            // diagnostic purposes, but all final failure logic and any retry logic can be coalesced into
            // PartyConnectChatControlCompletedStateChange.
        }
    }

    // If we experienced any unexpected failures to start the authentice or connect-chat-control operations, queue
    // a leave operation so we don't join the network in a half-state.
    if (PARTY_FAILED(err))
    {
        (void) newPartyNetwork->LeaveNetwork(nullptr);
    }
}

An Zustandsänderungen festhalten

Von StartProcessingStateChanges() bereitgestellte Zustandsänderungen werden in der Regel sofort verarbeitet und direkt an FinishProcessingStateChanges() zurückgegeben.

uint32_t stateChangeCount;
Party::PartyStateChangeArray stateChanges;
PartyError error = Party::PartyManager::GetSingleton().StartProcessingStateChanges(&stateChangeCount, &stateChanges);
if (PARTY_FAILED(error))
{
    return error;
}


// process the state changes
// ...

error = Party::PartyManager::GetSingleton().FinishProcessingStateChanges(stateChangeCount, stateChanges);
if (PARTY_FAILED(error))
{
    return error;
}

Dies ist der empfohlene Flow, da er einfach zu programmieren ist und die überwiegende Mehrheit der Titelszenarien unterstützt. Für einige erweiterte Szenarien kann es jedoch angebracht sein, eine Teilmenge der Zustandsänderungen zu behalten und sie zu einem späteren Zeitpunkt aus ihrer ursprünglichen Reihenfolge zurückzugeben. In der Regel wird dies getan, um die Lebensdauer einer Ressource zu verlängern, die dieser Zustandsänderung zugeordnet ist.

Ihr Titel kann z. B. Zustandsänderungen in Ihrem Standard Thread verarbeiten, aber die empfangenen Endpunktnachrichten in einem Hintergrundthread verarbeiten, der den Titelstatus basierend auf Netzwerkupdates aktualisiert. Im Rahmen des typischen Verarbeitungsablaufs für Zustandsänderungen müssen Sie die Endpunktnachricht aus partyEndpointMessageReceivedStateChange in einen puffer mit längerer Lebensdauer kopieren, da der Puffer der Zustandsänderung wieder freigegeben wird, wenn die Zustandsänderung an FinishProcessingStateChanges() zurückgegeben wird. Indem Sie partyEndpointMessageReceivedStateChange beiseite legen und die Rückgabe zurückstellen, bis Sie die Nachricht verarbeitet haben, können Sie häufige Kopien vermeiden.

uint32_t endpointMessageCount = 0;
for (uint32_t i = 0; i < stateChangeCount; ++i)
{
    switch (stateChanges[i]->stateChangeType)
    {
        //
        // ... Process all state change types except for EndpointMessageReceived here...
        //
    }
}

// if there were any endpoint messages in the queue, separate them out, queue them, and return the remaining state
// changes in a separate buffer

// NOTE: this sample uses local std::vectors for brevity, but avoiding heap allocations by reusing vectors or
// using a fixed sized buffer tuned for your game may be appropriate.
std::vector<const Party::PartyStateChange*> nonEndpointStateChanges;
std::vector<const Party::PartyStateChange*> endpointStateChanges;
for (uint32_t i = 0; i < stateChangeCount; ++i)
{
    if (stateChanges[i]->stateChangeType == Party::PartyStateChangeType::EndpointMessageReceived)
    {
        endpointStateChanges.push_back(stateChanges[i]);
    }
    else
    {
        nonEndpointStateChanges.push_back(stateChanges[i]);
    }
}

// Return all non-endpoint message state changes
err = PartyManager::GetSingleton().FinishProcessingStateChanges(
    static_cast<uint32_t>(nonEndpointStates.size()),
    nonEndpointStateChanges.data());

// Title-defined function to queue all endpoint message state changes for later processing.
// QueueEndpointMessagesForLaterProcessing will hold any locks necessary to write to the endpoint message queue
MyGame::QueueEndpointMessagesForLaterProcessing(endpointStateChanges.size(), endpointStateChanges.data());

//
// Elsewhere in another thread/execution context...
//
std::vector<const Party::PartyStateChange*> endpointStateChanges;
// Title-defined function to copy all endpoint message state change pointers that we've so far queued.
// TakeEndpointMessagesOutOfQueue will hold any locks necessary to drain the endpoint message queue.
MyGame::TakeEndpointMessagesOutOfQueue(&endpointStateChanges);

for (const Party::PartyStateChange* stateChange : endpointStateChanges)
{
    auto endpointMessage = static_cast<const Party::PartyEndpointMessageReceivedStateChange*>(stateChange);
    // process the endpoint message
}

err = PartyManager::GetSingleton().FinishProcessingStateChanges(
    static_cast<uint32_t>(endpointStateChanges.size()),
    endpointStateChanges.data());

Wenn Sie an Zustandsänderungen festhalten, können Titel auf Kosten einer komplexeren Titellogik eine engere Kontrolle über einige Speicher und Ressourcen von PlayFab Party erhalten.

Steuern asynchroner Arbeit

Es ist manchmal erforderlich, dass Titel steuern, wann und wo asynchrone Arbeit ausgeführt wird, um CPU-Konflikte zwischen Bibliotheken wie PlayFab Party und den Kern-CPU-Workloads ihres Titels zu vermeiden.

PlayFab Party bietet Titeln zwei Optionen, um zu steuern, wie asynchrone PlayFab Party-Arbeit ausgeführt wird:

  1. Steuern der Threadaffinität
  2. Manuelles Verteilen asynchroner Arbeit

Steuern der Threadaffinität

Standardmäßig erfolgt die asynchrone PlayFab-Party-Arbeit in sorgfältig gesteuerten Hintergrundthreads. Einige Titel benötigen nur eine grobe Kontrolle darüber , wo diese Hintergrundthreads geplant werden, um CPU-Konflikte zu vermeiden.

Für diese Titel stellt PlayFab Party Party::SetThreadAffinityMask() bereit. Auf unterstützten Plattformen können Titel so einschränken, welche CPU-Kerne für die Hintergrundthreads von PlayFab Party verwendet werden. Auf diese Weise können Titel garantieren, dass bestimmte Kerne für ihre eigenen CPU-Workloads reserviert sind, ohne dass Konflikte auftreten.

Manuelles Verteilen asynchroner Arbeit

Bei einigen Titeln reicht es nicht aus, nur zu steuern , wo PlayFab Party ihre asynchrone Arbeit ausführt. Sie müssen auch steuern , wann PlayFab Party ihre asynchrone Arbeit ausführt. Für diese Titel stellt PlayFab Party Party::SetWorkMode() und PartyManager::D oWork() bereit. PartyWorkMode::Manual ermöglicht Titeln die Verwendung der PlayFab Party-Bibliothek ohne Hintergrundthreads. Titel steuern manuell die asynchrone Hintergrundarbeit von Party im Ausführungskontext ihrer Wahl mit direkter Kontrolle darüber, wann diese Arbeit erfolgt.

Warnung

Die manuelle Verteilung asynchroner Parteiarbeit ist ein erweitertes Feature. Auch in PartyWorkMode::Manual muss asynchrone Parteiarbeit regelmäßig und regelmäßig verteilt werden. PlayFab Party funktioniert möglicherweise nicht ordnungsgemäß, wenn asynchrone Arbeit nicht rechtzeitig verteilt wird.

Bitte lesen Sie die Dokumentation für PartyManager::D oWork() sorgfältig durch, um eine Anleitung zur korrekten Verteilung dieser Arbeit zu erhalten.

Wenn keine manuelle Verteilung erforderlich ist, empfiehlt es sich, stattdessen die standardmäßige asynchrone Arbeitskonfiguration der Partei oder Threadaffinitätssteuerelemente zu verwenden.