diff --git a/android/ext/playfabxplatsdk/src/cppsdk/CMakeLists.txt b/android/ext/playfabxplatsdk/src/cppsdk/CMakeLists.txt index 7215396..52a190d 100644 --- a/android/ext/playfabxplatsdk/src/cppsdk/CMakeLists.txt +++ b/android/ext/playfabxplatsdk/src/cppsdk/CMakeLists.txt @@ -1,6 +1,7 @@ cmake_minimum_required(VERSION 3.4.1) include_directories( + ${CODE_REPOSIORY_ROOT}/external/gsl/include ${CODE_REPOSIORY_ROOT}/external/playfabxplatsdk/src/cppsdk ${CODE_REPOSIORY_ROOT}/external/playfabxplatsdk/src/cppsdk/include ${CODE_REPOSIORY_ROOT}/external/playfabxplatsdk/src/cppsdk/include/curl diff --git a/android/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabEventApi.h b/android/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabEventApi.h index 055caa2..3aa16d7 100644 --- a/android/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabEventApi.h +++ b/android/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabEventApi.h @@ -15,6 +15,10 @@ namespace PlayFabInternal { public: PlayFabEventAPI(); // Default constructor + + // BUMBLELION: enable manual pumping of event pipeline + PlayFabEventAPI(bool threadedEventPipeline); + std::shared_ptr GetEventRouter() const; /// @@ -23,9 +27,12 @@ namespace PlayFabInternal /// - callback is a pointer to user's function to receive a notification about the outcome of the operation when the event is sent out or any error occurred. /// void EmitEvent(std::unique_ptr event, const PlayFabEmitEventCallback callback) const; - + void EmitEvent(std::unique_ptr event, std::function, std::shared_ptr)> callback) const; + // BUMBLELION: enable manual pumping of event pipeline + void Update(); + private: std::shared_ptr eventRouter; }; diff --git a/android/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabEventPipeline.h b/android/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabEventPipeline.h index b4185ba..3b1c49b 100644 --- a/android/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabEventPipeline.h +++ b/android/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabEventPipeline.h @@ -25,7 +25,7 @@ namespace PlayFabInternal { public: PlayFabEventPipelineSettings(); - PlayFabEventPipelineSettings(PlayFabEventPipelineType emitType); + PlayFabEventPipelineSettings(PlayFabEventPipelineType emitType, bool useBackgroundThread); virtual ~PlayFabEventPipelineSettings() {}; size_t bufferSize; // The minimal size of buffer, in bytes. The actually allocated size will be a power of 2 that is equal or greater than this value. @@ -36,6 +36,9 @@ namespace PlayFabInternal int64_t readBufferWaitTime; // The wait time between attempts to read events from buffer when it is empty, in milliseconds. std::shared_ptr authenticationContext; // The optional PlayFab authentication context that can be used with static PlayFab events API PlayFabEventPipelineType emitType; // whether we call WriteEvent or WriteTelemetryEvent through PlayFab + + // BUMBLELION: enable manual pumping of event pipeline + bool useBackgroundThread; }; /// @@ -46,8 +49,9 @@ namespace PlayFabInternal public: virtual ~IPlayFabEventPipeline() {} virtual void Start() {} // Start pipeline's worker thread - // BUMBLELION: Added to keep the GameCore flavor from enqueuing playstream events after Cleanup is called + // BUMBLELION: needed so that we can stop the event pipeline if our token expires virtual void Stop() = 0; + virtual void Update() = 0; virtual void IntakeEvent(std::shared_ptr request) = 0; // Intake an event. This method must be thread-safe. }; @@ -67,17 +71,19 @@ namespace PlayFabInternal std::shared_ptr GetSettings() const; virtual void Start() override; - // BUMBLELION: Added to keep the GameCore flavor from enqueuing playstream events after Cleanup is called + // BUMBLELION: needed so that we can stop the event pipeline if our token expires virtual void Stop() override; + virtual void Update() override; virtual void IntakeEvent(std::shared_ptr request) override; void SetExceptionCallback(ExceptionCallback callback); protected: - virtual void SendBatch(size_t& batchCounter); + virtual void SendBatch(); private: void WorkerThread(); + bool DoWork(); void WriteEventsApiCallback(const EventsModels::WriteEventsResponse& result, void* customData); void WriteEventsApiErrorCallback(const PlayFabError& error, void* customData); void CallbackRequest(std::shared_ptr request, std::shared_ptr response); @@ -92,6 +98,8 @@ namespace PlayFabInternal std::vector> batch; private: + std::atomic_uintptr_t batchCounter; + std::chrono::steady_clock::time_point momentBatchStarted; std::shared_ptr settings; PlayFabEventBuffer buffer; std::thread workerThread; diff --git a/android/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabEventRouter.h b/android/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabEventRouter.h index 00d7626..ec1bd7a 100644 --- a/android/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabEventRouter.h +++ b/android/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabEventRouter.h @@ -26,6 +26,9 @@ namespace PlayFabInternal virtual void RouteEvent(std::shared_ptr request) const = 0; // Route an event to pipelines. This method must be thread-safe. const std::unordered_map>& GetPipelines() const; + // BUMBLELION: enable manual pumping of event pipeline + virtual void Update() = 0; + protected: std::unordered_map> pipelines; }; @@ -36,8 +39,13 @@ namespace PlayFabInternal class PlayFabEventRouter: public IPlayFabEventRouter { public: - PlayFabEventRouter(); - virtual void RouteEvent(std::shared_ptr request) const; + // BUMBLELION: enable manual pumping of event pipeline + PlayFabEventRouter(bool threadedEventPipeline); + virtual void RouteEvent(std::shared_ptr request) const override; + + // BUMBLELION: enable manual pumping of event pipeline + void Update() override; + private: }; } diff --git a/android/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabIXHR2HttpRequest.h b/android/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabIXHR2HttpRequest.h index 1fd3bde..92d4186 100644 --- a/android/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabIXHR2HttpRequest.h +++ b/android/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabIXHR2HttpRequest.h @@ -26,7 +26,12 @@ #include #include +// BUMBLELION: The header we need to include depends on the platform. +#ifdef PLAYFAB_XBOX #include +#else // defined(PLAYFAB_XBOX) +#include +#endif // defined(PLAYFAB_XBOX) #include namespace PlayFabInternal @@ -83,8 +88,8 @@ namespace PlayFabInternal // ---------------------------------------------------------------------------- // Name: RequestStream // Desc: Encapsulates a request data stream. It inherits ISequentialStream, - // which the IXMLHTTPRequest2 class uses to read from our buffer. It also - // inherits IDispatch, which the IXMLHTTPRequest2 interface on Xbox One requires + // which the IXMLHTTPRequest2 class uses to read from our buffer. It also + // inherits IDispatch, which the IXMLHTTPRequest2 interface on Xbox One requires // (unlike on Windows, where only ISequentialStream is necessary). // ---------------------------------------------------------------------------- class RequestStream : public Microsoft::WRL::RuntimeClass, ISequentialStream, IDispatch> diff --git a/android/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabTransportHeaders.h b/android/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabTransportHeaders.h index d956353..0a3ec0e 100644 --- a/android/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabTransportHeaders.h +++ b/android/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabTransportHeaders.h @@ -1,16 +1,16 @@ // Copyright (C) Microsoft Corporation. All rights reserved. -// +// // This header file is used to include headers of transport plugins supported on each platform. #pragma once #include -#ifdef PLAYFAB_PLATFORM_XBOX +#if defined(PLAYFAB_PLATFORM_XBOX) || defined(BUMBLELION_UWP) #include #endif // PLAYFAB_PLATFORM_XBOX -#ifdef PLAYFAB_PLATFORM_WINDOWS +#if defined(PLAYFAB_PLATFORM_WINDOWS) && !defined(BUMBLELION_UWP) #include #endif // PLAYFAB_PLATFORM_WINDOWS diff --git a/android/ext/playfabxplatsdk/src/cppsdk/playfabxplatcppsdk_lnm.vcxproj b/android/ext/playfabxplatsdk/src/cppsdk/playfabxplatcppsdk_lnm.vcxproj index 514da5e..9221ede 100644 --- a/android/ext/playfabxplatsdk/src/cppsdk/playfabxplatcppsdk_lnm.vcxproj +++ b/android/ext/playfabxplatsdk/src/cppsdk/playfabxplatcppsdk_lnm.vcxproj @@ -10,10 +10,6 @@ x64 - - - - StaticLibrary v141 diff --git a/android/ext/playfabxplatsdk/src/cppsdk/playfabxplatcppsdk_shared.vcxitems b/android/ext/playfabxplatsdk/src/cppsdk/playfabxplatcppsdk_shared.vcxitems index 7896a5c..561ae10 100644 --- a/android/ext/playfabxplatsdk/src/cppsdk/playfabxplatcppsdk_shared.vcxitems +++ b/android/ext/playfabxplatsdk/src/cppsdk/playfabxplatcppsdk_shared.vcxitems @@ -11,6 +11,8 @@ + + @@ -41,6 +43,8 @@ + + diff --git a/android/ext/playfabxplatsdk/src/cppsdk/playfabxplatcppsdk_uwp.vcxproj b/android/ext/playfabxplatsdk/src/cppsdk/playfabxplatcppsdk_uwp.vcxproj new file mode 100644 index 0000000..381e07e --- /dev/null +++ b/android/ext/playfabxplatsdk/src/cppsdk/playfabxplatcppsdk_uwp.vcxproj @@ -0,0 +1,44 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + StaticLibrary + v141 + {81432E82-9BC5-43E1-9D6A-D06477EEE9D4} + playfabxplatcpp_uwp + 15.0 + playfabxplatcpp_uwp + 10.0.18362.0 + + + + + + + + + + + + + playfabxplatcpp + + + \ No newline at end of file diff --git a/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabAndroidHttpPlugin.cpp b/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabAndroidHttpPlugin.cpp index 63cb336..847efe8 100644 --- a/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabAndroidHttpPlugin.cpp +++ b/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabAndroidHttpPlugin.cpp @@ -10,6 +10,9 @@ #include #include +#include +#include +#include #include thread_local unsigned int jniAttachedCount = 0; diff --git a/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabEventApi.cpp b/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabEventApi.cpp index 6984600..ef9e87b 100644 --- a/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabEventApi.cpp +++ b/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabEventApi.cpp @@ -6,8 +6,16 @@ namespace PlayFabInternal { + // BUMBLELION: enable manual pumping of event pipeline + // the default behavior is to use background threads to pump the event pipelines PlayFabEventAPI::PlayFabEventAPI() : - eventRouter(std::shared_ptr(new PlayFabEventRouter())) // default event router + PlayFabEventAPI(true) + { + } + + // BUMBLELION: enable manual pumping of event pipeline + PlayFabEventAPI::PlayFabEventAPI(bool threadedEventPipeline) : + eventRouter(std::make_shared(threadedEventPipeline)) { } @@ -35,6 +43,11 @@ namespace PlayFabInternal this->eventRouter->RouteEvent(eventRequest); } + + void PlayFabEventAPI::Update() + { + this->eventRouter->Update(); + } } #endif diff --git a/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabEventPipeline.cpp b/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabEventPipeline.cpp index a114c17..6f5313e 100644 --- a/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabEventPipeline.cpp +++ b/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabEventPipeline.cpp @@ -24,35 +24,43 @@ namespace PlayFabInternal maximalNumberOfBatchesInFlight(defaultMaxBatchesInFlight), readBufferWaitTime(defaultReadBufferWaitTimeInMs), authenticationContext(nullptr), - emitType(PlayFabEventPipelineType::PlayFabPlayStream) + emitType(PlayFabEventPipelineType::PlayFabPlayStream), + // BUMBLELION: enable manual pumping of event pipeline + useBackgroundThread(true) { } - PlayFabEventPipelineSettings::PlayFabEventPipelineSettings(PlayFabEventPipelineType type) : + PlayFabEventPipelineSettings::PlayFabEventPipelineSettings(PlayFabEventPipelineType type, bool useBackgroundThread) : bufferSize(defaultBufferSize), maximalNumberOfItemsInBatch(defaultMaxItemsInBatch), maximalBatchWaitTime(defaultMaxBatchWaitTimeInSeconds), maximalNumberOfBatchesInFlight(defaultMaxBatchesInFlight), readBufferWaitTime(defaultReadBufferWaitTimeInMs), authenticationContext(nullptr), - emitType(type) + emitType(type), + // BUMBLELION: enable manual pumping of event pipeline + useBackgroundThread(useBackgroundThread) { } PlayFabEventPipeline::PlayFabEventPipeline(std::shared_ptr settings) : + batchCounter(0), buffer(settings->bufferSize), isWorkerThreadRunning(false) { this->settings = std::move(settings); this->batch.reserve(this->settings->maximalNumberOfItemsInBatch); this->batchesInFlight.reserve(this->settings->maximalNumberOfBatchesInFlight); - this->Start(); + if (this->settings->useBackgroundThread) + { + this->Start(); + } } PlayFabEventPipeline::~PlayFabEventPipeline() { - // BUMBLELION: the stop functionality was refactored into its own function, so it could be reused for - // GameCore's need to pause and resume the event pipelines. + // BUMBLELION: the stop functionality was refactored into its own function so that when tokens expire we can + // prevent the pipeline from enqueuing a bunch of requests which we know will fail Stop(); } @@ -66,8 +74,8 @@ namespace PlayFabInternal } } - // BUMBLELION: the stop functionality was refactored into its own function, so it could be reused for - // GameCore's need to pause and resume the event pipelines. + // BUMBLELION: the stop functionality was refactored into its own function so that when tokens expire we can + // prevent the pipeline from enqueuing a bunch of requests which we know will fail void PlayFabEventPipeline::Stop() { // stop worker thread @@ -78,6 +86,20 @@ namespace PlayFabInternal } } + void PlayFabEventPipeline::Update() + { + if (this->settings->useBackgroundThread) + { + throw std::runtime_error("You should not call Update() when PlayFabEventPipelineSettings::useBackgroundThread == true"); + } + + bool hasMoreWorkToProcess; + do + { + hasMoreWorkToProcess = DoWork(); + } while (hasMoreWorkToProcess); + } + std::shared_ptr PlayFabEventPipeline::GetSettings() const { return this->settings; @@ -140,98 +162,107 @@ namespace PlayFabInternal } void PlayFabEventPipeline::WorkerThread() + { + while (this->isWorkerThreadRunning) + { + bool hasMoreWorkToProcess = DoWork(); + if (!hasMoreWorkToProcess) + { + // give some time for batches in flight to deflate + std::this_thread::sleep_for(std::chrono::milliseconds(this->settings->readBufferWaitTime)); + } + } + } + + bool PlayFabEventPipeline::DoWork() { using clock = std::chrono::steady_clock; using Result = PlayFabEventBuffer::EventConsumingResult; std::shared_ptr request; - size_t batchCounter = 0; // used to track uniqueness of batches in the map - std::chrono::steady_clock::time_point momentBatchStarted; // used to track when a currently assembled batch got its first event - while (this->isWorkerThreadRunning) + try { - try + // Process events in the loop + if (this->batchesInFlight.size() >= this->settings->maximalNumberOfBatchesInFlight) { - // Process events in the loop - if (this->batchesInFlight.size() >= this->settings->maximalNumberOfBatchesInFlight) - { - // do not take new events from buffer if batches currently in flight are at the maximum allowed number - // and are not sent out (or received an error) yet - std::this_thread::sleep_for(std::chrono::milliseconds(this->settings->readBufferWaitTime)); // give some time for batches in flight to deflate - continue; - } + // do not take new events from buffer if batches currently in flight are at the maximum allowed number + // and are not sent out (or received an error) yet + return false; + } - // BUMBLELION: Don't try taking a request until we get an entity token. We'd rather not lose events - // that were generated before an entity token was created (i.e. before a user was created). - if (!PlayFabSettings::entityToken.empty() || - (settings->authenticationContext != nullptr && !settings->authenticationContext->entityToken.empty())) + // BUMBLELION: Don't try taking a request until we get an entity token. We'd rather not lose events + // that were generated before an entity token was created (i.e. before a user was created). + if (PlayFabSettings::entityToken.empty() && + (settings->authenticationContext == nullptr || settings->authenticationContext->entityToken.empty())) + { + return false; + } + + switch (this->buffer.TryTake(request)) + { + case Result::Success: { - switch (this->buffer.TryTake(request)) + // add an event to batch + this->batch.push_back(std::move(request)); + + // if batch is full + if (this->batch.size() >= this->settings->maximalNumberOfItemsInBatch) { - case Result::Success: - { - // add an event to batch - this->batch.push_back(std::move(request)); - - // if batch is full - if (this->batch.size() >= this->settings->maximalNumberOfItemsInBatch) - { - this->SendBatch(batchCounter); - } - else if (this->batch.size() == 1) - { - // if it is the first event in an incomplete batch then set the batch creation moment - momentBatchStarted = clock::now(); - } - - continue; // immediately check if there is next event in buffer - } - break; - - case Result::Disabled: - case Result::Empty: - default: - break; + this->SendBatch(); } - - // if batch was started - if (this->batch.size() > 0) + else if (this->batch.size() == 1) { - // check if the batch wait time expired - std::chrono::seconds batchAge = std::chrono::duration_cast(clock::now() - momentBatchStarted); - if (batchAge.count() >= (int32_t)this->settings->maximalBatchWaitTime) - { - // batch wait time expired, send incomplete batch - this->SendBatch(batchCounter); - continue; // immediately check if there is next event in buffer - } + // if it is the first event in an incomplete batch then set the batch creation moment + momentBatchStarted = clock::now(); } + + return true; } - // event buffer is disabled or empty, and batch is not ready to be sent yet - // give some time back to CPU, don't starve it without a good reason - std::this_thread::sleep_for(std::chrono::milliseconds(this->settings->readBufferWaitTime)); + case Result::Disabled: + case Result::Empty: + default: + break; } - catch (const std::exception& ex) - { - LOG_PIPELINE("An exception was caught in PlayFabEventPipeline::WorkerThread method"); - this->isWorkerThreadRunning = false; - { // LOCK userCallbackMutex - std::unique_lock lock(userExceptionCallbackMutex); - if (userExceptionCallback) - { - userExceptionCallback(ex); - } - } // UNLOCK userCallbackMutex - } - catch(...) + // if batch was started + if (this->batch.size() > 0) { - LOG_PIPELINE("A non std::exception was caught in PlayFabEventPipeline::WorkerThread method"); + // check if the batch wait time expired + std::chrono::seconds batchAge = std::chrono::duration_cast(clock::now() - momentBatchStarted); + if (batchAge.count() >= (int32_t)this->settings->maximalBatchWaitTime) + { + // batch wait time expired, send incomplete batch + this->SendBatch(); + return true; + } } + + return false; + } + catch (const std::exception& ex) + { + LOG_PIPELINE("An exception was caught in PlayFabEventPipeline::WorkerThread method"); + this->isWorkerThreadRunning = false; + + { // LOCK userCallbackMutex + std::unique_lock lock(userExceptionCallbackMutex); + if (userExceptionCallback) + { + userExceptionCallback(ex); + } + } // UNLOCK userCallbackMutex + + return false; + } + catch(...) + { + LOG_PIPELINE("A non std::exception was caught in PlayFabEventPipeline::WorkerThread method"); + return false; } } - void PlayFabEventPipeline::SendBatch(size_t& batchCounter) + void PlayFabEventPipeline::SendBatch() { // create a WriteEvents API request to send the batch EventsModels::WriteEventsRequest batchReq; @@ -246,10 +277,10 @@ namespace PlayFabInternal batchReq.Events.push_back(playFabEmitRequest->event->eventContents); } + uintptr_t batchId = this->batchCounter.fetch_add(1); // add batch to flight tracking map - void* customData = reinterpret_cast(batchCounter); // used to track batches across asynchronous Events API + void* customData = reinterpret_cast(batchId); // used to track batches across asynchronous Events API this->batchesInFlight[customData] = std::move(this->batch); - batchCounter++; this->batch.clear(); // batch vector will be reused this->batch.reserve(this->settings->maximalNumberOfItemsInBatch); @@ -299,7 +330,7 @@ namespace PlayFabInternal playFabEmitEventResponse->playFabError = playFabError; playFabEmitEventResponse->writeEventsResponse = std::shared_ptr(new EventsModels::WriteEventsResponse(result)); playFabEmitEventResponse->batch = requestBatchPtr; - playFabEmitEventResponse->batchNumber = reinterpret_cast(customData); + playFabEmitEventResponse->batchNumber = static_cast(reinterpret_cast(customData)); // call an emit event callback CallbackRequest(playFabEmitRequest, std::move(playFabEmitEventResponse)); @@ -337,7 +368,7 @@ namespace PlayFabInternal playFabEmitEventResponse->emitEventResult = EmitEventResult::Success; playFabEmitEventResponse->playFabError = std::shared_ptr(new PlayFabError(error)); playFabEmitEventResponse->batch = requestBatchPtr; - playFabEmitEventResponse->batchNumber = reinterpret_cast(customData); + playFabEmitEventResponse->batchNumber = static_cast(reinterpret_cast(customData)); // call an emit event callback CallbackRequest(playFabEmitRequest, std::move(playFabEmitEventResponse)); diff --git a/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabEventRouter.cpp b/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabEventRouter.cpp index 25c6dc7..a41f784 100644 --- a/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabEventRouter.cpp +++ b/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabEventRouter.cpp @@ -11,10 +11,10 @@ namespace PlayFabInternal return this->pipelines; } - PlayFabEventRouter::PlayFabEventRouter() + PlayFabEventRouter::PlayFabEventRouter(bool threadedEventPipeline) { - this->pipelines.emplace(EventPipelineKey::PlayFabPlayStream, std::make_shared(std::make_shared(PlayFabEventPipelineType::PlayFabPlayStream))); - this->pipelines.emplace(EventPipelineKey::PlayFabTelemetry, std::make_shared(std::make_shared(PlayFabEventPipelineType::PlayFabTelemetry))); + this->pipelines.emplace(EventPipelineKey::PlayFabPlayStream, std::make_shared(std::make_shared(PlayFabEventPipelineType::PlayFabPlayStream, threadedEventPipeline))); + this->pipelines.emplace(EventPipelineKey::PlayFabTelemetry, std::make_shared(std::make_shared(PlayFabEventPipelineType::PlayFabTelemetry, threadedEventPipeline))); } void PlayFabEventRouter::RouteEvent(std::shared_ptr request) const @@ -54,6 +54,15 @@ namespace PlayFabInternal } } } + + // BUMBLELION: enable manual pumping of event pipeline + void PlayFabEventRouter::Update() + { + for (std::pair> pipeline : this->pipelines) + { + pipeline.second->Update(); + } + } } #endif \ No newline at end of file diff --git a/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabIXHR2HttpPlugin.cpp b/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabIXHR2HttpPlugin.cpp index 4c76398..2ec30c5 100644 --- a/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabIXHR2HttpPlugin.cpp +++ b/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabIXHR2HttpPlugin.cpp @@ -1,5 +1,7 @@ #include +#if defined(PLAYFAB_PLATFORM_XBOX) || defined(BUMBLELION_UWP) + #include #include @@ -150,7 +152,7 @@ namespace PlayFabInternal // Create request HttpRequest postEventRequest; - + // Setup headers std::vector headers; SetupRequestHeaders(reqContainer, headers); @@ -158,7 +160,7 @@ namespace PlayFabInternal // Setup url std::string urlString = reqContainer.GetFullUrl(); std::wstring url(urlString.begin(), urlString.end()); - + // Setup payload std::string payload = reqContainer.GetRequestBody(); @@ -272,3 +274,5 @@ namespace PlayFabInternal } } } + +#endif // defined(PLAYFAB_PLATFORM_XBOX) || defined(BUMBLELION_UWP) \ No newline at end of file diff --git a/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabIXHR2HttpRequest.cpp b/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabIXHR2HttpRequest.cpp index 114e9ae..326ed9c 100644 --- a/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabIXHR2HttpRequest.cpp +++ b/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabIXHR2HttpRequest.cpp @@ -11,6 +11,8 @@ #include +#if defined(PLAYFAB_PLATFORM_XBOX) || defined(BUMBLELION_UWP) + #include #include #include @@ -348,9 +350,9 @@ HttpRequest::HttpRequest() : return; } - // Specifies the HTTP stack should continuously call OnDataAvailable as data comes in with no threshold. + // Specifies the HTTP stack should continuously call OnDataAvailable as data comes in with no threshold. // For backwards compatibility this is the default behavior, but not the suggested setting. - //m_pXHR->SetProperty(XHR_PROP_ONDATA_THRESHOLD, XHR_PROP_ONDATA_ALWAYS); + //m_pXHR->SetProperty(XHR_PROP_ONDATA_THRESHOLD, XHR_PROP_ONDATA_ALWAYS); } // ---------------------------------------------------------------------------- @@ -388,7 +390,7 @@ HRESULT HttpRequest::Open(const std::wstring& verb, const std::wstring& url, con { return E_INVALIDARG; } - + HRESULT hr = E_FAIL; // Open a connection for an HTTP GET request. @@ -419,7 +421,8 @@ HRESULT HttpRequest::Open(const std::wstring& verb, const std::wstring& url, con // string following the standard header format "Header: value\r\n" to // be used with the token and signature retrieval API // - for (INT i = 0; i < headers.size(); i++) + // BUMBLELION: Change i from INT to UINT due to signed/unsigned mismatch. + for (UINT i = 0; i < headers.size(); i++) { std::wstring wstrHeaderName = headers[i].wstrHeaderName; std::wstring wstrHeaderValue = headers[i].wstrHeaderValue; @@ -511,7 +514,8 @@ HRESULT HttpRequest::Open(const std::wstring& verb, const std::wstring& url, con // string following the standard header format "Header: value\r\n" to // be used with the token and signature retrieval API // - for (INT i = 0; i < headers.size(); i++) + // BUMBLELION: Change i from INT to UINT due to signed/unsigned mismatch. + for (UINT i = 0; i < headers.size(); i++) { std::wstring wstrHeaderName = headers[i].wstrHeaderName; std::wstring wstrHeaderValue = headers[i].wstrHeaderValue; @@ -614,8 +618,8 @@ STDMETHODIMP RequestStream::Open(LPCSTR psBuffer, ULONG cbBufferSize) m_buffSize = cbBufferSize; m_buffSeekIndex = 0; - // Create a buffer to store a copy of the request (and include space for the null - // terminator, as generally this method can accept the result of strlen() for + // Create a buffer to store a copy of the request (and include space for the null + // terminator, as generally this method can accept the result of strlen() for // cbBufferSize). This buffer is deleted in the destructor. m_buffer = new (std::nothrow) char[cbBufferSize]; if (m_buffer == nullptr) @@ -819,4 +823,6 @@ HRESULT RequestStream::Invoke( return S_OK; } -// ---------------------------------------------------------------------------- \ No newline at end of file +// ---------------------------------------------------------------------------- + +#endif // defined(PLAYFAB_PLATFORM_XBOX) || defined(BUMBLELION_UWP) \ No newline at end of file diff --git a/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabPluginManager.cpp b/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabPluginManager.cpp index 91df380..304a90a 100644 --- a/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabPluginManager.cpp +++ b/android/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabPluginManager.cpp @@ -77,7 +77,7 @@ namespace PlayFabInternal std::shared_ptr PlayFabPluginManager::CreatePlayFabTransportPlugin() { -#ifdef PLAYFAB_PLATFORM_XBOX +#if defined(PLAYFAB_PLATFORM_XBOX) || defined(BUMBLELION_UWP) return std::make_shared(); #elif defined(PLAYFAB_PLATFORM_WINDOWS) return std::make_shared(); diff --git a/android/output/android/release/arm64-v8a/libpartysample.so b/android/output/android/release/arm64-v8a/libpartysample.so new file mode 100644 index 0000000..8053177 Binary files /dev/null and b/android/output/android/release/arm64-v8a/libpartysample.so differ diff --git a/android/output/android/release/armeabi-v7a/libpartysample.so b/android/output/android/release/armeabi-v7a/libpartysample.so new file mode 100644 index 0000000..c7b2883 Binary files /dev/null and b/android/output/android/release/armeabi-v7a/libpartysample.so differ diff --git a/android/output/android/release/x86/libpartysample.so b/android/output/android/release/x86/libpartysample.so new file mode 100644 index 0000000..c2e470e Binary files /dev/null and b/android/output/android/release/x86/libpartysample.so differ diff --git a/android/output/android/release/x86_64/libpartysample.so b/android/output/android/release/x86_64/libpartysample.so new file mode 100644 index 0000000..9db0011 Binary files /dev/null and b/android/output/android/release/x86_64/libpartysample.so differ diff --git a/iOS/ext/playfabxplatsdk/src/cppsdk/CMakeLists.txt b/iOS/ext/playfabxplatsdk/src/cppsdk/CMakeLists.txt index 7215396..52a190d 100644 --- a/iOS/ext/playfabxplatsdk/src/cppsdk/CMakeLists.txt +++ b/iOS/ext/playfabxplatsdk/src/cppsdk/CMakeLists.txt @@ -1,6 +1,7 @@ cmake_minimum_required(VERSION 3.4.1) include_directories( + ${CODE_REPOSIORY_ROOT}/external/gsl/include ${CODE_REPOSIORY_ROOT}/external/playfabxplatsdk/src/cppsdk ${CODE_REPOSIORY_ROOT}/external/playfabxplatsdk/src/cppsdk/include ${CODE_REPOSIORY_ROOT}/external/playfabxplatsdk/src/cppsdk/include/curl diff --git a/iOS/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabEventApi.h b/iOS/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabEventApi.h index 055caa2..3aa16d7 100644 --- a/iOS/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabEventApi.h +++ b/iOS/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabEventApi.h @@ -15,6 +15,10 @@ namespace PlayFabInternal { public: PlayFabEventAPI(); // Default constructor + + // BUMBLELION: enable manual pumping of event pipeline + PlayFabEventAPI(bool threadedEventPipeline); + std::shared_ptr GetEventRouter() const; /// @@ -23,9 +27,12 @@ namespace PlayFabInternal /// - callback is a pointer to user's function to receive a notification about the outcome of the operation when the event is sent out or any error occurred. /// void EmitEvent(std::unique_ptr event, const PlayFabEmitEventCallback callback) const; - + void EmitEvent(std::unique_ptr event, std::function, std::shared_ptr)> callback) const; + // BUMBLELION: enable manual pumping of event pipeline + void Update(); + private: std::shared_ptr eventRouter; }; diff --git a/iOS/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabEventPipeline.h b/iOS/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabEventPipeline.h index b4185ba..3b1c49b 100644 --- a/iOS/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabEventPipeline.h +++ b/iOS/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabEventPipeline.h @@ -25,7 +25,7 @@ namespace PlayFabInternal { public: PlayFabEventPipelineSettings(); - PlayFabEventPipelineSettings(PlayFabEventPipelineType emitType); + PlayFabEventPipelineSettings(PlayFabEventPipelineType emitType, bool useBackgroundThread); virtual ~PlayFabEventPipelineSettings() {}; size_t bufferSize; // The minimal size of buffer, in bytes. The actually allocated size will be a power of 2 that is equal or greater than this value. @@ -36,6 +36,9 @@ namespace PlayFabInternal int64_t readBufferWaitTime; // The wait time between attempts to read events from buffer when it is empty, in milliseconds. std::shared_ptr authenticationContext; // The optional PlayFab authentication context that can be used with static PlayFab events API PlayFabEventPipelineType emitType; // whether we call WriteEvent or WriteTelemetryEvent through PlayFab + + // BUMBLELION: enable manual pumping of event pipeline + bool useBackgroundThread; }; /// @@ -46,8 +49,9 @@ namespace PlayFabInternal public: virtual ~IPlayFabEventPipeline() {} virtual void Start() {} // Start pipeline's worker thread - // BUMBLELION: Added to keep the GameCore flavor from enqueuing playstream events after Cleanup is called + // BUMBLELION: needed so that we can stop the event pipeline if our token expires virtual void Stop() = 0; + virtual void Update() = 0; virtual void IntakeEvent(std::shared_ptr request) = 0; // Intake an event. This method must be thread-safe. }; @@ -67,17 +71,19 @@ namespace PlayFabInternal std::shared_ptr GetSettings() const; virtual void Start() override; - // BUMBLELION: Added to keep the GameCore flavor from enqueuing playstream events after Cleanup is called + // BUMBLELION: needed so that we can stop the event pipeline if our token expires virtual void Stop() override; + virtual void Update() override; virtual void IntakeEvent(std::shared_ptr request) override; void SetExceptionCallback(ExceptionCallback callback); protected: - virtual void SendBatch(size_t& batchCounter); + virtual void SendBatch(); private: void WorkerThread(); + bool DoWork(); void WriteEventsApiCallback(const EventsModels::WriteEventsResponse& result, void* customData); void WriteEventsApiErrorCallback(const PlayFabError& error, void* customData); void CallbackRequest(std::shared_ptr request, std::shared_ptr response); @@ -92,6 +98,8 @@ namespace PlayFabInternal std::vector> batch; private: + std::atomic_uintptr_t batchCounter; + std::chrono::steady_clock::time_point momentBatchStarted; std::shared_ptr settings; PlayFabEventBuffer buffer; std::thread workerThread; diff --git a/iOS/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabEventRouter.h b/iOS/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabEventRouter.h index 00d7626..ec1bd7a 100644 --- a/iOS/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabEventRouter.h +++ b/iOS/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabEventRouter.h @@ -26,6 +26,9 @@ namespace PlayFabInternal virtual void RouteEvent(std::shared_ptr request) const = 0; // Route an event to pipelines. This method must be thread-safe. const std::unordered_map>& GetPipelines() const; + // BUMBLELION: enable manual pumping of event pipeline + virtual void Update() = 0; + protected: std::unordered_map> pipelines; }; @@ -36,8 +39,13 @@ namespace PlayFabInternal class PlayFabEventRouter: public IPlayFabEventRouter { public: - PlayFabEventRouter(); - virtual void RouteEvent(std::shared_ptr request) const; + // BUMBLELION: enable manual pumping of event pipeline + PlayFabEventRouter(bool threadedEventPipeline); + virtual void RouteEvent(std::shared_ptr request) const override; + + // BUMBLELION: enable manual pumping of event pipeline + void Update() override; + private: }; } diff --git a/iOS/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabIXHR2HttpRequest.h b/iOS/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabIXHR2HttpRequest.h index 1fd3bde..92d4186 100644 --- a/iOS/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabIXHR2HttpRequest.h +++ b/iOS/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabIXHR2HttpRequest.h @@ -26,7 +26,12 @@ #include #include +// BUMBLELION: The header we need to include depends on the platform. +#ifdef PLAYFAB_XBOX #include +#else // defined(PLAYFAB_XBOX) +#include +#endif // defined(PLAYFAB_XBOX) #include namespace PlayFabInternal @@ -83,8 +88,8 @@ namespace PlayFabInternal // ---------------------------------------------------------------------------- // Name: RequestStream // Desc: Encapsulates a request data stream. It inherits ISequentialStream, - // which the IXMLHTTPRequest2 class uses to read from our buffer. It also - // inherits IDispatch, which the IXMLHTTPRequest2 interface on Xbox One requires + // which the IXMLHTTPRequest2 class uses to read from our buffer. It also + // inherits IDispatch, which the IXMLHTTPRequest2 interface on Xbox One requires // (unlike on Windows, where only ISequentialStream is necessary). // ---------------------------------------------------------------------------- class RequestStream : public Microsoft::WRL::RuntimeClass, ISequentialStream, IDispatch> diff --git a/iOS/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabTransportHeaders.h b/iOS/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabTransportHeaders.h index d956353..0a3ec0e 100644 --- a/iOS/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabTransportHeaders.h +++ b/iOS/ext/playfabxplatsdk/src/cppsdk/include/playfab/PlayFabTransportHeaders.h @@ -1,16 +1,16 @@ // Copyright (C) Microsoft Corporation. All rights reserved. -// +// // This header file is used to include headers of transport plugins supported on each platform. #pragma once #include -#ifdef PLAYFAB_PLATFORM_XBOX +#if defined(PLAYFAB_PLATFORM_XBOX) || defined(BUMBLELION_UWP) #include #endif // PLAYFAB_PLATFORM_XBOX -#ifdef PLAYFAB_PLATFORM_WINDOWS +#if defined(PLAYFAB_PLATFORM_WINDOWS) && !defined(BUMBLELION_UWP) #include #endif // PLAYFAB_PLATFORM_WINDOWS diff --git a/iOS/ext/playfabxplatsdk/src/cppsdk/playfabxplatcppsdk_lnm.vcxproj b/iOS/ext/playfabxplatsdk/src/cppsdk/playfabxplatcppsdk_lnm.vcxproj index 514da5e..9221ede 100644 --- a/iOS/ext/playfabxplatsdk/src/cppsdk/playfabxplatcppsdk_lnm.vcxproj +++ b/iOS/ext/playfabxplatsdk/src/cppsdk/playfabxplatcppsdk_lnm.vcxproj @@ -10,10 +10,6 @@ x64 - - - - StaticLibrary v141 diff --git a/iOS/ext/playfabxplatsdk/src/cppsdk/playfabxplatcppsdk_shared.vcxitems b/iOS/ext/playfabxplatsdk/src/cppsdk/playfabxplatcppsdk_shared.vcxitems index 7896a5c..561ae10 100644 --- a/iOS/ext/playfabxplatsdk/src/cppsdk/playfabxplatcppsdk_shared.vcxitems +++ b/iOS/ext/playfabxplatsdk/src/cppsdk/playfabxplatcppsdk_shared.vcxitems @@ -11,6 +11,8 @@ + + @@ -41,6 +43,8 @@ + + diff --git a/iOS/ext/playfabxplatsdk/src/cppsdk/playfabxplatcppsdk_uwp.vcxproj b/iOS/ext/playfabxplatsdk/src/cppsdk/playfabxplatcppsdk_uwp.vcxproj new file mode 100644 index 0000000..381e07e --- /dev/null +++ b/iOS/ext/playfabxplatsdk/src/cppsdk/playfabxplatcppsdk_uwp.vcxproj @@ -0,0 +1,44 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + StaticLibrary + v141 + {81432E82-9BC5-43E1-9D6A-D06477EEE9D4} + playfabxplatcpp_uwp + 15.0 + playfabxplatcpp_uwp + 10.0.18362.0 + + + + + + + + + + + + + playfabxplatcpp + + + \ No newline at end of file diff --git a/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabAndroidHttpPlugin.cpp b/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabAndroidHttpPlugin.cpp index 63cb336..847efe8 100644 --- a/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabAndroidHttpPlugin.cpp +++ b/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabAndroidHttpPlugin.cpp @@ -10,6 +10,9 @@ #include #include +#include +#include +#include #include thread_local unsigned int jniAttachedCount = 0; diff --git a/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabEventApi.cpp b/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabEventApi.cpp index 6984600..ef9e87b 100644 --- a/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabEventApi.cpp +++ b/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabEventApi.cpp @@ -6,8 +6,16 @@ namespace PlayFabInternal { + // BUMBLELION: enable manual pumping of event pipeline + // the default behavior is to use background threads to pump the event pipelines PlayFabEventAPI::PlayFabEventAPI() : - eventRouter(std::shared_ptr(new PlayFabEventRouter())) // default event router + PlayFabEventAPI(true) + { + } + + // BUMBLELION: enable manual pumping of event pipeline + PlayFabEventAPI::PlayFabEventAPI(bool threadedEventPipeline) : + eventRouter(std::make_shared(threadedEventPipeline)) { } @@ -35,6 +43,11 @@ namespace PlayFabInternal this->eventRouter->RouteEvent(eventRequest); } + + void PlayFabEventAPI::Update() + { + this->eventRouter->Update(); + } } #endif diff --git a/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabEventPipeline.cpp b/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabEventPipeline.cpp index a114c17..6f5313e 100644 --- a/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabEventPipeline.cpp +++ b/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabEventPipeline.cpp @@ -24,35 +24,43 @@ namespace PlayFabInternal maximalNumberOfBatchesInFlight(defaultMaxBatchesInFlight), readBufferWaitTime(defaultReadBufferWaitTimeInMs), authenticationContext(nullptr), - emitType(PlayFabEventPipelineType::PlayFabPlayStream) + emitType(PlayFabEventPipelineType::PlayFabPlayStream), + // BUMBLELION: enable manual pumping of event pipeline + useBackgroundThread(true) { } - PlayFabEventPipelineSettings::PlayFabEventPipelineSettings(PlayFabEventPipelineType type) : + PlayFabEventPipelineSettings::PlayFabEventPipelineSettings(PlayFabEventPipelineType type, bool useBackgroundThread) : bufferSize(defaultBufferSize), maximalNumberOfItemsInBatch(defaultMaxItemsInBatch), maximalBatchWaitTime(defaultMaxBatchWaitTimeInSeconds), maximalNumberOfBatchesInFlight(defaultMaxBatchesInFlight), readBufferWaitTime(defaultReadBufferWaitTimeInMs), authenticationContext(nullptr), - emitType(type) + emitType(type), + // BUMBLELION: enable manual pumping of event pipeline + useBackgroundThread(useBackgroundThread) { } PlayFabEventPipeline::PlayFabEventPipeline(std::shared_ptr settings) : + batchCounter(0), buffer(settings->bufferSize), isWorkerThreadRunning(false) { this->settings = std::move(settings); this->batch.reserve(this->settings->maximalNumberOfItemsInBatch); this->batchesInFlight.reserve(this->settings->maximalNumberOfBatchesInFlight); - this->Start(); + if (this->settings->useBackgroundThread) + { + this->Start(); + } } PlayFabEventPipeline::~PlayFabEventPipeline() { - // BUMBLELION: the stop functionality was refactored into its own function, so it could be reused for - // GameCore's need to pause and resume the event pipelines. + // BUMBLELION: the stop functionality was refactored into its own function so that when tokens expire we can + // prevent the pipeline from enqueuing a bunch of requests which we know will fail Stop(); } @@ -66,8 +74,8 @@ namespace PlayFabInternal } } - // BUMBLELION: the stop functionality was refactored into its own function, so it could be reused for - // GameCore's need to pause and resume the event pipelines. + // BUMBLELION: the stop functionality was refactored into its own function so that when tokens expire we can + // prevent the pipeline from enqueuing a bunch of requests which we know will fail void PlayFabEventPipeline::Stop() { // stop worker thread @@ -78,6 +86,20 @@ namespace PlayFabInternal } } + void PlayFabEventPipeline::Update() + { + if (this->settings->useBackgroundThread) + { + throw std::runtime_error("You should not call Update() when PlayFabEventPipelineSettings::useBackgroundThread == true"); + } + + bool hasMoreWorkToProcess; + do + { + hasMoreWorkToProcess = DoWork(); + } while (hasMoreWorkToProcess); + } + std::shared_ptr PlayFabEventPipeline::GetSettings() const { return this->settings; @@ -140,98 +162,107 @@ namespace PlayFabInternal } void PlayFabEventPipeline::WorkerThread() + { + while (this->isWorkerThreadRunning) + { + bool hasMoreWorkToProcess = DoWork(); + if (!hasMoreWorkToProcess) + { + // give some time for batches in flight to deflate + std::this_thread::sleep_for(std::chrono::milliseconds(this->settings->readBufferWaitTime)); + } + } + } + + bool PlayFabEventPipeline::DoWork() { using clock = std::chrono::steady_clock; using Result = PlayFabEventBuffer::EventConsumingResult; std::shared_ptr request; - size_t batchCounter = 0; // used to track uniqueness of batches in the map - std::chrono::steady_clock::time_point momentBatchStarted; // used to track when a currently assembled batch got its first event - while (this->isWorkerThreadRunning) + try { - try + // Process events in the loop + if (this->batchesInFlight.size() >= this->settings->maximalNumberOfBatchesInFlight) { - // Process events in the loop - if (this->batchesInFlight.size() >= this->settings->maximalNumberOfBatchesInFlight) - { - // do not take new events from buffer if batches currently in flight are at the maximum allowed number - // and are not sent out (or received an error) yet - std::this_thread::sleep_for(std::chrono::milliseconds(this->settings->readBufferWaitTime)); // give some time for batches in flight to deflate - continue; - } + // do not take new events from buffer if batches currently in flight are at the maximum allowed number + // and are not sent out (or received an error) yet + return false; + } - // BUMBLELION: Don't try taking a request until we get an entity token. We'd rather not lose events - // that were generated before an entity token was created (i.e. before a user was created). - if (!PlayFabSettings::entityToken.empty() || - (settings->authenticationContext != nullptr && !settings->authenticationContext->entityToken.empty())) + // BUMBLELION: Don't try taking a request until we get an entity token. We'd rather not lose events + // that were generated before an entity token was created (i.e. before a user was created). + if (PlayFabSettings::entityToken.empty() && + (settings->authenticationContext == nullptr || settings->authenticationContext->entityToken.empty())) + { + return false; + } + + switch (this->buffer.TryTake(request)) + { + case Result::Success: { - switch (this->buffer.TryTake(request)) + // add an event to batch + this->batch.push_back(std::move(request)); + + // if batch is full + if (this->batch.size() >= this->settings->maximalNumberOfItemsInBatch) { - case Result::Success: - { - // add an event to batch - this->batch.push_back(std::move(request)); - - // if batch is full - if (this->batch.size() >= this->settings->maximalNumberOfItemsInBatch) - { - this->SendBatch(batchCounter); - } - else if (this->batch.size() == 1) - { - // if it is the first event in an incomplete batch then set the batch creation moment - momentBatchStarted = clock::now(); - } - - continue; // immediately check if there is next event in buffer - } - break; - - case Result::Disabled: - case Result::Empty: - default: - break; + this->SendBatch(); } - - // if batch was started - if (this->batch.size() > 0) + else if (this->batch.size() == 1) { - // check if the batch wait time expired - std::chrono::seconds batchAge = std::chrono::duration_cast(clock::now() - momentBatchStarted); - if (batchAge.count() >= (int32_t)this->settings->maximalBatchWaitTime) - { - // batch wait time expired, send incomplete batch - this->SendBatch(batchCounter); - continue; // immediately check if there is next event in buffer - } + // if it is the first event in an incomplete batch then set the batch creation moment + momentBatchStarted = clock::now(); } + + return true; } - // event buffer is disabled or empty, and batch is not ready to be sent yet - // give some time back to CPU, don't starve it without a good reason - std::this_thread::sleep_for(std::chrono::milliseconds(this->settings->readBufferWaitTime)); + case Result::Disabled: + case Result::Empty: + default: + break; } - catch (const std::exception& ex) - { - LOG_PIPELINE("An exception was caught in PlayFabEventPipeline::WorkerThread method"); - this->isWorkerThreadRunning = false; - { // LOCK userCallbackMutex - std::unique_lock lock(userExceptionCallbackMutex); - if (userExceptionCallback) - { - userExceptionCallback(ex); - } - } // UNLOCK userCallbackMutex - } - catch(...) + // if batch was started + if (this->batch.size() > 0) { - LOG_PIPELINE("A non std::exception was caught in PlayFabEventPipeline::WorkerThread method"); + // check if the batch wait time expired + std::chrono::seconds batchAge = std::chrono::duration_cast(clock::now() - momentBatchStarted); + if (batchAge.count() >= (int32_t)this->settings->maximalBatchWaitTime) + { + // batch wait time expired, send incomplete batch + this->SendBatch(); + return true; + } } + + return false; + } + catch (const std::exception& ex) + { + LOG_PIPELINE("An exception was caught in PlayFabEventPipeline::WorkerThread method"); + this->isWorkerThreadRunning = false; + + { // LOCK userCallbackMutex + std::unique_lock lock(userExceptionCallbackMutex); + if (userExceptionCallback) + { + userExceptionCallback(ex); + } + } // UNLOCK userCallbackMutex + + return false; + } + catch(...) + { + LOG_PIPELINE("A non std::exception was caught in PlayFabEventPipeline::WorkerThread method"); + return false; } } - void PlayFabEventPipeline::SendBatch(size_t& batchCounter) + void PlayFabEventPipeline::SendBatch() { // create a WriteEvents API request to send the batch EventsModels::WriteEventsRequest batchReq; @@ -246,10 +277,10 @@ namespace PlayFabInternal batchReq.Events.push_back(playFabEmitRequest->event->eventContents); } + uintptr_t batchId = this->batchCounter.fetch_add(1); // add batch to flight tracking map - void* customData = reinterpret_cast(batchCounter); // used to track batches across asynchronous Events API + void* customData = reinterpret_cast(batchId); // used to track batches across asynchronous Events API this->batchesInFlight[customData] = std::move(this->batch); - batchCounter++; this->batch.clear(); // batch vector will be reused this->batch.reserve(this->settings->maximalNumberOfItemsInBatch); @@ -299,7 +330,7 @@ namespace PlayFabInternal playFabEmitEventResponse->playFabError = playFabError; playFabEmitEventResponse->writeEventsResponse = std::shared_ptr(new EventsModels::WriteEventsResponse(result)); playFabEmitEventResponse->batch = requestBatchPtr; - playFabEmitEventResponse->batchNumber = reinterpret_cast(customData); + playFabEmitEventResponse->batchNumber = static_cast(reinterpret_cast(customData)); // call an emit event callback CallbackRequest(playFabEmitRequest, std::move(playFabEmitEventResponse)); @@ -337,7 +368,7 @@ namespace PlayFabInternal playFabEmitEventResponse->emitEventResult = EmitEventResult::Success; playFabEmitEventResponse->playFabError = std::shared_ptr(new PlayFabError(error)); playFabEmitEventResponse->batch = requestBatchPtr; - playFabEmitEventResponse->batchNumber = reinterpret_cast(customData); + playFabEmitEventResponse->batchNumber = static_cast(reinterpret_cast(customData)); // call an emit event callback CallbackRequest(playFabEmitRequest, std::move(playFabEmitEventResponse)); diff --git a/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabEventRouter.cpp b/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabEventRouter.cpp index 25c6dc7..a41f784 100644 --- a/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabEventRouter.cpp +++ b/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabEventRouter.cpp @@ -11,10 +11,10 @@ namespace PlayFabInternal return this->pipelines; } - PlayFabEventRouter::PlayFabEventRouter() + PlayFabEventRouter::PlayFabEventRouter(bool threadedEventPipeline) { - this->pipelines.emplace(EventPipelineKey::PlayFabPlayStream, std::make_shared(std::make_shared(PlayFabEventPipelineType::PlayFabPlayStream))); - this->pipelines.emplace(EventPipelineKey::PlayFabTelemetry, std::make_shared(std::make_shared(PlayFabEventPipelineType::PlayFabTelemetry))); + this->pipelines.emplace(EventPipelineKey::PlayFabPlayStream, std::make_shared(std::make_shared(PlayFabEventPipelineType::PlayFabPlayStream, threadedEventPipeline))); + this->pipelines.emplace(EventPipelineKey::PlayFabTelemetry, std::make_shared(std::make_shared(PlayFabEventPipelineType::PlayFabTelemetry, threadedEventPipeline))); } void PlayFabEventRouter::RouteEvent(std::shared_ptr request) const @@ -54,6 +54,15 @@ namespace PlayFabInternal } } } + + // BUMBLELION: enable manual pumping of event pipeline + void PlayFabEventRouter::Update() + { + for (std::pair> pipeline : this->pipelines) + { + pipeline.second->Update(); + } + } } #endif \ No newline at end of file diff --git a/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabIXHR2HttpPlugin.cpp b/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabIXHR2HttpPlugin.cpp index 4c76398..2ec30c5 100644 --- a/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabIXHR2HttpPlugin.cpp +++ b/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabIXHR2HttpPlugin.cpp @@ -1,5 +1,7 @@ #include +#if defined(PLAYFAB_PLATFORM_XBOX) || defined(BUMBLELION_UWP) + #include #include @@ -150,7 +152,7 @@ namespace PlayFabInternal // Create request HttpRequest postEventRequest; - + // Setup headers std::vector headers; SetupRequestHeaders(reqContainer, headers); @@ -158,7 +160,7 @@ namespace PlayFabInternal // Setup url std::string urlString = reqContainer.GetFullUrl(); std::wstring url(urlString.begin(), urlString.end()); - + // Setup payload std::string payload = reqContainer.GetRequestBody(); @@ -272,3 +274,5 @@ namespace PlayFabInternal } } } + +#endif // defined(PLAYFAB_PLATFORM_XBOX) || defined(BUMBLELION_UWP) \ No newline at end of file diff --git a/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabIXHR2HttpRequest.cpp b/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabIXHR2HttpRequest.cpp index 114e9ae..326ed9c 100644 --- a/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabIXHR2HttpRequest.cpp +++ b/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabIXHR2HttpRequest.cpp @@ -11,6 +11,8 @@ #include +#if defined(PLAYFAB_PLATFORM_XBOX) || defined(BUMBLELION_UWP) + #include #include #include @@ -348,9 +350,9 @@ HttpRequest::HttpRequest() : return; } - // Specifies the HTTP stack should continuously call OnDataAvailable as data comes in with no threshold. + // Specifies the HTTP stack should continuously call OnDataAvailable as data comes in with no threshold. // For backwards compatibility this is the default behavior, but not the suggested setting. - //m_pXHR->SetProperty(XHR_PROP_ONDATA_THRESHOLD, XHR_PROP_ONDATA_ALWAYS); + //m_pXHR->SetProperty(XHR_PROP_ONDATA_THRESHOLD, XHR_PROP_ONDATA_ALWAYS); } // ---------------------------------------------------------------------------- @@ -388,7 +390,7 @@ HRESULT HttpRequest::Open(const std::wstring& verb, const std::wstring& url, con { return E_INVALIDARG; } - + HRESULT hr = E_FAIL; // Open a connection for an HTTP GET request. @@ -419,7 +421,8 @@ HRESULT HttpRequest::Open(const std::wstring& verb, const std::wstring& url, con // string following the standard header format "Header: value\r\n" to // be used with the token and signature retrieval API // - for (INT i = 0; i < headers.size(); i++) + // BUMBLELION: Change i from INT to UINT due to signed/unsigned mismatch. + for (UINT i = 0; i < headers.size(); i++) { std::wstring wstrHeaderName = headers[i].wstrHeaderName; std::wstring wstrHeaderValue = headers[i].wstrHeaderValue; @@ -511,7 +514,8 @@ HRESULT HttpRequest::Open(const std::wstring& verb, const std::wstring& url, con // string following the standard header format "Header: value\r\n" to // be used with the token and signature retrieval API // - for (INT i = 0; i < headers.size(); i++) + // BUMBLELION: Change i from INT to UINT due to signed/unsigned mismatch. + for (UINT i = 0; i < headers.size(); i++) { std::wstring wstrHeaderName = headers[i].wstrHeaderName; std::wstring wstrHeaderValue = headers[i].wstrHeaderValue; @@ -614,8 +618,8 @@ STDMETHODIMP RequestStream::Open(LPCSTR psBuffer, ULONG cbBufferSize) m_buffSize = cbBufferSize; m_buffSeekIndex = 0; - // Create a buffer to store a copy of the request (and include space for the null - // terminator, as generally this method can accept the result of strlen() for + // Create a buffer to store a copy of the request (and include space for the null + // terminator, as generally this method can accept the result of strlen() for // cbBufferSize). This buffer is deleted in the destructor. m_buffer = new (std::nothrow) char[cbBufferSize]; if (m_buffer == nullptr) @@ -819,4 +823,6 @@ HRESULT RequestStream::Invoke( return S_OK; } -// ---------------------------------------------------------------------------- \ No newline at end of file +// ---------------------------------------------------------------------------- + +#endif // defined(PLAYFAB_PLATFORM_XBOX) || defined(BUMBLELION_UWP) \ No newline at end of file diff --git a/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabPluginManager.cpp b/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabPluginManager.cpp index 91df380..304a90a 100644 --- a/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabPluginManager.cpp +++ b/iOS/ext/playfabxplatsdk/src/cppsdk/source/playfab/PlayFabPluginManager.cpp @@ -77,7 +77,7 @@ namespace PlayFabInternal std::shared_ptr PlayFabPluginManager::CreatePlayFabTransportPlugin() { -#ifdef PLAYFAB_PLATFORM_XBOX +#if defined(PLAYFAB_PLATFORM_XBOX) || defined(BUMBLELION_UWP) return std::make_shared(); #elif defined(PLAYFAB_PLATFORM_WINDOWS) return std::make_shared(); diff --git a/include/Party.h b/include/Party.h index 1cf4864..f5365cb 100644 --- a/include/Party.h +++ b/include/Party.h @@ -735,7 +735,6 @@ enum class PartyStateChangeType : uint32_t /// The PartyStateChange object should be cast to a /// object for more information. /// - /// ConfigureAudioManipulationVoiceStreamCompleted, /// @@ -746,7 +745,6 @@ enum class PartyStateChangeType : uint32_t /// The PartyStateChange object should be cast to a /// object for more information. /// - /// ConfigureAudioManipulationCaptureStreamCompleted, /// @@ -757,7 +755,6 @@ enum class PartyStateChangeType : uint32_t /// The PartyStateChange object should be cast to a /// object for more information. /// - /// ConfigureAudioManipulationRenderStreamCompleted, }; @@ -787,7 +784,7 @@ enum class PartyStateChangeResult InternetConnectivityError, /// - /// The operation failed because of an unexpected error in the Party Service. + /// The operation failed because of an unexpected error in the Party service. /// PartyServiceError, @@ -1560,11 +1557,6 @@ enum class PartyNetworkStatistic /// target endpoints at once via the transparent cloud relay server. Such messages will only be counted a single /// time rather than multiplied per target endpoint. /// - /// - /// Note that only local and remote endpoints that currently exist will be included when querying this statistic. - /// When endpoints that had timed out messages get destroyed between queries, this statistic might be perceived as - /// "going backward". - /// /// TimedOutSendMessages, @@ -1582,11 +1574,6 @@ enum class PartyNetworkStatistic /// target endpoints at once via the transparent cloud relay server. Such messages' bytes will only be counted a /// single time rather than multiplied per target endpoint. /// - /// - /// Note that only local and remote endpoints that currently exist will be included when querying this statistic. - /// When endpoints that had timed out messages get destroyed between queries, this statistic might be perceived as - /// "going backward". - /// /// TimedOutSendMessageBytes, @@ -1602,11 +1589,6 @@ enum class PartyNetworkStatistic /// target endpoints at once via the transparent cloud relay server. Such messages will only be counted a single /// time rather than multiplied per target endpoint. /// - /// - /// Note that only local and remote endpoints that currently exist will be included when querying this statistic. - /// When endpoints that had canceled messages get destroyed between queries, this statistic might be perceived as - /// "going backward". - /// /// CanceledSendMessages, @@ -1622,11 +1604,6 @@ enum class PartyNetworkStatistic /// target endpoints at once via the transparent cloud relay server. Such messages' bytes will only be counted a /// single time rather than multiplied per target endpoint. /// - /// - /// Note that only local and remote endpoints that currently exist will be included when querying this statistic. - /// When endpoints that had canceled messages get destroyed between queries, this statistic might be perceived as - /// "going backward". - /// /// CanceledSendMessageBytes, }; @@ -1820,24 +1797,86 @@ enum class PartyInvitationRevocability enum class PartyChatPermissionOptions { /// - /// No chat communication between the local PartyChatControl and the target PartyChatControl is allowed. + /// No chat communication between the local chat control and the target chat control is allowed. /// None = 0x0, /// - /// Audio communication from the local PartyChatControl to the target PartyChatControl is allowed. + /// Microphone audio communication from the local chat control to the target chat control is allowed. + /// + /// + /// For most game scenarios, either all or no audio communication should be sent and SendAudio should be used + /// instead. This flag can be used for game scenarios where microphone audio should be sent to the target chat + /// control, but other types of audio should not. + /// + /// Audio optionally submitted to a capture sink via + /// is treated as microphone audio when applying chat permissions and determining which chat controls should receive + /// the audio. + /// + /// + SendMicrophoneAudio = 0x1, + + /// + /// Text-to-speech audio communication from the local chat control to the target chat control is allowed. + /// + /// + /// For most game scenarios, either all or no audio communication should be sent and SendAudio should be used + /// instead. This flag can be used for game scenarios where text-to-speech audio should be sent to the target chat + /// control, but other types of audio should not. + /// + SendTextToSpeechAudio = 0x2, + + /// + /// All audio communication from the local chat control to the target chat control is allowed. /// - SendAudio = 0x1, + /// + /// This flag is equivalent to SendMicrophoneAudio | SendTextToSpeechAudio. + /// + SendAudio = SendMicrophoneAudio | SendTextToSpeechAudio, /// - /// Audio communication from the target PartyChatControl to the local PartyChatControl is allowed. + /// Microphone audio communication from the target chat control to the local chat control is allowed. /// - ReceiveAudio = 0x2, + /// + /// For most game scenarios, either all or no audio communication should be received and ReceiveAudio should + /// be used instead. This flag can be used for game scenarios where microphone audio should be received from the + /// target chat control, but other types of audio should not. + /// + /// Audio optionally submitted to a capture sink via + /// is treated as microphone audio when applying chat permissions and determining which chat controls should receive + /// the audio. + /// + /// + ReceiveMicrophoneAudio = 0x4, + + /// + /// Text-to-speech audio communication from the target chat control to the local chat control is allowed. + /// + /// + /// For most game scenarios, either all or no audio communication should be received and ReceiveAudio should + /// be used instead. This flag can be used for game scenarios where text-to-speech audio should be received from the + /// target chat control, but other types of audio should not. + /// + ReceiveTextToSpeechAudio = 0x8, + + /// + /// Audio communication from the target chat control to the local chat control is allowed. + /// + /// + /// This flag is equivalent to ReceiveMicrophoneAudio | ReceiveTextToSpeechAudio. + /// + ReceiveAudio = ReceiveMicrophoneAudio | ReceiveTextToSpeechAudio, /// - /// Text communication from the target PartyChatControl to the local PartyChatControl is allowed. + /// Text communication from the target chat control to the local chat control is allowed. /// - ReceiveText = 0x4, + /// + /// There is no permission associated with sending chat text because each call to requires + /// an explicit list of target chat controls. Including or omitting a target is equivalent to granting or denying + /// send permission for that text message. The target still must have set ReceiveText in their instance of + /// the library for it to actually be delivered. + /// + ReceiveText = 0x10, }; DEFINE_ENUM_FLAG_OPERATORS(PartyChatPermissionOptions); @@ -2054,6 +2093,31 @@ enum class PartyGender Male, }; +/// +/// Types of Party audio sources. +/// +enum class PartyAudioSourceType +{ + /// + /// A microphone audio source. + /// + /// + /// Audio captured from a microphone or recording device, configured via + /// , or optionally submitted to a capture sink, via + /// , is treated as microphone audio. + /// + Microphone, + + /// + /// A text-to-speech audio source. + /// + /// + /// Audio that is generated by is treated as + /// text-to-speech audio. + /// + TextToSpeech, +}; + /// /// Types of transcription phrases. /// @@ -2081,8 +2145,8 @@ enum class PartyGender /// /// /// If the associated transcription represents the audio generated by a call to -/// , the text string used to generated the audio will be -/// used the transcription. No post-processing, such as capitalization and punctuation, will be applied to the text. +/// , the transcription will match the text string used to +/// generate the audio. No post-processing, such as capitalization and punctuation, will be applied to the text. /// /// /// @@ -2140,15 +2204,20 @@ enum class PartyVoiceChatTranscriptionOptions /// Hypothesis phrases will be disabled. /// /// + /// This option is currently unimplemented. Using it with + /// will fail. + /// /// Use of hypothesis phrases is encouraged as a best practice to minimize the perceived latency of the /// transcription. If the phrases will not be used, however, they can be disabled in order to reduce the network /// bandwidth used to send the phrases to the local chat control. + /// /// /// This option will have no effect unless also combined with TranscribeSelf, /// TranscribeOtherChatControlsWithMatchingLanguages, and/or /// TranscribeOtherChatControlsWithNonMatchingLanguages. /// /// + /// DisableHypothesisPhrases = 0x8, /// @@ -2172,6 +2241,16 @@ enum class PartyVoiceChatTranscriptionOptions /// /// TranslateToLocalLanguage = 0x10, + + /// + /// Transcriptions will be provided without masking profanity. + /// + /// + /// By default, profanity is masked by replacing each character with an asterisk. For instance, a 4-letter profanity + /// is replaced with "****". When this option is enabled, no masking will be applied to profanity; the raw text will + /// be provided. + /// + DisableProfanityMasking = 0x20, }; DEFINE_ENUM_FLAG_OPERATORS(PartyVoiceChatTranscriptionOptions); @@ -2276,7 +2355,6 @@ enum class PartySynthesizeTextToSpeechType /// /// Types of Party audio samples. /// -/// enum class PartyAudioSampleType { /// @@ -2641,7 +2719,6 @@ struct PartyDataBuffer /// /// A data buffer that can be modified by the app. /// -/// struct PartyMutableDataBuffer { /// @@ -2697,6 +2774,13 @@ struct PartyTranslation /// The translation string. /// /// + /// By default, profanity masking is enabled and replaces each character of a profane word with an asterisk. + /// Profanity masking is applied after translating the unmasked source transcription. Depending on context, it's + /// possible for the transcription to contain masked profanity but not the translation. Similarly, it's possible for + /// the translation to contain masked profanity but not the original transcription. Profanity masking can be + /// disabled via + /// . + /// /// The translation string may be up to c_maxChatTextMessageLength characters long, not including the null /// terminator. Truncation occurs if the translated string length would exceed that limit, which can happen due to /// language differences even though the original string length is less than or equal to @@ -2704,6 +2788,7 @@ struct PartyTranslation /// . Truncation may occur at an arbitrary point in the /// UTF-8 byte sequence and may not result in a complete, valid character or word. Strings are always null /// terminated, even when truncated. + /// /// PartyString translation; }; @@ -2711,7 +2796,6 @@ struct PartyTranslation /// /// The format information needed to interpret Party audio data. /// -/// struct PartyAudioFormat { /// @@ -2750,13 +2834,16 @@ struct PartyAudioFormat /// The configuration information needed to set up an audio source stream. /// /// -/// struct PartyAudioManipulationSourceStreamConfiguration { /// - /// The format of the audio that should be produced by the source stream. + /// Optionally specifies the format of the audio that should be produced by the source stream. /// - PartyAudioFormat format; + /// + /// If this value is nullptr, the source stream will produce audio in the format most efficient for the library. The + /// format can be queried via . + /// + _Maybenull_ PartyAudioFormat * format; /// /// The maximum total size of audio buffers that can concurrently exist for this queue, in milliseconds. @@ -2781,13 +2868,16 @@ struct PartyAudioManipulationSourceStreamConfiguration /// /// /// -/// struct PartyAudioManipulationSinkStreamConfiguration { /// - /// The format of the audio that will be submitted to the sink stream. + /// Optionally specifies the format of the audio that will be submitted to the sink stream. /// - PartyAudioFormat format; + /// + /// If this value is nullptr, the sink stream will be configured to use the format most efficient for the library. + /// The format can be queried via . + /// + _Maybenull_ PartyAudioFormat * format; /// /// The maximum total size of audio buffers that can concurrently exist for this queue, in milliseconds. @@ -4170,6 +4260,16 @@ struct PartyVoiceChatTranscriptionReceivedStateChange : PartyStateChange /// _Field_size_(receiverChatControlCount) PartyLocalChatControlArray receiverChatControls; + /// + /// The type of audio source this transcription represents. + /// + /// + /// The audio source type can optionally be used for game scenarios in which transcriptions from different source + /// types should be treated differently. For instance, different iconography may be shown depending on whether the + /// transcription is associated with microphone or text-to-speech audio. + /// + PartyAudioSourceType sourceType; + /// /// The language code of the transcription. /// @@ -4770,7 +4870,6 @@ struct PartyPopulateAvailableTextToSpeechProfilesCompletedStateChange : PartySta /// Information specific to the ConfigureAudioManipulationVoiceStreamCompleted type of state change. /// /// -/// struct PartyConfigureAudioManipulationVoiceStreamCompletedStateChange : PartyStateChange { /// @@ -4811,7 +4910,6 @@ struct PartyConfigureAudioManipulationVoiceStreamCompletedStateChange : PartySta /// Information specific to the ConfigureAudioManipulationCaptureStreamCompleted type of state change. /// /// -/// struct PartyConfigureAudioManipulationCaptureStreamCompletedStateChange : PartyStateChange { /// @@ -4852,7 +4950,6 @@ struct PartyConfigureAudioManipulationCaptureStreamCompletedStateChange : PartyS /// Information specific to the ConfigureAudioManipulationRenderStreamCompleted type of state change. /// /// -/// struct PartyConfigureAudioManipulationRenderStreamCompletedStateChange : PartyStateChange { /// @@ -5206,7 +5303,7 @@ class PartyEndpoint /// PartyError GetUniqueIdentifier( _Out_ uint16_t * uniqueIdentifier - ) const; + ) const party_no_throw; /// /// Gets the value of a shared property. @@ -7115,37 +7212,62 @@ class PartyNetwork /// /// The management class for obtaining audio from an audio source stream. /// -/// class PartyAudioManipulationSourceStream { public: + /// + /// Retrieves the stream configuration. + /// + /// + /// The stream configuration matches the configuration provided to the call to + /// used to create this stream. + /// + /// + /// The stream configuration. + /// + /// + /// c_partyErrorSuccess if the call succeeded or an error code otherwise. The human-readable form of the + /// error code can be retrieved via . + /// + /// + PartyError GetConfiguration( + _Out_ PartyAudioManipulationSourceStreamConfiguration * configuration + ) const party_no_throw; + /// /// Retrieves the format of the buffers that will be provided by - /// . This is the format specified in the - /// configuration provided to . + /// . /// + /// + /// If a format is specified in the configuration provided by + /// , this format will match. Otherwise, this + /// format will be the most efficient format automatically selected by the library. + /// /// /// The format of the buffers that will be provided by - /// . + /// . /// /// /// c_partyErrorSuccess if the call succeeded or an error code otherwise. The human-readable form of the /// error code can be retrieved via . /// + /// + /// + /// PartyError GetFormat( _Out_ PartyAudioFormat * format ) const party_no_throw; /// /// Retrieves the total number of buffers available to retrieve from this stream via - /// . + /// . /// /// /// This can be useful if the caller prefers to send audio through their pipeline in batches of buffers. Because /// this buffer count is limited by the max audio queue size specified via - /// , callers should give their audio + /// , callers should give their audio /// processing pipeline ample time to process the buffers and return them to - /// to prevent dropped audio. + /// to prevent dropped audio. /// /// /// The output count of available buffers. @@ -7164,11 +7286,11 @@ class PartyAudioManipulationSourceStream /// /// When voice activity is detected, a new buffer will be available every 40 ms. Otherwise, no buffers will be /// available. Buffers retrieved by this method must be returned to the library via - /// when they are done being used. + /// when they are done being used. /// /// The total number of buffers instantaneously available can be retrieved via - /// . Multiple buffers can be retrieved in - /// succession before any are returned. + /// . Multiple buffers can be retrieved + /// in succession before any are returned. /// /// /// Each buffer will be in the format specified by . @@ -7196,7 +7318,7 @@ class PartyAudioManipulationSourceStream /// /// /// The buffer to return, which is the buffer field of a PartyDataBuffer previously retrieved from this - /// source stream . + /// source stream . /// /// /// c_partyErrorSuccess if the call succeeded or an error code otherwise. The human-readable form of the @@ -7262,18 +7384,41 @@ class PartyAudioManipulationSourceStream /// /// The management class for submitting audio to an audio sink stream. /// -/// class PartyAudioManipulationSinkStream { public: + /// + /// Retrieves the stream configuration. + /// + /// + /// The stream configuration matches the configuration provided to the configuration method used to create this + /// stream. + /// + /// + /// The stream configuration. + /// + /// + /// c_partyErrorSuccess if the call succeeded or an error code otherwise. The human-readable form of the + /// error code can be retrieved via . + /// + /// + /// + PartyError GetConfiguration( + _Out_ PartyAudioManipulationSinkStreamConfiguration * configuration + ) const party_no_throw; + /// /// Retrieves the format of the buffers that will be submitted to - /// . This is the format specified in the configuration - /// method used to set up this stream. + /// . /// + /// + /// If a format is specified in the configuration provided by + /// , this format will match. Otherwise, this + /// format will be the most efficient format automatically selected by the library. + /// /// /// The format of the buffers that will be submitted to - /// . + /// . /// /// /// c_partyErrorSuccess if the call succeeded or an error code otherwise. The human-readable form of the @@ -7281,6 +7426,8 @@ class PartyAudioManipulationSinkStream /// /// /// + /// + /// PartyError GetFormat( _Out_ PartyAudioFormat * format ) const party_no_throw; @@ -7295,16 +7442,21 @@ class PartyAudioManipulationSinkStream /// /// The buffer is copied to an allocated buffer before PartyAudioManipulationSinkStream::SubmitBuffer() returns and /// can be immediately freed afterwards. + /// + /// When applying chat permissions and determining which chat controls should receive audio, audio submitted to a + /// capture sink via this method is treated as microphone audio. + /// /// /// /// The audio buffer. Typically this audio buffer is generated by retrieving the next buffer available from each /// incoming source stream, and then processing and mixing each buffer based on game logic. This buffer must have - /// the format format specified by . + /// the format format specified by . /// /// /// c_partyErrorSuccess if the call succeeded or an error code otherwise. The human-readable form of the /// error code can be retrieved via . /// + /// PartyError SubmitBuffer( const PartyDataBuffer * buffer ) party_no_throw; @@ -7559,6 +7711,23 @@ class PartyChatControl /// this chat control can be queried via . /// Completion is indicated by a . /// + /// Platform support and supported formats + /// + /// This function is only supported on Windows and Xbox. Calls on other platforms will fail. + /// + /// + /// The following format options are supported. + /// + /// + /// `` | Format option | Supported value for local chat controls | Supported value for remote chat controls | + /// ` | --- | --- | --- | + /// ` | Samples per second | 24 kHz | 24 kHz | + /// ` | Channel mask | 0 | 0 | + /// ` | Channel count | 1 | 1 | + /// ` | Bits per sample | 32 | 16 | + /// ` | Sample type | PartyAudioSampleType::Float | PartyAudioSampleType::Integer | + /// ` | Interleaved | false | false | + /// /// /// /// The stream configuration. @@ -7571,7 +7740,6 @@ class PartyChatControl /// c_partyErrorSuccess if the call succeeded or an error code otherwise. The human-readable form of the /// error code can be retrieved via . /// - /// PartyError ConfigureAudioManipulationVoiceStream( _In_opt_ PartyAudioManipulationSourceStreamConfiguration * configuration, _In_opt_ void * asyncIdentifier @@ -7615,7 +7783,6 @@ class PartyChatControl /// c_partyErrorSuccess if the call succeeded or an error code otherwise. The human-readable form of the /// error code can be retrieved via . /// - /// PartyError GetAudioManipulationVoiceStream( _Outptr_ PartyAudioManipulationSourceStream ** sourceStream ) const party_no_throw; @@ -7730,6 +7897,10 @@ class PartyLocalChatControl : public PartyChatControl /// text string will be presented with the text as is. /// /// + /// The text string will only be delivered to target chat controls that have configured their own instances to + /// receive text messages from the local chat control via . + /// + /// /// If a target chat control is not connected to at least one network in common with the source chat control, the /// text string will not be delivered. This is possible if there are two local chat controls. A target chat control /// connected to one of the local chat controls may not be connected to all other local chat controls. @@ -8592,8 +8763,24 @@ class PartyLocalChatControl : public PartyChatControl /// /// Upon completion of the asynchronous operation, when a non-null configuration was specified, a capture stream for /// this chat control can be queried via . - /// Completion is indicated by a - /// . + /// Completion is indicated by a . + /// + /// Platform support and supported formats + /// + /// This function is only supported on Windows and Xbox. Calls on other platforms will fail. + /// + /// + /// The following format options are supported. + /// + /// + /// `` | Format option | Supported value | + /// ` | --- | --- | + /// ` | Samples per second | 24 kHz | + /// ` | Channel mask | 0 | + /// ` | Channel count | 1 | + /// ` | Bits per sample | 32 | + /// ` | Sample type | PartyAudioSampleType::Float | + /// ` | Interleaved | false | /// /// /// @@ -8607,7 +8794,6 @@ class PartyLocalChatControl : public PartyChatControl /// c_partyErrorSuccess if the call succeeded or an error code otherwise. The human-readable form of the /// error code can be retrieved via . /// - /// PartyError ConfigureAudioManipulationCaptureStream( _In_opt_ PartyAudioManipulationSinkStreamConfiguration * configuration, _In_opt_ void * asyncIdentifier @@ -8618,7 +8804,7 @@ class PartyLocalChatControl : public PartyChatControl /// /// /// This stream represents acts as the associated chat control's audio input, i.e. the audio that will be treated as - /// the local chat control's voice and sent to other chat controls . Typically, the app will retrieve audio from the + /// the local chat control's voice and sent to other chat controls. Typically, the app will retrieve audio from the /// voice manipulation stream stream via PartyAudioManipulationSourceStream::GetNextBuffer(), process the audio /// using app logic, and then submit the audio back to the library via this stream. /// @@ -8635,7 +8821,6 @@ class PartyLocalChatControl : public PartyChatControl /// c_partyErrorSuccess if the call succeeded or an error code otherwise. The human-readable form of the /// error code can be retrieved via . /// - /// PartyError GetAudioManipulationCaptureStream( _Outptr_ PartyAudioManipulationSinkStream ** stream ) const party_no_throw; @@ -8652,6 +8837,24 @@ class PartyLocalChatControl : public PartyChatControl /// this chat control can be queried via . /// Completion is indicated by a . /// + /// Platform support and supported formats + /// + /// This function is only supported on Windows and Xbox. Calls on other platforms will fail. + /// + /// + /// The following format options are supported. + /// + /// + /// `` | Format option | Supported value(s) | + /// ` | --- | --- | + /// ` | Samples per second | Any value between 8 kHz and 48 kHz, inclusive. | + /// ` | Channel mask | Any value | + /// ` | Channel count | Any value between 1 and 64, inclusive. | + /// ` | Bits per sample | If sample type is PartyAudioSampleType::Float, 32. If sample type is + /// PartyAudioSampleType::Integer, 16 or 32. | + /// ` | Sample type | PartyAudioSampleType::Float or PartyAudioSampleType::Integer | + /// ` | Interleaved | true or false | + /// /// /// /// The stream configuration. @@ -8664,7 +8867,6 @@ class PartyLocalChatControl : public PartyChatControl /// c_partyErrorSuccess if the call succeeded or an error code otherwise. The human-readable form of the /// error code can be retrieved via . /// - /// PartyError ConfigureAudioManipulationRenderStream( _In_opt_ PartyAudioManipulationSinkStreamConfiguration * configuration, _In_opt_ void * asyncIdentifier @@ -8687,7 +8889,6 @@ class PartyLocalChatControl : public PartyChatControl /// c_partyErrorSuccess if the call succeeded or an error code otherwise. The human-readable form of the /// error code can be retrieved via . /// - /// PartyError GetAudioManipulationRenderStream( _Outptr_ PartyAudioManipulationSinkStream ** stream ) const party_no_throw; @@ -8971,7 +9172,7 @@ class PartyManager static PartyError SerializeNetworkDescriptor( const PartyNetworkDescriptor * networkDescriptor, _Out_writes_z_(c_maxSerializedNetworkDescriptorStringLength + 1) char * serializedNetworkDescriptorString - ); + ) party_no_throw; /// /// Deserializes a network descriptor structure from an opaque string serialized via a prior call to @@ -8999,7 +9200,7 @@ class PartyManager static PartyError DeserializeNetworkDescriptor( PartyString serializedNetworkDescriptorString, _Out_ PartyNetworkDescriptor * networkDescriptor - ); + ) party_no_throw; /// /// Optionally configures the memory allocation and freeing callbacks the Party library should use. @@ -9347,9 +9548,9 @@ class PartyManager /// network, on behalf of the user represented by but does not connect the local /// device to the network. /// - /// If no devices connect to the network within ten minutes of the relay's creation, it will shutdown. The network + /// If no devices connect to the network within ten minutes of the relay's creation, it will shut down. The network /// will stay active indefinitely while at least one device is connected, migrating to a new relay if required. If - /// no devices are connected to the network, the relay will become inactive and shutdown after one minute of + /// no devices are connected to the network, the relay will become inactive and shut down after one minute of /// inactivity. /// /// @@ -9504,7 +9705,7 @@ class PartyManager /// asynchronous network operations, such as and /// . These asynchronous operations will be internally queued until the /// connection completes, at which point they will be processed. This will also be - /// provided on the resulting PartyConnectToNetworkCompletedStateChange where it will be fully-connected and + /// provided on the resulting PartyConnectToNetworkCompletedStateChange where it will be fully connected and /// associated with the provided . /// /// diff --git a/include/PartyImpl.h b/include/PartyImpl.h index f168b07..07181dc 100644 --- a/include/PartyImpl.h +++ b/include/PartyImpl.h @@ -220,7 +220,7 @@ PartyError PartyEndpoint::GetDevice( PartyError PartyEndpoint::GetUniqueIdentifier( _Out_ uint16_t * uniqueIdentifier - ) const + ) const party_no_throw { return PartyEndpointGetUniqueIdentifier( reinterpret_cast(this), @@ -1244,6 +1244,15 @@ PartyError PartyTextToSpeechProfile::SetCustomContext( customContext); } +PartyError PartyAudioManipulationSourceStream::GetConfiguration( + _Out_ PartyAudioManipulationSourceStreamConfiguration* configuration + ) const party_no_throw +{ + return PartyAudioManipulationSourceStreamGetConfiguration( + reinterpret_cast(this), + reinterpret_cast(configuration)); +} + PartyError PartyAudioManipulationSourceStream::GetFormat( _Out_ PartyAudioFormat* format ) const party_no_throw @@ -1298,6 +1307,15 @@ PartyError PartyAudioManipulationSourceStream::SetCustomContext( customContext); } +PartyError PartyAudioManipulationSinkStream::GetConfiguration( + _Out_ PartyAudioManipulationSinkStreamConfiguration* configuration + ) const party_no_throw +{ + return PartyAudioManipulationSinkStreamGetConfiguration( + reinterpret_cast(this), + reinterpret_cast(configuration)); +} + PartyError PartyAudioManipulationSinkStream::GetFormat( _Out_ PartyAudioFormat* format ) const party_no_throw @@ -1384,7 +1402,7 @@ PartyError PartyManager::GetErrorMessage( PartyError PartyManager::SerializeNetworkDescriptor( const PartyNetworkDescriptor * networkDescriptor, _Out_writes_z_(c_maxSerializedNetworkDescriptorStringLength + 1) char * serializedNetworkDescriptorString - ) + ) party_no_throw { return PartySerializeNetworkDescriptor( reinterpret_cast(networkDescriptor), @@ -1394,7 +1412,7 @@ PartyError PartyManager::SerializeNetworkDescriptor( PartyError PartyManager::DeserializeNetworkDescriptor( PartyString serializedNetworkDescriptorString, _Out_ PartyNetworkDescriptor * networkDescriptor - ) + ) party_no_throw { return PartyDeserializeNetworkDescriptor( serializedNetworkDescriptorString, @@ -1787,7 +1805,11 @@ PARTY_C_ASSERT(PARTY_INVITATION_REVOCABILITY_CREATOR == static_cast(Pa PARTY_C_ASSERT(PARTY_INVITATION_REVOCABILITY_ANYONE == static_cast(PartyInvitationRevocability::Anyone)); PARTY_C_ASSERT(PARTY_CHAT_PERMISSION_OPTIONS_NONE == static_cast(PartyChatPermissionOptions::None)); +PARTY_C_ASSERT(PARTY_CHAT_PERMISSION_OPTIONS_SEND_MICROPHONE_AUDIO == static_cast(PartyChatPermissionOptions::SendMicrophoneAudio)); +PARTY_C_ASSERT(PARTY_CHAT_PERMISSION_OPTIONS_SEND_TEXT_TO_SPEECH_AUDIO == static_cast(PartyChatPermissionOptions::SendTextToSpeechAudio)); PARTY_C_ASSERT(PARTY_CHAT_PERMISSION_OPTIONS_SEND_AUDIO == static_cast(PartyChatPermissionOptions::SendAudio)); +PARTY_C_ASSERT(PARTY_CHAT_PERMISSION_OPTIONS_RECEIVE_MICROPHONE_AUDIO == static_cast(PartyChatPermissionOptions::ReceiveMicrophoneAudio)); +PARTY_C_ASSERT(PARTY_CHAT_PERMISSION_OPTIONS_RECEIVE_TEXT_TO_SPEECH_AUDIO == static_cast(PartyChatPermissionOptions::ReceiveTextToSpeechAudio)); PARTY_C_ASSERT(PARTY_CHAT_PERMISSION_OPTIONS_RECEIVE_AUDIO == static_cast(PartyChatPermissionOptions::ReceiveAudio)); PARTY_C_ASSERT(PARTY_CHAT_PERMISSION_OPTIONS_RECEIVE_TEXT == static_cast(PartyChatPermissionOptions::ReceiveText)); @@ -1825,6 +1847,9 @@ PARTY_C_ASSERT(PARTY_GENDER_NEUTRAL == static_cast(PartyGender::Neutra PARTY_C_ASSERT(PARTY_GENDER_FEMALE == static_cast(PartyGender::Female)); PARTY_C_ASSERT(PARTY_GENDER_MALE == static_cast(PartyGender::Male)); +PARTY_C_ASSERT(PARTY_AUDIO_SOURCE_TYPE_MICROPHONE == static_cast(PartyAudioSourceType::Microphone)); +PARTY_C_ASSERT(PARTY_AUDIO_SOURCE_TYPE_TEXT_TO_SPEECH == static_cast(PartyAudioSourceType::TextToSpeech)); + PARTY_C_ASSERT(PARTY_VOICE_CHAT_TRANSCRIPTION_PHRASE_TYPE_HYPOTHESIS == static_cast(PartyVoiceChatTranscriptionPhraseType::Hypothesis)); PARTY_C_ASSERT(PARTY_VOICE_CHAT_TRANSCRIPTION_PHRASE_TYPE_FINAL == static_cast(PartyVoiceChatTranscriptionPhraseType::Final)); @@ -1834,6 +1859,7 @@ PARTY_C_ASSERT(PARTY_VOICE_CHAT_TRANSCRIPTION_OPTIONS_TRANSCRIBE_OTHER_CHAT_CONT PARTY_C_ASSERT(PARTY_VOICE_CHAT_TRANSCRIPTION_OPTIONS_TRANSCRIBE_OTHER_CHAT_CONTROLS_WITH_NON_MATCHING_LANGUAGES == static_cast(PartyVoiceChatTranscriptionOptions::TranscribeOtherChatControlsWithNonMatchingLanguages)); PARTY_C_ASSERT(PARTY_VOICE_CHAT_TRANSCRIPTION_OPTIONS_DISABLE_HYPOTHESIS_PHRASES == static_cast(PartyVoiceChatTranscriptionOptions::DisableHypothesisPhrases)); PARTY_C_ASSERT(PARTY_VOICE_CHAT_TRANSCRIPTION_OPTIONS_TRANSLATE_TO_LOCAL_LANGUAGE == static_cast(PartyVoiceChatTranscriptionOptions::TranslateToLocalLanguage)); +PARTY_C_ASSERT(PARTY_VOICE_CHAT_TRANSCRIPTION_OPTIONS_DISABLE_PROFANITY_MASKING == static_cast(PartyVoiceChatTranscriptionOptions::DisableProfanityMasking)); PARTY_C_ASSERT(PARTY_TEXT_CHAT_OPTIONS_NONE == static_cast(PartyTextChatOptions::None)); PARTY_C_ASSERT(PARTY_TEXT_CHAT_OPTIONS_TRANSLATE_TO_LOCAL_LANGUAGE == static_cast(PartyTextChatOptions::TranslateToLocalLanguage)); @@ -2349,6 +2375,8 @@ PARTY_C_ASSERT(sizeof(PARTY_VOICE_CHAT_TRANSCRIPTION_RECEIVED_STATE_CHANGE::rece PARTY_C_ASSERT(offsetof(PARTY_VOICE_CHAT_TRANSCRIPTION_RECEIVED_STATE_CHANGE, receiverChatControlCount) == offsetof(PartyVoiceChatTranscriptionReceivedStateChange, receiverChatControlCount)); PARTY_C_ASSERT(sizeof(PARTY_VOICE_CHAT_TRANSCRIPTION_RECEIVED_STATE_CHANGE::receiverChatControls) == sizeof(PartyVoiceChatTranscriptionReceivedStateChange::receiverChatControls)); PARTY_C_ASSERT(offsetof(PARTY_VOICE_CHAT_TRANSCRIPTION_RECEIVED_STATE_CHANGE, receiverChatControls) == offsetof(PartyVoiceChatTranscriptionReceivedStateChange, receiverChatControls)); +PARTY_C_ASSERT(sizeof(PARTY_VOICE_CHAT_TRANSCRIPTION_RECEIVED_STATE_CHANGE::sourceType) == sizeof(PartyVoiceChatTranscriptionReceivedStateChange::sourceType)); +PARTY_C_ASSERT(offsetof(PARTY_VOICE_CHAT_TRANSCRIPTION_RECEIVED_STATE_CHANGE, sourceType) == offsetof(PartyVoiceChatTranscriptionReceivedStateChange, sourceType)); PARTY_C_ASSERT(sizeof(PARTY_VOICE_CHAT_TRANSCRIPTION_RECEIVED_STATE_CHANGE::languageCode) == sizeof(PartyVoiceChatTranscriptionReceivedStateChange::languageCode)); PARTY_C_ASSERT(offsetof(PARTY_VOICE_CHAT_TRANSCRIPTION_RECEIVED_STATE_CHANGE, languageCode) == offsetof(PartyVoiceChatTranscriptionReceivedStateChange, languageCode)); PARTY_C_ASSERT(sizeof(PARTY_VOICE_CHAT_TRANSCRIPTION_RECEIVED_STATE_CHANGE::transcription) == sizeof(PartyVoiceChatTranscriptionReceivedStateChange::transcription)); diff --git a/include/Party_c.h b/include/Party_c.h index 2ad4345..71b88ad 100644 --- a/include/Party_c.h +++ b/include/Party_c.h @@ -255,9 +255,13 @@ typedef enum PARTY_INVITATION_REVOCABILITY typedef enum PARTY_CHAT_PERMISSION_OPTIONS { PARTY_CHAT_PERMISSION_OPTIONS_NONE = 0x0000, - PARTY_CHAT_PERMISSION_OPTIONS_SEND_AUDIO = 0x0001, - PARTY_CHAT_PERMISSION_OPTIONS_RECEIVE_AUDIO = 0x0002, - PARTY_CHAT_PERMISSION_OPTIONS_RECEIVE_TEXT = 0x0004, + PARTY_CHAT_PERMISSION_OPTIONS_SEND_MICROPHONE_AUDIO = 0x0001, + PARTY_CHAT_PERMISSION_OPTIONS_SEND_TEXT_TO_SPEECH_AUDIO = 0x0002, + PARTY_CHAT_PERMISSION_OPTIONS_SEND_AUDIO = 0x0003, + PARTY_CHAT_PERMISSION_OPTIONS_RECEIVE_MICROPHONE_AUDIO = 0x0004, + PARTY_CHAT_PERMISSION_OPTIONS_RECEIVE_TEXT_TO_SPEECH_AUDIO = 0x0008, + PARTY_CHAT_PERMISSION_OPTIONS_RECEIVE_AUDIO = 0x000c, + PARTY_CHAT_PERMISSION_OPTIONS_RECEIVE_TEXT = 0x0010, } PARTY_CHAT_PERMISSION_OPTIONS; typedef enum PARTY_AUDIO_DEVICE_SELECTION_TYPE @@ -312,6 +316,12 @@ typedef enum PARTY_GENDER PARTY_GENDER_MALE, } PARTY_GENDER; +typedef enum PARTY_AUDIO_SOURCE_TYPE +{ + PARTY_AUDIO_SOURCE_TYPE_MICROPHONE, + PARTY_AUDIO_SOURCE_TYPE_TEXT_TO_SPEECH, +} PARTY_AUDIO_SOURCE_TYPE; + typedef enum PARTY_VOICE_CHAT_TRANSCRIPTION_PHRASE_TYPE { PARTY_VOICE_CHAT_TRANSCRIPTION_PHRASE_TYPE_HYPOTHESIS, @@ -326,6 +336,7 @@ typedef enum PARTY_VOICE_CHAT_TRANSCRIPTION_OPTIONS PARTY_VOICE_CHAT_TRANSCRIPTION_OPTIONS_TRANSCRIBE_OTHER_CHAT_CONTROLS_WITH_NON_MATCHING_LANGUAGES = 0x0004, PARTY_VOICE_CHAT_TRANSCRIPTION_OPTIONS_DISABLE_HYPOTHESIS_PHRASES = 0x0008, PARTY_VOICE_CHAT_TRANSCRIPTION_OPTIONS_TRANSLATE_TO_LOCAL_LANGUAGE = 0x0010, + PARTY_VOICE_CHAT_TRANSCRIPTION_OPTIONS_DISABLE_PROFANITY_MASKING = 0x0020, } PARTY_VOICE_CHAT_TRANSCRIPTION_OPTIONS; typedef enum PARTY_TEXT_CHAT_OPTIONS @@ -434,13 +445,13 @@ typedef struct PARTY_AUDIO_FORMAT typedef struct PARTY_AUDIO_MANIPULATION_SOURCE_STREAM_CONFIGURATION { - PARTY_AUDIO_FORMAT format; + _Maybenull_ PARTY_AUDIO_FORMAT* format; uint32_t maxTotalAudioBufferSizeInMilliseconds; } PARTY_AUDIO_MANIPULATION_SOURCE_STREAM_CONFIGURATION; typedef struct PARTY_AUDIO_MANIPULATION_SINK_STREAM_CONFIGURATION { - PARTY_AUDIO_FORMAT format; + _Maybenull_ PARTY_AUDIO_FORMAT* format; uint32_t maxTotalAudioBufferSizeInMilliseconds; } PARTY_AUDIO_MANIPULATION_SINK_STREAM_CONFIGURATION; @@ -801,6 +812,7 @@ typedef struct PARTY_VOICE_CHAT_TRANSCRIPTION_RECEIVED_STATE_CHANGE PARTY_CHAT_CONTROL_HANDLE senderChatControl; uint32_t receiverChatControlCount; _Field_size_(receiverChatControlCount) PARTY_CHAT_CONTROL_HANDLE* receiverChatControls; + PARTY_AUDIO_SOURCE_TYPE sourceType; PartyString languageCode; PartyString transcription; PARTY_VOICE_CHAT_TRANSCRIPTION_PHRASE_TYPE type; @@ -1955,6 +1967,14 @@ PartyTextToSpeechProfileSetCustomContext( _In_opt_ void* customContext ); +PARTY_API_ATTRIBUTES +PartyError +PARTY_API +PartyAudioManipulationSourceStreamGetConfiguration( + PARTY_AUDIO_MANIPULATION_SOURCE_STREAM_HANDLE stream, + _Out_ PARTY_AUDIO_MANIPULATION_SOURCE_STREAM_CONFIGURATION * configuration + ); + PARTY_API_ATTRIBUTES PartyError PARTY_API @@ -2003,6 +2023,14 @@ PartyAudioManipulationSourceStreamSetCustomContext( _In_opt_ void* customContext ); +PARTY_API_ATTRIBUTES +PartyError +PARTY_API +PartyAudioManipulationSinkStreamGetConfiguration( + PARTY_AUDIO_MANIPULATION_SINK_STREAM_HANDLE stream, + _Out_ PARTY_AUDIO_MANIPULATION_SINK_STREAM_CONFIGURATION * configuration + ); + PARTY_API_ATTRIBUTES PartyError PARTY_API