diff options
| author | daoge_cmd <3523206925@qq.com> | 2026-03-01 12:16:08 +0800 |
|---|---|---|
| committer | daoge_cmd <3523206925@qq.com> | 2026-03-01 12:16:08 +0800 |
| commit | b691c43c44ff180d10e7d4a9afc83b98551ff586 (patch) | |
| tree | 3e9849222cbc6ba49f2f1fc6e5fe7179632c7390 /Minecraft.Client/Durango/Network/ChatIntegrationLayer.cpp | |
| parent | def8cb415354ac390b7e89052a50605285f1aca9 (diff) | |
Initial commit
Diffstat (limited to 'Minecraft.Client/Durango/Network/ChatIntegrationLayer.cpp')
| -rw-r--r-- | Minecraft.Client/Durango/Network/ChatIntegrationLayer.cpp | 805 |
1 files changed, 805 insertions, 0 deletions
diff --git a/Minecraft.Client/Durango/Network/ChatIntegrationLayer.cpp b/Minecraft.Client/Durango/Network/ChatIntegrationLayer.cpp new file mode 100644 index 00000000..232b4dd3 --- /dev/null +++ b/Minecraft.Client/Durango/Network/ChatIntegrationLayer.cpp @@ -0,0 +1,805 @@ +//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +//// PARTICULAR PURPOSE. +//// +//// Copyright (c) Microsoft Corporation. All rights reserved +#include "stdafx.h" +#include "ChatIntegrationLayer.h" +#include "DQRNetworkManager.h" +#include <robuffer.h> + +using namespace Windows::Foundation; +using namespace Windows::Xbox::System; + +// To integrate the Chat DLL in your game, you can use this ChatIntegrationLayer class with modifications, +// or create your own design your own class using the code in this file a guide. +std::shared_ptr<ChatIntegrationLayer> GetChatIntegrationLayer() +{ + static std::shared_ptr<ChatIntegrationLayer> chatIntegrationLayerInstance; + if (chatIntegrationLayerInstance == nullptr) + { + chatIntegrationLayerInstance.reset( new ChatIntegrationLayer() ); + } + + return chatIntegrationLayerInstance; +} + +ChatIntegrationLayer::ChatIntegrationLayer() +{ + ZeroMemory( m_chatVoicePacketsStatistic, sizeof(m_chatVoicePacketsStatistic) ); +} + +void ChatIntegrationLayer::InitializeChatManager( + __in bool combineCaptureBuffersIntoSinglePacket, + __in bool useKinectAsCaptureSource, + __in bool applySoundEffectsToCapturedAudio, + __in bool applySoundEffectsToChatRenderedAudio, + DQRNetworkManager *pDQRNet + ) +{ + m_pDQRNet = pDQRNet; + { + Concurrency::critical_section::scoped_lock lock(m_chatPacketStatsLock); + ZeroMemory( m_chatVoicePacketsStatistic, sizeof(m_chatVoicePacketsStatistic) ); + } + + m_chatManager = ref new Microsoft::Xbox::GameChat::ChatManager(); + m_chatManager->ChatSettings->DiagnosticsTraceLevel = Microsoft::Xbox::GameChat::GameChatDiagnosticsTraceLevel::Verbose; + + // Optionally, change the default settings below as desired by commenting out and editing any of the following lines + // Otherwise these defaults are used. + // + // m_chatManager = ref new Microsoft::Xbox::GameChat::ChatManager( ChatSessionPeriod::ChatPeriodOf40Milliseconds ); + // m_chatManager->ChatSettings->AudioThreadPeriodInMilliseconds = 40; + // m_chatManager->ChatSettings->AudioThreadAffinityMask = XAUDIO2_DEFAULT_PROCESSOR; // <- this means is core 5, same as the default XAudio2 core + // m_chatManager->ChatSettings->AudioThreadPriority = THREAD_PRIORITY_TIME_CRITICAL; + // m_chatManager->ChatSettings->AudioEncodingQuality = Windows::Xbox::Chat::EncodingQuality::Normal; + // m_chatManager->ChatSettings->DiagnosticsTraceLevel = Microsoft::Xbox::GameChat::GameChatDiagnosticsTraceLevel::Verbose; + m_chatManager->ChatSettings->CombineCaptureBuffersIntoSinglePacket = combineCaptureBuffersIntoSinglePacket; // if unset, it defaults to TRUE + m_chatManager->ChatSettings->UseKinectAsCaptureSource = useKinectAsCaptureSource; // if unset, it defaults to FALSE + m_chatManager->ChatSettings->PreEncodeCallbackEnabled = applySoundEffectsToCapturedAudio; // if unset, it defaults to FALSE + m_chatManager->ChatSettings->PostDecodeCallbackEnabled = applySoundEffectsToChatRenderedAudio; // if unset, it defaults to FALSE + + InitializeCriticalSection(&m_csAddedUsers); + + std::weak_ptr<ChatIntegrationLayer> weakPtrToThis = shared_from_this(); + +#ifdef PROFILE + m_chatManager->ChatSettings->PerformanceCountersEnabled = true; +#endif + + // Upon enter constrained mode, mute everyone. + // Upon leaving constrained mode, unmute everyone who was previously muted. + m_tokenResourceAvailabilityChanged = Windows::ApplicationModel::Core::CoreApplication::ResourceAvailabilityChanged += + ref new EventHandler< Platform::Object^ >( [weakPtrToThis] (Platform::Object^, Platform::Object^ ) + { + // Using a std::weak_ptr instead of 'this' to avoid dangling pointer if caller class is released. + // Simply unregistering the callback in the destructor isn't enough to prevent a dangling pointer + std::shared_ptr<ChatIntegrationLayer> sharedPtrToThis(weakPtrToThis.lock()); + if( sharedPtrToThis != nullptr ) + { + if (Windows::ApplicationModel::Core::CoreApplication::ResourceAvailability == Windows::ApplicationModel::Core::ResourceAvailability::Constrained) + { + if( sharedPtrToThis->m_chatManager != nullptr ) + { + sharedPtrToThis->m_chatManager->MuteAllUsersFromAllChannels(); + } + } + else if(Windows::ApplicationModel::Core::CoreApplication::ResourceAvailability == Windows::ApplicationModel::Core::ResourceAvailability::Full) + { + if( sharedPtrToThis->m_chatManager != nullptr ) + { + sharedPtrToThis->m_chatManager->UnmuteAllUsersFromAllChannels(); + + // The title should remember who was muted so when the Resume even occurs + // to avoid unmuting users who has been previously muted. Simply re-mute them here + } + } + } + }); + + m_tokenOnDebugMessage = m_chatManager->OnDebugMessage += + ref new Windows::Foundation::EventHandler<Microsoft::Xbox::GameChat::DebugMessageEventArgs^>( + [weakPtrToThis] ( Platform::Object^, Microsoft::Xbox::GameChat::DebugMessageEventArgs^ args ) + { + // Using a std::weak_ptr instead of 'this' to avoid dangling pointer if caller class is released. + // Simply unregistering the callback in the destructor isn't enough to prevent a dangling pointer + std::shared_ptr<ChatIntegrationLayer> sharedPtrToThis(weakPtrToThis.lock()); + if( sharedPtrToThis != nullptr ) + { + sharedPtrToThis->OnDebugMessageReceived(args); + } + }); + + m_tokenOnOutgoingChatPacketReady = m_chatManager->OnOutgoingChatPacketReady += + ref new Windows::Foundation::EventHandler<Microsoft::Xbox::GameChat::ChatPacketEventArgs^>( + [weakPtrToThis] ( Platform::Object^, Microsoft::Xbox::GameChat::ChatPacketEventArgs^ args ) + { + // Using a std::weak_ptr instead of 'this' to avoid dangling pointer if caller class is released. + // Simply unregistering the callback in the destructor isn't enough to prevent a dangling pointer + std::shared_ptr<ChatIntegrationLayer> sharedPtrToThis(weakPtrToThis.lock()); + if( sharedPtrToThis != nullptr ) + { + sharedPtrToThis->OnOutgoingChatPacketReady(args); + } + }); + + m_tokenOnCompareUniqueConsoleIdentifiers = m_chatManager->OnCompareUniqueConsoleIdentifiers += + ref new Microsoft::Xbox::GameChat::CompareUniqueConsoleIdentifiersHandler( + [weakPtrToThis] ( Platform::Object^ obj1, Platform::Object^ obj2 ) + { + // Using a std::weak_ptr instead of 'this' to avoid dangling pointer if caller class is released. + // Simply unregistering the callback in the destructor isn't enough to prevent a dangling pointer + std::shared_ptr<ChatIntegrationLayer> sharedPtrToThis(weakPtrToThis.lock()); + if( sharedPtrToThis != nullptr ) + { + return sharedPtrToThis->CompareUniqueConsoleIdentifiers(obj1, obj2); + } + else + { + return false; + } + }); + + m_tokenUserAudioDeviceAdded = WXS::User::AudioDeviceAdded += + ref new Windows::Foundation::EventHandler<WXS::AudioDeviceAddedEventArgs^>( + [weakPtrToThis] ( Platform::Object^, WXS::AudioDeviceAddedEventArgs^ value ) + { + std::shared_ptr<ChatIntegrationLayer> sharedPtrToThis(weakPtrToThis.lock()); + if( sharedPtrToThis != nullptr ) + { + sharedPtrToThis->EvaluateDevicesForUser(value->User); + } + }); + + m_tokenUserAudioDeviceRemoved = WXS::User::AudioDeviceRemoved += + ref new Windows::Foundation::EventHandler<WXS::AudioDeviceRemovedEventArgs^>( + [weakPtrToThis] ( Platform::Object^, WXS::AudioDeviceRemovedEventArgs^ value ) + { + std::shared_ptr<ChatIntegrationLayer> sharedPtrToThis(weakPtrToThis.lock()); + if( sharedPtrToThis != nullptr ) + { + sharedPtrToThis->EvaluateDevicesForUser(value->User); + } + }); + + m_tokenUserAudioDeviceChanged = WXS::User::AudioDeviceChanged += + ref new Windows::Foundation::EventHandler<WXS::AudioDeviceChangedEventArgs^>( + [weakPtrToThis] ( Platform::Object^, WXS::AudioDeviceChangedEventArgs^ value ) + { + std::shared_ptr<ChatIntegrationLayer> sharedPtrToThis(weakPtrToThis.lock()); + if( sharedPtrToThis != nullptr ) + { + sharedPtrToThis->EvaluateDevicesForUser(value->User); + } + }); + + //m_tokenSuspending = Windows::ApplicationModel::Core::CoreApplication::Suspending += + // ref new EventHandler< Windows::ApplicationModel::SuspendingEventArgs^ >( [weakPtrToThis] (Platform::Object^, Windows::ApplicationModel::SuspendingEventArgs^ args) + //{ + // // Upon Suspending, nothing needs to be done + //}); + + //m_tokenResuming = Windows::ApplicationModel::Core::CoreApplication::Resuming += + // ref new EventHandler< Platform::Object^ >( [weakPtrToThis] (Platform::Object^, Windows::ApplicationModel::SuspendingEventArgs^ args) + //{ + // // Upon Resuming, re-initialize the network, and reinitialize the chat session + //}); +} + + +void ChatIntegrationLayer::Shutdown() +{ + if( m_chatManager != nullptr ) + { + m_chatManager->OnDebugMessage -= m_tokenOnDebugMessage; + m_chatManager->OnOutgoingChatPacketReady -= m_tokenOnOutgoingChatPacketReady; + m_chatManager->OnCompareUniqueConsoleIdentifiers -= m_tokenOnCompareUniqueConsoleIdentifiers; + Windows::ApplicationModel::Core::CoreApplication::ResourceAvailabilityChanged -= m_tokenResourceAvailabilityChanged; + if( m_chatManager->ChatSettings->PreEncodeCallbackEnabled ) + { + m_chatManager->OnPreEncodeAudioBuffer -= m_tokenOnPreEncodeAudioBuffer; + } + if( m_chatManager->ChatSettings->PostDecodeCallbackEnabled ) + { + m_chatManager->OnPostDecodeAudioBuffer -= m_tokenOnPostDecodeAudioBuffer; + } + WXS::User::AudioDeviceAdded -= m_tokenUserAudioDeviceAdded; + WXS::User::AudioDeviceRemoved -= m_tokenUserAudioDeviceRemoved; + WXS::User::AudioDeviceChanged -= m_tokenUserAudioDeviceChanged; + + DeleteCriticalSection(&m_csAddedUsers); + + m_chatManager = nullptr; + } +} + +void ChatIntegrationLayer::OnDebugMessageReceived( + __in Microsoft::Xbox::GameChat::DebugMessageEventArgs^ args + ) +{ + // To integrate the Chat DLL in your game, + // change this to false and remove the LogComment calls, + // or integrate with your game's own UI/debug message logging system + bool outputToUI = false; + + if( outputToUI ) + { + if (args->ErrorCode == S_OK ) + { + m_pDQRNet->LogComment(L"GameChat: " + args->Message); + } + else + { + m_pDQRNet->LogCommentWithError(L"GameChat: " + args->Message, args->ErrorCode); + } + } + else + { + // The string appear in the Visual Studio Output window +#ifndef _CONTENT_PACKAGE + OutputDebugString( args->Message->Data() ); +#endif + } +} + +void ChatIntegrationLayer::GameUI_RecordPacketStatistic( + __in Microsoft::Xbox::GameChat::ChatMessageType messageType, + __in ChatPacketType chatPacketType + ) +{ + uint32 messageTypeInt = static_cast<uint32>(messageType); + if( messageType > Microsoft::Xbox::GameChat::ChatMessageType::InvalidMessage ) + { + return; + } + + { + Concurrency::critical_section::scoped_lock lock(m_chatPacketStatsLock); + m_chatVoicePacketsStatistic[static_cast<int>(chatPacketType)][messageTypeInt]++; + } +} + +int ChatIntegrationLayer::GameUI_GetPacketStatistic( + __in Microsoft::Xbox::GameChat::ChatMessageType messageType, + __in ChatPacketType chatPacketType + ) +{ + uint32 messageTypeInt = static_cast<uint32>(messageType); + if( messageType > Microsoft::Xbox::GameChat::ChatMessageType::InvalidMessage ) + { + return 0; + } + + { + Concurrency::critical_section::scoped_lock lock(m_chatPacketStatsLock); + return m_chatVoicePacketsStatistic[static_cast<int>(chatPacketType)][messageTypeInt]; + } +} + +void ChatIntegrationLayer::OnOutgoingChatPacketReady( + __in Microsoft::Xbox::GameChat::ChatPacketEventArgs^ args + ) +{ + byte *bytes; + int byteCount; + + GetBufferBytes(args->PacketBuffer, &bytes); + byteCount = args->PacketBuffer->Length; + unsigned int address = 0; + if( !args->SendPacketToAllConnectedConsoles ) + { + address = safe_cast<unsigned int>(args->UniqueTargetConsoleIdentifier); + } + m_pDQRNet->SendBytesChat(address, bytes, byteCount, args->SendReliable, args->SendInOrder, args->SendPacketToAllConnectedConsoles); + + GameUI_RecordPacketStatistic( args->ChatMessageType, ChatPacketType::OutgoingPacket ); + +} + +void ChatIntegrationLayer::OnIncomingChatMessage( + unsigned int sessionAddress, + Platform::Array<byte>^ message + ) +{ + // To integrate the Chat DLL in your game, change the following code to use your game's network layer. + // Ignore the OnChatMessageReceived event as that is specific to this sample's simple network layer. + // Instead your title should upon receiving a packet, extract the chat message from it, and then call m_chatManager->ProcessIncomingChatMessage as shown below + // You will need to isolate chat messages to be unique from the rest of you game's other message types. + + // uniqueRemoteConsoleIdentifier is a Platform::Object^ and can be cast or unboxed to most types. + // What exactly you use doesn't matter, but optimally it would be something that uniquely identifies a console on in the session. + // A Windows::Xbox::Networking::SecureDeviceAssociation^ is perfect to use if you have access to it. + + // This is how you would convert from byte array to a IBuffer^ + // + // Windows::Storage::Streams::IBuffer^ destBuffer = ref new Windows::Storage::Streams::Buffer( sourceByteBufferSize ); + // byte* destBufferBytes = nullptr; + // GetBufferBytes( destBuffer, &destBufferBytes ); + // errno_t err = memcpy_s( destBufferBytes, destBuffer->Capacity, sourceByteBuffer, sourceByteBufferSize ); + // THROW_HR_IF(err != 0, E_FAIL); + // destBuffer->Length = sourceByteBufferSize; + + // This is how you would convert from an int to a Platform::Object^ + // Platform::Object obj = IntToPlatformObject(5); + + Windows::Storage::Streams::IBuffer^ chatMessage = ArrayToBuffer(message); + Platform::Object^ uniqueRemoteConsoleIdentifier = (Platform::Object^)sessionAddress; + + if( m_chatManager != nullptr ) + { + Microsoft::Xbox::GameChat::ChatMessageType chatMessageType = m_chatManager->ProcessIncomingChatMessage(chatMessage, uniqueRemoteConsoleIdentifier); + + GameUI_RecordPacketStatistic( chatMessageType, ChatPacketType::IncomingPacket ); + } +} + +// Only add people who intend to play. +void ChatIntegrationLayer::AddAllLocallySignedInUsersToChatClient( + __in uint8 channelIndex, + __in Windows::Foundation::Collections::IVectorView<Windows::Xbox::System::User^>^ locallySignedInUsers + ) +{ + // To integrate the Chat DLL in your game, + // add all locally signed in users to the chat client + for each( Windows::Xbox::System::User^ user in locallySignedInUsers ) + { + if( user != nullptr ) + { +// LogComment(L"Adding Local User to Chat Client"); + AddLocalUserToChatChannel( channelIndex, user ); + } + } +} + +ChatIntegrationLayer::AddedUser::AddedUser(Windows::Xbox::System::IUser^ user, bool canCaptureAudio) +{ + m_user = user; + m_canCaptureAudio = canCaptureAudio; +} + + +void ChatIntegrationLayer::AddLocalUser( __in Windows::Xbox::System::IUser^ user ) +{ + // Check we haven't added already + for( int i = 0; i < m_addedUsers.size(); i++ ) + { + if( m_addedUsers[i]->m_user->XboxUserId == user->XboxUserId ) + { + return; + } + } + + bool kinectAvailable = false; + Windows::Kinect::KinectSensor^ sensor = Windows::Kinect::KinectSensor::GetDefault(); + if( sensor ) + { + sensor->Open(); + if( sensor->IsAvailable ) + { + kinectAvailable = true; + m_pDQRNet->LogComment(L"Evaluated that kinect is available\n"); + } + sensor->Close(); + } + + EnterCriticalSection(&m_csAddedUsers); + // First establish whether we have an appropriate audio device at this time + bool canCaptureAudio = false; + for each( WXS::IAudioDeviceInfo^ audioDevice in user->AudioDevices ) + { + m_pDQRNet->LogComment(L"Evaluating device " + audioDevice->DeviceCategory.ToString() + L" " + + audioDevice->DeviceType.ToString() + L" " + + audioDevice->Id + L" " + + audioDevice->Sharing.ToString() + L" " + + audioDevice->IsMicrophoneMuted.ToString() + L"\n"); + + // Consider shared devices only if kinect is actually available - every machine seems to claim a shared device whether kinect is attached or not + if( ( audioDevice->DeviceType == WXS::AudioDeviceType::Capture ) && ( kinectAvailable || ( audioDevice->Sharing != WXS::AudioDeviceSharing::Shared) ) ) + { + canCaptureAudio = true; + } + } + + // If we can capture audio initially, then register with the chat session. Otherwise we'll reevaluate this situation when audio devices change + if( canCaptureAudio ) + { + AddLocalUserToChatChannel( 0 , user ); + } + + // Add to vector of users that we are tracking in the chat system + m_addedUsers.push_back(new AddedUser(user, canCaptureAudio)); + LeaveCriticalSection(&m_csAddedUsers); +} + +// Remove from our list of tracked users, if the user is already there +void ChatIntegrationLayer::RemoveLocalUser( __in Windows::Xbox::System::IUser^ user ) +{ + EnterCriticalSection(&m_csAddedUsers); + for( auto it = m_addedUsers.begin(); it != m_addedUsers.end(); it++ ) + { + if( (*it)->m_user->XboxUserId == user->XboxUserId ) + { + delete (*it); + m_addedUsers.erase(it); + LeaveCriticalSection(&m_csAddedUsers); + return; + } + } + LeaveCriticalSection(&m_csAddedUsers); +} + +// This is called when the audio devices for a user change in any way, and establishes whether we can now capture audio. Any change in this status from before will cause the user to be added/removed from the chat session. +void ChatIntegrationLayer::EvaluateDevicesForUser(__in Windows::Xbox::System::IUser^ user ) +{ + bool kinectAvailable = false; + Windows::Kinect::KinectSensor^ sensor = Windows::Kinect::KinectSensor::GetDefault(); + if( sensor ) + { + sensor->Open(); + if( sensor->IsAvailable ) + { + kinectAvailable = true; + m_pDQRNet->LogComment(L"Evaluated that kinect is available\n"); + } + sensor->Close(); + } + + EnterCriticalSection(&m_csAddedUsers); + for( int i = 0; i < m_addedUsers.size(); i++ ) + { + AddedUser *addedUser = m_addedUsers[i]; + if( addedUser->m_user->XboxUserId == user->XboxUserId ) + { + bool canCaptureAudio = false; + for each( WXS::IAudioDeviceInfo^ audioDevice in addedUser->m_user->AudioDevices ) + { + // Consider shared devices only if kinect is actually available - every machine seems to claim a shared device whether kinect is attached or not + if( ( audioDevice->DeviceType == WXS::AudioDeviceType::Capture ) && ( kinectAvailable || ( audioDevice->Sharing != WXS::AudioDeviceSharing::Shared) ) ) + { + canCaptureAudio = true; + break; + } + } + if( canCaptureAudio != addedUser->m_canCaptureAudio ) + { + if( canCaptureAudio ) + { + AddLocalUserToChatChannel(0, addedUser->m_user ); + } + else + { + RemoveUserFromChatChannel(0, addedUser->m_user ); + } + addedUser->m_canCaptureAudio = canCaptureAudio; + LeaveCriticalSection(&m_csAddedUsers); + return; + } + } + } + LeaveCriticalSection(&m_csAddedUsers); +} + +void ChatIntegrationLayer::AddLocalUserToChatChannel( + __in uint8 channelIndex, + __in Windows::Xbox::System::IUser^ user + ) +{ + // Adds a local user to a specific channel. + + // This is helper function waits for the task to cm_chatManageromplete so shouldn't be called from the UI thread + // Remove the .wait() and return the result of concurrency::create_task() if you want to call it from the UI thread + // and chain PPL tasks together + + m_pDQRNet->LogComment( L">>>>>>>>>>>>> AddLocalUserToChatChannel" ); + if( m_chatManager != nullptr ) + { + auto asyncOp = m_chatManager->AddLocalUserToChatChannelAsync( channelIndex, user ); + concurrency::create_task( asyncOp ) + .then( [this] ( concurrency::task<void> t ) + { + // Error handling + try + { + t.get(); + } + catch ( Platform::Exception^ ex ) + { + m_pDQRNet->LogCommentWithError( L"AddLocalUserToChatChannelAsync failed", ex->HResult ); + } + }) + .wait(); + } +} + +void ChatIntegrationLayer::RemoveRemoteConsole( + unsigned int address + ) +{ + // uniqueConsoleIdentifier is a Platform::Object^ and can be cast or unboxed to most types. + // What exactly you use doesn't matter, but optimally it would be something that uniquely identifies a console on in the session. + // A Windows::Xbox::Networking::SecureDeviceAssociation^ is perfect to use if you have access to it. + + // This is how you would convert from an int to a Platform::Object^ + // Platform::Object obj = IntToPlatformObject(5); + + // This is helper function waits for the task to complete so shouldn't be called from the UI thread + // Remove the .wait() and return the result of concurrency::create_task() if you want to call it from the UI thread + // and chain PPL tasks together + + Platform::Object^ uniqueRemoteConsoleIdentifier = (Platform::Object^)address; + + if( m_chatManager != nullptr ) + { + auto asyncOp = m_chatManager->RemoveRemoteConsoleAsync( uniqueRemoteConsoleIdentifier ); + concurrency::create_task( asyncOp ).then( [this] ( concurrency::task<void> t ) + { + // Error handling + try + { + t.get(); + } + catch ( Platform::Exception^ ex ) + { + m_pDQRNet->LogCommentWithError( L"RemoveRemoteConsoleAsync failed", ex->HResult ); + } + }) + .wait(); + } +} + +void ChatIntegrationLayer::RemoveUserFromChatChannel( + __in uint8 channelIndex, + __in Windows::Xbox::System::IUser^ user + ) +{ + if( m_chatManager != nullptr ) + { + // This is helper function waits for the task to complete so shouldn't be called from the UI thread + // Remove the .wait() and return the result of concurrency::create_task() if you want to call it from the UI thread + // and chain PPL tasks together + + auto asyncOp = m_chatManager->RemoveLocalUserFromChatChannelAsync( channelIndex, user ); + concurrency::create_task( asyncOp ).then( [this] ( concurrency::task<void> t ) + { + // Error handling + try + { + t.get(); + } + catch ( Platform::Exception^ ex ) + { + m_pDQRNet->LogCommentWithError( L"RemoveLocalUserFromChatChannelAsync failed", ex->HResult ); + } + }) + .wait(); + } +} + +void ChatIntegrationLayer::OnNewSessionAddressAdded( + __in unsigned int address + ) +{ + m_pDQRNet->LogCommentFormat( L">>>>>>>>>>>>> OnNewSessionAddressAdded (%d)",address ); + Platform::Object^ uniqueConsoleIdentifier = (Platform::Object^)address; + /// Call this when a new console connects. + /// This adds this console to the chat layer + if( m_chatManager != nullptr ) + { + m_chatManager->HandleNewRemoteConsole(uniqueConsoleIdentifier ); + } +} + +Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ ChatIntegrationLayer::GetChatUsers() +{ + if( m_chatManager != nullptr ) + { + return m_chatManager->GetChatUsers(); + } + + return nullptr; +} + +bool ChatIntegrationLayer::HasMicFocus() +{ + if( m_chatManager != nullptr ) + { + return m_chatManager->HasMicFocus; + } + + return false; +} + +Platform::Object^ ChatIntegrationLayer::IntToPlatformObject( + __in int val + ) +{ + return (Platform::Object^)val; + + // You can also do the same using a PropertyValue. + //return Windows::Foundation::PropertyValue::CreateInt32(val); +} + +int ChatIntegrationLayer::PlatformObjectToInt( + __in Platform::Object^ uniqueRemoteConsoleIdentifier + ) +{ + return safe_cast<int>( uniqueRemoteConsoleIdentifier ); + + // You can also do the same using a PropertyValue. + //return safe_cast<Windows::Foundation::IPropertyValue^>(uniqueRemoteConsoleIdentifier)->GetInt32(); +} + +void ChatIntegrationLayer::HandleChatChannelChanged( + __in uint8 oldChatChannelIndex, + __in uint8 newChatChannelIndex, + __in Microsoft::Xbox::GameChat::ChatUser^ chatUser + ) +{ + // We remember if the local user was currently muted from all channels. And when we switch channels, + // we ensure that the state persists. For remote users, title should implement this themselves + // based on title game design if they want to persist the muting state. + + bool wasUserMuted = false; + IUser^ userBeingRemoved = nullptr; + + if (chatUser != nullptr && chatUser->IsLocal) + { + wasUserMuted = chatUser->IsMuted; + userBeingRemoved = chatUser->User; + if (userBeingRemoved != nullptr) + { + RemoveUserFromChatChannel(oldChatChannelIndex, userBeingRemoved); + AddLocalUserToChatChannel(newChatChannelIndex, userBeingRemoved); + } + } + + // If the local user was muted earlier, get the latest chat users and mute him again on the newly added channel. + if (wasUserMuted && userBeingRemoved != nullptr) + { + auto chatUsers = GetChatUsers(); + if (chatUsers != nullptr ) + { + for (UINT chatUserIndex = 0; chatUserIndex < chatUsers->Size; chatUserIndex++) + { + Microsoft::Xbox::GameChat::ChatUser^ chatUser = chatUsers->GetAt(chatUserIndex); + if( chatUser != nullptr && (chatUser->XboxUserId == userBeingRemoved->XboxUserId) ) + { + m_chatManager->MuteUserFromAllChannels(chatUser); + break; + } + } + } + } +} + +void ChatIntegrationLayer::ChangeChatUserMuteState( + __in Microsoft::Xbox::GameChat::ChatUser^ chatUser + ) +{ + /// Helper function to swap the mute state of a specific chat user + if( m_chatManager != nullptr && chatUser != nullptr) + { + if (chatUser->IsMuted) + { + m_chatManager->UnmuteUserFromAllChannels(chatUser); + } + else + { + m_chatManager->MuteUserFromAllChannels(chatUser); + } + } +} + +Microsoft::Xbox::GameChat::ChatUser^ ChatIntegrationLayer::GetChatUserByXboxUserId( + __in Platform::String^ xboxUserId + ) +{ + Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ chatUsers = GetChatUsers(); + for each (Microsoft::Xbox::GameChat::ChatUser^ chatUser in chatUsers) + { + if (chatUser != nullptr && ( chatUser->XboxUserId == xboxUserId ) ) + { + return chatUser; + } + } + + return nullptr; +} + + +bool ChatIntegrationLayer::CompareUniqueConsoleIdentifiers( + __in Platform::Object^ uniqueRemoteConsoleIdentifier1, + __in Platform::Object^ uniqueRemoteConsoleIdentifier2 + ) +{ + if (uniqueRemoteConsoleIdentifier1 == nullptr || uniqueRemoteConsoleIdentifier2 == nullptr) + { + return false; + } + + // uniqueRemoteConsoleIdentifier is a Platform::Object^ and can be cast or unboxed to most types. + // We're using XRNS addresses, which are unsigned ints + unsigned int address1 = safe_cast<unsigned int>(uniqueRemoteConsoleIdentifier1); + unsigned int address2 = safe_cast<unsigned int>(uniqueRemoteConsoleIdentifier2); + return address1 == address2; +} + +Microsoft::Xbox::GameChat::ChatPerformanceCounters^ ChatIntegrationLayer::GetChatPerformanceCounters() +{ + if( m_chatManager != nullptr && + m_chatManager->ChatSettings != nullptr && + m_chatManager->ChatSettings->PerformanceCountersEnabled ) + { + return m_chatManager->ChatPerformanceCounters; + } + + return nullptr; +} + +void ChatIntegrationLayer::OnControllerPairingChanged( Windows::Xbox::Input::ControllerPairingChangedEventArgs^ args ) +{ +#if 0 + auto controller = args->Controller; + if ( controller ) + { + if ( controller->Type == L"Windows.Xbox.Input.Gamepad" ) + { + // Either add the user or sign one in + User^ user = args->User; + if ( user != nullptr ) + { + g_sampleInstance->GetLoggingUI()->LogCommentToUI("OnControllerPairingChanged: " + user->DisplayInfo->Gamertag); + AddLocalUserToChatChannel( + g_sampleInstance->GetUISelectionChatChannelIndex(), + user + ); + } + } + } +#endif +} + +void ChatIntegrationLayer::ToggleRenderTargetVolume() +{ + // Simple toggle logic to just show usage of LocalRenderTargetVolume property + static bool makeRenderTargetVolumeQuiet = false; + makeRenderTargetVolumeQuiet = !makeRenderTargetVolumeQuiet; + + auto chatUsers = GetChatUsers(); + if (chatUsers != nullptr ) + { + for (UINT chatUserIndex = 0; chatUserIndex < chatUsers->Size; chatUserIndex++) + { + Microsoft::Xbox::GameChat::ChatUser^ chatUser = chatUsers->GetAt(chatUserIndex); + if( chatUser != nullptr && chatUser->IsLocal ) + { + chatUser->LocalRenderTargetVolume = ( makeRenderTargetVolumeQuiet ) ? 0.1f : 1.0f; + } + } + } +} + + +void ChatIntegrationLayer::GetBufferBytes( __in Windows::Storage::Streams::IBuffer^ buffer, __out byte** ppOut ) +{ + if ( ppOut == nullptr || buffer == nullptr ) + { + throw ref new Platform::InvalidArgumentException(); + } + + *ppOut = nullptr; + + Microsoft::WRL::ComPtr<IInspectable> srcBufferInspectable(reinterpret_cast<IInspectable*>( buffer )); + Microsoft::WRL::ComPtr<Windows::Storage::Streams::IBufferByteAccess> srcBufferByteAccess; + srcBufferInspectable.As(&srcBufferByteAccess); + srcBufferByteAccess->Buffer(ppOut); +} + +Windows::Storage::Streams::IBuffer^ ChatIntegrationLayer::ArrayToBuffer( __in Platform::Array<byte>^ array ) +{ + Windows::Storage::Streams::DataWriter^ writer = ref new Windows::Storage::Streams::DataWriter(); + writer->WriteBytes(array); + return writer->DetachBuffer(); +}
\ No newline at end of file |
