HRESULT MultiplayerService::WriteSessionUsingSubpath()

in Source/Services/Multiplayer/multiplayer_service.cpp [972:1132]


HRESULT MultiplayerService::WriteSessionUsingSubpath(
    _In_ std::shared_ptr<XblMultiplayerSession> session,
    _In_ XblMultiplayerSessionWriteMode mode,
    _In_ const String& subpathAndQuery,
    _In_ AsyncContext<Result<std::shared_ptr<XblMultiplayerSession>>> async
) noexcept
{
    RETURN_HR_INVALIDARGUMENT_IF(subpathAndQuery.empty());

    Result<User> userResult = m_user.Copy();
    RETURN_HR_IF_FAILED(userResult.Hresult());

    auto httpCall = MakeShared<XblHttpCall>(userResult.ExtractPayload());
    RETURN_HR_IF_FAILED(httpCall->Init(
        m_xboxLiveContextSettings,
        "PUT",
        XblHttpCall::BuildUrl("sessiondirectory", subpathAndQuery),
        xbox_live_api::write_session_using_subpath
    ));

    RETURN_HR_IF_FAILED(httpCall->SetRetryAllowed(false));
    RETURN_HR_IF_FAILED(httpCall->SetXblServiceContractVersion(MULTIPLAYER_SERVICE_CONTRACT_VERSION));

    switch (mode)
    {
    case XblMultiplayerSessionWriteMode::CreateNew:
    {
        RETURN_HR_IF_FAILED(httpCall->SetHeader("If-None-Match", "*"));
        break;
    }
    case XblMultiplayerSessionWriteMode::UpdateExisting:
    {
        RETURN_HR_IF_FAILED(httpCall->SetHeader("If-Match", "*"));
        break;
    }
    case XblMultiplayerSessionWriteMode::UpdateOrCreateNew:
    {
        // No match header
        break;
    }
    case XblMultiplayerSessionWriteMode::SynchronizedUpdate:
    {
        if (session->ETag().empty())
        {
            RETURN_HR_IF_FAILED(httpCall->SetHeader("If-None-Match", "*"));
        }
        else
        {
            RETURN_HR_IF_FAILED(httpCall->SetHeader("If-Match", session->ETag()));
        }
        break;
    }
    default:
    {
        return E_INVALIDARG;
    }
    }

    // Set the ConnectionId for the session
    TaskQueue derivedQueue{ async.Queue().DeriveWorkerQueue() };

    return SetRtaConnectionId(session, AsyncContext<Result<void>>{ derivedQueue,
        [
            httpCall,
            xuid{ m_user.Xuid() },
            sessionReference{ session->SessionReference() },
            session,
            async{ std::move(async) }
        ]
    (Result<void> setConnectionIdResult)
    {
        if (Failed(setConnectionIdResult))
        {
            return async.Complete({ setConnectionIdResult.Hresult(), "Failed to establish MPSD RTA subscription" });
        }

        JsonDocument requestBody{ rapidjson::kObjectType };
        session->Serialize(requestBody, requestBody.GetAllocator());

        HRESULT hr = httpCall->SetRequestBody(requestBody);
        if (FAILED(hr))
        {
            return async.Complete(hr);
        }

        hr = httpCall->Perform(AsyncContext<HttpResult>{ async.Queue().DeriveWorkerQueue(),
            [
                xuid,
                sessionReference,
                async
            ]
        (HttpResult httpResult)
        {
            HRESULT hr = httpResult.Hresult();
            if (FAILED(hr))
            {
                return async.Complete({ hr, "Http call failed" });
            }

            hr = httpResult.Payload()->Result();
            auto statusCode = httpResult.Payload()->HttpStatus();
            if (FAILED(hr) && statusCode != 412)
            {
                return async.Complete(hr);
            }
            else if (statusCode == 204)
            {
                // Consistent with XDK behavior, return success on 204 when writing session
                return async.Complete(S_OK);
            }

            auto responseJson = httpResult.Payload()->GetResponseBodyJson();
            if (responseJson.IsNull())
            {
                return async.Complete(hr);
            }

            XblMultiplayerSessionReference localSessionRef;
            if (sessionReference.Scid[0] == 0)
            {
                auto contentLocation = httpResult.Payload()->GetResponseHeader("Content-Location");

                hr = XblMultiplayerSessionReferenceParseFromUriPath(contentLocation.c_str(), &localSessionRef);
                if (FAILED(hr))
                {
                    return async.Complete({ E_FAIL, "Failed to parse session reference from URI" });
                }
            }
            else
            {
                localSessionRef = sessionReference;
            }

            auto session = MakeShared<XblMultiplayerSession>(
                xuid,
                localSessionRef,
                httpResult.Payload()->GetResponseHeader(ETAG_HEADER),
                httpResult.Payload()->GetResponseHeader(DATE_HEADER),
                httpResult.Payload()->GetResponseBodyJson()
                );

            if (FAILED(session->DeserializationError()) && SUCCEEDED(hr))
            {
                // WriteSession failed due to deserialization error
                hr = session->DeserializationError();
            }

            session->SetWriteSessionStatus(
                statusCode
            );

            return async.Complete(Result<std::shared_ptr<XblMultiplayerSession>>(session, hr));
        }});

        if (FAILED(hr))
        {
            return async.Complete(hr);
        }
    }
    });
}