the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 1205 lines 43 kB view raw
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}