//// 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 "pch.h" #include "Thread.h" #include "ChatManagerEvents.h" #include "ChatAudioThread.h" #include "ChatClient.h" #include "Clock.h" #include "StringUtils.h" #include "BufferUtils.h" #include "ChatManagerEvents.h" #include "ChatManager.h" #include #if TV_API using namespace Windows::Xbox::Chat; using namespace Windows::Xbox::System; using namespace Windows::Foundation; using namespace Windows::Foundation::Collections; using namespace Platform; using namespace Concurrency; using namespace Microsoft::WRL::Details; namespace Microsoft { namespace Xbox { namespace GameChat { ChatClient::ChatClient( _In_ uint32 chatPeriodInMilliseconds, _In_ ChatManager^ chatManager, _In_ ChatManagerSettings^ chatManagerSettings ) : m_chatManager(chatManager), m_chatManagerSettings( chatManagerSettings ) { m_userIdsToUserData = std::map(); m_audioCaptureSourceIdToUserId = std::map>(); TimeSpan chatPeriod; chatPeriod.Duration = chatPeriodInMilliseconds * 10000; // milliseconds to hundred nanoseconds m_chatSession = ref new ChatSession(chatPeriod, chatManagerSettings->AudioThreadAffinityMask, ChatFeatures::Default); // Hook up the state changed handler Platform::WeakReference wr(this); m_tokenStateChangedEvent = m_chatSession->StateChangedEvent += ref new ChatSessionStateChangedHandler( [ wr ]( IChatSession^ chatSession, ChatSessionStateChangeReason reason ) { ChatClient^ chatClient = wr.Resolve(); if( chatClient != nullptr ) { chatClient->ChatSessionStateChanged(chatSession, reason); } }); } ChatClient::~ChatClient() { CHAT_LOG_INFO_MSG(L"ChatClient::~ChatClient"); if( m_chatSession != nullptr ) { m_chatSession->StateChangedEvent -= m_tokenStateChangedEvent; } } bool ChatClient::DoesChannelContainUser( _In_ Windows::Foundation::Collections::IVector^ participants, _In_ IUser^ user ) { for ( UINT i = 0; i < participants->Size; ++i ) { if( StringUtils::IsStringEqualCaseInsenstive(participants->GetAt( i )->User->XboxUserId, user->XboxUserId) ) { return true; } } return false; } void ChatClient::LogCommentFormat( _In_ LPCWSTR strMsg, ... ) { va_list args; va_start(args, strMsg); LogComment(StringUtils::GetStringFormat(strMsg, args)); va_end(args); } Microsoft::Xbox::GameChat::ChatUser^ ChatClient::AddUserToChatChannel( _In_ uint8 channelIndex, _In_ IUser^ user, _In_opt_ Platform::Object^ uniqueConsoleIdentifier, _In_ bool isLocal, _In_opt_ Windows::Xbox::Chat::IChatParticipant^ chatParticipantToUpdate ) { CHAT_THROW_INVALIDARGUMENT_IF_NULL( user ); CHAT_THROW_INVALIDARGUMENT_IF( channelIndex >= NUMBER_OF_CHAT_CHANNELS ); Microsoft::Xbox::GameChat::ChatUser^ chatUserAddedOrUpdated = nullptr; { Concurrency::critical_section::scoped_lock lock(m_chatSessionLock); // If the channel index doesn't exist, add 0-channelIndex channels first. int channelsNeeded = channelIndex + 1; int channelsExisting = m_chatSession->Channels->Size; int channelsToAdd = 0; if (channelsExisting < channelsNeeded) { channelsToAdd = abs(channelsExisting - channelsNeeded); } for (int i = 0; i < channelsToAdd; i++) { m_chatSession->Channels->Append( ref new ChatChannel() ); } IChatChannel^ channel = m_chatSession->Channels->GetAt(channelIndex); CHAT_THROW_HR_IF( channel == nullptr, E_UNEXPECTED ); // We must have a channel for channelIndex since we just created it above if( chatParticipantToUpdate != nullptr ) { // If the audio devices don't match, that means the remote user packet contained an updated user // and we should remove the user from the chat channel and add the user back bool removeFromAllChannels = false; RemoveUserFromChatChannelHelper(channel, chatParticipantToUpdate->User, channelIndex, removeFromAllChannels); } // Check for an existing ChatUser to use Windows::Foundation::Collections::IVectorView^ chatUsers = GetChatUsers(); for each (Microsoft::Xbox::GameChat::ChatUser^ chatUser in chatUsers) { if (StringUtils::IsStringEqualCaseInsenstive(chatUser->User->XboxUserId, user->XboxUserId) ) { chatUserAddedOrUpdated = chatUser; break; } } // Verify the user isn't already in the channel to avoid adding the user twice if( false == DoesChannelContainUser( channel->Participants, user ) ) { // Add a ChatParticipant to the channel Windows::Xbox::Chat::IChatParticipant^ chatParticipant = ref new ChatParticipant( user ); channel->Participants->Append( chatParticipant ); if( chatUserAddedOrUpdated != nullptr ) { // If the ChatUser already exists, then append the channel to his channel list. chatUserAddedOrUpdated->AddChannelIndexToChannelList(channelIndex); if( m_chatManagerSettings->IsAtDiagnosticsTraceLevel(GameChatDiagnosticsTraceLevel::Info) ) { LogCommentFormat( L"AddUserToChatChannel: AddChannelIndexToChannelList: XUID: %s chatUser: 0x%0.8x", user->XboxUserId->Data(), chatUserAddedOrUpdated ); } } else { // No pre-existing ChatUser found, so create one chatUserAddedOrUpdated = AddUserToUserDataMap(user, channelIndex, isLocal, uniqueConsoleIdentifier, chatParticipant); if( m_chatManagerSettings->IsAtDiagnosticsTraceLevel(GameChatDiagnosticsTraceLevel::Info) ) { LogCommentFormat( L"AddUserToChatChannel: AddUserToAudioDevicesMap: XUID: %s UserId: 0x%0.8x chatUser: 0x%0.8x", user->XboxUserId->Data(), user->Id, chatUserAddedOrUpdated ); } } } } // The user may now have a new audio device, so link all of this user's audio devices to the userId AddUserToAudioDevicesMap(user); UpdateSessionState(); return chatUserAddedOrUpdated; } uint8 ChatClient::GetLocalNameOfRemoteConsole( _In_ Platform::Object^ consoleIdentifier ) { // Note that the local console will not be added to this list bool matchFound = false; uint8 localName = 0; Concurrency::critical_section::scoped_lock lock(m_localNameOfRemoteConsolesLock); ChatManager^ chatManager = m_chatManager.Resolve(); if( chatManager != nullptr ) { for each (std::shared_ptr uniqueConsole in m_localNameOfRemoteConsoles) { if( chatManager->OnCompareUniqueConsoleIdentifiersHandler(uniqueConsole->uniqueConsoleIdentifier, consoleIdentifier) ) { matchFound = true; localName = uniqueConsole->consoleName; break; } } } if( !matchFound ) { std::shared_ptr consoleNameIdentifierPair(new ConsoleNameIdentifierPair()); consoleNameIdentifierPair->uniqueConsoleIdentifier = consoleIdentifier; bool success = CallerLock_GetUnusedLocalNameOfRemoteConsole(localName); if( success ) { consoleNameIdentifierPair->consoleName = localName; m_localNameOfRemoteConsoles.push_back( consoleNameIdentifierPair ); } } return localName; } std::vector< std::shared_ptr > ChatClient::GetMyLocalNameOfRemoteConsolesCopy() { Concurrency::critical_section::scoped_lock lock(m_localNameOfRemoteConsolesLock); std::vector< std::shared_ptr > myLocalNameOfRemoteConsolesCopy( m_localNameOfRemoteConsoles ); return myLocalNameOfRemoteConsolesCopy; } void ChatClient::RemoveUserFromAllChatChannels( _In_ IUser^ user ) { CHAT_THROW_INVALIDARGUMENT_IF_NULL( user ); { Concurrency::critical_section::scoped_lock lock(m_chatSessionLock); UINT numChannels = m_chatSession->Channels->Size; for (uint8 channelIndex = 0; channelIndex < numChannels; channelIndex++) { if (DoesChannelExist(channelIndex)) { IChatChannel^ channel = m_chatSession->Channels->GetAt(channelIndex); bool removeFromAllChannels = true; RemoveUserFromChatChannelHelper(channel, user, 0, removeFromAllChannels); } } } UpdateSessionState(); } void ChatClient::RemoveUserFromChatChannel( _In_ uint8 channelIndex, _In_ IUser^ user, _In_ bool updateSessionState ) { CHAT_THROW_INVALIDARGUMENT_IF_NULL( user ); CHAT_THROW_INVALIDARGUMENT_IF( channelIndex >= NUMBER_OF_CHAT_CHANNELS ); { Concurrency::critical_section::scoped_lock lock(m_chatSessionLock); if (DoesChannelExist(channelIndex)) { IChatChannel^ channel = m_chatSession->Channels->GetAt(channelIndex); bool removeFromAllChannels = false; RemoveUserFromChatChannelHelper(channel, user, channelIndex, removeFromAllChannels); } } if( updateSessionState ) { UpdateSessionState(); } } void ChatClient::RemoveUserFromChatChannelHelper( _In_ IChatChannel^ channel, _In_ IUser^ user, _In_ uint8 channelIndex, _In_ bool removeFromAllChannels ) { CHAT_THROW_INVALIDARGUMENT_IF_NULL( user ); CHAT_THROW_INVALIDARGUMENT_IF_NULL( channel ); // Note: This function does not require a lock as the functions // that call his helper already have a m_chatSessionLock. UINT numParticipants = channel->Participants->Size; for (UINT j=0; j < numParticipants; ) { IChatParticipant^ participant = channel->Participants->GetAt(j); if( participant != nullptr && participant->User != nullptr && StringUtils::IsStringEqualCaseInsenstive(participant->User->XboxUserId, user->XboxUserId) ) { channel->Participants->RemoveAt(j); // Update the UserDataMap. RemoveUserFromUserDataMap(user, channelIndex, removeFromAllChannels); break; } else { j++; } } } void ChatClient::RemoveLocalNameOfRemoteConsole( _In_ Platform::Object^ uniqueRemoteConsoleIdentifier ) { Concurrency::critical_section::scoped_lock lock(m_localNameOfRemoteConsolesLock); ChatManager^ chatManager = m_chatManager.Resolve(); if( chatManager != nullptr ) { bool found = false; auto iter = m_localNameOfRemoteConsoles.begin(); for( ; iter != m_localNameOfRemoteConsoles.end(); iter++ ) { std::shared_ptr iterConsoleNameIdentifier = *iter; if( chatManager->OnCompareUniqueConsoleIdentifiersHandler(iterConsoleNameIdentifier->uniqueConsoleIdentifier, uniqueRemoteConsoleIdentifier) ) { found = true; break; } } if (found) { m_localNameOfRemoteConsoles.erase(iter); } } } bool ChatClient::DoesChannelExist( _In_ uint8 channelIndex ) { CHAT_THROW_INVALIDARGUMENT_IF( channelIndex >= NUMBER_OF_CHAT_CHANNELS ); // Note: This function does not require a lock as the functions // that call his helper already have a m_chatSessionLock. if (channelIndex >= m_chatSession->Channels->Size) { return false; } IChatChannel^ channel = m_chatSession->Channels->GetAt(channelIndex); return channel != nullptr; } IChatParticipant^ ChatClient::GetChatParticipantFromUserId( _In_ unsigned int userId ) { Concurrency::critical_section::scoped_lock lock(m_chatSessionLock); // Note that this could be turned into a lookup map for more efficient lookup // The map can be updated updated every time the session state changes for (UINT i=0; i < m_chatSession->Channels->Size; i++) { IChatChannel^ channel = m_chatSession->Channels->GetAt(i); for (UINT j=0; j < channel->Participants->Size; j++) { IChatParticipant^ participant = channel->Participants->GetAt(j); if( participant != nullptr && participant->User != nullptr && participant->User->Id == userId ) { return participant; } } } return nullptr; } UINT ChatClient::GetChatChannelIndexFromUserId( _In_ unsigned int userId ) { Concurrency::critical_section::scoped_lock lock(m_chatSessionLock); // Note that this could be turned into a lookup map for more efficient lookup // The map can be updated updated every time the session state changes for (UINT i=0; i < m_chatSession->Channels->Size; i++) { IChatChannel^ channel = m_chatSession->Channels->GetAt(i); for (UINT j=0; j < channel->Participants->Size; j++) { IChatParticipant^ participant = channel->Participants->GetAt(j); if( participant != nullptr && participant->User != nullptr && participant->User->Id == userId ) { return i; } } } return NUMBER_OF_CHAT_CHANNELS; } void ChatClient::ChangeMuteStateForChatParticipantFromAllChannels( _In_ unsigned int userId, _In_ float volume ) { Concurrency::critical_section::scoped_lock lock(m_chatSessionLock); for (UINT i=0; i < m_chatSession->Channels->Size; i++) { IChatChannel^ channel = m_chatSession->Channels->GetAt(i); for (UINT j=0; j < channel->Participants->Size; j++) { IChatParticipant^ participant = channel->Participants->GetAt(j); if( participant != nullptr && participant->User != nullptr && participant->User->Id == userId ) { participant->Volume = volume; break; } } } } void ChatClient::ChatSessionStateChanged( _In_ IChatSession^ chatSession, _In_ ChatSessionStateChangeReason reason ) { UNREFERENCED_PARAMETER( chatSession ); if (reason == ChatSessionStateChangeReason::MicrophoneFocusGained) { ChatAudioThread^ chatAudioThread = m_chatAudioThread.Resolve(); if (chatAudioThread != nullptr) { chatAudioThread->HasMicFocus = true; #if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE TraceChatMicFocus( chatAudioThread->HasMicFocus ); #endif } } UpdateSessionState(); } void ChatClient::RemoveAllUsers() { Concurrency::critical_section::scoped_lock lock(m_chatSessionLock); m_chatSession->Channels->Clear(); } void ChatClient::RemoveRemoteConsole( _In_ Platform::Object^ uniqueRemoteConsoleIdentifier ) { CHAT_THROW_INVALIDARGUMENT_IF_NULL(uniqueRemoteConsoleIdentifier); RemoveLocalNameOfRemoteConsole(uniqueRemoteConsoleIdentifier); Windows::Foundation::Collections::IVectorView^ userIds = GetAllUserIdsForConsoleId( uniqueRemoteConsoleIdentifier ); for each (unsigned int userId in userIds) { IChatParticipant^ chatUserToRemove = GetChatParticipantFromUserId(userId); if( chatUserToRemove != nullptr ) { IUser^ userToRemove = chatUserToRemove->User; if( userToRemove != nullptr ) { RemoveUserFromAllChatChannels(userToRemove); } } } } IChatParticipant^ ChatClient::GetChatParticipantFromChatSession( _In_ Platform::String^ xboxUserId, _In_ UINT channelIndex ) { Concurrency::critical_section::scoped_lock lock(m_chatSessionLock); if (DoesChannelExist((uint8)channelIndex)) { IChatChannel^ channel = m_chatSession->Channels->GetAt(channelIndex); for (UINT j=0; j < channel->Participants->Size; ++j) { IChatParticipant^ participant = channel->Participants->GetAt(j); if( participant != nullptr && participant->User != nullptr && StringUtils::IsStringEqualCaseInsenstive(participant->User->XboxUserId, xboxUserId) ) { return participant; } } } return nullptr; } Windows::Foundation::Collections::IVectorView^ ChatClient::GetAllUserIdsForConsoleId( _In_ Platform::Object^ uniqueConsoleIdentifier ) { Platform::Collections::Vector^ userIds = ref new Platform::Collections::Vector(); ChatManager^ chatManager = m_chatManager.Resolve(); if( chatManager != nullptr ) { Windows::Foundation::Collections::IVectorView^ chatUsers = GetChatUsers(); for each (Microsoft::Xbox::GameChat::ChatUser^ chatUser in chatUsers) { if( chatManager->OnCompareUniqueConsoleIdentifiersHandler(chatUser->UniqueConsoleIdentifier, uniqueConsoleIdentifier) ) { unsigned int userId = chatUser->User->Id; userIds->Append(userId); } } } return userIds->GetView(); } Windows::Foundation::Collections::IVectorView^ ChatClient::GetChatUsers() { Concurrency::critical_section::scoped_lock lock(m_userIdsToUserDataLock); Platform::Collections::Vector^ chatUsers = ref new Platform::Collections::Vector(); for(std::map::iterator iter = m_userIdsToUserData.begin(); iter != m_userIdsToUserData.end(); ++iter) { ChatUser^ user = iter->second; if( user != nullptr ) { chatUsers->Append(user); } } return chatUsers->GetView(); } ChatUser^ ChatClient::GetChatUserForUserId( _In_ unsigned int userId ) { Concurrency::critical_section::scoped_lock lock(m_userIdsToUserDataLock); if (m_userIdsToUserData.find(userId) != m_userIdsToUserData.end()) { return m_userIdsToUserData[userId]; } return nullptr; }; std::vector ChatClient::GetUserIdsForDevice( _In_ Platform::String^ captureSourceId ) { std::vector ids; Concurrency::critical_section::scoped_lock lock(m_audioCaptureSourceIdToUserIdLock); if( m_chatManagerSettings->IsAtDiagnosticsTraceLevel(GameChatDiagnosticsTraceLevel::Verbose) ) { LogCommentFormat( L"GetUserIdsForDevice: Size:%d", m_audioCaptureSourceIdToUserId.size() ); for(std::map>::iterator iter = m_audioCaptureSourceIdToUserId.begin(); iter != m_audioCaptureSourceIdToUserId.end(); ++iter) { LogCommentFormat( L"GetUserIdsForDevice: captureSourceId: %s", iter->first->Data() ); std::hash_set hashSet = iter->second; for each (unsigned int userId in hashSet) { LogCommentFormat( L"GetUserIdsForDevice: userId: 0x%0.8x", userId ); } } } if (m_audioCaptureSourceIdToUserId.find(captureSourceId) != m_audioCaptureSourceIdToUserId.end()) { ids.insert( std::begin( ids ), std::begin( m_audioCaptureSourceIdToUserId[captureSourceId] ), std::end( m_audioCaptureSourceIdToUserId[captureSourceId] ) ); } return std::move( ids ); }; void ChatClient::CorrelateDeviceToUser( _In_ Platform::String^ deviceId, _In_ unsigned int userId, _In_ bool isSharedDevice ) { CHAT_THROW_INVALIDARGUMENT_IF_STRING_EMPTY( deviceId ); Concurrency::critical_section::scoped_lock lock(m_audioCaptureSourceIdToUserIdLock); if( !isSharedDevice ) { // Exclusive devices like headset can only have one user tied to them so clear out old users. // This happens when 2 users have sign-in with Kinect enabled and swap controllers m_audioCaptureSourceIdToUserId[deviceId].clear(); } if( m_audioCaptureSourceIdToUserId[deviceId].find(userId) == m_audioCaptureSourceIdToUserId[deviceId].end() ) { m_audioCaptureSourceIdToUserId[deviceId].insert( userId ); } #if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE TraceChatCorrelateAudioDeviceToUser( deviceId->Data(), userId, isSharedDevice, false ); #endif } IChatSession^ ChatClient::GetChatSession() { return m_chatSession; } void ChatClient::UpdateSessionState() { Concurrency::critical_section::scoped_lock lock(m_chatSessionLock); CHAT_LOG_INFO_MSG( L"Windows::Xbox::Chat::IChatSession::GetStateAsync starting" ); auto asyncOp = m_chatSession->GetStateAsync(); create_task(asyncOp) .then([this] (task t) { try { CHAT_LOG_INFO_MSG( L"Windows::Xbox::Chat::IChatSession::GetStateAsync complete" ); IChatSessionState^ chatSessionState = t.get(); ChatManager^ chatManager = m_chatManager.Resolve(); if( chatManager != nullptr ) { chatManager->OnChatSessionStateChangedHandler( chatSessionState ); } } catch ( Platform::COMException^ ex ) { if (ex->HResult == (HRESULT)Windows::Xbox::Chat::ChatErrorStatus::NoMicrophoneFocus) { ChatAudioThread^ chatAudioThread = m_chatAudioThread.Resolve(); if (chatAudioThread != nullptr) { chatAudioThread->HasMicFocus = false; #if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE TraceChatMicFocus( chatAudioThread->HasMicFocus ); #endif } } // ERROR NOT FOUND is expected if (ex->HResult != HRESULT_FROM_WIN32(ERROR_NOT_FOUND)) { if (ex->HResult == AUDCLNT_E_DEVICE_IN_USE) { // Package.appxmanifest should contain capabilities like this: // // // // // // LogCommentWithError( L"GetStateAsync failed. This error often indicates missing kinectGamechat in package.appxmanifest", ex->HResult ); } else { LogCommentWithError( L"GetStateAsync", ex->HResult ); } } } }).wait(); } void ChatClient::RemoveUserFromUserDataMap( _In_ IUser^ user, _In_ uint8 channelIndex, _In_ bool removeFromAllChannels ) { CHAT_THROW_INVALIDARGUMENT_IF_NULL( user ); Concurrency::critical_section::scoped_lock lock(m_userIdsToUserDataLock); if( !user->XboxUserId->IsEmpty() ) { if (m_userIdsToUserData.find(user->Id) != m_userIdsToUserData.end()) { ChatUser^ chatUser = m_userIdsToUserData[user->Id]; if (removeFromAllChannels || chatUser->GetAllChannels()->Size == 1) { RemoveUserIdFromAudioCaptureSourceIdMap(user); // If the user is only in 1 channel, then remove him completely. m_userIdsToUserData.erase(user->Id); } else { // else, just remove the channel index from the channel list. if (chatUser) { chatUser->RemoveChannelIndexFromChannelList(channelIndex); } } } } } void ChatClient::RemoveUserIdFromAudioCaptureSourceIdMap( _In_ IUser^ user ) { CHAT_THROW_INVALIDARGUMENT_IF_NULL( user ); Concurrency::critical_section::scoped_lock lock(m_audioCaptureSourceIdToUserIdLock); for(std::map>::iterator iter = m_audioCaptureSourceIdToUserId.begin(); iter != m_audioCaptureSourceIdToUserId.end(); ++iter) { m_audioCaptureSourceIdToUserId[iter->first].erase( user->Id ); #if WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE TraceChatCorrelateAudioDeviceToUser( iter->first->Data(), user->Id, false, true ); #endif } } Microsoft::Xbox::GameChat::ChatUser^ ChatClient::AddUserToUserDataMap( _In_ IUser^ user, _In_ uint8 channelIndex, _In_ bool isLocal, _In_opt_ Platform::Object^ uniqueConsoleIdentifier, _In_ Windows::Xbox::Chat::IChatParticipant^ chatParticipant ) { CHAT_THROW_E_POINTER_IF_NULL( user ); CHAT_THROW_E_POINTER_IF_NULL( chatParticipant ); // Add user meta data for lookup Concurrency::critical_section::scoped_lock lock(m_userIdsToUserDataLock); if ( m_userIdsToUserData.find( user->Id ) == m_userIdsToUserData.end() ) { m_userIdsToUserData[user->Id] = ref new ChatUser(user->XboxUserId, isLocal, uniqueConsoleIdentifier, chatParticipant); } ChatUser^ chatUser = m_userIdsToUserData[user->Id]; m_userIdsToUserData[user->Id]->AddChannelIndexToChannelList(channelIndex); return chatUser; } void ChatClient::AddUserToAudioDevicesMap( _In_ IUser^ user ) { CHAT_THROW_E_POINTER_IF_NULL( user ); auto audioDevices = user->AudioDevices; for (UINT i = 0; i < audioDevices->Size; i++) { // Store relation of device ids to user id auto device = audioDevices->GetAt(i); auto deviceId = device->Id; auto userId = user->Id; bool isSharedDevice = (device->Sharing == Windows::Xbox::System::AudioDeviceSharing::Shared); CorrelateDeviceToUser(deviceId, userId, isSharedDevice); } } std::vector ChatClient::GetChatUsersForCaptureSourceId( _In_ Platform::String^ captureSourceId ) { std::vector chatUsers; auto userIdsForDevice = GetUserIdsForDevice( captureSourceId ); for ( auto uid : userIdsForDevice ) { auto user = GetChatUserForUserId( uid ); if (user != nullptr) { chatUsers.push_back(user); } } return chatUsers; } void ChatClient::LogComment( _In_ Platform::String^ message ) { LogCommentWithError(message, S_OK); } void ChatClient::LogCommentWithError( _In_ Platform::String^ message, _In_ HRESULT hr ) { DebugMessageEventArgs^ args = ref new DebugMessageEventArgs(message, hr); ChatManager^ chatManager = m_chatManager.Resolve(); if( chatManager != nullptr ) { chatManager->OnDebugMessageHandler( args ); } } void ChatClient::ClearTalkingModeForAllChatUsers() { Windows::Foundation::Collections::IVectorView^ chatUsers = GetChatUsers(); for each (Microsoft::Xbox::GameChat::ChatUser^ chatUser in chatUsers) { chatUser->SetTalkingMode(ChatUserTalkingMode::NotTalking); } } bool ChatClient::CallerLock_GetUnusedLocalNameOfRemoteConsole( _Out_ uint8& localNameOfRemoteConsole ) { // Note that the local console will not be added to this list unsigned int localName = 0; // Not taking lock here since caller will hold the lock // Concurrency::critical_section::scoped_lock lock(m_localNameOfRemoteConsolesLock); // Look for the first unused name in our list of remote console names. 0 is reserved for local console. bool foundUnusedLocalName = true; for( localName=1; localName<=0xFF; localName++ ) { foundUnusedLocalName = !IsLocalNameTaken( static_cast(localName) ); // If we found a free slot then stop. We will hand that unused name. Otherwise keep looking if( foundUnusedLocalName ) { break; } } if( !foundUnusedLocalName ) { LogCommentWithError( L"Couldn't find unused local name for remote console. Should not happen due limited of number of network connections allowed", E_UNEXPECTED ); return false; } localNameOfRemoteConsole = static_cast(localName); return true; } void ChatClient::SetChatAudioThread( _In_ ChatAudioThread^ chatAudioThread ) { m_chatAudioThread = chatAudioThread; } bool ChatClient::IsLocalNameTaken( uint8 localName ) { bool isTaken = false; for each (std::shared_ptr uniqueConsole in m_localNameOfRemoteConsoles) { if( uniqueConsole->consoleName == localName ) { isTaken = true; break; } } return isTaken; } bool ChatClient::DoesRemoteUniqueConsoleIdentifierExist( _In_ Platform::Object^ remoteUniqueConsoleIdentifier ) { CHAT_THROW_INVALIDARGUMENT_IF_NULL( remoteUniqueConsoleIdentifier ); ChatManager^ chatManager = m_chatManager.Resolve(); if( remoteUniqueConsoleIdentifier != nullptr && chatManager != nullptr ) { Windows::Foundation::Collections::IVectorView^ chatUsers = GetChatUsers(); for each (Microsoft::Xbox::GameChat::ChatUser^ chatUser in chatUsers) { if( chatManager->OnCompareUniqueConsoleIdentifiersHandler(chatUser->UniqueConsoleIdentifier, remoteUniqueConsoleIdentifier) ) { return true; } } } return false; } }}} #endif