the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 591 lines 20 kB view raw
1#include "stdafx.h" 2 3#include "DQRNetworkManager.h" 4#include "PartyController.h" 5#include <collection.h> 6#include <ppltasks.h> 7#include <ws2tcpip.h> 8#include "..\Minecraft.World\StringHelpers.h" 9#include "base64.h" 10 11#ifdef _DURANGO 12#include "..\Minecraft.World\DurangoStats.h" 13#endif 14 15#include "ChatIntegrationLayer.h" 16 17using namespace Concurrency; 18using namespace Windows::Foundation::Collections; 19 20// Returns true if we are already processing a request to find game parties of friends 21bool DQRNetworkManager::FriendPartyManagerIsBusy() 22{ 23 if( m_GetFriendPartyThread ) 24 { 25 if( m_GetFriendPartyThread->isRunning() ) 26 { 27 return true; 28 } 29 } 30 return false; 31} 32 33// Returns the total count of game parties that we found for our friends 34int DQRNetworkManager::FriendPartyManagerGetCount() 35{ 36 return m_sessionResultCount; 37} 38 39// Initiate the (asynchronous) search for game parties of our friends 40bool DQRNetworkManager::FriendPartyManagerSearch() 41{ 42 if( m_GetFriendPartyThread ) 43 { 44 if( m_GetFriendPartyThread->isRunning() ) 45 { 46 return false; 47 } 48 } 49 50 m_sessionResultCount = 0; 51 delete [] m_sessionSearchResults; 52 m_sessionSearchResults = NULL; 53 54 m_GetFriendPartyThread = new C4JThread(&_GetFriendsThreadProc,this,"GetFriendsThreadProc"); 55 m_GetFriendPartyThread->Run(); 56 57 return true; 58} 59 60// Get a particular search result for a game party that we have discovered. Index should be from 0 to the value returned by FriendPartyManagerGetCount. 61void DQRNetworkManager::FriendPartyManagerGetSessionInfo(int idx, SessionSearchResult *searchResult) 62{ 63 assert( idx < m_sessionResultCount ); 64 assert( ( m_GetFriendPartyThread == NULL ) || ( !m_GetFriendPartyThread->isRunning()) ); 65 66 // Need to make sure that copied data has independently allocated m_extData, so both copies can be freed 67 *searchResult = m_sessionSearchResults[idx]; 68 searchResult->m_extData = malloc(sizeof(GameSessionData)); 69 memcpy(searchResult->m_extData, m_sessionSearchResults[idx].m_extData, sizeof(GameSessionData)); 70} 71 72int DQRNetworkManager::_GetFriendsThreadProc(void* lpParameter) 73{ 74 DQRNetworkManager *pDQR = (DQRNetworkManager *)lpParameter; 75 return pDQR->GetFriendsThreadProc(); 76} 77 78// This is the main thread that is kicked off to find game sessions associated with our friends. We have to do this 79// by finding parties associated with our friends, and from the parties get the assocated game session. 80int DQRNetworkManager::GetFriendsThreadProc() 81{ 82 LogComment(L"Starting GetFriendsThreadProc"); 83 WXS::User^ primaryUser = ProfileManager.GetUser(0); 84 85 if( primaryUser == nullptr ) 86 { 87 return -1; 88 } 89 MXS::XboxLiveContext^ primaryUserXboxLiveContext = ref new MXS::XboxLiveContext(primaryUser); 90 if( primaryUserXboxLiveContext == nullptr ) 91 { 92 return -1; 93 } 94 95 MXSS::XboxSocialRelationshipResult^ socialRelationshipResult = nullptr; 96 97 // First get our friends list (people we follow who may or may not follow us back), note we're requesting all friends 98 auto getSocialRelationshipsAsync = primaryUserXboxLiveContext->SocialService->GetSocialRelationshipsAsync(MXSS::SocialRelationship::All, 0, 1100); 99 create_task(getSocialRelationshipsAsync).then([this,&socialRelationshipResult](task<MXSS::XboxSocialRelationshipResult^> t) 100 { 101 try 102 { 103 socialRelationshipResult = t.get(); 104 } 105 catch (Platform::COMException^ ex) 106 { 107 LogCommentWithError( L"GetSocialRelationshipsAsync failed", ex->HResult ); 108 } 109 }) 110 .wait(); 111 if( socialRelationshipResult == nullptr ) 112 { 113 return -1; 114 } 115 116 IVector<Platform::String^>^ friendXUIDs = ref new Platform::Collections::Vector<Platform::String^>; 117 118 // Now construct a vector of these users, that follow us back - these are our "friends" 119 for( int i = 0; i < socialRelationshipResult->TotalCount; i++ ) 120 { 121 MXSS::XboxSocialRelationship^ relationship = socialRelationshipResult->Items->GetAt(i); 122 if(relationship->IsFollowingCaller) 123 { 124 friendXUIDs->Append(relationship->XboxUserId); 125 } 126 } 127 128 // If we don't have any such friends, we're done 129 if( friendXUIDs->Size == 0 ) 130 { 131 return 0; 132 } 133 134 // Now get party associations for these friends 135 auto getPartyAssociationsAsync = WXM::Party::GetUserPartyAssociationsAsync(primaryUser, friendXUIDs->GetView() ); 136 137 IVectorView<WXM::UserPartyAssociation^>^ partyResults = nullptr; 138 139 create_task(getPartyAssociationsAsync).then([this,&partyResults](task<IVectorView<WXM::UserPartyAssociation^>^> t) 140 { 141 try 142 { 143 partyResults = t.get(); 144 } 145 catch (Platform::COMException^ ex) 146 { 147 LogCommentWithError( L"getPartyAssociationsAsync failed", ex->HResult ); 148 } 149 }) 150 .wait(); 151 152 if( partyResults == nullptr ) 153 { 154 return -1; 155 } 156 157 if( partyResults->Size == 0 ) 158 { 159 return 0; 160 } 161 162 // Filter these parties by whether we have permission to see them online 163 partyResults = FilterPartiesByPermission(primaryUserXboxLiveContext, partyResults); 164 165 166 // At this point, we have Party Ids for our friends. Now we need to get Party Views for each of these Ids. 167 168 LogComment("Parties found"); 169 170 // Get party views for each of the user party associations that we have. These seem to be able to (individually) raise errors, so 171 // accumulate results into 2 matched vectors declared below so that we can ignore any broken UserPartyAssociations from now 172 vector<WXM::PartyView^> partyViewVector; 173 vector<WXM::UserPartyAssociation^> partyResultsVector; 174 175 vector<task<void>> taskVector; 176 for each(WXM::UserPartyAssociation^ remoteParty in partyResults) 177 { 178 auto asyncOp = WXM::Party::GetPartyViewByPartyIdAsync( primaryUser, remoteParty->PartyId ); 179 task<WXM::PartyView^> asyncTask = create_task(asyncOp); 180 181 taskVector.push_back(asyncTask.then([this, &partyViewVector, &partyResultsVector, remoteParty] (task<WXM::PartyView^> t) 182 { 183 try 184 { 185 WXM::PartyView^ partyView = t.get(); 186 187 if( partyView != nullptr ) 188 { 189 app.DebugPrintf("Got party view\n"); 190 EnterCriticalSection(&m_csPartyViewVector); 191 partyViewVector.push_back(partyView); 192 partyResultsVector.push_back(remoteParty); 193 LeaveCriticalSection(&m_csPartyViewVector); 194 } 195 } 196 catch ( Platform::COMException^ ex ) 197 { 198 app.DebugPrintf("Getting party view error 0x%x\n",ex->HResult); 199 } 200 })); 201 } 202 for( auto it = taskVector.begin(); it != taskVector.end(); it++ ) 203 { 204 it->wait(); 205 } 206 207 if( partyViewVector.size() == 0 ) 208 { 209 return 0; 210 } 211 212 // Filter the party view, and party results vector (partyResultsVector) this is matched to, to remove any that don't have game sessions - or game sessions that aren't this game 213 vector<WXM::PartyView^> partyViewVectorFiltered; 214 vector<WXM::UserPartyAssociation^> partyResultsFiltered; 215 216 for( int i = 0; i < partyViewVector.size(); i++ ) 217 { 218 WXM::PartyView^ partyView = partyViewVector[i]; 219 220 if( partyView->Joinability == WXM::SessionJoinability::JoinableByFriends ) 221 { 222 if( partyView->GameSession ) 223 { 224 if( partyView->GameSession->ServiceConfigurationId == SERVICE_CONFIG_ID ) 225 { 226 partyViewVectorFiltered.push_back( partyView ); 227 partyResultsFiltered.push_back( partyResultsVector[i] ); 228 } 229 } 230 } 231 } 232 233 // We now have matched vectors: 234 // 235 // partyResultsFiltered 236 // partyViewVectorFiltered 237 // 238 // and, from the party views, we can now attempt to get game sessions 239 240 vector<MXSM::MultiplayerSession^> sessionVector; 241 vector<WXM::PartyView^> partyViewVectorValid; 242 vector<WXM::UserPartyAssociation^> partyResultsValid; 243 244 for( int i = 0; i < partyViewVectorFiltered.size(); i++ ) 245 { 246 WXM::PartyView^ partyView = partyViewVectorFiltered[i]; 247 Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionReference^ sessionRef = ConvertToMicrosoftXboxServicesMultiplayerSessionReference(partyView->GameSession); 248 249 LogComment(L"Party view vector " + sessionRef->SessionName + L" " + partyResultsFiltered[i]->QueriedXboxUserIds->GetAt(0)); 250 251 MXSM::MultiplayerSession^ session = nullptr; 252 auto asyncOp = primaryUserXboxLiveContext->MultiplayerService->GetCurrentSessionAsync( sessionRef ); 253 create_task(asyncOp).then([&session] (task<MXSM::MultiplayerSession^> t) 254 { 255 try 256 { 257 session = t.get(); 258 } 259 catch (Platform::COMException^ ex) 260 { 261 } 262 }) 263 .wait(); 264 if( session ) 265 { 266 sessionVector.push_back(session); 267 partyViewVectorValid.push_back(partyView); 268 partyResultsValid.push_back(partyResultsFiltered[i]); 269 } 270 } 271 272 if( sessionVector.size() == 0 ) 273 { 274 return 0; 275 } 276 277 // We now have matched vectors: 278 // 279 // partyResultsValid 280 // partyViewVectorValid 281 // sessionVector 282 283 // The next stage is to resolve the display names for the XUIDs of all the players in each of the sessions. It is possible that 284 // a session won't have any XUIDs to resolve, which would make GetUserProfilesAsync unhappy, so we'll only be creating a task 285 // when there are members. Creating new matching arrays for party results and sessions, to match the results (we don't care about the party view anymore) 286 287 vector<task<IVectorView<MXSS::XboxUserProfile^>^>> nameResolveTaskVector; 288 vector<IVectorView<MXSS::XboxUserProfile^>^> nameResolveVector; 289 vector<MXSM::MultiplayerSession^> newSessionVector; 290 vector<WXM::UserPartyAssociation^> newPartyVector; 291 292 for( int j = 0; j < sessionVector.size(); j++ ) 293 { 294 MXSM::MultiplayerSession^ session = sessionVector[j]; 295 IVector<Platform::String^>^ memberXUIDs = ref new Platform::Collections::Vector<Platform::String^>; 296 297 Windows::Data::Json::JsonArray^ roomSyncArray = nullptr; 298 try 299 { 300 Windows::Data::Json::JsonObject^ customJson = Windows::Data::Json::JsonObject::Parse(session->SessionProperties->SessionCustomPropertiesJson); 301 Windows::Data::Json::JsonValue^ customValue = customJson->GetNamedValue(L"RoomSyncData"); 302 roomSyncArray = customValue->GetArray(); 303 LogComment("Attempting to parse RoomSyncData"); 304 for( int i = 0; i < roomSyncArray->Size; i++ ) 305 { 306 LogComment(roomSyncArray->GetAt(i)->GetString()); 307 } 308 } 309 catch (Platform::COMException^ ex) 310 { 311 LogCommentWithError( L"Custom RoomSyncData Parse/GetNamedValue failed", ex->HResult ); 312 continue; 313 } 314 315 if( roomSyncArray && ( roomSyncArray->Size > 0 ) ) 316 { 317 // For each session, we want to order these XUIDs so the display name of the first one is what we will name the session by. Prioritise doing this by: 318 // 319 // (1) If the host player (indicated by having a small id of 0) is our friend, use that 320 // (2) Otherwise use anyone who is our friend 321 322 // Default to true 323 bool friendsOfFriends = true; 324 325 int hostIndexFound = -1; 326 int friendIndexFound = -1; 327 328 friendsOfFriends = IsSessionFriendsOfFriends(session); 329 330 for( int i = 0; i < roomSyncArray->Size; i++ ) 331 { 332 Platform::String^ roomSyncXuid = roomSyncArray->GetAt(i)->GetString(); 333 334 // Determine if this player is a friend 335 bool isFriend = false; 336 for each( Platform::String^ friendXUID in friendXUIDs ) 337 { 338 if( friendXUID == roomSyncXuid ) 339 { 340 isFriend = true; 341 break; 342 } 343 } 344 345 bool isHost = i == 0; 346 347 // Store that what we found at this index if it is a friend, or a friend who is a host 348 if( isFriend && ( friendsOfFriends || isHost ) ) 349 { 350 friendIndexFound = i; 351 if( isHost ) // Host is always in slot 0 352 { 353 hostIndexFound = i; 354 } 355 } 356 } 357 358 // Prefer to use index of host who is our friend 359 int bestIndex = friendIndexFound; 360 if( hostIndexFound != -1 ) 361 { 362 bestIndex = hostIndexFound; 363 } 364 365 // Only consider if we have at least found one friend in the list of players 366 if( bestIndex != -1 ) 367 { 368 // Compile list of XUIDs to resolve with our specially chosen player as entry 0, then the rest 369 memberXUIDs->Append(roomSyncArray->GetAt(bestIndex)->GetString()); 370 for( int i = 0; i < roomSyncArray->Size; i++ ) 371 { 372 if( i != bestIndex ) 373 { 374 memberXUIDs->Append(roomSyncArray->GetAt(i)->GetString()); 375 } 376 } 377 nameResolveTaskVector.push_back( create_task( primaryUserXboxLiveContext->ProfileService->GetUserProfilesAsync( memberXUIDs->GetView() ) ) ); 378 newSessionVector.push_back(session); 379 newPartyVector.push_back(partyResultsValid[j]); 380 } 381 } 382 } 383 384 try 385 { 386 auto joinTask = when_all(begin(nameResolveTaskVector), end(nameResolveTaskVector) ).then([this, &nameResolveVector](vector<IVectorView<MXSS::XboxUserProfile^>^> results) 387 { 388 nameResolveVector = results; 389 }) 390 .wait(); 391 } 392 catch(Platform::COMException^ ex) 393 { 394 return -1; 395 } 396 397 // We now have matched vectors: 398 // 399 // newPartyVector - contains the party Ids that we'll need should we wish to join 400 // nameResolveVector - contains vectors views of the names of the members of the session each of these parties is in 401 // newSessionVector - contains the session information itself associated with each of the parties 402 403 // Construct the final result vector 404 m_sessionResultCount = newSessionVector.size(); 405 m_sessionSearchResults = new SessionSearchResult[m_sessionResultCount]; 406 for( int i = 0; i < m_sessionResultCount; i++ ) 407 { 408 m_sessionSearchResults[i].m_partyId = newPartyVector[i]->PartyId->Data(); 409 m_sessionSearchResults[i].m_sessionName = newSessionVector[i]->SessionReference->SessionName->Data(); 410 for( int j = 0; j < nameResolveVector[i]->Size; j++ ) 411 { 412 m_sessionSearchResults[i].m_playerNames[j] = nameResolveVector[i]->GetAt(j)->GameDisplayName->Data(); 413 m_sessionSearchResults[i].m_playerXuids[j] = PlayerUID(nameResolveVector[i]->GetAt(j)->XboxUserId->Data()); 414 } 415 m_sessionSearchResults[i].m_playerCount = nameResolveVector[i]->Size; 416 m_sessionSearchResults[i].m_usedSlotCount = newSessionVector[i]->Members->Size; 417 if( m_sessionSearchResults[i].m_usedSlotCount > MAX_ONLINE_PLAYER_COUNT ) 418 { 419 // Don't think this could ever happen, but no harm in checking 420 m_sessionSearchResults[i].m_usedSlotCount = MAX_ONLINE_PLAYER_COUNT; 421 } 422 for( int j = 0; j < m_sessionSearchResults[i].m_usedSlotCount; j++ ) 423 { 424 m_sessionSearchResults[i].m_sessionXuids[j] = wstring( newSessionVector[i]->Members->GetAt(j)->XboxUserId->Data() ); 425 } 426 427 m_sessionSearchResults[i].m_extData = malloc( sizeof(GameSessionData) ); 428 memset( m_sessionSearchResults[i].m_extData, 0, sizeof(GameSessionData) ); 429 430 GetGameSessionData(newSessionVector[i], m_sessionSearchResults[i].m_extData); 431 } 432 433 return 0; 434} 435 436// Filters list of parties based on online presence permission (whether the friend is set to invisible or not) 437IVectorView<WXM::UserPartyAssociation^>^ DQRNetworkManager::FilterPartiesByPermission(MXS::XboxLiveContext ^context, IVectorView<WXM::UserPartyAssociation^>^ partyResults) 438{ 439 Platform::Collections::Vector<WXM::UserPartyAssociation^>^ filteredPartyResults = ref new Platform::Collections::Vector<WXM::UserPartyAssociation^>(); 440 441 // List of permissions we want 442 auto permissionIds = ref new Platform::Collections::Vector<Platform::String^>(1, ref new Platform::String(L"ViewTargetPresence")); 443 444 // List of target users 445 auto targetXboxUserIds = ref new Platform::Collections::Vector<Platform::String^>(); 446 for (int i = 0; i < partyResults->Size; i++) 447 { 448 assert(partyResults->GetAt(i)->QueriedXboxUserIds->Size > 0); 449 targetXboxUserIds->Append( partyResults->GetAt(i)->QueriedXboxUserIds->GetAt(0) ); 450 } 451 452 // Check 453 auto checkPermissionsAsync = context->PrivacyService->CheckMultiplePermissionsWithMultipleTargetUsersAsync(permissionIds->GetView(), targetXboxUserIds->GetView()); 454 create_task(checkPermissionsAsync).then([&partyResults, &filteredPartyResults](task<IVectorView<MXS::Privacy::MultiplePermissionsCheckResult^>^> t) 455 { 456 try 457 { 458 auto results = t.get(); 459 460 // For each party, check to see if we have permission for the user 461 for (int i = 0; i < partyResults->Size; i++) 462 { 463 // For each permissions result 464 for (int j = 0; j < results->Size; j++) 465 { 466 auto result = results->GetAt(j); 467 468 // If allowed to see this user AND it's the same user, add the party to the just 469 if ((result->Items->GetAt(0)->IsAllowed) && (partyResults->GetAt(i)->QueriedXboxUserIds->GetAt(0) == result->XboxUserId)) 470 { 471 filteredPartyResults->Append(partyResults->GetAt(i)); 472 break; 473 } 474 } 475 } 476 } 477 catch (Platform::COMException^ ex) 478 { 479 LogCommentWithError( L"CheckMultiplePermissionsWithMultipleTargetUsersAsync failed", ex->HResult ); 480 } 481 }) 482 .wait(); 483 484 app.DebugPrintf("DQRNetworkManager::FilterPartiesByPermission: Removed %i parties because of online presence permissions\n", partyResults->Size - filteredPartyResults->Size); 485 486 return filteredPartyResults->GetView(); 487} 488 489// Get all friends (list of XUIDs) syncronously from the service (slow, may take 300ms+), returns empty list if something goes wrong 490Platform::Collections::Vector<Platform::String^>^ DQRNetworkManager::GetFriends() 491{ 492 auto friends = ref new Platform::Collections::Vector<Platform::String^>; 493 494 auto primaryUser = ProfileManager.GetUser(0); 495 if (primaryUser == nullptr) 496 { 497 // Return empty 498 return friends; 499 } 500 501 auto xboxLiveContext = ref new MXS::XboxLiveContext(primaryUser); 502 503 // Request ALL friends because there's no other way to check friendships without using the REST API 504 auto getSocialRelationshipsAsync = xboxLiveContext->SocialService->GetSocialRelationshipsAsync(MXSS::SocialRelationship::All, 0, 1100); 505 MXSS::XboxSocialRelationshipResult^ socialRelationshipResult = nullptr; 506 507 // First get our friends list (people we follow who may or may not follow us back) 508 Concurrency::create_task(getSocialRelationshipsAsync).then([&socialRelationshipResult](Concurrency::task<MXSS::XboxSocialRelationshipResult^> t) 509 { 510 try 511 { 512 socialRelationshipResult = t.get(); 513 } 514 catch (Platform::COMException^ ex) 515 { 516 app.DebugPrintf("DQRNetworkManager::GetFriends: GetSocialRelationshipsAsync failed ()\n", ex->HResult); 517 } 518 }) 519 .wait(); 520 521 if (socialRelationshipResult == nullptr) 522 { 523 // Return empty 524 return friends; 525 } 526 527 app.DebugPrintf("DQRNetworkManager::GetFriends: Retrieved %i relationships\n", socialRelationshipResult->TotalCount); 528 529 // Now construct a vector of these users, that follow us back - these are our "friends" 530 for( int i = 0; i < socialRelationshipResult->TotalCount; i++ ) 531 { 532 MXSS::XboxSocialRelationship^ relationship = socialRelationshipResult->Items->GetAt(i); 533 if(relationship->IsFollowingCaller) 534 { 535 app.DebugPrintf("DQRNetworkManager::GetFriends: Found friend \"%ls\"\n", relationship->XboxUserId->Data()); 536 friends->Append(relationship->XboxUserId); 537 } 538 } 539 540 app.DebugPrintf("DQRNetworkManager::GetFriends: Found %i 2-way friendships\n", friends->Size); 541 542 return friends; 543} 544 545// If data for game settings exists returns FriendsOfFriends value, otherwise returns true 546bool DQRNetworkManager::IsSessionFriendsOfFriends(MXSM::MultiplayerSession^ session) 547{ 548 // Default to true, don't want to incorrectly prevent joining 549 bool friendsOfFriends = true; 550 551 // We retrieve the game session data later too, shouldn't really duplicate this 552 void *gameSessionData = malloc( sizeof(GameSessionData)); 553 memset(gameSessionData, 0, sizeof(GameSessionData)); 554 555 bool result = GetGameSessionData(session, gameSessionData); 556 557 if (result) 558 { 559 friendsOfFriends = app.GetGameHostOption(((GameSessionData *)gameSessionData)->m_uiGameHostSettings, eGameHostOption_FriendsOfFriends); 560 } 561 562 free(gameSessionData); 563 564 return friendsOfFriends; 565} 566 567// Parses custom json data from session and populates game session data param, return true if parse succeeded 568bool DQRNetworkManager::GetGameSessionData(MXSM::MultiplayerSession^ session, void *gameSessionData) 569{ 570 Platform::String ^gameSessionDataJson = session->SessionProperties->SessionCustomPropertiesJson; 571 if( gameSessionDataJson ) 572 { 573 try 574 { 575 Windows::Data::Json::JsonObject^ customParam = Windows::Data::Json::JsonObject::Parse(gameSessionDataJson); 576 Windows::Data::Json::JsonValue^ customValue = customParam->GetNamedValue(L"GameSessionData"); 577 Platform::String ^customValueString = customValue->GetString(); 578 if( customValueString ) 579 { 580 base64_decode( customValueString, (unsigned char *)gameSessionData, sizeof(GameSessionData) ); 581 return true; 582 } 583 } 584 catch (Platform::COMException^ ex) 585 { 586 LogCommentWithError( L"Custom GameSessionData parameter Parse/GetNamedValue failed", ex->HResult ); 587 } 588 } 589 590 return false; 591}