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 "PartyController.h"
9#include "DQRNetworkManager.h"
10#include <collection.h>
11
12/*
13#include "time.h"
14#include "../GameLogic/Game.h"
15#include "../Utils/LogController.h"
16#include "../Utils/Utils.h"
17#include "UserController.h"
18#include "SessionController.h"
19*/
20
21using namespace Concurrency;
22using namespace Microsoft::Xbox::Services::Multiplayer;
23using namespace Microsoft::Xbox::Services::Matchmaking;
24using namespace Microsoft::Xbox::Services;
25using namespace Windows::Foundation::Collections;
26using namespace Windows::Foundation;
27using namespace Windows::Storage::Streams;
28using namespace Windows::Storage;
29using namespace Windows::System;
30using namespace Windows::UI::Core;
31using namespace Windows::Xbox::Multiplayer;
32using namespace Windows::Xbox::Networking;
33using namespace Windows::Xbox::System;
34
35
36PartyController::PartyController(DQRNetworkManager *pDQRNet) :
37 m_isGameSessionReadyEventTriggered(false),
38 m_isGamePlayerEventRegistered(false),
39 m_pDQRNet(pDQRNet)
40{
41}
42
43void PartyController::DebugPrintPartyView( Windows::Xbox::Multiplayer::PartyView^ partyView )
44{
45 DQRNetworkManager::LogComment( L"PartyView:" );
46 if( partyView == nullptr )
47 {
48 DQRNetworkManager::LogComment( L" No party view" );
49 return;
50 }
51
52 DQRNetworkManager::LogComment( L" GameSession::SessionName: " + (partyView->GameSession ? partyView->GameSession->SessionName : L"NONE") );
53 DQRNetworkManager::LogComment( L" GameSession::SessionTemplateName " + (partyView->GameSession ? partyView->GameSession->SessionTemplateName : L"NONE") );
54 DQRNetworkManager::LogComment( L" GameSession::ServiceConfigurationId " + (partyView->GameSession ? partyView->GameSession->ServiceConfigurationId : L"NONE") );
55 DQRNetworkManager::LogComment( L" MatchSession: " + (partyView->MatchSession ? partyView->MatchSession->SessionName : L"NONE") );
56 DQRNetworkManager::LogComment( L" IsPartyInAnotherTitle: " + partyView->IsPartyInAnotherTitle.ToString() );
57
58 DQRNetworkManager::LogComment( L" Members: " + partyView->Members->Size.ToString() );
59 for( PartyMember^ member : partyView->Members )
60 {
61 DQRNetworkManager::LogComment( L" Member:" );
62 DQRNetworkManager::LogComment( L" XboxUserID: " + member->XboxUserId );
63 DQRNetworkManager::LogCommentFormat( L" IsLocalUser: %s", member->IsLocal ? L"TRUE" : L"FALSE" );
64// DQRNetworkManager::LogComment( L" JoinTime: " + Utils::DateTimeToString(member->JoinTime) );
65 }
66
67 DQRNetworkManager::LogComment( L" ReservedMembers: " + partyView->ReservedMembers->Size.ToString() );
68 for( Platform::String^ memberXuid : partyView->ReservedMembers )
69 {
70 DQRNetworkManager::LogComment( L" ReservedMember:" );
71 DQRNetworkManager::LogComment( L" XboxUserID " + memberXuid );
72 }
73
74 DQRNetworkManager::LogComment( L" MembersGroupedByDevice: " + partyView->MembersGroupedByDevice->Size.ToString() );
75 for( PartyMemberDeviceGroup^ partyMemberDeviceGroup : partyView->MembersGroupedByDevice )
76 {
77 DQRNetworkManager::LogComment( L" Device:" );
78 for( PartyMember^ member : partyMemberDeviceGroup->Members )
79 {
80 DQRNetworkManager::LogComment( L" Member:" );
81 DQRNetworkManager::LogComment( L" XboxUserID: " + member->XboxUserId );
82 DQRNetworkManager::LogCommentFormat( L" IsLocalUser: %s", member->IsLocal ? L"TRUE" : L"FALSE" );
83// DQRNetworkManager::LogComment( L" JoinTime: " + Utils::DateTimeToString(member->JoinTime) );
84 }
85 }
86}
87
88void PartyController::RefreshPartyView()
89{
90// LogComment( L"RefreshPartyView" );
91
92 PartyView^ partyView;
93 try
94 {
95 IAsyncOperation<PartyView^>^ partyOperation = Party::GetPartyViewAsync();
96 create_task(partyOperation)
97 .then([this, &partyView] (task<PartyView^> t)
98 {
99 try
100 {
101 Concurrency::critical_section::scoped_lock lock(m_lock);
102 partyView = t.get();
103 }
104 catch ( Platform::Exception^ ex )
105 {
106 if( ex->HResult != (int)Windows::Xbox::Multiplayer::PartyErrorStatus::EmptyParty )
107 {
108// LogCommentWithError( L"GetPartyView failed", ex->HResult );
109 }
110
111 }
112 }).wait();
113 }
114 catch ( Platform::Exception^ ex )
115 {
116 partyView = nullptr;
117// LogCommentWithError( L"GetPartyView failed", ex->HResult );
118 }
119
120 DebugPrintPartyView(partyView);
121
122 SetPartyView(partyView);
123}
124
125// Add any players specified in userMask to the party. This method returns a bool that is true if the a player was added, which wasn't already
126// in the game session associated with the party. This is used to determine whether we should be waiting for a slot to be added for it by the host.
127bool PartyController::AddLocalUsersToParty(int userMask, Windows::Xbox::System::User^ primaryUser)
128{
129 bool addedNewPlayerToPartyThatIsntInSession = false;
130
131 PartyView^ partyView = GetPartyView();
132
133 // If there's already a party, then attempt to get a session document as we'll be needing this later
134 MultiplayerSession^ session;
135 if( partyView )
136 {
137 MXS::XboxLiveContext^ xblContext = ref new MXS::XboxLiveContext(primaryUser);
138 if( xblContext )
139 {
140 // Get a copy of the session document, for this user
141 auto multiplayerSessionAsync = xblContext->MultiplayerService->GetCurrentSessionAsync( m_pDQRNet->ConvertToMicrosoftXboxServicesMultiplayerSessionReference(partyView->GameSession));
142 create_task(multiplayerSessionAsync).then([&session,this](task<MXSM::MultiplayerSession^> t)
143 {
144 try
145 {
146 session = t.get(); // if t.get() didn't throw, it succeeded
147 }
148 catch (Platform::COMException^ ex)
149 {
150 DQRNetworkManager::LogCommentWithError( L"GetCurrentSessionAsync failed", ex->HResult );
151 }
152 })
153 .wait();
154 }
155 }
156
157 // Adds all local players that are specified in userMask to the party belonging to the primary player
158 IVector<Windows::Xbox::System::User^>^ localUsersToAddVector = ref new Platform::Collections::Vector<Windows::Xbox::System::User^>;
159 for( int i = 0; i < 4; i++ )
160 {
161 if( userMask & ( 1 << i ) )
162 {
163 bool alreadyHere = false;
164 if( partyView )
165 {
166 Windows::Xbox::System::User^ userToAdd = ProfileManager.GetUser(i, true);
167 for( int j = 0; j < partyView->Members->Size; j++ )
168 {
169 if( partyView->Members->GetAt(j)->XboxUserId == userToAdd->XboxUserId )
170 {
171 primaryUser = userToAdd; // If there is already a party, then the acting user passed to AddLocalUsersAsync must be a user that is in that party, so make sure this is the case. Doesn't matter Which user.
172 alreadyHere = true;
173 break;
174 }
175 }
176 }
177
178 if( !alreadyHere )
179 {
180 localUsersToAddVector->Append(ProfileManager.GetUser(i));
181 bool alreadyInSession = false;
182 if( session )
183 {
184 if( m_pDQRNet->IsPlayerInSession( ProfileManager.GetUser(i, true)->XboxUserId, session, NULL ) )
185 {
186 alreadyInSession = true;
187 }
188 }
189 if( !alreadyInSession )
190 {
191 addedNewPlayerToPartyThatIsntInSession = true;
192 }
193 }
194 }
195 }
196
197 IVectorView<Windows::Xbox::System::User^>^ localUsersToAddVecView = localUsersToAddVector->GetView();
198
199 try
200 {
201 IAsyncAction^ addMemberOperation = Party::AddLocalUsersAsync( primaryUser, localUsersToAddVecView );
202
203 create_task(addMemberOperation)
204 .then([this] (task<void> t)
205 {
206 try
207 {
208 t.get();
209 RefreshPartyView();
210 }
211 catch (Platform::COMException^ ex)
212 {
213 DQRNetworkManager::LogCommentWithError( L"AddLocalUsersAsync failed", ex->HResult );
214 }
215 catch (Platform::OperationCanceledException^ ex)
216 {
217 DQRNetworkManager::LogCommentWithError( L"AddLocalUsersAsync failed - operation cancelled", ex->HResult );
218 }
219 }).wait();
220 }
221 catch (Platform::COMException^ ex)
222 {
223 DQRNetworkManager::LogCommentWithError( L"AddLocalUsersAsync failed to create", ex->HResult );
224 }
225
226 return addedNewPlayerToPartyThatIsntInSession;
227}
228
229void PartyController::CheckPartySessionFull(Windows::Xbox::System::User^ primaryUser)
230{
231 RefreshPartyView();
232
233 PartyView^ partyView = GetPartyView();
234
235 // If there's already a party, then attempt to get a session document
236 MultiplayerSession^ session;
237 if( partyView && partyView->GameSession)
238 {
239 MXS::XboxLiveContext^ xblContext = ref new MXS::XboxLiveContext(primaryUser);
240 if( xblContext )
241 {
242 // Get a copy of the session document, for this user
243 auto multiplayerSessionAsync = xblContext->MultiplayerService->GetCurrentSessionAsync( m_pDQRNet->ConvertToMicrosoftXboxServicesMultiplayerSessionReference(partyView->GameSession));
244 create_task(multiplayerSessionAsync).then([&session,this](task<MXSM::MultiplayerSession^> t)
245 {
246 try
247 {
248 session = t.get(); // if t.get() didn't throw, it succeeded
249 }
250 catch (Platform::COMException^ ex)
251 {
252 DQRNetworkManager::LogCommentWithError( L"GetCurrentSessionAsync failed", ex->HResult );
253 }
254 })
255 .wait();
256 }
257 if( session )
258 {
259 int nonLocalPlayerCount = 0;
260 for( int i = 0; i < session->Members->Size; i++ )
261 {
262 MXSM::MultiplayerSessionMember^ member = session->Members->GetAt(i);
263
264 bool isLocalPlayer = false;
265 for( int j = 4; j < 12; j++ )
266 {
267 WXS::User ^user = InputManager.GetUserForGamepad(j);
268 if( user != nullptr )
269 {
270 if( user->XboxUserId == member->XboxUserId )
271 {
272 isLocalPlayer = true;
273 }
274 }
275 }
276 if( !isLocalPlayer )
277 {
278 nonLocalPlayerCount++;
279 }
280 }
281 app.DebugPrintf(">>>>> Invited to a game with a non-local player count of %d\n", nonLocalPlayerCount);
282
283 if( nonLocalPlayerCount >= DQRNetworkManager::MAX_ONLINE_PLAYER_COUNT )
284 {
285 m_pDQRNet->FlagInvitedToFullSession();
286 }
287 }
288 }
289}
290
291void PartyController::SetJoinability(bool isJoinable)
292{
293 Party::Joinability = isJoinable ? WXM::SessionJoinability::JoinableByFriends : WXM::SessionJoinability::InviteOnly;
294}
295
296void PartyController::DisassociateSessionFromParty( Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionReference^ sessionReference)
297{
298 RefreshPartyView();
299
300 PartyView^ partyView = GetPartyView();
301
302 if( partyView )
303 {
304 // We need a user to be the acting user, when disassociating the party. Attempt to find a party member who is local.
305 for( int j = 0; j < partyView->Members->Size; j++ )
306 {
307 Windows::Xbox::Multiplayer::PartyMember^ partyMember = partyView->Members->GetAt(j);
308 if( partyMember->IsLocal )
309 {
310 IVectorView<Windows::Xbox::System::User^>^ localUsers = Windows::Xbox::System::User::Users;
311
312 for( int i = 0; i < localUsers->Size; i++ )
313 {
314 Windows::Xbox::System::User^ localUser = localUsers->GetAt(i);
315 if( localUser->XboxUserId == partyMember->XboxUserId )
316 {
317 // Convert the session reference (in Microsoft::Xbox::Services::Multiplayer namespace) to Windows::Xbox::Multiplayer names space so we can use in the party system
318 WXM::MultiplayerSessionReference^ winSessionRef = m_pDQRNet->ConvertToWindowsXboxMultiplayerSessionReference(sessionReference);
319
320 auto disassociateSessionAsync = WXM::Party::DisassociateGameSessionAsync(localUser, winSessionRef);
321 create_task(disassociateSessionAsync).then([this](task<void> t)
322 {
323 try
324 {
325 t.get(); // if t.get() didn't throw, it succeeded
326 }
327 catch (Platform::COMException^ ex)
328 {
329 m_pDQRNet->LogCommentWithError( L"DisassociateGameSessionAsync failed", ex->HResult );
330 }
331 });
332 //.wait(); // There are situations in which this never completes (?!) so waiting isn't a good idea
333 return;
334 }
335 }
336 }
337 }
338 }
339
340
341}
342
343void PartyController::RemoveLocalUsersFromParty(Windows::Xbox::System::User^ primaryUser)
344{
345 PartyView^ partyView = GetPartyView();
346
347 if( partyView == nullptr ) return;
348
349 // Attempt to remove all local users that are currently in the party
350 IVectorView<Windows::Xbox::System::User^>^ localUsers = Windows::Xbox::System::User::Users;
351 IVector<Windows::Xbox::System::User^>^ localUsersToRemoveVector = ref new Platform::Collections::Vector<Windows::Xbox::System::User^>;
352 for( int i = 0; i < localUsers->Size; i++ )
353 {
354 Windows::Xbox::System::User^ userToRemove = localUsers->GetAt(i);
355 for( int j = 0; j < partyView->Members->Size; j++ )
356 {
357 if( partyView->Members->GetAt(j)->XboxUserId == userToRemove->XboxUserId )
358 {
359 localUsersToRemoveVector->Append(userToRemove);
360 break;
361 }
362 }
363 }
364
365 IVectorView<Windows::Xbox::System::User^>^ localUsersToRemoveVecView = localUsersToRemoveVector->GetView();
366
367 IAsyncAction^ removeMemberOperation = Party::RemoveLocalUsersAsync( localUsersToRemoveVecView );
368
369 create_task(removeMemberOperation)
370 .then([this] (task<void> t)
371 {
372 try
373 {
374 t.get();
375 RefreshPartyView();
376 }
377 catch (Platform::COMException^ ex)
378 {
379 DQRNetworkManager::LogCommentWithError( L"RemoveLocalUsersAsync failed", ex->HResult );
380 }
381 }).wait();
382}
383
384void PartyController::RemoveLocalUsersFromParty(Windows::Xbox::System::User^ primaryUser, int playerMask, Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionReference^ sessionReference)
385{
386 PartyView^ partyView = GetPartyView();
387
388 if( partyView == nullptr ) return;
389
390 // Don't leave the party if it isn't actually the party belonging to the session we are in - this can happen when switching from one game session to another, after accepting an invite to a game whilst already playing
391 if( sessionReference != nullptr )
392 {
393 if( partyView->GameSession != nullptr )
394 {
395 if( partyView->GameSession->SessionName != sessionReference->SessionName ) return;
396 }
397 }
398
399 // Attempt to remove all specified local users that are currently in the party. Check that each player is actually in
400 // the party, as we'll get an exception for trying to remove players that aren't
401
402 IVector<Windows::Xbox::System::User^>^ localUsersToRemoveVector = ref new Platform::Collections::Vector<Windows::Xbox::System::User^>;
403
404 for( int i = 0; i < MAX_LOCAL_PLAYERS; i++ )
405 {
406 if( playerMask & ( 1 << i ) )
407 {
408 Windows::Xbox::System::User^ userToRemove = ProfileManager.GetUser(i, true);
409 if(userToRemove!=nullptr)
410 {
411 for( int j = 0; j < partyView->Members->Size; j++ )
412 {
413 if( partyView->Members->GetAt(j)->XboxUserId == userToRemove->XboxUserId )
414 {
415 localUsersToRemoveVector->Append(userToRemove);
416 break;
417 }
418 }
419 }
420 }
421 }
422
423 IVectorView<Windows::Xbox::System::User^>^ localUsersToRemoveVecView = localUsersToRemoveVector->GetView();
424
425 IAsyncAction^ removeMemberOperation = Party::RemoveLocalUsersAsync( localUsersToRemoveVecView );
426
427 create_task(removeMemberOperation)
428 .then([this] (task<void> t)
429 {
430 try
431 {
432 t.get();
433 RefreshPartyView();
434 }
435 catch (Platform::COMException^ ex)
436 {
437 DQRNetworkManager::LogCommentWithError( L"RemoveLocalUsersAsync failed", ex->HResult );
438 }
439 }).wait();
440}
441
442void PartyController::RemoveLocalUserFromParty(Windows::Xbox::System::User^ userToRemove)
443{
444 // Attempt to a specific local user from the party
445
446 RefreshPartyView();
447
448 // Don't need to do anything if there isn't currently a party
449 PartyView^ partyView = GetPartyView();
450
451 if( partyView == nullptr ) return;
452
453 // Don't need to do anything if this player isn't in the current party - the remove will raise an exception
454 bool inParty = false;
455 for( int i = 0; i < partyView->Members->Size; i++ )
456 {
457 if( partyView->Members->GetAt(i)->XboxUserId == userToRemove->XboxUserId )
458 {
459 inParty = true;
460 break;
461 }
462 }
463 if( !inParty ) return;
464
465 IVector<Windows::Xbox::System::User^>^ localUsersToRemoveVector = ref new Platform::Collections::Vector<Windows::Xbox::System::User^>;
466 localUsersToRemoveVector->Append(userToRemove);
467 IVectorView<Windows::Xbox::System::User^>^ localUsersToRemoveVecView = localUsersToRemoveVector->GetView();
468
469 IAsyncAction^ removeMemberOperation = Party::RemoveLocalUsersAsync( localUsersToRemoveVecView );
470
471 create_task(removeMemberOperation)
472 .then([this] (task<void> t)
473 {
474 try
475 {
476 t.get();
477 RefreshPartyView();
478 }
479 catch (Platform::COMException^ ex)
480 {
481 DQRNetworkManager::LogCommentWithError( L"RemoveLocalUsersAsync failed", ex->HResult );
482 }
483 }).wait();
484}
485
486void PartyController::RegisterGamePlayersChangedEventHandler()
487{
488 // Listen to Party::GamePlayersChanged
489 // Only the HOST should register for this event to detect if any party members were added that should be pulled into the game.
490 EventHandler<GamePlayersChangedEventArgs^>^ partyGamePlayersChanged = ref new EventHandler<GamePlayersChangedEventArgs^>(
491 [this] (Platform::Object^, GamePlayersChangedEventArgs^ eventArgs)
492 {
493 OnGamePlayersChanged( eventArgs );
494 });
495 m_partyGamePlayersChangedToken = Party::GamePlayersChanged += partyGamePlayersChanged;
496 m_isGamePlayerEventRegistered = true;
497}
498
499void PartyController::RegisterEventHandlers()
500{
501 // Listen to Party Roster Changed
502 EventHandler<PartyRosterChangedEventArgs^>^ partyRosterChangedEvent = ref new EventHandler<PartyRosterChangedEventArgs^>(
503 [this] (Platform::Object^, PartyRosterChangedEventArgs^ eventArgs)
504 {
505 OnPartyRosterChanged( eventArgs );
506 });
507 m_partyRosterChangedToken = Party::PartyRosterChanged += partyRosterChangedEvent;
508
509 // Listen to Party State Changed
510 EventHandler<PartyStateChangedEventArgs^>^ partyStateChangedEvent = ref new EventHandler<PartyStateChangedEventArgs^>(
511 [this] (Platform::Object^, PartyStateChangedEventArgs^ eventArgs)
512 {
513 OnPartyStateChanged( eventArgs );
514 });
515 m_partyStateChangedToken = Party::PartyStateChanged += partyStateChangedEvent;
516 // Listen to Game Session Ready
517 EventHandler<GameSessionReadyEventArgs^>^ gameSessionReadyEvent = ref new EventHandler<GameSessionReadyEventArgs^>(
518 [this] (Platform::Object^, GameSessionReadyEventArgs^ eventArgs)
519 {
520 OnGameSessionReady(eventArgs);
521 });
522 m_partyGameSessionReadyToken = Party::GameSessionReady += gameSessionReadyEvent;
523}
524
525void PartyController::UnregisterEventHandlers()
526{
527 Party::PartyRosterChanged -= m_partyRosterChangedToken;
528 Party::PartyStateChanged -= m_partyStateChangedToken;
529 Party::GameSessionReady -= m_partyGameSessionReadyToken;
530}
531
532void PartyController::UnregisterGamePlayersEventHandler()
533{
534 if(m_isGamePlayerEventRegistered)
535 {
536 Party::GamePlayersChanged -= m_partyGamePlayersChangedToken;
537 }
538}
539
540// This event will fire when :
541// - A new Match Session registered to party, or
542// - A new Game Session is registered to party (via RegisterGame call, or as a result of matchmaking), and
543// - Party Title ID changes (which will trigger change in IsPartyInAnotherTItle bool flag).
544void PartyController::OnPartyStateChanged( PartyStateChangedEventArgs^ eventArgs )
545{
546 DQRNetworkManager::LogComment( L"OnPartyStateChanged");
547 RefreshPartyView();
548}
549
550void PartyController::OnPartyRosterChanged( PartyRosterChangedEventArgs^ eventArgs )
551{
552 if( m_pDQRNet->m_multiplayerSession == nullptr) return;
553
554 RefreshPartyView();
555
556 XboxLiveContext^ xboxLiveContext = m_pDQRNet->m_primaryUserXboxLiveContext;
557 if( xboxLiveContext == nullptr ) return;
558
559 DQRNetworkManager::LogComment( L"OnPartyRosterChanged");
560
561 if( eventArgs->RemovedMembers->Size )
562 {
563 DQRNetworkManager::LogComment( L"Removed Members:");
564 }
565
566 // First, establish whether an active player local to this machine have been removed
567 bool activePlayerRemoved = false;
568 int playerLeavingMask = 0;
569
570 // If there's no party anymore, then we're all leaving
571 if( m_partyView == nullptr )
572 {
573 playerLeavingMask = m_pDQRNet->m_currentUserMask;
574 }
575 else
576 {
577 // Still a party, find out who left
578 for( int i = 0; i < eventArgs->RemovedMembers->Size; i++ )
579 {
580 DQRNetworkPlayer *player = m_pDQRNet->GetPlayerByXuid(PlayerUID(eventArgs->RemovedMembers->GetAt(i)->Data()));
581 if( player )
582 {
583 if( player->IsLocal() )
584 {
585 playerLeavingMask |= ( 1 << player->GetLocalPlayerIndex() );
586 }
587 }
588 DQRNetworkManager::LogComment(eventArgs->RemovedMembers->GetAt(i));
589 }
590 }
591
592 // If a local player is leaving the party, we want to handle it generally as if they had selected to exit from within the game, assuming that they have just deliberatly removed themselves from
593 // the party via the system interface. However... we may be being removed from this party because we have just accepted a request to join Another party via a "game session ready" sort of prompt.
594 // I don't think there's any way to distinguish these two things happening at this stage, so at this point we will signal to the DQR layer what has just happened,
595 // and it will only do something about it after some period of time has passed without a new game party becoming ready for the player to join
596 if( playerLeavingMask )
597 {
598 m_pDQRNet->HandlePlayerRemovedFromParty(playerLeavingMask);
599 }
600}
601
602#define LAST_INVITED_TIME_TIMEOUT 3 * 60 //secs
603void PartyController::AddAvailableGamePlayers(IVectorView<GamePlayer^>^ availablePlayers, int& remainingSlots, MultiplayerSession^ currentSession)
604{
605 bool bNewMembersAdded = false;
606 for each (Windows::Xbox::Multiplayer::GamePlayer^ player in availablePlayers)
607 {
608 if( remainingSlots <= 0 )
609 {
610 DQRNetworkManager::LogCommentFormat( L"No more available slots - broadcasting failure of adding player %s",player->XboxUserId->Data());
611 m_pDQRNet->SendAddPlayerFailed(player->XboxUserId);
612 continue;
613 }
614
615 // Not sure what this condition is actually for - removing until I can see a use for it
616#if 0
617 if( GetTimeBetweenInSeconds(player->LastInvitedTime, GetCurrentTime()) < LAST_INVITED_TIME_TIMEOUT )
618 {
619 DQRNetworkManager::LogComment( L"Possible user just exited; skipping join request" );
620 continue;
621 }
622#endif
623
624 if( m_pDQRNet->IsPlayerInSession(player->XboxUserId, currentSession, NULL))
625 {
626 DQRNetworkManager::LogComment( L"Player is already in session; skipping join request: " + player->XboxUserId->ToString() );
627 continue;
628 }
629
630 // Have a search through our local players, to see if we have a controller for this player. If so, then we'll want to add them directly to the game (if at all), rather than using
631 // the reservation system
632 bool bFoundLocal = false;
633 for( int i = 4; i < 12; i++ )
634 {
635 WXS::User^ user = InputManager.GetUserForGamepad(i);
636 if( user != nullptr )
637 {
638 if( user->XboxUserId == player->XboxUserId )
639 {
640 bFoundLocal = true;
641 // Check that they aren't in the game already (don't see how this could be the case anyway)
642 if( m_pDQRNet->GetPlayerByXuid( PlayerUID(player->XboxUserId->Data()) ) == NULL )
643 {
644 // And check whether they are signed in yet or not
645 int userIdx = -1;
646 for( int j = 0; j < MAX_LOCAL_PLAYERS; j++ )
647 {
648 WXS::User^ user2 = ProfileManager.GetUser(j);
649 if( user2 != nullptr )
650 {
651 if(user2->XboxUserId == user->XboxUserId)
652 {
653 userIdx = j;
654 break;
655 }
656 }
657 }
658
659 // If not found, then attempt to add them since we've found a controller
660 if( userIdx == -1 )
661 {
662 // Found the appropriate controller. Attempt to add it - this will return -1 if unsuccessful
663 userIdx = ProfileManager.AddGamepadToGame(i);
664 }
665
666 // Found a slot, so just need to add the user now
667 if( userIdx != -1 )
668 {
669 m_pDQRNet->AddLocalPlayerByUserIndex(userIdx);
670 }
671 }
672 }
673 }
674 }
675 if(bFoundLocal)
676 {
677 continue;
678 }
679
680 currentSession->AddMemberReservation( player->XboxUserId, m_pDQRNet->GetNextSmallIdAsJsonString(), true );
681 DQRNetworkManager::LogComment( L"Member added: " + player->XboxUserId->ToString() );
682 bNewMembersAdded = true;
683 remainingSlots--;
684 }
685
686 if(bNewMembersAdded)
687 {
688 XboxLiveContext^ xboxLiveContext = m_pDQRNet->m_primaryUserXboxLiveContext;
689 DQRNetworkManager::LogComment( L"New members found and added from related parties" );
690 HRESULT hr = S_OK;
691 Microsoft::Xbox::Services::Multiplayer::MultiplayerSession^ outputSession = m_pDQRNet->WriteSessionHelper(
692 xboxLiveContext,
693 currentSession,
694 Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionWriteMode::UpdateExisting,
695 hr
696 );
697
698 if(outputSession != nullptr)
699 {
700 Windows::Xbox::Multiplayer::MultiplayerSessionReference^ convertedSessionRef =
701 m_pDQRNet->ConvertToWindowsXboxMultiplayerSessionReference(currentSession->SessionReference);
702 Windows::Xbox::System::User^ actingUser = m_pDQRNet->m_primaryUser;
703 Party::PullReservedPlayersAsync(actingUser, convertedSessionRef);
704
705 m_pDQRNet->HandleSessionChange( outputSession );
706 }
707 }
708}
709
710void PartyController::OnGamePlayersChanged( GamePlayersChangedEventArgs^ eventArgs )
711{
712 DQRNetworkManager::LogComment( L"OnGamePlayersChanged");
713
714 RefreshPartyView();
715
716 if( m_pDQRNet->m_primaryUser == nullptr ) return;
717
718 IVectorView<GamePlayer^ >^ availablePlayers = nullptr;
719 auto asyncGetAvailablePlayers = Party::GetAvailableGamePlayersAsync(m_pDQRNet->m_primaryUser);
720 create_task(asyncGetAvailablePlayers).then([&availablePlayers](task<IVectorView<GamePlayer^ >^> t)
721 {
722 try
723 {
724 availablePlayers = t.get();
725 }
726 catch( Platform::COMException^ ex )
727 {
728 }
729 }).wait();
730
731 if( availablePlayers == nullptr ) return;
732
733 Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionReference^ sessionRef =
734 m_pDQRNet->ConvertToMicrosoftXboxServicesMultiplayerSessionReference(eventArgs->GameSession);
735
736 if(sessionRef == nullptr)
737 {
738// LogComment(L"OnGamePlayersChanged: invalid sessionRef.");
739 return;
740 }
741
742 XboxLiveContext^ xboxLiveContext = m_pDQRNet->m_primaryUserXboxLiveContext;
743 IAsyncOperation<Microsoft::Xbox::Services::Multiplayer::MultiplayerSession^>^ asyncOp = xboxLiveContext->MultiplayerService->GetCurrentSessionAsync( sessionRef );
744 create_task(asyncOp)
745 .then([this, availablePlayers, &xboxLiveContext] (task<Microsoft::Xbox::Services::Multiplayer::MultiplayerSession^> t)
746 {
747 try
748 {
749 Microsoft::Xbox::Services::Multiplayer::MultiplayerSession^ currentSession = t.get();
750
751 int remainingSlots = currentSession->SessionConstants->MaxMembersInSession - currentSession->Members->Size;
752
753 DQRNetworkManager::LogCommentFormat( L"OnGamePlayersChanged - calling AddAvailableGamePlayers, with %d players available",availablePlayers->Size);
754
755 // This should be called if we have available slots or not, because it also handles broadcasting failed joins to clients already in the game, which
756 // the clients need to know that adding a local player has failed
757 AddAvailableGamePlayers(availablePlayers, remainingSlots, currentSession);
758 }
759 catch ( Platform::COMException^ ex )
760 {
761// LogCommentWithError( L"OnGameSessionReady failed to retrieve current session", ex->HResult );
762 }
763 }).wait();
764}
765
766void PartyController::OnGameSessionReady( GameSessionReadyEventArgs^ eventArgs )
767{
768 DQRNetworkManager::LogComment( L"OnGameSessionReady");
769
770 // If we are already in this session, then we'll be trying to add a player to the session rather than start up a new game. Set a flag if this is the case.
771 bool bInSession = false;
772 if( m_pDQRNet->m_multiplayerSession )
773 {
774 if( m_pDQRNet->m_multiplayerSession->SessionReference->SessionName == eventArgs->GameSession->SessionName )
775 {
776 bInSession = true;
777 }
778 }
779
780 // Get a current copy of the MPSD, and search for the players that we are trying to join in it
781 Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionReference^ sessionRef =
782 m_pDQRNet->ConvertToMicrosoftXboxServicesMultiplayerSessionReference(eventArgs->GameSession);
783
784 vector<Platform::String^> localAdhocAdditions;
785
786 // Use context from any user at all for this, since this might happen before we are in a game and won't have anything set up in the network manager itself. We are only
787 // using it to read the session so there shouldn't be any requirements to use a particular live context
788 WXS::User ^anyUser = nullptr;
789 if( WXS::User::Users->Size > 0 )
790 {
791 anyUser = WXS::User::Users->GetAt(0);
792 }
793 if( anyUser == nullptr )
794 {
795 app.DebugPrintf("Abandoning gamesessionready, no user found\n");
796 return;
797 }
798 XboxLiveContext^ xboxLiveContext = ref new MXS::XboxLiveContext(anyUser);
799
800 app.DebugPrintf("Gamesessionready user and xboxlivecontext found\n");
801
802 IAsyncOperation<Microsoft::Xbox::Services::Multiplayer::MultiplayerSession^>^ asyncOp = xboxLiveContext->MultiplayerService->GetCurrentSessionAsync( sessionRef );
803 create_task(asyncOp)
804 .then([this, eventArgs, bInSession, &localAdhocAdditions,sessionRef] (task<Microsoft::Xbox::Services::Multiplayer::MultiplayerSession^> t)
805 {
806 try
807 {
808 Microsoft::Xbox::Services::Multiplayer::MultiplayerSession^ session = t.get();
809
810 // Check if our joining users are in the session
811 int userFoundMask = 0;
812 m_pDQRNet->LogCommentFormat(L"Found session, size %d\n",session->Members->Size);
813 for( int i = 0; i < session->Members->Size; i++ )
814 {
815 MXSM::MultiplayerSessionMember^ member = session->Members->GetAt(i);
816
817 Platform::String^ memberXUID = member->XboxUserId;
818 bool isAJoiningXuid = false;
819 for( int j = 0; j < MAX_LOCAL_PLAYERS; j++ )
820 {
821 if( m_pDQRNet->m_joinSessionUserMask & ( 1 << j ) )
822 {
823 if( m_pDQRNet->m_joinSessionXUIDs[j] == memberXUID )
824 {
825 userFoundMask |= ( 1 << j );
826 isAJoiningXuid = true;
827 }
828 }
829 }
830
831 // If:
832 //
833 // (1) this isn't a player we are actively trying to join
834 // (2) it isn't currently in the game (only applicable if we are in the same session we're considering going to)
835 // (3) we've got a controller assigned to a user with a matching xuid
836 //
837 // then we might still be interested in this as this could be someone joining via the system interface
838 if( !isAJoiningXuid ) // Isn't someone we are actively trying to join
839 {
840 if( (!bInSession) || // If not in a game session at all
841 ( ( m_pDQRNet->GetState() == DQRNetworkManager::DNM_INT_STATE_PLAYING ) && ( m_pDQRNet->GetPlayerByXuid( PlayerUID(memberXUID->Data()) ) == NULL ) ) // Or we're fully in, and this player isn't in it
842 )
843 {
844 for( int j = 4; j < 12; j++ ) // Final check that we have a gamepad for this xuid so that we might possibly be able to pair someone up
845 {
846 WXS::User^ user = InputManager.GetUserForGamepad(j);
847 if( user != nullptr )
848 {
849 m_pDQRNet->LogCommentFormat(L"%d %d %s vs %s\n",i,j,user->XboxUserId->Data(), memberXUID->Data());
850 if( user->XboxUserId == memberXUID )
851 {
852 localAdhocAdditions.push_back( memberXUID );
853 }
854 }
855 }
856 }
857 }
858 }
859
860 // If we are in the middle of a game-controlled join, then we only (currently) care about users involved in this turning up in the session
861 if( m_pDQRNet->m_joinSessionUserMask != 0 )
862 {
863 m_pDQRNet->LogComment(L"In game controlled join\n");
864 // If all the users we are expecting to join are here, then proceed to either start a new game or just add to the existing session{
865 if( userFoundMask == m_pDQRNet->m_joinSessionUserMask )
866 {
867 m_pDQRNet->m_joinSessionUserMask = 0;
868 m_pDQRNet->m_currentUserMask |= userFoundMask;
869
870 if( m_pDQRNet->m_state == DQRNetworkManager::DNM_INT_STATE_JOINING_WAITING_FOR_RESERVATIONS )
871 {
872 // Attempting to join a game via the discovered list of parties that our friends are in
873 m_pDQRNet->JoinSession(userFoundMask);
874 }
875 else
876 {
877 if( bInSession )
878 {
879 // Already in a game, and adding a new local player - make them active
880 m_pDQRNet->AddUsersToSession( userFoundMask, m_pDQRNet->ConvertToMicrosoftXboxServicesMultiplayerSessionReference( eventArgs->GameSession ));
881 }
882 }
883 }
884 if( localAdhocAdditions.size() > 0 )
885 {
886 // TODO - need to flag up the fact that there were adhoc additions here that we've just ignored, so we can come back and do something with them at a more appropriate time (ie when m_joinSessionUserMask transitions back to 0)
887 }
888 }
889 else if( localAdhocAdditions.size() > 0 )
890 {
891 m_pDQRNet->LogComment(L"Not in game controlled join, but have other player(s) of possible interest\n");
892 // Not trying to do a game controlled join at the moment, and we've got at least one user of interest that we've got a controller for and isn't currently playing
893
894 // If we are in a session, then we might be able to add local players into the game
895 if( bInSession )
896 {
897 m_pDQRNet->LogComment(L"In session, may be able to add local player to game\n");
898 int adhocMask = 0;
899 for( int i = 0; i < localAdhocAdditions.size(); i++ )
900 {
901 // First search to see if we already have the player signed in
902 int userIdx = -1;
903 for( int j = 0; j < MAX_LOCAL_PLAYERS; j++ )
904 {
905 WXS::User^ user = ProfileManager.GetUser(j);
906 if( user != nullptr )
907 {
908 if(user->XboxUserId == localAdhocAdditions[i])
909 {
910 userIdx = j;
911 break;
912 }
913 }
914 }
915 // If not found - see if we have a controller for that person so we can add them
916 if( userIdx == -1 )
917 {
918 for( int j = 4; j < 12; j++ )
919 {
920 WXS::User^ user = InputManager.GetUserForGamepad(j);
921 if( user != nullptr )
922 {
923 if( user->XboxUserId == localAdhocAdditions[i] )
924 {
925 // Found the appropriate controller. Attempt to add it - this will return -1 if unsuccessful
926 userIdx = ProfileManager.AddGamepadToGame(j);
927 break;
928 }
929 }
930 }
931 }
932 // If we found or were able to add a player to one of the 4 local player slots, accumulate in a mask
933 if( userIdx != -1 )
934 {
935 m_pDQRNet->m_joinSessionXUIDs[userIdx] = localAdhocAdditions[i];
936 adhocMask |= ( 1 << userIdx );
937 }
938 }
939 // If we found anyone adhoc to add, join them into the game now
940 if( adhocMask )
941 {
942 m_pDQRNet->AddUsersToSession( adhocMask, m_pDQRNet->ConvertToMicrosoftXboxServicesMultiplayerSessionReference( eventArgs->GameSession ));
943 }
944 }
945 else
946 {
947 // Not in a game, or not in The game that we've just been notified of. We need to try to do a best-fit to work out who
948 // of our signed in players or controllers we know about, should be being considered as being invited to this game
949 m_pDQRNet->LogComment(L"Not in a game, considering as possible invite scenario\n");
950 int playerIdx = -1;
951 for( int i = 0; i < session->Members->Size; i++ ) // First pass through to see if we've a signed in user that is in the session we've been added to
952 {
953 MXSM::MultiplayerSessionMember^ member = session->Members->GetAt(i);
954 for( int j = 0; j < MAX_LOCAL_PLAYERS; j++ )
955 {
956 WXS::User ^user = ProfileManager.GetUser(j);
957 if( user != nullptr )
958 {
959 if( user->XboxUserId == member->XboxUserId )
960 {
961 DQRNetworkManager::LogCommentFormat(L"Found already signed in player %s to act as invite recipient",member->XboxUserId->Data());
962 playerIdx = j;
963 break;
964 }
965 }
966 }
967 }
968 if( playerIdx == -1 ) // If nothing found, second pass through to attempt to find a controller that matches
969 {
970 for( int i = 0; i < session->Members->Size; i++ )
971 {
972 MXSM::MultiplayerSessionMember^ member = session->Members->GetAt(i);
973 for( int j = 4; j < 12; j++ )
974 {
975 WXS::User ^user = InputManager.GetUserForGamepad(j);
976 if( user != nullptr )
977 {
978 if( user->XboxUserId == member->XboxUserId )
979 {
980 DQRNetworkManager::LogCommentFormat(L"Found controller %d for %s (session idx %d) to act as invite recipient",j,member->XboxUserId->Data(),i);
981 playerIdx = ProfileManager.AddGamepadToGame(j);
982 if( playerIdx != -1 )
983 {
984 DQRNetworkManager::LogCommentFormat(L"Assigned controller to user index %d",playerIdx);
985 break;
986 }
987 }
988 }
989 }
990 }
991 }
992 if( playerIdx != -1 )
993 {
994 m_pDQRNet->LogComment(L"Player found, considered as invite scenario\n");
995 m_pDQRNet->HandleNewPartyFoundForPlayer();
996 // Note - must pass player 0 here as the player that the invite is for, or else the xbox 1 code generally breaks because it sets a non-zero primary player. We're going to
997 // be trying to join all current local users to the new game session though. no matter who the invite is for.
998 DQRNetworkManager::SetPartyProcessJoinSession(0, sessionRef->SessionName, sessionRef->ServiceConfigurationId, sessionRef->SessionTemplateName);
999 }
1000 else
1001 {
1002 app.DebugPrintf("No player found to join party with\n");
1003 }
1004 }
1005 }
1006
1007 }
1008 catch ( Platform::COMException^ ex )
1009 {
1010 m_pDQRNet->LogCommentWithError( L"OnGameSessionReady failed to retrieve current session", ex->HResult );
1011 }
1012 }).wait();
1013
1014}
1015
1016bool PartyController::CanJoinParty()
1017{
1018 Concurrency::critical_section::scoped_lock lock(m_lock);
1019 if( m_partyView == nullptr )
1020 {
1021 return false;
1022 }
1023
1024 return (m_partyView->GameSession != nullptr && !m_partyView->IsPartyInAnotherTitle);
1025}
1026
1027bool PartyController::CanInvitePartyToMyGame(
1028 Microsoft::Xbox::Services::Multiplayer::MultiplayerSession^ multiplayerSession
1029 )
1030{
1031 if( multiplayerSession == nullptr )
1032 {
1033 // If my session doesn't exist then shouldn't invite party
1034 return false;
1035 }
1036
1037 PartyView^ partyView = GetPartyView();
1038 if( partyView == nullptr )
1039 {
1040 // If the party view doesn't have a session, then could invite party
1041 return true;
1042 }
1043
1044 if( partyView->IsPartyInAnotherTitle )
1045 {
1046 // If my session doesn't exist then shouldn't invite party
1047 return true;
1048 }
1049
1050 if( partyView->GameSession != nullptr )
1051 {
1052 // If my session and the party session differs, then could invite party
1053 if ( _wcsicmp(partyView->GameSession->ServiceConfigurationId->Data(), multiplayerSession->SessionReference->ServiceConfigurationId->Data() ) != 0 )
1054 {
1055 return true;
1056 }
1057 if ( _wcsicmp(partyView->GameSession->SessionName->Data(), multiplayerSession->SessionReference->SessionName->Data() ) != 0 )
1058 {
1059 return true;
1060 }
1061 if ( _wcsicmp(partyView->GameSession->SessionTemplateName->Data(), multiplayerSession->SessionReference->SessionTemplateName->Data() ) != 0 )
1062 {
1063 return true;
1064 }
1065 }
1066 else
1067 {
1068 // If the party doesn't have a session, then I could invite party
1069 return true;
1070 }
1071
1072 // If the party is in my session then return false
1073 return false;
1074}
1075
1076int PartyController::GetActiveAndReservedMemberPartySize()
1077{
1078 int partySize = 0;
1079
1080 PartyView^ partyView = GetPartyView();
1081 if ( partyView != nullptr )
1082 {
1083 partySize = partyView->Members->Size + partyView->ReservedMembers->Size;
1084 }
1085
1086 return partySize;
1087}
1088
1089bool PartyController::IsPartyInAnotherTitle()
1090{
1091 PartyView^ partyView = GetPartyView();
1092 if( partyView == nullptr )
1093 {
1094 return false;
1095 }
1096
1097 return partyView->IsPartyInAnotherTitle;
1098}
1099
1100bool PartyController::IsGameSessionReadyEventTriggered()
1101{
1102 Concurrency::critical_section::scoped_lock lock(m_lock);
1103 return m_isGameSessionReadyEventTriggered;
1104}
1105
1106void PartyController::ClearGameSessionReadyEventTriggered()
1107{
1108 Concurrency::critical_section::scoped_lock lock(m_lock);
1109 m_isGameSessionReadyEventTriggered = false;
1110}
1111
1112bool PartyController::DoesPartySessionExist()
1113{
1114 Concurrency::critical_section::scoped_lock lock(m_lock);
1115
1116 if( m_partyView != nullptr && m_partyView->GameSession != nullptr)
1117 {
1118 return true;
1119 }
1120 return false;
1121}
1122
1123Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionReference ^ PartyController::GetGamePartySessionReference()
1124{
1125 Concurrency::critical_section::scoped_lock lock(m_lock);
1126
1127 if( m_partyView != nullptr )
1128 {
1129 if( m_partyView->GameSession != nullptr)
1130 {
1131 return m_pDQRNet->ConvertToMicrosoftXboxServicesMultiplayerSessionReference(m_partyView->GameSession);
1132 }
1133 }
1134 return nullptr;
1135}
1136
1137PartyView^ PartyController::GetPartyView()
1138{
1139 Concurrency::critical_section::scoped_lock lock(m_lock);
1140 return m_partyView;
1141}
1142
1143bool PartyController::DoesPartyAndSessionPlayersMatch(
1144 Microsoft::Xbox::Services::Multiplayer::MultiplayerSession^ session
1145 )
1146{
1147 PartyView^ partyView = GetPartyView();
1148
1149 // Verify that session size and party size match
1150 if ( session->Members->Size != partyView->Members->Size )
1151 {
1152 return false;
1153 }
1154
1155 bool inParty;
1156
1157 // Verify that session players match current party players
1158 for ( unsigned int i = 0; i < session->Members->Size; i++ )
1159 {
1160 inParty = false;
1161
1162 MultiplayerSessionMember^ member = session->Members->GetAt( i );
1163
1164 for ( PartyMember^ partyMember : partyView->Members )
1165 {
1166 if ( _wcsicmp(member->XboxUserId->Data(), partyMember->XboxUserId->Data() ) == 0 )
1167 {
1168 inParty = inParty || true;
1169 }
1170 }
1171
1172 if ( !inParty )
1173 {
1174 return false;
1175 }
1176 }
1177
1178 return true;
1179}
1180
1181void PartyController::SetPartyView( PartyView^ partyView )
1182{
1183 Concurrency::critical_section::scoped_lock lock(m_lock);
1184 m_partyView = partyView;
1185}
1186
1187Windows::Foundation::DateTime PartyController::GetCurrentTime()
1188{
1189 ULARGE_INTEGER uInt;
1190 FILETIME ft;
1191 GetSystemTimeAsFileTime(&ft);
1192 uInt.LowPart = ft.dwLowDateTime;
1193 uInt.HighPart = ft.dwHighDateTime;
1194
1195 Windows::Foundation::DateTime time;
1196 time.UniversalTime = uInt.QuadPart;
1197 return time;
1198}
1199
1200double PartyController::GetTimeBetweenInSeconds(Windows::Foundation::DateTime dt1, Windows::Foundation::DateTime dt2)
1201{
1202 const uint64 tickPerSecond = 10000000i64;
1203 uint64 deltaTime = dt2.UniversalTime - dt1.UniversalTime;
1204 return (double)deltaTime / (double)tickPerSecond;
1205}