2026-03-01 12:16:08 +08:00
//// 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 ;
2026-03-02 15:58:20 +07:00
// To integrate the Chat DLL in your game, you can use this ChatIntegrationLayer class with modifications,
2026-03-01 12:16:08 +08:00
// 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 ;
}
2026-03-02 15:58:20 +07:00
ChatIntegrationLayer : : ChatIntegrationLayer ( )
2026-03-01 12:16:08 +08:00
{
ZeroMemory ( m_chatVoicePacketsStatistic , sizeof ( m_chatVoicePacketsStatistic ) ) ;
}
2026-03-02 15:58:20 +07:00
void ChatIntegrationLayer : : InitializeChatManager (
2026-03-01 12:16:08 +08:00
__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
2026-03-02 15:58:20 +07:00
// Upon enter constrained mode, mute everyone.
2026-03-01 12:16:08 +08:00
// Upon leaving constrained mode, unmute everyone who was previously muted.
2026-03-02 15:58:20 +07:00
m_tokenResourceAvailabilityChanged = Windows : : ApplicationModel : : Core : : CoreApplication : : ResourceAvailabilityChanged + =
2026-03-01 12:16:08 +08:00
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
}
}
}
} ) ;
2026-03-02 15:58:20 +07:00
m_tokenOnDebugMessage = m_chatManager - > OnDebugMessage + =
2026-03-01 12:16:08 +08:00
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 ) ;
}
} ) ;
2026-03-02 15:58:20 +07:00
m_tokenOnOutgoingChatPacketReady = m_chatManager - > OnOutgoingChatPacketReady + =
ref new Windows : : Foundation : : EventHandler < Microsoft : : Xbox : : GameChat : : ChatPacketEventArgs ^ > (
2026-03-01 12:16:08 +08:00
[ 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 ) ;
}
} ) ;
2026-03-02 15:58:20 +07:00
m_tokenOnCompareUniqueConsoleIdentifiers = m_chatManager - > OnCompareUniqueConsoleIdentifiers + =
ref new Microsoft : : Xbox : : GameChat : : CompareUniqueConsoleIdentifiersHandler (
[ weakPtrToThis ] ( Platform : : Object ^ obj1 , Platform : : Object ^ obj2 )
{
2026-03-01 12:16:08 +08:00
// 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 )
{
2026-03-02 15:58:20 +07:00
return sharedPtrToThis - > CompareUniqueConsoleIdentifiers ( obj1 , obj2 ) ;
2026-03-01 12:16:08 +08:00
}
else
{
return false ;
}
} ) ;
2026-03-02 15:58:20 +07:00
m_tokenUserAudioDeviceAdded = WXS : : User : : AudioDeviceAdded + =
2026-03-01 12:16:08 +08:00
ref new Windows : : Foundation : : EventHandler < WXS : : AudioDeviceAddedEventArgs ^ > (
2026-03-02 15:58:20 +07:00
[ weakPtrToThis ] ( Platform : : Object ^ , WXS : : AudioDeviceAddedEventArgs ^ value )
{
2026-03-01 12:16:08 +08:00
std : : shared_ptr < ChatIntegrationLayer > sharedPtrToThis ( weakPtrToThis . lock ( ) ) ;
if ( sharedPtrToThis ! = nullptr )
{
sharedPtrToThis - > EvaluateDevicesForUser ( value - > User ) ;
}
} ) ;
2026-03-02 15:58:20 +07:00
m_tokenUserAudioDeviceRemoved = WXS : : User : : AudioDeviceRemoved + =
2026-03-01 12:16:08 +08:00
ref new Windows : : Foundation : : EventHandler < WXS : : AudioDeviceRemovedEventArgs ^ > (
2026-03-02 15:58:20 +07:00
[ weakPtrToThis ] ( Platform : : Object ^ , WXS : : AudioDeviceRemovedEventArgs ^ value )
{
2026-03-01 12:16:08 +08:00
std : : shared_ptr < ChatIntegrationLayer > sharedPtrToThis ( weakPtrToThis . lock ( ) ) ;
if ( sharedPtrToThis ! = nullptr )
{
sharedPtrToThis - > EvaluateDevicesForUser ( value - > User ) ;
}
} ) ;
2026-03-02 15:58:20 +07:00
m_tokenUserAudioDeviceChanged = WXS : : User : : AudioDeviceChanged + =
2026-03-01 12:16:08 +08:00
ref new Windows : : Foundation : : EventHandler < WXS : : AudioDeviceChangedEventArgs ^ > (
2026-03-02 15:58:20 +07:00
[ weakPtrToThis ] ( Platform : : Object ^ , WXS : : AudioDeviceChangedEventArgs ^ value )
{
2026-03-01 12:16:08 +08:00
std : : shared_ptr < ChatIntegrationLayer > sharedPtrToThis ( weakPtrToThis . lock ( ) ) ;
if ( sharedPtrToThis ! = nullptr )
{
sharedPtrToThis - > EvaluateDevicesForUser ( value - > User ) ;
}
} ) ;
2026-03-02 15:58:20 +07:00
//m_tokenSuspending = Windows::ApplicationModel::Core::CoreApplication::Suspending +=
2026-03-01 12:16:08 +08:00
// ref new EventHandler< Windows::ApplicationModel::SuspendingEventArgs^ >( [weakPtrToThis] (Platform::Object^, Windows::ApplicationModel::SuspendingEventArgs^ args)
//{
2026-03-02 15:58:20 +07:00
// // Upon Suspending, nothing needs to be done
2026-03-01 12:16:08 +08:00
//});
2026-03-02 15:58:20 +07:00
//m_tokenResuming = Windows::ApplicationModel::Core::CoreApplication::Resuming +=
2026-03-01 12:16:08 +08:00
// ref new EventHandler< Platform::Object^ >( [weakPtrToThis] (Platform::Object^, Windows::ApplicationModel::SuspendingEventArgs^ args)
//{
2026-03-02 15:58:20 +07:00
// // Upon Resuming, re-initialize the network, and reinitialize the chat session
2026-03-01 12:16:08 +08:00
//});
}
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 ;
}
}
2026-03-02 15:58:20 +07:00
void ChatIntegrationLayer : : OnDebugMessageReceived (
__in Microsoft : : Xbox : : GameChat : : DebugMessageEventArgs ^ args
2026-03-01 12:16:08 +08:00
)
{
2026-03-02 15:58:20 +07:00
// To integrate the Chat DLL in your game,
// change this to false and remove the LogComment calls,
2026-03-01 12:16:08 +08:00
// or integrate with your game's own UI/debug message logging system
2026-03-02 15:58:20 +07:00
bool outputToUI = false ;
2026-03-01 12:16:08 +08:00
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
{
2026-03-02 15:58:20 +07:00
// The string appear in the Visual Studio Output window
2026-03-01 12:16:08 +08:00
# ifndef _CONTENT_PACKAGE
OutputDebugString ( args - > Message - > Data ( ) ) ;
# endif
}
}
2026-03-02 15:58:20 +07:00
void ChatIntegrationLayer : : GameUI_RecordPacketStatistic (
2026-03-01 12:16:08 +08:00
__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 ] + + ;
}
}
2026-03-02 15:58:20 +07:00
int ChatIntegrationLayer : : GameUI_GetPacketStatistic (
2026-03-01 12:16:08 +08:00
__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 ] ;
}
}
2026-03-02 15:58:20 +07:00
void ChatIntegrationLayer : : OnOutgoingChatPacketReady (
__in Microsoft : : Xbox : : GameChat : : ChatPacketEventArgs ^ args
2026-03-01 12:16:08 +08:00
)
{
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 ) ;
}
2026-03-02 15:58:20 +07:00
void ChatIntegrationLayer : : OnIncomingChatMessage (
2026-03-01 12:16:08 +08:00
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.
2026-03-02 15:58:20 +07:00
// 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.
2026-03-01 12:16:08 +08:00
// 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 ) ;
2026-03-02 15:58:20 +07:00
GameUI_RecordPacketStatistic ( chatMessageType , ChatPacketType : : IncomingPacket ) ;
2026-03-01 12:16:08 +08:00
}
}
// Only add people who intend to play.
void ChatIntegrationLayer : : AddAllLocallySignedInUsersToChatClient (
__in uint8 channelIndex ,
__in Windows : : Foundation : : Collections : : IVectorView < Windows : : Xbox : : System : : User ^ > ^ locallySignedInUsers
)
{
2026-03-02 15:58:20 +07:00
// To integrate the Chat DLL in your game,
2026-03-01 12:16:08 +08:00
// 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
)
{
2026-03-02 15:58:20 +07:00
// Adds a local user to a specific channel.
2026-03-01 12:16:08 +08:00
2026-03-02 15:58:20 +07:00
// 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
2026-03-01 12:16:08 +08:00
// 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
{
2026-03-02 15:58:20 +07:00
t . get ( ) ;
2026-03-01 12:16:08 +08:00
}
catch ( Platform : : Exception ^ ex )
{
m_pDQRNet - > LogCommentWithError ( L " AddLocalUserToChatChannelAsync failed " , ex - > HResult ) ;
}
} )
. wait ( ) ;
}
}
2026-03-02 15:58:20 +07:00
void ChatIntegrationLayer : : RemoveRemoteConsole (
2026-03-01 12:16:08 +08:00
unsigned int address
)
{
2026-03-02 15:58:20 +07:00
// 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.
2026-03-01 12:16:08 +08:00
// 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);
2026-03-02 15:58:20 +07:00
// 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
2026-03-01 12:16:08 +08:00
// 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
{
2026-03-02 15:58:20 +07:00
t . get ( ) ;
2026-03-01 12:16:08 +08:00
}
catch ( Platform : : Exception ^ ex )
{
m_pDQRNet - > LogCommentWithError ( L " RemoveRemoteConsoleAsync failed " , ex - > HResult ) ;
}
} )
. wait ( ) ;
}
}
2026-03-02 15:58:20 +07:00
void ChatIntegrationLayer : : RemoveUserFromChatChannel (
2026-03-01 12:16:08 +08:00
__in uint8 channelIndex ,
2026-03-02 15:58:20 +07:00
__in Windows : : Xbox : : System : : IUser ^ user
2026-03-01 12:16:08 +08:00
)
{
if ( m_chatManager ! = nullptr )
{
2026-03-02 15:58:20 +07:00
// 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
2026-03-01 12:16:08 +08:00
// 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 ( ) ;
}
}
2026-03-02 15:58:20 +07:00
void ChatIntegrationLayer : : OnNewSessionAddressAdded (
2026-03-01 12:16:08 +08:00
__in unsigned int address
)
{
m_pDQRNet - > LogCommentFormat ( L " >>>>>>>>>>>>> OnNewSessionAddressAdded (%d) " , address ) ;
Platform : : Object ^ uniqueConsoleIdentifier = ( Platform : : Object ^ ) address ;
2026-03-02 15:58:20 +07:00
/// Call this when a new console connects.
2026-03-01 12:16:08 +08:00
/// 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 ;
}
2026-03-02 15:58:20 +07:00
Platform : : Object ^ ChatIntegrationLayer : : IntToPlatformObject (
__in int val
2026-03-01 12:16:08 +08:00
)
{
return ( Platform : : Object ^ ) val ;
// You can also do the same using a PropertyValue.
//return Windows::Foundation::PropertyValue::CreateInt32(val);
}
2026-03-02 15:58:20 +07:00
int ChatIntegrationLayer : : PlatformObjectToInt (
__in Platform : : Object ^ uniqueRemoteConsoleIdentifier
2026-03-01 12:16:08 +08:00
)
{
2026-03-02 15:58:20 +07:00
return safe_cast < int > ( uniqueRemoteConsoleIdentifier ) ;
2026-03-01 12:16:08 +08:00
// You can also do the same using a PropertyValue.
//return safe_cast<Windows::Foundation::IPropertyValue^>(uniqueRemoteConsoleIdentifier)->GetInt32();
}
2026-03-02 15:58:20 +07:00
void ChatIntegrationLayer : : HandleChatChannelChanged (
__in uint8 oldChatChannelIndex ,
__in uint8 newChatChannelIndex ,
__in Microsoft : : Xbox : : GameChat : : ChatUser ^ chatUser
2026-03-01 12:16:08 +08:00
)
{
2026-03-02 15:58:20 +07:00
// We remember if the local user was currently muted from all channels. And when we switch channels,
2026-03-01 12:16:08 +08:00
// 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 ;
}
}
}
}
}
2026-03-02 15:58:20 +07:00
void ChatIntegrationLayer : : ChangeChatUserMuteState (
__in Microsoft : : Xbox : : GameChat : : ChatUser ^ chatUser
2026-03-01 12:16:08 +08:00
)
{
/// 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 ) ;
}
}
}
2026-03-02 15:58:20 +07:00
Microsoft : : Xbox : : GameChat : : ChatUser ^ ChatIntegrationLayer : : GetChatUserByXboxUserId (
__in Platform : : String ^ xboxUserId
2026-03-01 12:16:08 +08:00
)
{
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 ;
}
}
2026-03-02 15:58:20 +07:00
2026-03-01 12:16:08 +08:00
return nullptr ;
}
2026-03-02 15:58:20 +07:00
bool ChatIntegrationLayer : : CompareUniqueConsoleIdentifiers (
__in Platform : : Object ^ uniqueRemoteConsoleIdentifier1 ,
__in Platform : : Object ^ uniqueRemoteConsoleIdentifier2
2026-03-01 12:16:08 +08:00
)
{
if ( uniqueRemoteConsoleIdentifier1 = = nullptr | | uniqueRemoteConsoleIdentifier2 = = nullptr )
{
return false ;
}
2026-03-02 15:58:20 +07:00
// uniqueRemoteConsoleIdentifier is a Platform::Object^ and can be cast or unboxed to most types.
2026-03-01 12:16:08 +08:00
// 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 ( ) ;
}