aboutsummaryrefslogtreecommitdiff
path: root/Minecraft.Client/Durango/Network/ChatIntegrationLayer.cpp
diff options
context:
space:
mode:
authordaoge_cmd <3523206925@qq.com>2026-03-01 12:16:08 +0800
committerdaoge_cmd <3523206925@qq.com>2026-03-01 12:16:08 +0800
commitb691c43c44ff180d10e7d4a9afc83b98551ff586 (patch)
tree3e9849222cbc6ba49f2f1fc6e5fe7179632c7390 /Minecraft.Client/Durango/Network/ChatIntegrationLayer.cpp
parentdef8cb415354ac390b7e89052a50605285f1aca9 (diff)
Initial commit
Diffstat (limited to 'Minecraft.Client/Durango/Network/ChatIntegrationLayer.cpp')
-rw-r--r--Minecraft.Client/Durango/Network/ChatIntegrationLayer.cpp805
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