the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
1//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
2//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
3//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
4//// PARTICULAR PURPOSE.
5////
6//// Copyright (c) Microsoft Corporation. All rights reserved
7#include "stdafx.h"
8#include "ChatIntegrationLayer.h"
9#include "DQRNetworkManager.h"
10#include <robuffer.h>
11
12using namespace Windows::Foundation;
13using namespace Windows::Xbox::System;
14
15// To integrate the Chat DLL in your game, you can use this ChatIntegrationLayer class with modifications,
16// or create your own design your own class using the code in this file a guide.
17std::shared_ptr<ChatIntegrationLayer> GetChatIntegrationLayer()
18{
19 static std::shared_ptr<ChatIntegrationLayer> chatIntegrationLayerInstance;
20 if (chatIntegrationLayerInstance == nullptr)
21 {
22 chatIntegrationLayerInstance.reset( new ChatIntegrationLayer() );
23 }
24
25 return chatIntegrationLayerInstance;
26}
27
28ChatIntegrationLayer::ChatIntegrationLayer()
29{
30 ZeroMemory( m_chatVoicePacketsStatistic, sizeof(m_chatVoicePacketsStatistic) );
31}
32
33void ChatIntegrationLayer::InitializeChatManager(
34 __in bool combineCaptureBuffersIntoSinglePacket,
35 __in bool useKinectAsCaptureSource,
36 __in bool applySoundEffectsToCapturedAudio,
37 __in bool applySoundEffectsToChatRenderedAudio,
38 DQRNetworkManager *pDQRNet
39 )
40{
41 m_pDQRNet = pDQRNet;
42 {
43 Concurrency::critical_section::scoped_lock lock(m_chatPacketStatsLock);
44 ZeroMemory( m_chatVoicePacketsStatistic, sizeof(m_chatVoicePacketsStatistic) );
45 }
46
47 m_chatManager = ref new Microsoft::Xbox::GameChat::ChatManager();
48 m_chatManager->ChatSettings->DiagnosticsTraceLevel = Microsoft::Xbox::GameChat::GameChatDiagnosticsTraceLevel::Verbose;
49
50 // Optionally, change the default settings below as desired by commenting out and editing any of the following lines
51 // Otherwise these defaults are used.
52 //
53 // m_chatManager = ref new Microsoft::Xbox::GameChat::ChatManager( ChatSessionPeriod::ChatPeriodOf40Milliseconds );
54 // m_chatManager->ChatSettings->AudioThreadPeriodInMilliseconds = 40;
55 // m_chatManager->ChatSettings->AudioThreadAffinityMask = XAUDIO2_DEFAULT_PROCESSOR; // <- this means is core 5, same as the default XAudio2 core
56 // m_chatManager->ChatSettings->AudioThreadPriority = THREAD_PRIORITY_TIME_CRITICAL;
57 // m_chatManager->ChatSettings->AudioEncodingQuality = Windows::Xbox::Chat::EncodingQuality::Normal;
58 // m_chatManager->ChatSettings->DiagnosticsTraceLevel = Microsoft::Xbox::GameChat::GameChatDiagnosticsTraceLevel::Verbose;
59 m_chatManager->ChatSettings->CombineCaptureBuffersIntoSinglePacket = combineCaptureBuffersIntoSinglePacket; // if unset, it defaults to TRUE
60 m_chatManager->ChatSettings->UseKinectAsCaptureSource = useKinectAsCaptureSource; // if unset, it defaults to FALSE
61 m_chatManager->ChatSettings->PreEncodeCallbackEnabled = applySoundEffectsToCapturedAudio; // if unset, it defaults to FALSE
62 m_chatManager->ChatSettings->PostDecodeCallbackEnabled = applySoundEffectsToChatRenderedAudio; // if unset, it defaults to FALSE
63
64 InitializeCriticalSection(&m_csAddedUsers);
65
66 std::weak_ptr<ChatIntegrationLayer> weakPtrToThis = shared_from_this();
67
68#ifdef PROFILE
69 m_chatManager->ChatSettings->PerformanceCountersEnabled = true;
70#endif
71
72 // Upon enter constrained mode, mute everyone.
73 // Upon leaving constrained mode, unmute everyone who was previously muted.
74 m_tokenResourceAvailabilityChanged = Windows::ApplicationModel::Core::CoreApplication::ResourceAvailabilityChanged +=
75 ref new EventHandler< Platform::Object^ >( [weakPtrToThis] (Platform::Object^, Platform::Object^ )
76 {
77 // Using a std::weak_ptr instead of 'this' to avoid dangling pointer if caller class is released.
78 // Simply unregistering the callback in the destructor isn't enough to prevent a dangling pointer
79 std::shared_ptr<ChatIntegrationLayer> sharedPtrToThis(weakPtrToThis.lock());
80 if( sharedPtrToThis != nullptr )
81 {
82 if (Windows::ApplicationModel::Core::CoreApplication::ResourceAvailability == Windows::ApplicationModel::Core::ResourceAvailability::Constrained)
83 {
84 if( sharedPtrToThis->m_chatManager != nullptr )
85 {
86 sharedPtrToThis->m_chatManager->MuteAllUsersFromAllChannels();
87 }
88 }
89 else if(Windows::ApplicationModel::Core::CoreApplication::ResourceAvailability == Windows::ApplicationModel::Core::ResourceAvailability::Full)
90 {
91 if( sharedPtrToThis->m_chatManager != nullptr )
92 {
93 sharedPtrToThis->m_chatManager->UnmuteAllUsersFromAllChannels();
94
95 // The title should remember who was muted so when the Resume even occurs
96 // to avoid unmuting users who has been previously muted. Simply re-mute them here
97 }
98 }
99 }
100 });
101
102 m_tokenOnDebugMessage = m_chatManager->OnDebugMessage +=
103 ref new Windows::Foundation::EventHandler<Microsoft::Xbox::GameChat::DebugMessageEventArgs^>(
104 [weakPtrToThis] ( Platform::Object^, Microsoft::Xbox::GameChat::DebugMessageEventArgs^ args )
105 {
106 // Using a std::weak_ptr instead of 'this' to avoid dangling pointer if caller class is released.
107 // Simply unregistering the callback in the destructor isn't enough to prevent a dangling pointer
108 std::shared_ptr<ChatIntegrationLayer> sharedPtrToThis(weakPtrToThis.lock());
109 if( sharedPtrToThis != nullptr )
110 {
111 sharedPtrToThis->OnDebugMessageReceived(args);
112 }
113 });
114
115 m_tokenOnOutgoingChatPacketReady = m_chatManager->OnOutgoingChatPacketReady +=
116 ref new Windows::Foundation::EventHandler<Microsoft::Xbox::GameChat::ChatPacketEventArgs^>(
117 [weakPtrToThis] ( Platform::Object^, Microsoft::Xbox::GameChat::ChatPacketEventArgs^ args )
118 {
119 // Using a std::weak_ptr instead of 'this' to avoid dangling pointer if caller class is released.
120 // Simply unregistering the callback in the destructor isn't enough to prevent a dangling pointer
121 std::shared_ptr<ChatIntegrationLayer> sharedPtrToThis(weakPtrToThis.lock());
122 if( sharedPtrToThis != nullptr )
123 {
124 sharedPtrToThis->OnOutgoingChatPacketReady(args);
125 }
126 });
127
128 m_tokenOnCompareUniqueConsoleIdentifiers = m_chatManager->OnCompareUniqueConsoleIdentifiers +=
129 ref new Microsoft::Xbox::GameChat::CompareUniqueConsoleIdentifiersHandler(
130 [weakPtrToThis] ( Platform::Object^ obj1, Platform::Object^ obj2 )
131 {
132 // Using a std::weak_ptr instead of 'this' to avoid dangling pointer if caller class is released.
133 // Simply unregistering the callback in the destructor isn't enough to prevent a dangling pointer
134 std::shared_ptr<ChatIntegrationLayer> sharedPtrToThis(weakPtrToThis.lock());
135 if( sharedPtrToThis != nullptr )
136 {
137 return sharedPtrToThis->CompareUniqueConsoleIdentifiers(obj1, obj2);
138 }
139 else
140 {
141 return false;
142 }
143 });
144
145 m_tokenUserAudioDeviceAdded = WXS::User::AudioDeviceAdded +=
146 ref new Windows::Foundation::EventHandler<WXS::AudioDeviceAddedEventArgs^>(
147 [weakPtrToThis] ( Platform::Object^, WXS::AudioDeviceAddedEventArgs^ value )
148 {
149 std::shared_ptr<ChatIntegrationLayer> sharedPtrToThis(weakPtrToThis.lock());
150 if( sharedPtrToThis != nullptr )
151 {
152 sharedPtrToThis->EvaluateDevicesForUser(value->User);
153 }
154 });
155
156 m_tokenUserAudioDeviceRemoved = WXS::User::AudioDeviceRemoved +=
157 ref new Windows::Foundation::EventHandler<WXS::AudioDeviceRemovedEventArgs^>(
158 [weakPtrToThis] ( Platform::Object^, WXS::AudioDeviceRemovedEventArgs^ value )
159 {
160 std::shared_ptr<ChatIntegrationLayer> sharedPtrToThis(weakPtrToThis.lock());
161 if( sharedPtrToThis != nullptr )
162 {
163 sharedPtrToThis->EvaluateDevicesForUser(value->User);
164 }
165 });
166
167 m_tokenUserAudioDeviceChanged = WXS::User::AudioDeviceChanged +=
168 ref new Windows::Foundation::EventHandler<WXS::AudioDeviceChangedEventArgs^>(
169 [weakPtrToThis] ( Platform::Object^, WXS::AudioDeviceChangedEventArgs^ value )
170 {
171 std::shared_ptr<ChatIntegrationLayer> sharedPtrToThis(weakPtrToThis.lock());
172 if( sharedPtrToThis != nullptr )
173 {
174 sharedPtrToThis->EvaluateDevicesForUser(value->User);
175 }
176 });
177
178 //m_tokenSuspending = Windows::ApplicationModel::Core::CoreApplication::Suspending +=
179 // ref new EventHandler< Windows::ApplicationModel::SuspendingEventArgs^ >( [weakPtrToThis] (Platform::Object^, Windows::ApplicationModel::SuspendingEventArgs^ args)
180 //{
181 // // Upon Suspending, nothing needs to be done
182 //});
183
184 //m_tokenResuming = Windows::ApplicationModel::Core::CoreApplication::Resuming +=
185 // ref new EventHandler< Platform::Object^ >( [weakPtrToThis] (Platform::Object^, Windows::ApplicationModel::SuspendingEventArgs^ args)
186 //{
187 // // Upon Resuming, re-initialize the network, and reinitialize the chat session
188 //});
189}
190
191
192void ChatIntegrationLayer::Shutdown()
193{
194 if( m_chatManager != nullptr )
195 {
196 m_chatManager->OnDebugMessage -= m_tokenOnDebugMessage;
197 m_chatManager->OnOutgoingChatPacketReady -= m_tokenOnOutgoingChatPacketReady;
198 m_chatManager->OnCompareUniqueConsoleIdentifiers -= m_tokenOnCompareUniqueConsoleIdentifiers;
199 Windows::ApplicationModel::Core::CoreApplication::ResourceAvailabilityChanged -= m_tokenResourceAvailabilityChanged;
200 if( m_chatManager->ChatSettings->PreEncodeCallbackEnabled )
201 {
202 m_chatManager->OnPreEncodeAudioBuffer -= m_tokenOnPreEncodeAudioBuffer;
203 }
204 if( m_chatManager->ChatSettings->PostDecodeCallbackEnabled )
205 {
206 m_chatManager->OnPostDecodeAudioBuffer -= m_tokenOnPostDecodeAudioBuffer;
207 }
208 WXS::User::AudioDeviceAdded -= m_tokenUserAudioDeviceAdded;
209 WXS::User::AudioDeviceRemoved -= m_tokenUserAudioDeviceRemoved;
210 WXS::User::AudioDeviceChanged -= m_tokenUserAudioDeviceChanged;
211
212 DeleteCriticalSection(&m_csAddedUsers);
213
214 m_chatManager = nullptr;
215 }
216}
217
218void ChatIntegrationLayer::OnDebugMessageReceived(
219 __in Microsoft::Xbox::GameChat::DebugMessageEventArgs^ args
220 )
221{
222 // To integrate the Chat DLL in your game,
223 // change this to false and remove the LogComment calls,
224 // or integrate with your game's own UI/debug message logging system
225 bool outputToUI = false;
226
227 if( outputToUI )
228 {
229 if (args->ErrorCode == S_OK )
230 {
231 m_pDQRNet->LogComment(L"GameChat: " + args->Message);
232 }
233 else
234 {
235 m_pDQRNet->LogCommentWithError(L"GameChat: " + args->Message, args->ErrorCode);
236 }
237 }
238 else
239 {
240 // The string appear in the Visual Studio Output window
241#ifndef _CONTENT_PACKAGE
242 OutputDebugString( args->Message->Data() );
243#endif
244 }
245}
246
247void ChatIntegrationLayer::GameUI_RecordPacketStatistic(
248 __in Microsoft::Xbox::GameChat::ChatMessageType messageType,
249 __in ChatPacketType chatPacketType
250 )
251{
252 uint32 messageTypeInt = static_cast<uint32>(messageType);
253 if( messageType > Microsoft::Xbox::GameChat::ChatMessageType::InvalidMessage )
254 {
255 return;
256 }
257
258 {
259 Concurrency::critical_section::scoped_lock lock(m_chatPacketStatsLock);
260 m_chatVoicePacketsStatistic[static_cast<int>(chatPacketType)][messageTypeInt]++;
261 }
262}
263
264int ChatIntegrationLayer::GameUI_GetPacketStatistic(
265 __in Microsoft::Xbox::GameChat::ChatMessageType messageType,
266 __in ChatPacketType chatPacketType
267 )
268{
269 uint32 messageTypeInt = static_cast<uint32>(messageType);
270 if( messageType > Microsoft::Xbox::GameChat::ChatMessageType::InvalidMessage )
271 {
272 return 0;
273 }
274
275 {
276 Concurrency::critical_section::scoped_lock lock(m_chatPacketStatsLock);
277 return m_chatVoicePacketsStatistic[static_cast<int>(chatPacketType)][messageTypeInt];
278 }
279}
280
281void ChatIntegrationLayer::OnOutgoingChatPacketReady(
282 __in Microsoft::Xbox::GameChat::ChatPacketEventArgs^ args
283 )
284{
285 byte *bytes;
286 int byteCount;
287
288 GetBufferBytes(args->PacketBuffer, &bytes);
289 byteCount = args->PacketBuffer->Length;
290 unsigned int address = 0;
291 if( !args->SendPacketToAllConnectedConsoles )
292 {
293 address = safe_cast<unsigned int>(args->UniqueTargetConsoleIdentifier);
294 }
295 m_pDQRNet->SendBytesChat(address, bytes, byteCount, args->SendReliable, args->SendInOrder, args->SendPacketToAllConnectedConsoles);
296
297 GameUI_RecordPacketStatistic( args->ChatMessageType, ChatPacketType::OutgoingPacket );
298
299}
300
301void ChatIntegrationLayer::OnIncomingChatMessage(
302 unsigned int sessionAddress,
303 Platform::Array<byte>^ message
304 )
305{
306 // To integrate the Chat DLL in your game, change the following code to use your game's network layer.
307 // Ignore the OnChatMessageReceived event as that is specific to this sample's simple network layer.
308 // Instead your title should upon receiving a packet, extract the chat message from it, and then call m_chatManager->ProcessIncomingChatMessage as shown below
309 // You will need to isolate chat messages to be unique from the rest of you game's other message types.
310
311 // uniqueRemoteConsoleIdentifier is a Platform::Object^ and can be cast or unboxed to most types.
312 // What exactly you use doesn't matter, but optimally it would be something that uniquely identifies a console on in the session.
313 // A Windows::Xbox::Networking::SecureDeviceAssociation^ is perfect to use if you have access to it.
314
315 // This is how you would convert from byte array to a IBuffer^
316 //
317 // Windows::Storage::Streams::IBuffer^ destBuffer = ref new Windows::Storage::Streams::Buffer( sourceByteBufferSize );
318 // byte* destBufferBytes = nullptr;
319 // GetBufferBytes( destBuffer, &destBufferBytes );
320 // errno_t err = memcpy_s( destBufferBytes, destBuffer->Capacity, sourceByteBuffer, sourceByteBufferSize );
321 // THROW_HR_IF(err != 0, E_FAIL);
322 // destBuffer->Length = sourceByteBufferSize;
323
324 // This is how you would convert from an int to a Platform::Object^
325 // Platform::Object obj = IntToPlatformObject(5);
326
327 Windows::Storage::Streams::IBuffer^ chatMessage = ArrayToBuffer(message);
328 Platform::Object^ uniqueRemoteConsoleIdentifier = (Platform::Object^)sessionAddress;
329
330 if( m_chatManager != nullptr )
331 {
332 Microsoft::Xbox::GameChat::ChatMessageType chatMessageType = m_chatManager->ProcessIncomingChatMessage(chatMessage, uniqueRemoteConsoleIdentifier);
333
334 GameUI_RecordPacketStatistic( chatMessageType, ChatPacketType::IncomingPacket );
335 }
336}
337
338// Only add people who intend to play.
339void ChatIntegrationLayer::AddAllLocallySignedInUsersToChatClient(
340 __in uint8 channelIndex,
341 __in Windows::Foundation::Collections::IVectorView<Windows::Xbox::System::User^>^ locallySignedInUsers
342 )
343{
344 // To integrate the Chat DLL in your game,
345 // add all locally signed in users to the chat client
346 for each( Windows::Xbox::System::User^ user in locallySignedInUsers )
347 {
348 if( user != nullptr )
349 {
350// LogComment(L"Adding Local User to Chat Client");
351 AddLocalUserToChatChannel( channelIndex, user );
352 }
353 }
354}
355
356ChatIntegrationLayer::AddedUser::AddedUser(Windows::Xbox::System::IUser^ user, bool canCaptureAudio)
357{
358 m_user = user;
359 m_canCaptureAudio = canCaptureAudio;
360}
361
362
363void ChatIntegrationLayer::AddLocalUser( __in Windows::Xbox::System::IUser^ user )
364{
365 // Check we haven't added already
366 for( int i = 0; i < m_addedUsers.size(); i++ )
367 {
368 if( m_addedUsers[i]->m_user->XboxUserId == user->XboxUserId )
369 {
370 return;
371 }
372 }
373
374 bool kinectAvailable = false;
375 Windows::Kinect::KinectSensor^ sensor = Windows::Kinect::KinectSensor::GetDefault();
376 if( sensor )
377 {
378 sensor->Open();
379 if( sensor->IsAvailable )
380 {
381 kinectAvailable = true;
382 m_pDQRNet->LogComment(L"Evaluated that kinect is available\n");
383 }
384 sensor->Close();
385 }
386
387 EnterCriticalSection(&m_csAddedUsers);
388 // First establish whether we have an appropriate audio device at this time
389 bool canCaptureAudio = false;
390 for each( WXS::IAudioDeviceInfo^ audioDevice in user->AudioDevices )
391 {
392 m_pDQRNet->LogComment(L"Evaluating device " + audioDevice->DeviceCategory.ToString() + L" " +
393 audioDevice->DeviceType.ToString() + L" " +
394 audioDevice->Id + L" " +
395 audioDevice->Sharing.ToString() + L" " +
396 audioDevice->IsMicrophoneMuted.ToString() + L"\n");
397
398 // Consider shared devices only if kinect is actually available - every machine seems to claim a shared device whether kinect is attached or not
399 if( ( audioDevice->DeviceType == WXS::AudioDeviceType::Capture ) && ( kinectAvailable || ( audioDevice->Sharing != WXS::AudioDeviceSharing::Shared) ) )
400 {
401 canCaptureAudio = true;
402 }
403 }
404
405 // If we can capture audio initially, then register with the chat session. Otherwise we'll reevaluate this situation when audio devices change
406 if( canCaptureAudio )
407 {
408 AddLocalUserToChatChannel( 0 , user );
409 }
410
411 // Add to vector of users that we are tracking in the chat system
412 m_addedUsers.push_back(new AddedUser(user, canCaptureAudio));
413 LeaveCriticalSection(&m_csAddedUsers);
414}
415
416// Remove from our list of tracked users, if the user is already there
417void ChatIntegrationLayer::RemoveLocalUser( __in Windows::Xbox::System::IUser^ user )
418{
419 EnterCriticalSection(&m_csAddedUsers);
420 for( auto it = m_addedUsers.begin(); it != m_addedUsers.end(); it++ )
421 {
422 if( (*it)->m_user->XboxUserId == user->XboxUserId )
423 {
424 delete (*it);
425 m_addedUsers.erase(it);
426 LeaveCriticalSection(&m_csAddedUsers);
427 return;
428 }
429 }
430 LeaveCriticalSection(&m_csAddedUsers);
431}
432
433// 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.
434void ChatIntegrationLayer::EvaluateDevicesForUser(__in Windows::Xbox::System::IUser^ user )
435{
436 bool kinectAvailable = false;
437 Windows::Kinect::KinectSensor^ sensor = Windows::Kinect::KinectSensor::GetDefault();
438 if( sensor )
439 {
440 sensor->Open();
441 if( sensor->IsAvailable )
442 {
443 kinectAvailable = true;
444 m_pDQRNet->LogComment(L"Evaluated that kinect is available\n");
445 }
446 sensor->Close();
447 }
448
449 EnterCriticalSection(&m_csAddedUsers);
450 for( int i = 0; i < m_addedUsers.size(); i++ )
451 {
452 AddedUser *addedUser = m_addedUsers[i];
453 if( addedUser->m_user->XboxUserId == user->XboxUserId )
454 {
455 bool canCaptureAudio = false;
456 for each( WXS::IAudioDeviceInfo^ audioDevice in addedUser->m_user->AudioDevices )
457 {
458 // Consider shared devices only if kinect is actually available - every machine seems to claim a shared device whether kinect is attached or not
459 if( ( audioDevice->DeviceType == WXS::AudioDeviceType::Capture ) && ( kinectAvailable || ( audioDevice->Sharing != WXS::AudioDeviceSharing::Shared) ) )
460 {
461 canCaptureAudio = true;
462 break;
463 }
464 }
465 if( canCaptureAudio != addedUser->m_canCaptureAudio )
466 {
467 if( canCaptureAudio )
468 {
469 AddLocalUserToChatChannel(0, addedUser->m_user );
470 }
471 else
472 {
473 RemoveUserFromChatChannel(0, addedUser->m_user );
474 }
475 addedUser->m_canCaptureAudio = canCaptureAudio;
476 LeaveCriticalSection(&m_csAddedUsers);
477 return;
478 }
479 }
480 }
481 LeaveCriticalSection(&m_csAddedUsers);
482}
483
484void ChatIntegrationLayer::AddLocalUserToChatChannel(
485 __in uint8 channelIndex,
486 __in Windows::Xbox::System::IUser^ user
487 )
488{
489 // Adds a local user to a specific channel.
490
491 // This is helper function waits for the task to cm_chatManageromplete so shouldn't be called from the UI thread
492 // Remove the .wait() and return the result of concurrency::create_task() if you want to call it from the UI thread
493 // and chain PPL tasks together
494
495 m_pDQRNet->LogComment( L">>>>>>>>>>>>> AddLocalUserToChatChannel" );
496 if( m_chatManager != nullptr )
497 {
498 auto asyncOp = m_chatManager->AddLocalUserToChatChannelAsync( channelIndex, user );
499 concurrency::create_task( asyncOp )
500 .then( [this] ( concurrency::task<void> t )
501 {
502 // Error handling
503 try
504 {
505 t.get();
506 }
507 catch ( Platform::Exception^ ex )
508 {
509 m_pDQRNet->LogCommentWithError( L"AddLocalUserToChatChannelAsync failed", ex->HResult );
510 }
511 })
512 .wait();
513 }
514}
515
516void ChatIntegrationLayer::RemoveRemoteConsole(
517 unsigned int address
518 )
519{
520 // uniqueConsoleIdentifier is a Platform::Object^ and can be cast or unboxed to most types.
521 // What exactly you use doesn't matter, but optimally it would be something that uniquely identifies a console on in the session.
522 // A Windows::Xbox::Networking::SecureDeviceAssociation^ is perfect to use if you have access to it.
523
524 // This is how you would convert from an int to a Platform::Object^
525 // Platform::Object obj = IntToPlatformObject(5);
526
527 // This is helper function waits for the task to complete so shouldn't be called from the UI thread
528 // Remove the .wait() and return the result of concurrency::create_task() if you want to call it from the UI thread
529 // and chain PPL tasks together
530
531 Platform::Object^ uniqueRemoteConsoleIdentifier = (Platform::Object^)address;
532
533 if( m_chatManager != nullptr )
534 {
535 auto asyncOp = m_chatManager->RemoveRemoteConsoleAsync( uniqueRemoteConsoleIdentifier );
536 concurrency::create_task( asyncOp ).then( [this] ( concurrency::task<void> t )
537 {
538 // Error handling
539 try
540 {
541 t.get();
542 }
543 catch ( Platform::Exception^ ex )
544 {
545 m_pDQRNet->LogCommentWithError( L"RemoveRemoteConsoleAsync failed", ex->HResult );
546 }
547 })
548 .wait();
549 }
550}
551
552void ChatIntegrationLayer::RemoveUserFromChatChannel(
553 __in uint8 channelIndex,
554 __in Windows::Xbox::System::IUser^ user
555 )
556{
557 if( m_chatManager != nullptr )
558 {
559 // This is helper function waits for the task to complete so shouldn't be called from the UI thread
560 // Remove the .wait() and return the result of concurrency::create_task() if you want to call it from the UI thread
561 // and chain PPL tasks together
562
563 auto asyncOp = m_chatManager->RemoveLocalUserFromChatChannelAsync( channelIndex, user );
564 concurrency::create_task( asyncOp ).then( [this] ( concurrency::task<void> t )
565 {
566 // Error handling
567 try
568 {
569 t.get();
570 }
571 catch ( Platform::Exception^ ex )
572 {
573 m_pDQRNet->LogCommentWithError( L"RemoveLocalUserFromChatChannelAsync failed", ex->HResult );
574 }
575 })
576 .wait();
577 }
578}
579
580void ChatIntegrationLayer::OnNewSessionAddressAdded(
581 __in unsigned int address
582 )
583{
584 m_pDQRNet->LogCommentFormat( L">>>>>>>>>>>>> OnNewSessionAddressAdded (%d)",address );
585 Platform::Object^ uniqueConsoleIdentifier = (Platform::Object^)address;
586 /// Call this when a new console connects.
587 /// This adds this console to the chat layer
588 if( m_chatManager != nullptr )
589 {
590 m_chatManager->HandleNewRemoteConsole(uniqueConsoleIdentifier );
591 }
592}
593
594Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ ChatIntegrationLayer::GetChatUsers()
595{
596 if( m_chatManager != nullptr )
597 {
598 return m_chatManager->GetChatUsers();
599 }
600
601 return nullptr;
602}
603
604bool ChatIntegrationLayer::HasMicFocus()
605{
606 if( m_chatManager != nullptr )
607 {
608 return m_chatManager->HasMicFocus;
609 }
610
611 return false;
612}
613
614Platform::Object^ ChatIntegrationLayer::IntToPlatformObject(
615 __in int val
616 )
617{
618 return (Platform::Object^)val;
619
620 // You can also do the same using a PropertyValue.
621 //return Windows::Foundation::PropertyValue::CreateInt32(val);
622}
623
624int ChatIntegrationLayer::PlatformObjectToInt(
625 __in Platform::Object^ uniqueRemoteConsoleIdentifier
626 )
627{
628 return safe_cast<int>( uniqueRemoteConsoleIdentifier );
629
630 // You can also do the same using a PropertyValue.
631 //return safe_cast<Windows::Foundation::IPropertyValue^>(uniqueRemoteConsoleIdentifier)->GetInt32();
632}
633
634void ChatIntegrationLayer::HandleChatChannelChanged(
635 __in uint8 oldChatChannelIndex,
636 __in uint8 newChatChannelIndex,
637 __in Microsoft::Xbox::GameChat::ChatUser^ chatUser
638 )
639{
640 // We remember if the local user was currently muted from all channels. And when we switch channels,
641 // we ensure that the state persists. For remote users, title should implement this themselves
642 // based on title game design if they want to persist the muting state.
643
644 bool wasUserMuted = false;
645 IUser^ userBeingRemoved = nullptr;
646
647 if (chatUser != nullptr && chatUser->IsLocal)
648 {
649 wasUserMuted = chatUser->IsMuted;
650 userBeingRemoved = chatUser->User;
651 if (userBeingRemoved != nullptr)
652 {
653 RemoveUserFromChatChannel(oldChatChannelIndex, userBeingRemoved);
654 AddLocalUserToChatChannel(newChatChannelIndex, userBeingRemoved);
655 }
656 }
657
658 // If the local user was muted earlier, get the latest chat users and mute him again on the newly added channel.
659 if (wasUserMuted && userBeingRemoved != nullptr)
660 {
661 auto chatUsers = GetChatUsers();
662 if (chatUsers != nullptr )
663 {
664 for (UINT chatUserIndex = 0; chatUserIndex < chatUsers->Size; chatUserIndex++)
665 {
666 Microsoft::Xbox::GameChat::ChatUser^ chatUser = chatUsers->GetAt(chatUserIndex);
667 if( chatUser != nullptr && (chatUser->XboxUserId == userBeingRemoved->XboxUserId) )
668 {
669 m_chatManager->MuteUserFromAllChannels(chatUser);
670 break;
671 }
672 }
673 }
674 }
675}
676
677void ChatIntegrationLayer::ChangeChatUserMuteState(
678 __in Microsoft::Xbox::GameChat::ChatUser^ chatUser
679 )
680{
681 /// Helper function to swap the mute state of a specific chat user
682 if( m_chatManager != nullptr && chatUser != nullptr)
683 {
684 if (chatUser->IsMuted)
685 {
686 m_chatManager->UnmuteUserFromAllChannels(chatUser);
687 }
688 else
689 {
690 m_chatManager->MuteUserFromAllChannels(chatUser);
691 }
692 }
693}
694
695Microsoft::Xbox::GameChat::ChatUser^ ChatIntegrationLayer::GetChatUserByXboxUserId(
696 __in Platform::String^ xboxUserId
697 )
698{
699 Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ chatUsers = GetChatUsers();
700 for each (Microsoft::Xbox::GameChat::ChatUser^ chatUser in chatUsers)
701 {
702 if (chatUser != nullptr && ( chatUser->XboxUserId == xboxUserId ) )
703 {
704 return chatUser;
705 }
706 }
707
708 return nullptr;
709}
710
711
712bool ChatIntegrationLayer::CompareUniqueConsoleIdentifiers(
713 __in Platform::Object^ uniqueRemoteConsoleIdentifier1,
714 __in Platform::Object^ uniqueRemoteConsoleIdentifier2
715 )
716{
717 if (uniqueRemoteConsoleIdentifier1 == nullptr || uniqueRemoteConsoleIdentifier2 == nullptr)
718 {
719 return false;
720 }
721
722 // uniqueRemoteConsoleIdentifier is a Platform::Object^ and can be cast or unboxed to most types.
723 // We're using XRNS addresses, which are unsigned ints
724 unsigned int address1 = safe_cast<unsigned int>(uniqueRemoteConsoleIdentifier1);
725 unsigned int address2 = safe_cast<unsigned int>(uniqueRemoteConsoleIdentifier2);
726 return address1 == address2;
727}
728
729Microsoft::Xbox::GameChat::ChatPerformanceCounters^ ChatIntegrationLayer::GetChatPerformanceCounters()
730{
731 if( m_chatManager != nullptr &&
732 m_chatManager->ChatSettings != nullptr &&
733 m_chatManager->ChatSettings->PerformanceCountersEnabled )
734 {
735 return m_chatManager->ChatPerformanceCounters;
736 }
737
738 return nullptr;
739}
740
741void ChatIntegrationLayer::OnControllerPairingChanged( Windows::Xbox::Input::ControllerPairingChangedEventArgs^ args )
742{
743#if 0
744 auto controller = args->Controller;
745 if ( controller )
746 {
747 if ( controller->Type == L"Windows.Xbox.Input.Gamepad" )
748 {
749 // Either add the user or sign one in
750 User^ user = args->User;
751 if ( user != nullptr )
752 {
753 g_sampleInstance->GetLoggingUI()->LogCommentToUI("OnControllerPairingChanged: " + user->DisplayInfo->Gamertag);
754 AddLocalUserToChatChannel(
755 g_sampleInstance->GetUISelectionChatChannelIndex(),
756 user
757 );
758 }
759 }
760 }
761#endif
762}
763
764void ChatIntegrationLayer::ToggleRenderTargetVolume()
765{
766 // Simple toggle logic to just show usage of LocalRenderTargetVolume property
767 static bool makeRenderTargetVolumeQuiet = false;
768 makeRenderTargetVolumeQuiet = !makeRenderTargetVolumeQuiet;
769
770 auto chatUsers = GetChatUsers();
771 if (chatUsers != nullptr )
772 {
773 for (UINT chatUserIndex = 0; chatUserIndex < chatUsers->Size; chatUserIndex++)
774 {
775 Microsoft::Xbox::GameChat::ChatUser^ chatUser = chatUsers->GetAt(chatUserIndex);
776 if( chatUser != nullptr && chatUser->IsLocal )
777 {
778 chatUser->LocalRenderTargetVolume = ( makeRenderTargetVolumeQuiet ) ? 0.1f : 1.0f;
779 }
780 }
781 }
782}
783
784
785void ChatIntegrationLayer::GetBufferBytes( __in Windows::Storage::Streams::IBuffer^ buffer, __out byte** ppOut )
786{
787 if ( ppOut == nullptr || buffer == nullptr )
788 {
789 throw ref new Platform::InvalidArgumentException();
790 }
791
792 *ppOut = nullptr;
793
794 Microsoft::WRL::ComPtr<IInspectable> srcBufferInspectable(reinterpret_cast<IInspectable*>( buffer ));
795 Microsoft::WRL::ComPtr<Windows::Storage::Streams::IBufferByteAccess> srcBufferByteAccess;
796 srcBufferInspectable.As(&srcBufferByteAccess);
797 srcBufferByteAccess->Buffer(ppOut);
798}
799
800Windows::Storage::Streams::IBuffer^ ChatIntegrationLayer::ArrayToBuffer( __in Platform::Array<byte>^ array )
801{
802 Windows::Storage::Streams::DataWriter^ writer = ref new Windows::Storage::Streams::DataWriter();
803 writer->WriteBytes(array);
804 return writer->DetachBuffer();
805}