mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-07 13:57:48 +00:00
GEEKING
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
//// 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 "AudioDeviceIDMapper.h"
|
||||
#include "StringUtils.h"
|
||||
|
||||
#if TV_API
|
||||
|
||||
namespace Microsoft {
|
||||
namespace Xbox {
|
||||
namespace GameChat {
|
||||
|
||||
AudioDeviceIDMapper::AudioDeviceIDMapper()
|
||||
{
|
||||
}
|
||||
|
||||
void AudioDeviceIDMapper::Reset()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
m_stringToLocalDeviceID.clear();
|
||||
}
|
||||
|
||||
|
||||
DEVICE_ID AudioDeviceIDMapper::GetLocalDeviceID(
|
||||
_In_ Platform::String^ stringID
|
||||
)
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
|
||||
// Only add if not already there
|
||||
if (!StringDeviceIDExists(stringID))
|
||||
{
|
||||
m_nextLocalDeviceID = (DEVICE_ID)m_nextLocalDeviceIDLong;
|
||||
m_stringToLocalDeviceID[stringID] = m_nextLocalDeviceID;
|
||||
InterlockedIncrement(&m_nextLocalDeviceIDLong);
|
||||
|
||||
return m_nextLocalDeviceID;
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_stringToLocalDeviceID[stringID];
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioDeviceIDMapper::StringDeviceIDExists(
|
||||
_In_ Platform::String^ stringID
|
||||
)
|
||||
{
|
||||
return m_stringToLocalDeviceID.find(stringID) != m_stringToLocalDeviceID.end();
|
||||
}
|
||||
|
||||
bool AudioDeviceIDMapper::StringRemoteIDExists(
|
||||
_In_ Platform::String^ stringID
|
||||
)
|
||||
{
|
||||
return m_stringToRemoteDeviceID.find(stringID) != m_stringToRemoteDeviceID.end();
|
||||
}
|
||||
|
||||
bool AudioDeviceIDMapper::DoesLookupIDExistsForRemote(
|
||||
_In_ LOOKUP_ID lookupId
|
||||
)
|
||||
{
|
||||
return m_remoteIdToString.find(lookupId) != m_remoteIdToString.end();
|
||||
}
|
||||
|
||||
|
||||
LOOKUP_ID AudioDeviceIDMapper::ConstructLookupID(
|
||||
_In_ CONSOLE_NAME localNameOfRemoteConsole,
|
||||
_In_ DEVICE_ID deviceID
|
||||
)
|
||||
{
|
||||
return (localNameOfRemoteConsole << 8) + deviceID;
|
||||
}
|
||||
|
||||
void AudioDeviceIDMapper::RemoveRemoteConsole(
|
||||
_In_ CONSOLE_NAME localNameOfRemoteConsoleToRemove
|
||||
)
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
|
||||
#ifdef DEBUG_REMOTE_AUDIO_DEVICES
|
||||
OutputDebugString( L"AudioDeviceIDMapper::RemoveRemoteConsole before\r\n" );
|
||||
DebugDumpRemoteIdToStringMap();
|
||||
#endif
|
||||
|
||||
for(auto iter = m_remoteIdToString.cbegin(); iter != m_remoteIdToString.cend(); )
|
||||
{
|
||||
LOOKUP_ID lookupId = iter->first;
|
||||
|
||||
CONSOLE_NAME localNameOfRemoteConsole = lookupId >> 8;
|
||||
//DEVICE_ID deviceID = lookupId & 0xFF;
|
||||
|
||||
if( localNameOfRemoteConsoleToRemove == localNameOfRemoteConsole )
|
||||
{
|
||||
m_remoteIdToString.erase(iter++);
|
||||
}
|
||||
else
|
||||
{
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_REMOTE_AUDIO_DEVICES
|
||||
OutputDebugString( L"AudioDeviceIDMapper::RemoveRemoteConsole after\r\n" );
|
||||
DebugDumpRemoteIdToStringMap();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Platform::String^ AudioDeviceIDMapper::GetRemoteAudioDevice(
|
||||
_In_ LOOKUP_ID lookUpId
|
||||
)
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
|
||||
// Only add if not already there
|
||||
if (DoesLookupIDExistsForRemote(lookUpId))
|
||||
{
|
||||
return m_remoteIdToString[lookUpId];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AudioDeviceIDMapper::AddRemoteAudioDevice(
|
||||
_In_ LOOKUP_ID lookUpId,
|
||||
_In_ Platform::String^ stringID
|
||||
)
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
m_stringToRemoteDeviceID[stringID] = lookUpId;
|
||||
m_remoteIdToString[lookUpId] = stringID;
|
||||
|
||||
#ifdef DEBUG_REMOTE_AUDIO_DEVICES
|
||||
OutputDebugString( L"AudioDeviceIDMapper::AddRemoteAudioDevice\r\n" );
|
||||
DebugDumpRemoteIdToStringMap();
|
||||
#endif
|
||||
}
|
||||
|
||||
void AudioDeviceIDMapper::DebugDumpRemoteIdToStringMap()
|
||||
{
|
||||
#ifdef DEBUG_REMOTE_AUDIO_DEVICES
|
||||
int iterIndex = 0;
|
||||
for(std::map<LOOKUP_ID, Platform::String^>::iterator iter = m_remoteIdToString.begin(); iter != m_remoteIdToString.end(); ++iter)
|
||||
{
|
||||
LOOKUP_ID iterLookupId = iter->first;
|
||||
Platform::String^ audioStringId = iter->second;
|
||||
WCHAR text[1024];
|
||||
swprintf_s(text, ARRAYSIZE(text), L"m_remoteIdToString[%d] = %d -> %s\r\n",
|
||||
iterIndex,
|
||||
iterLookupId,
|
||||
audioStringId->Data()
|
||||
);
|
||||
OutputDebugString( text );
|
||||
|
||||
iterIndex++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}}}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,77 @@
|
||||
//// 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
|
||||
#pragma once
|
||||
|
||||
#if TV_API
|
||||
|
||||
// Make this so we can make the underlying data type larger or smaller if we want
|
||||
#define LOOKUP_ID UINT16 // The type of the full lookup id (CONSOLE_ID << 8 + LOOKUP_ID)
|
||||
#define DEVICE_ID UINT8 // The type of the second half of the lookup id (unique device id)
|
||||
#define CONSOLE_NAME UINT8 // The type of the first half of the lookup id (unique console id)
|
||||
|
||||
namespace Microsoft {
|
||||
namespace Xbox {
|
||||
namespace GameChat {
|
||||
|
||||
|
||||
ref class AudioDeviceIDMapper sealed
|
||||
{
|
||||
public:
|
||||
AudioDeviceIDMapper();
|
||||
|
||||
void Reset();
|
||||
|
||||
DEVICE_ID GetLocalDeviceID(
|
||||
_In_ Platform::String^ stringID
|
||||
);
|
||||
|
||||
void AddRemoteAudioDevice(
|
||||
_In_ LOOKUP_ID deviceId,
|
||||
_In_ Platform::String^ strId
|
||||
);
|
||||
|
||||
Platform::String^ GetRemoteAudioDevice(
|
||||
_In_ LOOKUP_ID lookupId
|
||||
);
|
||||
|
||||
LOOKUP_ID ConstructLookupID(
|
||||
_In_ CONSOLE_NAME localNameOfRemoteConsole,
|
||||
_In_ DEVICE_ID deviceID
|
||||
);
|
||||
|
||||
void RemoveRemoteConsole(
|
||||
_In_ CONSOLE_NAME localNameOfRemoteConsole
|
||||
);
|
||||
|
||||
void DebugDumpRemoteIdToStringMap();
|
||||
|
||||
private:
|
||||
bool StringDeviceIDExists(
|
||||
_In_ Platform::String^ stringID
|
||||
);
|
||||
|
||||
bool StringRemoteIDExists(
|
||||
_In_ Platform::String^ stringID
|
||||
);
|
||||
|
||||
bool DoesLookupIDExistsForRemote(
|
||||
_In_ LOOKUP_ID lookupId
|
||||
);
|
||||
|
||||
private:
|
||||
Concurrency::critical_section m_stateLock;
|
||||
std::map<Platform::String^, DEVICE_ID> m_stringToLocalDeviceID;
|
||||
std::map<Platform::String^, LOOKUP_ID> m_stringToRemoteDeviceID;
|
||||
std::map<LOOKUP_ID, Platform::String^> m_remoteIdToString;
|
||||
DEVICE_ID m_nextLocalDeviceID;
|
||||
LONG m_nextLocalDeviceIDLong;
|
||||
};
|
||||
|
||||
|
||||
}}}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,920 @@
|
||||
//// 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 <Audioclient.h>
|
||||
|
||||
#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<unsigned int, ChatUser^>();
|
||||
m_audioCaptureSourceIdToUserId = std::map<Platform::String^, std::hash_set<unsigned int>>();
|
||||
|
||||
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<ChatClient>();
|
||||
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<Windows::Xbox::Chat::IChatParticipant^>^ 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<Microsoft::Xbox::GameChat::ChatUser^>^ 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<ChatManager>();
|
||||
if( chatManager != nullptr )
|
||||
{
|
||||
for each (std::shared_ptr<ConsoleNameIdentifierPair> uniqueConsole in m_localNameOfRemoteConsoles)
|
||||
{
|
||||
if( chatManager->OnCompareUniqueConsoleIdentifiersHandler(uniqueConsole->uniqueConsoleIdentifier, consoleIdentifier) )
|
||||
{
|
||||
matchFound = true;
|
||||
localName = uniqueConsole->consoleName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !matchFound )
|
||||
{
|
||||
std::shared_ptr<ConsoleNameIdentifierPair> 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<ConsoleNameIdentifierPair> > ChatClient::GetMyLocalNameOfRemoteConsolesCopy()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_localNameOfRemoteConsolesLock);
|
||||
std::vector< std::shared_ptr<ConsoleNameIdentifierPair> > 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<ChatManager>();
|
||||
if( chatManager != nullptr )
|
||||
{
|
||||
bool found = false;
|
||||
auto iter = m_localNameOfRemoteConsoles.begin();
|
||||
for( ; iter != m_localNameOfRemoteConsoles.end(); iter++ )
|
||||
{
|
||||
std::shared_ptr<ConsoleNameIdentifierPair> 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<ChatAudioThread>();
|
||||
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<unsigned int>^ 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<unsigned int>^ ChatClient::GetAllUserIdsForConsoleId(
|
||||
_In_ Platform::Object^ uniqueConsoleIdentifier
|
||||
)
|
||||
{
|
||||
Platform::Collections::Vector<unsigned int>^ userIds = ref new Platform::Collections::Vector<unsigned int>();
|
||||
|
||||
ChatManager^ chatManager = m_chatManager.Resolve<ChatManager>();
|
||||
if( chatManager != nullptr )
|
||||
{
|
||||
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ 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<ChatUser^>^ ChatClient::GetChatUsers()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_userIdsToUserDataLock);
|
||||
|
||||
Platform::Collections::Vector<ChatUser^>^ chatUsers = ref new Platform::Collections::Vector<ChatUser^>();
|
||||
for(std::map<unsigned int, ChatUser^>::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<unsigned int> ChatClient::GetUserIdsForDevice(
|
||||
_In_ Platform::String^ captureSourceId
|
||||
)
|
||||
{
|
||||
std::vector<unsigned int> 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<Platform::String^, std::hash_set<unsigned int>>::iterator iter = m_audioCaptureSourceIdToUserId.begin(); iter != m_audioCaptureSourceIdToUserId.end(); ++iter)
|
||||
{
|
||||
LogCommentFormat( L"GetUserIdsForDevice: captureSourceId: %s", iter->first->Data() );
|
||||
std::hash_set<unsigned int> 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<Windows::Xbox::Chat::IChatSessionState^> t)
|
||||
{
|
||||
try
|
||||
{
|
||||
CHAT_LOG_INFO_MSG( L"Windows::Xbox::Chat::IChatSession::GetStateAsync complete" );
|
||||
IChatSessionState^ chatSessionState = t.get();
|
||||
|
||||
ChatManager^ chatManager = m_chatManager.Resolve<ChatManager>();
|
||||
if( chatManager != nullptr )
|
||||
{
|
||||
chatManager->OnChatSessionStateChangedHandler( chatSessionState );
|
||||
}
|
||||
}
|
||||
catch ( Platform::COMException^ ex )
|
||||
{
|
||||
if (ex->HResult == (HRESULT)Windows::Xbox::Chat::ChatErrorStatus::NoMicrophoneFocus)
|
||||
{
|
||||
ChatAudioThread^ chatAudioThread = m_chatAudioThread.Resolve<ChatAudioThread>();
|
||||
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:
|
||||
//
|
||||
// <Capabilities>
|
||||
// <Capability Name="internetClientServer" />
|
||||
// <mx:Capability Name="kinectAudio"/>
|
||||
// <mx:Capability Name="kinectGamechat"/>
|
||||
// </Capabilities>
|
||||
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<Platform::String^, std::hash_set<unsigned int>>::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<ChatUser^> ChatClient::GetChatUsersForCaptureSourceId(
|
||||
_In_ Platform::String^ captureSourceId
|
||||
)
|
||||
{
|
||||
std::vector<ChatUser^> 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<ChatManager>();
|
||||
if( chatManager != nullptr )
|
||||
{
|
||||
chatManager->OnDebugMessageHandler( args );
|
||||
}
|
||||
}
|
||||
|
||||
void ChatClient::ClearTalkingModeForAllChatUsers()
|
||||
{
|
||||
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ 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<uint8>(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<uint8>(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<ConsoleNameIdentifierPair> 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<ChatManager>();
|
||||
if( remoteUniqueConsoleIdentifier != nullptr &&
|
||||
chatManager != nullptr )
|
||||
{
|
||||
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ chatUsers = GetChatUsers();
|
||||
for each (Microsoft::Xbox::GameChat::ChatUser^ chatUser in chatUsers)
|
||||
{
|
||||
if( chatManager->OnCompareUniqueConsoleIdentifiersHandler(chatUser->UniqueConsoleIdentifier, remoteUniqueConsoleIdentifier) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}}}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,254 @@
|
||||
//// 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
|
||||
#pragma once
|
||||
#include "ChatUser.h"
|
||||
#include "Thread.h"
|
||||
#include "ChatManagerSettings.h"
|
||||
|
||||
#define NUMBER_OF_CHAT_BUFFER_LEVELS 16
|
||||
#define NUMBER_OF_CHAT_CHANNELS 256
|
||||
|
||||
#if TV_API
|
||||
|
||||
// Forward declare
|
||||
namespace Microsoft { namespace Xbox { namespace GameChat { ref class ChatAudioThread; } } }
|
||||
namespace Microsoft { namespace Xbox { namespace GameChat { ref class ChatManager; } } }
|
||||
|
||||
namespace Microsoft {
|
||||
namespace Xbox {
|
||||
namespace GameChat {
|
||||
|
||||
struct ConsoleNameIdentifierPair
|
||||
{
|
||||
Platform::Object^ uniqueConsoleIdentifier;
|
||||
uint8 consoleName;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// ChatClient class implements the guts of the chat system capture / render interface.
|
||||
/// This class handles management of the chat session, adding and removing
|
||||
/// participants from the chat channel as appropriate.
|
||||
/// </summary>
|
||||
ref class ChatClient sealed
|
||||
{
|
||||
public:
|
||||
ChatClient(
|
||||
_In_ uint32 chatPeriodInMilliseconds,
|
||||
_In_ ChatManager^ chatManager,
|
||||
_In_ ChatManagerSettings^ chatManagerSettings
|
||||
);
|
||||
|
||||
Windows::Xbox::Chat::IChatSession^ GetChatSession();
|
||||
|
||||
void SetChatAudioThread(
|
||||
_In_ ChatAudioThread^ chatAudioThread
|
||||
);
|
||||
|
||||
void AddNewChannel();
|
||||
|
||||
Microsoft::Xbox::GameChat::ChatUser^ AddUserToChatChannel(
|
||||
_In_ uint8 channelIndex,
|
||||
_In_ Windows::Xbox::System::IUser^ user,
|
||||
_In_opt_ Platform::Object^ uniqueConsoleIdentifier,
|
||||
_In_ bool isLocal,
|
||||
_In_opt_ Windows::Xbox::Chat::IChatParticipant^ chatParticipantToUpdate
|
||||
);
|
||||
|
||||
Windows::Xbox::Chat::IChatParticipant^ GetChatParticipantFromChatSession(
|
||||
_In_ Platform::String^ xboxUserId,
|
||||
_In_ UINT channelIndex
|
||||
);
|
||||
|
||||
void RemoveAllUsers();
|
||||
|
||||
/// <summary>
|
||||
/// Remove all users from the channel that are on a given console. Used when
|
||||
/// console connectivity is lost
|
||||
/// </summary>
|
||||
void RemoveRemoteConsole(
|
||||
_In_ Platform::Object^ uniqueRemoteConsoleIdentifier
|
||||
);
|
||||
|
||||
void RemoveUserFromChatChannel(
|
||||
_In_ uint8 channelIndex,
|
||||
_In_ Windows::Xbox::System::IUser^ user,
|
||||
_In_ bool updateSessionState
|
||||
);
|
||||
|
||||
void RemoveUserFromAllChatChannels(
|
||||
_In_ Windows::Xbox::System::IUser^ user
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Takes an IUser as input, generates a ChatParitcipant and adds to the
|
||||
/// chat channel. Note that device::user correlation is done here as well
|
||||
/// </summary>
|
||||
Windows::Foundation::Collections::IVectorView<unsigned int>^ GetAllUserIdsForConsoleId(
|
||||
_In_ Platform::Object^ uniqueConsoleIdentifier
|
||||
);
|
||||
|
||||
Windows::Xbox::Chat::IChatParticipant^ GetChatParticipantFromUserId(
|
||||
_In_ unsigned int userId
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Returns NUMBER_OF_CHAT_CHANNELS if the user doesn't exists
|
||||
/// </summary>
|
||||
UINT GetChatChannelIndexFromUserId(
|
||||
_In_ unsigned int userId
|
||||
);
|
||||
|
||||
Windows::Foundation::Collections::IVectorView<ChatUser^>^ GetChatUsers();
|
||||
|
||||
internal:
|
||||
void LogCommentFormat(
|
||||
_In_ LPCWSTR strMsg, ...
|
||||
);
|
||||
|
||||
std::vector<ChatUser^> GetChatUsersForCaptureSourceId(
|
||||
_In_ Platform::String^ captureSourceId
|
||||
);
|
||||
|
||||
std::vector<unsigned int> GetUserIdsForDevice(
|
||||
_In_ Platform::String^ captureSourceId
|
||||
);
|
||||
|
||||
void ChangeMuteStateForChatParticipantFromAllChannels(
|
||||
_In_ unsigned int userId,
|
||||
_In_ float volume
|
||||
);
|
||||
|
||||
ChatUser^ GetChatUserForUserId(
|
||||
_In_ unsigned int userId
|
||||
);
|
||||
|
||||
void RemoveUserFromChatChannelHelper(
|
||||
_In_ Windows::Xbox::Chat::IChatChannel^ channel,
|
||||
_In_ Windows::Xbox::System::IUser^ user,
|
||||
_In_ uint8 channelIndex,
|
||||
_In_ bool removeFromAllChannels
|
||||
);
|
||||
|
||||
void UpdateSessionState();
|
||||
|
||||
/// <summary>
|
||||
/// We set all chat users to NotTalking, and let the capture and render source
|
||||
/// set the talking mode in the AudioThreadDoWork update.
|
||||
/// </summary>
|
||||
void ClearTalkingModeForAllChatUsers();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the local name of a remote console. The name is uint8
|
||||
/// </summary>
|
||||
/// <param name="consoleIdentifier">The remote console to return the local name of</param>
|
||||
uint8 GetLocalNameOfRemoteConsole(
|
||||
_In_ Platform::Object^ consoleIdentifier
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the list of names this local console has assigned to the remote consoles
|
||||
/// </summary>
|
||||
std::vector< std::shared_ptr<ConsoleNameIdentifierPair> > GetMyLocalNameOfRemoteConsolesCopy();
|
||||
|
||||
bool DoesRemoteUniqueConsoleIdentifierExist(
|
||||
_In_ Platform::Object^ remoteUniqueConsoleIdentifier
|
||||
);
|
||||
|
||||
private:
|
||||
void LogComment(
|
||||
_In_ Platform::String^ message
|
||||
);
|
||||
|
||||
void LogCommentWithError(
|
||||
_In_ Platform::String^ message,
|
||||
_In_ HRESULT hr
|
||||
);
|
||||
|
||||
bool DoesChannelContainUser(
|
||||
_In_ Windows::Foundation::Collections::IVector<Windows::Xbox::Chat::IChatParticipant^>^ participants,
|
||||
_In_ Windows::Xbox::System::IUser^ user
|
||||
);
|
||||
|
||||
void CorrelateDeviceToUser(
|
||||
_In_ Platform::String^ deviceId,
|
||||
_In_ unsigned int userId,
|
||||
_In_ bool isSharedDevice
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Called by most of the event handlers when a player or device is added or
|
||||
/// removed, updates the session state (above)
|
||||
/// </summary>
|
||||
void ChatSessionStateChanged(
|
||||
_In_ Windows::Xbox::Chat::IChatSession^ chatSession,
|
||||
_In_ Windows::Xbox::Chat::ChatSessionStateChangeReason reason
|
||||
);
|
||||
|
||||
Windows::Xbox::Chat::IChatParticipant^ AddUserToChatSessionChannel(
|
||||
_In_ uint8 channelIndex,
|
||||
_In_ Windows::Xbox::System::IUser^ user
|
||||
);
|
||||
|
||||
void AddUserToAudioDevicesMap(
|
||||
_In_ Windows::Xbox::System::IUser^ user
|
||||
);
|
||||
|
||||
Microsoft::Xbox::GameChat::ChatUser^
|
||||
AddUserToUserDataMap(
|
||||
_In_ Windows::Xbox::System::IUser^ user,
|
||||
_In_ uint8 channelIndex,
|
||||
_In_ bool isLocal,
|
||||
_In_opt_ Platform::Object^ uniqueConsoleIdentifier,
|
||||
_In_ Windows::Xbox::Chat::IChatParticipant^ chatParticipant
|
||||
);
|
||||
|
||||
void ChatClient::RemoveUserFromUserDataMap(
|
||||
_In_ Windows::Xbox::System::IUser^ user,
|
||||
_In_ uint8 channelIndex,
|
||||
_In_ bool removeFromAllChannels
|
||||
);
|
||||
|
||||
void RemoveUserIdFromAudioCaptureSourceIdMap(
|
||||
_In_ Windows::Xbox::System::IUser^ user
|
||||
);
|
||||
|
||||
void RemoveLocalNameOfRemoteConsole(
|
||||
_In_ Platform::Object^ uniqueRemoteConsoleIdentifier
|
||||
);
|
||||
|
||||
bool DoesChannelExist(
|
||||
_In_ uint8 channelIndex
|
||||
);
|
||||
|
||||
bool CallerLock_GetUnusedLocalNameOfRemoteConsole( _Out_ uint8& localNameOfRemoteConsole );
|
||||
|
||||
bool IsLocalNameTaken( uint8 localName );
|
||||
|
||||
private:
|
||||
~ChatClient();
|
||||
Windows::Foundation::EventRegistrationToken m_tokenStateChangedEvent;
|
||||
|
||||
Concurrency::critical_section m_chatSessionLock; // Locks down m_chatSession
|
||||
Concurrency::critical_section m_audioCaptureSourceIdToUserIdLock;
|
||||
Concurrency::critical_section m_userIdsToUserDataLock;
|
||||
Concurrency::critical_section m_localNameOfRemoteConsolesLock;
|
||||
|
||||
Windows::Xbox::Chat::IChatSession^ m_chatSession;
|
||||
std::map<UINT, UINT> m_userIdsToBufferSizes;
|
||||
Platform::WeakReference m_chatManager;
|
||||
ChatManagerSettings^ m_chatManagerSettings;
|
||||
|
||||
std::vector< std::shared_ptr<ConsoleNameIdentifierPair> > m_localNameOfRemoteConsoles;
|
||||
|
||||
std::map<Platform::String^, std::hash_set<unsigned int>> m_audioCaptureSourceIdToUserId;
|
||||
std::map<unsigned int, ChatUser^> m_userIdsToUserData;
|
||||
Platform::WeakReference m_chatAudioThread;
|
||||
};
|
||||
|
||||
}}}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,300 @@
|
||||
//// 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 "ChatUser.h"
|
||||
#include "StringUtils.h"
|
||||
|
||||
#if TV_API
|
||||
|
||||
namespace Microsoft {
|
||||
namespace Xbox {
|
||||
namespace GameChat {
|
||||
|
||||
ChatUser::ChatUser(
|
||||
_In_ Platform::String^ xuid,
|
||||
_In_ bool isLocal,
|
||||
_In_opt_ Platform::Object^ uniqueConsoleIdentifier,
|
||||
_In_ Windows::Xbox::Chat::IChatParticipant^ chatParticipant
|
||||
) :
|
||||
m_xuid(xuid),
|
||||
m_isLocal(isLocal),
|
||||
m_uniqueConsoleIdentifier(uniqueConsoleIdentifier),
|
||||
m_chatParticipant(chatParticipant),
|
||||
m_talkingMode(ChatUserTalkingMode::NotTalking),
|
||||
m_restrictionMode(Windows::Xbox::Chat::ChatRestriction::None),
|
||||
m_chatParticipantVolume( 0.0f ),
|
||||
m_dynamicNeededPacketCount( 0 ),
|
||||
m_numberOfPendingAudioPacketsToPlay( 0 ),
|
||||
m_isMuted( false ),
|
||||
m_shouldBepermanentlyMuted( false )
|
||||
{
|
||||
CHAT_THROW_E_POINTER_IF_NULL( chatParticipant );
|
||||
CHAT_THROW_INVALIDARGUMENT_IF_STRING_EMPTY( xuid );
|
||||
}
|
||||
|
||||
Platform::String^ ChatUser::XboxUserId::get()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
return m_xuid;
|
||||
};
|
||||
|
||||
|
||||
Platform::Object^ ChatUser::UniqueConsoleIdentifier::get()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
return m_uniqueConsoleIdentifier;
|
||||
}
|
||||
|
||||
Windows::Xbox::Chat::IChatParticipant^ ChatUser::ChatParticipant::get()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
return m_chatParticipant;
|
||||
}
|
||||
|
||||
Windows::Xbox::System::IUser^ ChatUser::User::get()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
return m_chatParticipant->User;
|
||||
}
|
||||
|
||||
ChatUserTalkingMode ChatUser::TalkingMode::get()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
ChatUserTalkingMode result = (ChatUserTalkingMode) m_talkingMode;
|
||||
return result;
|
||||
}
|
||||
|
||||
void ChatUser::SetTalkingMode(
|
||||
_In_ ChatUserTalkingMode mode
|
||||
)
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
m_talkingMode = mode;
|
||||
};
|
||||
|
||||
|
||||
uint32 ChatUser::NumberOfPendingAudioPacketsToPlay::get()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
return m_numberOfPendingAudioPacketsToPlay;
|
||||
}
|
||||
|
||||
void ChatUser::SetNumberOfPendingAudioPacketsToPlay(
|
||||
_In_ uint32 numberOfPendingAudioPacketsToPlay
|
||||
)
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
m_numberOfPendingAudioPacketsToPlay = numberOfPendingAudioPacketsToPlay;
|
||||
}
|
||||
|
||||
Windows::Xbox::Chat::ChatRestriction ChatUser::RestrictionMode::get()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
return m_restrictionMode;
|
||||
}
|
||||
|
||||
void ChatUser::SetRestrictionMode(
|
||||
_In_ Windows::Xbox::Chat::ChatRestriction restriction
|
||||
)
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
m_restrictionMode = restriction;
|
||||
};
|
||||
|
||||
bool ChatUser::IsLocal::get()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
return m_isLocal;
|
||||
}
|
||||
|
||||
bool ChatUser::IsMutedPermanently::get()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
return m_shouldBepermanentlyMuted;
|
||||
}
|
||||
|
||||
|
||||
bool ChatUser::IsLocalUserMuted::get()
|
||||
{
|
||||
// If muting myself, then don't send capture packets from that user
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
return m_isMuted && m_isLocal;
|
||||
}
|
||||
|
||||
void ChatUser::Mute()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
m_isMuted = true;
|
||||
}
|
||||
|
||||
void ChatUser::Unmute()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
|
||||
if (!m_shouldBepermanentlyMuted)
|
||||
{
|
||||
m_isMuted = false;
|
||||
|
||||
// The restriction will be re-instantiated for the remote user on the next update.
|
||||
m_restrictionMode = Windows::Xbox::Chat::ChatRestriction::None;
|
||||
}
|
||||
}
|
||||
|
||||
void ChatUser::MutePermanently()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
m_shouldBepermanentlyMuted = true;
|
||||
m_isMuted = true;
|
||||
}
|
||||
|
||||
void ChatUser::UnmutePermanently()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
m_shouldBepermanentlyMuted = false;
|
||||
|
||||
// The restriction will be re-instantiated for the remote user on the next update.
|
||||
m_restrictionMode = Windows::Xbox::Chat::ChatRestriction::None;
|
||||
}
|
||||
|
||||
bool ChatUser::IsMuted::get()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
return m_isMuted;
|
||||
}
|
||||
|
||||
Windows::Foundation::Collections::IVectorView<uint8>^ ChatUser::GetAllChannels()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
|
||||
Platform::Collections::Vector<uint8>^ channels = ref new Platform::Collections::Vector<uint8>();
|
||||
for each (uint8 channelIndex in m_channels)
|
||||
{
|
||||
channels->Append(channelIndex);
|
||||
}
|
||||
|
||||
return channels->GetView();
|
||||
}
|
||||
|
||||
void ChatUser::AddChannelIndexToChannelList(
|
||||
_In_ uint8 channelIndex
|
||||
)
|
||||
{
|
||||
bool channelIndexFound = false;
|
||||
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
for each (uint8 existingChannelIndex in m_channels)
|
||||
{
|
||||
if( existingChannelIndex == channelIndex )
|
||||
{
|
||||
channelIndexFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( !channelIndexFound )
|
||||
{
|
||||
m_channels.push_back( channelIndex );
|
||||
}
|
||||
}
|
||||
|
||||
void ChatUser::RemoveChannelIndexFromChannelList(
|
||||
_In_ uint8 channelIndex
|
||||
)
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
|
||||
bool found = false;
|
||||
auto iter = m_channels.begin();
|
||||
for( ; iter != m_channels.end(); iter++ )
|
||||
{
|
||||
uint8 existingChannelIndex = *iter;
|
||||
if ( existingChannelIndex == channelIndex )
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
m_channels.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
uint32 ChatUser::DynamicNeededPacketCount::get()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
return m_dynamicNeededPacketCount;
|
||||
}
|
||||
|
||||
void ChatUser::SetDynamicNeededPacketCount(
|
||||
_In_ uint32 dynamicNeededPacketCount
|
||||
)
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
m_dynamicNeededPacketCount = dynamicNeededPacketCount;
|
||||
}
|
||||
|
||||
Windows::Xbox::Chat::ChatParticipantTypes ChatUser::ParticipantType::get()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
return m_chatParticipant->ParticipantType;
|
||||
}
|
||||
|
||||
void ChatUser::ParticipantType::set( Windows::Xbox::Chat::ChatParticipantTypes val )
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
m_chatParticipant->ParticipantType = val;
|
||||
}
|
||||
|
||||
float ChatUser::Volume::get()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
return m_chatParticipant->Volume;
|
||||
}
|
||||
|
||||
void ChatUser::Volume::set( float val )
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
m_chatParticipant->Volume = val;
|
||||
}
|
||||
|
||||
void ChatUser::SetChatRenderTarget(
|
||||
_In_ Windows::Xbox::Chat::IChatRenderTarget^ chatRenderTarget
|
||||
)
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
m_chatRenderTarget = chatRenderTarget;
|
||||
}
|
||||
|
||||
float ChatUser::LocalRenderTargetVolume::get()
|
||||
{
|
||||
if( !IsLocal || m_chatRenderTarget == nullptr )
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
Windows::Xbox::Chat::IChatRenderTargetVolume^ targetVolume = (Windows::Xbox::Chat::IChatRenderTargetVolume^)m_chatRenderTarget;
|
||||
return targetVolume->Volume;
|
||||
}
|
||||
|
||||
void ChatUser::LocalRenderTargetVolume::set( float val )
|
||||
{
|
||||
if( !IsLocal || m_chatRenderTarget == nullptr )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Concurrency::critical_section::scoped_lock lock(m_stateLock);
|
||||
Windows::Xbox::Chat::IChatRenderTargetVolume^ targetVolume = (Windows::Xbox::Chat::IChatRenderTargetVolume^)m_chatRenderTarget;
|
||||
targetVolume->Volume = val;
|
||||
}
|
||||
|
||||
}}}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,300 @@
|
||||
//// 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
|
||||
#pragma once
|
||||
|
||||
#if TV_API
|
||||
|
||||
namespace Microsoft {
|
||||
namespace Xbox {
|
||||
namespace GameChat {
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the talking mode of the user
|
||||
/// </summary>
|
||||
public enum class ChatUserTalkingMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates the chat user is not talking
|
||||
/// </summary>
|
||||
NotTalking = 0x0,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the chat user is talking over a headset
|
||||
/// </summary>
|
||||
TalkingOverHeadset = 0x1,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the chat user is talking over the Kinect
|
||||
/// </summary>
|
||||
TalkingOverKinect = 0x2
|
||||
};
|
||||
|
||||
public ref class ChatUser sealed
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// The XboxUserID of this ChatUser
|
||||
/// </summary>
|
||||
property Platform::String^ XboxUserId
|
||||
{
|
||||
Platform::String^ get();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The UniqueConsoleIdentifier for this ChatUser
|
||||
/// </summary>
|
||||
property Platform::Object^ UniqueConsoleIdentifier
|
||||
{
|
||||
Platform::Object^ get();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the this ChatUser is talking or not
|
||||
/// </summary>
|
||||
property ChatUserTalkingMode TalkingMode
|
||||
{
|
||||
ChatUserTalkingMode get();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates number of pending audio packets to play.
|
||||
/// This is controlled by the jitter buffer.
|
||||
/// The jitter buffer can be controlled by some settings found on ChatManagerSettings
|
||||
/// </summary>
|
||||
property uint32 NumberOfPendingAudioPacketsToPlay
|
||||
{
|
||||
uint32 get();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the current value of the dynamic needed packet count.
|
||||
/// This is the number of packets required before audio will start playing.
|
||||
/// This number is dynamically adjusted based on heuristics monitoring how often packets arrive from the network
|
||||
/// </summary>
|
||||
property uint32 DynamicNeededPacketCount
|
||||
{
|
||||
uint32 get();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the chat user is restricted
|
||||
/// </summary>
|
||||
property Windows::Xbox::Chat::ChatRestriction RestrictionMode
|
||||
{
|
||||
Windows::Xbox::Chat::ChatRestriction get();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the IUser for this ChatUser
|
||||
/// </summary>
|
||||
property Windows::Xbox::System::IUser^ User
|
||||
{
|
||||
Windows::Xbox::System::IUser^ get();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if the user is local for the console
|
||||
/// </summary>
|
||||
property bool IsLocal
|
||||
{
|
||||
bool get();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if the local user is muted.
|
||||
/// If local user is muted, then doesn't send capture packets to others.
|
||||
/// </summary>
|
||||
property bool IsLocalUserMuted
|
||||
{
|
||||
bool get();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if the user is muted.
|
||||
/// </summary>
|
||||
property bool IsMuted
|
||||
{
|
||||
bool get();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if the user is muted permanently.
|
||||
/// </summary>
|
||||
property bool IsMutedPermanently
|
||||
{
|
||||
bool get();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of all channels the user is part of.
|
||||
/// </summary>
|
||||
Windows::Foundation::Collections::IVectorView<uint8>^ GetAllChannels();
|
||||
|
||||
property Windows::Xbox::Chat::ChatParticipantTypes ParticipantType
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the ParticipantType for this ChatUser
|
||||
/// </summary>
|
||||
Windows::Xbox::Chat::ChatParticipantTypes get();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the ParticipantType for this ChatUser
|
||||
/// </summary>
|
||||
void set(Windows::Xbox::Chat::ChatParticipantTypes val);
|
||||
}
|
||||
|
||||
property float Volume
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the volume for this ChatUser
|
||||
/// Volume is a percentage 0.0f to 1.0f
|
||||
/// </summary>
|
||||
float get();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the volume for this ChatUser
|
||||
/// Volume is a percentage 0.0f to 1.0f
|
||||
/// </summary>
|
||||
void set(float val);
|
||||
}
|
||||
|
||||
property float LocalRenderTargetVolume
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the volume for the render target if this is a local user
|
||||
/// Volume is a percentage 0.0f to 1.0f.
|
||||
/// Returns 0 if this is a remote user
|
||||
/// </summary>
|
||||
float get();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the volume for the chat render target if this is a local user.
|
||||
/// Volume is a percentage 0.0f to 1.0f
|
||||
/// Value is ignored if this is a remote user
|
||||
/// </summary>
|
||||
void set(float val);
|
||||
}
|
||||
|
||||
internal:
|
||||
|
||||
/// <summary>
|
||||
/// Returns the IChatParticipant if this ChatUser
|
||||
/// </summary>
|
||||
property Windows::Xbox::Chat::IChatParticipant^ ChatParticipant
|
||||
{
|
||||
Windows::Xbox::Chat::IChatParticipant^ get();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mute user permanently
|
||||
/// </summary>
|
||||
void MutePermanently();
|
||||
|
||||
/// <summary>
|
||||
/// UnMute user permanently
|
||||
/// </summary>
|
||||
void UnmutePermanently();
|
||||
|
||||
/// <summary>
|
||||
/// Internal helper function. If muting local user, then don't send capture packets to others.
|
||||
/// Else, set ChatParticipant volume to 0.
|
||||
/// </summary>
|
||||
void Mute();
|
||||
|
||||
/// <summary>
|
||||
/// Internal helper function to unmute user
|
||||
/// </summary>
|
||||
void Unmute();
|
||||
|
||||
/// <summary>
|
||||
/// Internal function to construct a ChatUser
|
||||
/// </summary>
|
||||
ChatUser(
|
||||
_In_ Platform::String^ xuid,
|
||||
_In_ bool isLocal,
|
||||
_In_opt_ Platform::Object^ uniqueConsoleIdentifier,
|
||||
_In_ Windows::Xbox::Chat::IChatParticipant^ chatParticipant
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Internal function to change the talking mode of the ChatUser
|
||||
/// </summary>
|
||||
void SetTalkingMode(
|
||||
_In_ ChatUserTalkingMode mode
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Internal function to set number of pending audio packets for this ChatUser
|
||||
/// </summary>
|
||||
void SetNumberOfPendingAudioPacketsToPlay(
|
||||
_In_ uint32 numberOfPendingAudioPacketsToPlay
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Internal function to set the restriction mode for this chat user
|
||||
/// </summary>
|
||||
void SetRestrictionMode(
|
||||
_In_ Windows::Xbox::Chat::ChatRestriction restriction
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Internal function to add this chat user to a specific channel
|
||||
/// </summary>
|
||||
void AddChannelIndexToChannelList(
|
||||
_In_ uint8 channelIndex
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Internal function to remove this chat user from a specific channel
|
||||
/// </summary>
|
||||
void RemoveChannelIndexFromChannelList(
|
||||
_In_ uint8 channelIndex
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Set the dynamic needed packet count for this chat user
|
||||
/// </summary>
|
||||
void SetDynamicNeededPacketCount(
|
||||
_In_ uint32 dynamicNeededPacketCount
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Set the Windows::Xbox::Chat::IChatRenderTarget for this chat user
|
||||
/// </summary>
|
||||
void SetChatRenderTarget(
|
||||
_In_ Windows::Xbox::Chat::IChatRenderTarget^ chatRenderTarget
|
||||
);
|
||||
|
||||
private:
|
||||
Concurrency::critical_section m_stateLock;
|
||||
Platform::String^ m_xuid;
|
||||
Platform::Object^ m_uniqueConsoleIdentifier;
|
||||
Windows::Xbox::Chat::IChatParticipant^ m_chatParticipant;
|
||||
Platform::String^ m_gamertag;
|
||||
ChatUserTalkingMode m_talkingMode;
|
||||
uint32 m_numberOfPendingAudioPacketsToPlay;
|
||||
Windows::Xbox::Chat::ChatRestriction m_restrictionMode;
|
||||
bool m_isLocal;
|
||||
bool m_isMuted;
|
||||
bool m_shouldBepermanentlyMuted;
|
||||
uint32 m_dynamicNeededPacketCount;
|
||||
Windows::Xbox::Chat::IChatRenderTarget^ m_chatRenderTarget;
|
||||
|
||||
/// <summary>
|
||||
/// List of channels the user is currently part of.
|
||||
/// </summary>
|
||||
std::vector<uint8> m_channels;
|
||||
|
||||
/// <summary>
|
||||
/// ChatParticipant volume prior to mute.
|
||||
/// </summary>
|
||||
float m_chatParticipantVolume;
|
||||
};
|
||||
|
||||
}}}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user