Something went wrong. Try again.
the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
Something went wrong. Try again.
20 kB · 591 lines
C++
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591#include "stdafx.h"
#include "DQRNetworkManager.h"#include "PartyController.h"#include <collection.h>#include <ppltasks.h>#include <ws2tcpip.h>#include "..\Minecraft.World\StringHelpers.h"#include "base64.h"
#ifdef _DURANGO#include "..\Minecraft.World\DurangoStats.h"#endif
#include "ChatIntegrationLayer.h"
using namespace Concurrency;using namespace Windows::Foundation::Collections;
// Returns true if we are already processing a request to find game parties of friendsbool DQRNetworkManager::FriendPartyManagerIsBusy(){ if( m_GetFriendPartyThread ) { if( m_GetFriendPartyThread->isRunning() ) { return true; } } return false;}
// Returns the total count of game parties that we found for our friendsint DQRNetworkManager::FriendPartyManagerGetCount(){ return m_sessionResultCount;}
// Initiate the (asynchronous) search for game parties of our friendsbool DQRNetworkManager::FriendPartyManagerSearch(){ if( m_GetFriendPartyThread ) { if( m_GetFriendPartyThread->isRunning() ) { return false; } }
m_sessionResultCount = 0; delete [] m_sessionSearchResults; m_sessionSearchResults = NULL;
m_GetFriendPartyThread = new C4JThread(&_GetFriendsThreadProc,this,"GetFriendsThreadProc"); m_GetFriendPartyThread->Run();
return true;}
// Get a particular search result for a game party that we have discovered. Index should be from 0 to the value returned by FriendPartyManagerGetCount.void DQRNetworkManager::FriendPartyManagerGetSessionInfo(int idx, SessionSearchResult *searchResult){ assert( idx < m_sessionResultCount ); assert( ( m_GetFriendPartyThread == NULL ) || ( !m_GetFriendPartyThread->isRunning()) );
// Need to make sure that copied data has independently allocated m_extData, so both copies can be freed *searchResult = m_sessionSearchResults[idx]; searchResult->m_extData = malloc(sizeof(GameSessionData)); memcpy(searchResult->m_extData, m_sessionSearchResults[idx].m_extData, sizeof(GameSessionData));}
int DQRNetworkManager::_GetFriendsThreadProc(void* lpParameter){ DQRNetworkManager *pDQR = (DQRNetworkManager *)lpParameter; return pDQR->GetFriendsThreadProc();}
// This is the main thread that is kicked off to find game sessions associated with our friends. We have to do this// by finding parties associated with our friends, and from the parties get the assocated game session.int DQRNetworkManager::GetFriendsThreadProc(){ LogComment(L"Starting GetFriendsThreadProc"); WXS::User^ primaryUser = ProfileManager.GetUser(0);
if( primaryUser == nullptr ) { return -1; } MXS::XboxLiveContext^ primaryUserXboxLiveContext = ref new MXS::XboxLiveContext(primaryUser); if( primaryUserXboxLiveContext == nullptr ) { return -1; }
MXSS::XboxSocialRelationshipResult^ socialRelationshipResult = nullptr;
// First get our friends list (people we follow who may or may not follow us back), note we're requesting all friends auto getSocialRelationshipsAsync = primaryUserXboxLiveContext->SocialService->GetSocialRelationshipsAsync(MXSS::SocialRelationship::All, 0, 1100); create_task(getSocialRelationshipsAsync).then([this,&socialRelationshipResult](task<MXSS::XboxSocialRelationshipResult^> t) { try { socialRelationshipResult = t.get(); } catch (Platform::COMException^ ex) { LogCommentWithError( L"GetSocialRelationshipsAsync failed", ex->HResult ); } }) .wait(); if( socialRelationshipResult == nullptr ) { return -1; }
IVector<Platform::String^>^ friendXUIDs = ref new Platform::Collections::Vector<Platform::String^>;
// Now construct a vector of these users, that follow us back - these are our "friends" for( int i = 0; i < socialRelationshipResult->TotalCount; i++ ) { MXSS::XboxSocialRelationship^ relationship = socialRelationshipResult->Items->GetAt(i); if(relationship->IsFollowingCaller) { friendXUIDs->Append(relationship->XboxUserId); } }
// If we don't have any such friends, we're done if( friendXUIDs->Size == 0 ) { return 0; }
// Now get party associations for these friends auto getPartyAssociationsAsync = WXM::Party::GetUserPartyAssociationsAsync(primaryUser, friendXUIDs->GetView() );
IVectorView<WXM::UserPartyAssociation^>^ partyResults = nullptr;
create_task(getPartyAssociationsAsync).then([this,&partyResults](task<IVectorView<WXM::UserPartyAssociation^>^> t) { try { partyResults = t.get(); } catch (Platform::COMException^ ex) { LogCommentWithError( L"getPartyAssociationsAsync failed", ex->HResult ); } }) .wait();
if( partyResults == nullptr ) { return -1; }
if( partyResults->Size == 0 ) { return 0; }
// Filter these parties by whether we have permission to see them online partyResults = FilterPartiesByPermission(primaryUserXboxLiveContext, partyResults);
// At this point, we have Party Ids for our friends. Now we need to get Party Views for each of these Ids.
LogComment("Parties found"); // Get party views for each of the user party associations that we have. These seem to be able to (individually) raise errors, so // accumulate results into 2 matched vectors declared below so that we can ignore any broken UserPartyAssociations from now vector<WXM::PartyView^> partyViewVector; vector<WXM::UserPartyAssociation^> partyResultsVector;
vector<task<void>> taskVector; for each(WXM::UserPartyAssociation^ remoteParty in partyResults) { auto asyncOp = WXM::Party::GetPartyViewByPartyIdAsync( primaryUser, remoteParty->PartyId ); task<WXM::PartyView^> asyncTask = create_task(asyncOp); taskVector.push_back(asyncTask.then([this, &partyViewVector, &partyResultsVector, remoteParty] (task<WXM::PartyView^> t) { try { WXM::PartyView^ partyView = t.get();
if( partyView != nullptr ) { app.DebugPrintf("Got party view\n"); EnterCriticalSection(&m_csPartyViewVector); partyViewVector.push_back(partyView); partyResultsVector.push_back(remoteParty); LeaveCriticalSection(&m_csPartyViewVector); } } catch ( Platform::COMException^ ex ) { app.DebugPrintf("Getting party view error 0x%x\n",ex->HResult); } })); } for( auto it = taskVector.begin(); it != taskVector.end(); it++ ) { it->wait(); }
if( partyViewVector.size() == 0 ) { return 0; }
// 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 vector<WXM::PartyView^> partyViewVectorFiltered; vector<WXM::UserPartyAssociation^> partyResultsFiltered;
for( int i = 0; i < partyViewVector.size(); i++ ) { WXM::PartyView^ partyView = partyViewVector[i];
if( partyView->Joinability == WXM::SessionJoinability::JoinableByFriends ) { if( partyView->GameSession ) { if( partyView->GameSession->ServiceConfigurationId == SERVICE_CONFIG_ID ) { partyViewVectorFiltered.push_back( partyView ); partyResultsFiltered.push_back( partyResultsVector[i] ); } } } }
// We now have matched vectors: // // partyResultsFiltered // partyViewVectorFiltered // // and, from the party views, we can now attempt to get game sessions
vector<MXSM::MultiplayerSession^> sessionVector; vector<WXM::PartyView^> partyViewVectorValid; vector<WXM::UserPartyAssociation^> partyResultsValid;
for( int i = 0; i < partyViewVectorFiltered.size(); i++ ) { WXM::PartyView^ partyView = partyViewVectorFiltered[i]; Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionReference^ sessionRef = ConvertToMicrosoftXboxServicesMultiplayerSessionReference(partyView->GameSession);
LogComment(L"Party view vector " + sessionRef->SessionName + L" " + partyResultsFiltered[i]->QueriedXboxUserIds->GetAt(0));
MXSM::MultiplayerSession^ session = nullptr; auto asyncOp = primaryUserXboxLiveContext->MultiplayerService->GetCurrentSessionAsync( sessionRef ); create_task(asyncOp).then([&session] (task<MXSM::MultiplayerSession^> t) { try { session = t.get(); } catch (Platform::COMException^ ex) { } }) .wait(); if( session ) { sessionVector.push_back(session); partyViewVectorValid.push_back(partyView); partyResultsValid.push_back(partyResultsFiltered[i]); } }
if( sessionVector.size() == 0 ) { return 0; }
// We now have matched vectors: // // partyResultsValid // partyViewVectorValid // sessionVector
// 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 // a session won't have any XUIDs to resolve, which would make GetUserProfilesAsync unhappy, so we'll only be creating a task // 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)
vector<task<IVectorView<MXSS::XboxUserProfile^>^>> nameResolveTaskVector; vector<IVectorView<MXSS::XboxUserProfile^>^> nameResolveVector; vector<MXSM::MultiplayerSession^> newSessionVector; vector<WXM::UserPartyAssociation^> newPartyVector;
for( int j = 0; j < sessionVector.size(); j++ ) { MXSM::MultiplayerSession^ session = sessionVector[j]; IVector<Platform::String^>^ memberXUIDs = ref new Platform::Collections::Vector<Platform::String^>;
Windows::Data::Json::JsonArray^ roomSyncArray = nullptr; try { Windows::Data::Json::JsonObject^ customJson = Windows::Data::Json::JsonObject::Parse(session->SessionProperties->SessionCustomPropertiesJson); Windows::Data::Json::JsonValue^ customValue = customJson->GetNamedValue(L"RoomSyncData"); roomSyncArray = customValue->GetArray(); LogComment("Attempting to parse RoomSyncData"); for( int i = 0; i < roomSyncArray->Size; i++ ) { LogComment(roomSyncArray->GetAt(i)->GetString()); } } catch (Platform::COMException^ ex) { LogCommentWithError( L"Custom RoomSyncData Parse/GetNamedValue failed", ex->HResult ); continue; }
if( roomSyncArray && ( roomSyncArray->Size > 0 ) ) { // 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: // // (1) If the host player (indicated by having a small id of 0) is our friend, use that // (2) Otherwise use anyone who is our friend
// Default to true bool friendsOfFriends = true;
int hostIndexFound = -1; int friendIndexFound = -1;
friendsOfFriends = IsSessionFriendsOfFriends(session);
for( int i = 0; i < roomSyncArray->Size; i++ ) { Platform::String^ roomSyncXuid = roomSyncArray->GetAt(i)->GetString();
// Determine if this player is a friend bool isFriend = false; for each( Platform::String^ friendXUID in friendXUIDs ) { if( friendXUID == roomSyncXuid ) { isFriend = true; break; } }
bool isHost = i == 0;
// Store that what we found at this index if it is a friend, or a friend who is a host if( isFriend && ( friendsOfFriends || isHost ) ) { friendIndexFound = i; if( isHost ) // Host is always in slot 0 { hostIndexFound = i; } } }
// Prefer to use index of host who is our friend int bestIndex = friendIndexFound; if( hostIndexFound != -1 ) { bestIndex = hostIndexFound; }
// Only consider if we have at least found one friend in the list of players if( bestIndex != -1 ) { // Compile list of XUIDs to resolve with our specially chosen player as entry 0, then the rest memberXUIDs->Append(roomSyncArray->GetAt(bestIndex)->GetString()); for( int i = 0; i < roomSyncArray->Size; i++ ) { if( i != bestIndex ) { memberXUIDs->Append(roomSyncArray->GetAt(i)->GetString()); } } nameResolveTaskVector.push_back( create_task( primaryUserXboxLiveContext->ProfileService->GetUserProfilesAsync( memberXUIDs->GetView() ) ) ); newSessionVector.push_back(session); newPartyVector.push_back(partyResultsValid[j]); } } }
try { auto joinTask = when_all(begin(nameResolveTaskVector), end(nameResolveTaskVector) ).then([this, &nameResolveVector](vector<IVectorView<MXSS::XboxUserProfile^>^> results) { nameResolveVector = results; }) .wait(); } catch(Platform::COMException^ ex) { return -1; }
// We now have matched vectors: // // newPartyVector - contains the party Ids that we'll need should we wish to join // nameResolveVector - contains vectors views of the names of the members of the session each of these parties is in // newSessionVector - contains the session information itself associated with each of the parties
// Construct the final result vector m_sessionResultCount = newSessionVector.size(); m_sessionSearchResults = new SessionSearchResult[m_sessionResultCount]; for( int i = 0; i < m_sessionResultCount; i++ ) { m_sessionSearchResults[i].m_partyId = newPartyVector[i]->PartyId->Data(); m_sessionSearchResults[i].m_sessionName = newSessionVector[i]->SessionReference->SessionName->Data(); for( int j = 0; j < nameResolveVector[i]->Size; j++ ) { m_sessionSearchResults[i].m_playerNames[j] = nameResolveVector[i]->GetAt(j)->GameDisplayName->Data(); m_sessionSearchResults[i].m_playerXuids[j] = PlayerUID(nameResolveVector[i]->GetAt(j)->XboxUserId->Data()); } m_sessionSearchResults[i].m_playerCount = nameResolveVector[i]->Size; m_sessionSearchResults[i].m_usedSlotCount = newSessionVector[i]->Members->Size; if( m_sessionSearchResults[i].m_usedSlotCount > MAX_ONLINE_PLAYER_COUNT ) { // Don't think this could ever happen, but no harm in checking m_sessionSearchResults[i].m_usedSlotCount = MAX_ONLINE_PLAYER_COUNT; } for( int j = 0; j < m_sessionSearchResults[i].m_usedSlotCount; j++ ) { m_sessionSearchResults[i].m_sessionXuids[j] = wstring( newSessionVector[i]->Members->GetAt(j)->XboxUserId->Data() ); } m_sessionSearchResults[i].m_extData = malloc( sizeof(GameSessionData) ); memset( m_sessionSearchResults[i].m_extData, 0, sizeof(GameSessionData) ); GetGameSessionData(newSessionVector[i], m_sessionSearchResults[i].m_extData); }
return 0;}
// Filters list of parties based on online presence permission (whether the friend is set to invisible or not)IVectorView<WXM::UserPartyAssociation^>^ DQRNetworkManager::FilterPartiesByPermission(MXS::XboxLiveContext ^context, IVectorView<WXM::UserPartyAssociation^>^ partyResults){ Platform::Collections::Vector<WXM::UserPartyAssociation^>^ filteredPartyResults = ref new Platform::Collections::Vector<WXM::UserPartyAssociation^>();
// List of permissions we want auto permissionIds = ref new Platform::Collections::Vector<Platform::String^>(1, ref new Platform::String(L"ViewTargetPresence"));
// List of target users auto targetXboxUserIds = ref new Platform::Collections::Vector<Platform::String^>(); for (int i = 0; i < partyResults->Size; i++) { assert(partyResults->GetAt(i)->QueriedXboxUserIds->Size > 0); targetXboxUserIds->Append( partyResults->GetAt(i)->QueriedXboxUserIds->GetAt(0) ); }
// Check auto checkPermissionsAsync = context->PrivacyService->CheckMultiplePermissionsWithMultipleTargetUsersAsync(permissionIds->GetView(), targetXboxUserIds->GetView()); create_task(checkPermissionsAsync).then([&partyResults, &filteredPartyResults](task<IVectorView<MXS::Privacy::MultiplePermissionsCheckResult^>^> t) { try { auto results = t.get();
// For each party, check to see if we have permission for the user for (int i = 0; i < partyResults->Size; i++) { // For each permissions result for (int j = 0; j < results->Size; j++) { auto result = results->GetAt(j);
// If allowed to see this user AND it's the same user, add the party to the just if ((result->Items->GetAt(0)->IsAllowed) && (partyResults->GetAt(i)->QueriedXboxUserIds->GetAt(0) == result->XboxUserId)) { filteredPartyResults->Append(partyResults->GetAt(i)); break; } } } } catch (Platform::COMException^ ex) { LogCommentWithError( L"CheckMultiplePermissionsWithMultipleTargetUsersAsync failed", ex->HResult ); } }) .wait();
app.DebugPrintf("DQRNetworkManager::FilterPartiesByPermission: Removed %i parties because of online presence permissions\n", partyResults->Size - filteredPartyResults->Size);
return filteredPartyResults->GetView();}
// Get all friends (list of XUIDs) syncronously from the service (slow, may take 300ms+), returns empty list if something goes wrongPlatform::Collections::Vector<Platform::String^>^ DQRNetworkManager::GetFriends(){ auto friends = ref new Platform::Collections::Vector<Platform::String^>;
auto primaryUser = ProfileManager.GetUser(0); if (primaryUser == nullptr) { // Return empty return friends; }
auto xboxLiveContext = ref new MXS::XboxLiveContext(primaryUser);
// Request ALL friends because there's no other way to check friendships without using the REST API auto getSocialRelationshipsAsync = xboxLiveContext->SocialService->GetSocialRelationshipsAsync(MXSS::SocialRelationship::All, 0, 1100); MXSS::XboxSocialRelationshipResult^ socialRelationshipResult = nullptr;
// First get our friends list (people we follow who may or may not follow us back) Concurrency::create_task(getSocialRelationshipsAsync).then([&socialRelationshipResult](Concurrency::task<MXSS::XboxSocialRelationshipResult^> t) { try { socialRelationshipResult = t.get(); } catch (Platform::COMException^ ex) { app.DebugPrintf("DQRNetworkManager::GetFriends: GetSocialRelationshipsAsync failed ()\n", ex->HResult); } }) .wait();
if (socialRelationshipResult == nullptr) { // Return empty return friends; }
app.DebugPrintf("DQRNetworkManager::GetFriends: Retrieved %i relationships\n", socialRelationshipResult->TotalCount);
// Now construct a vector of these users, that follow us back - these are our "friends" for( int i = 0; i < socialRelationshipResult->TotalCount; i++ ) { MXSS::XboxSocialRelationship^ relationship = socialRelationshipResult->Items->GetAt(i); if(relationship->IsFollowingCaller) { app.DebugPrintf("DQRNetworkManager::GetFriends: Found friend \"%ls\"\n", relationship->XboxUserId->Data()); friends->Append(relationship->XboxUserId); } }
app.DebugPrintf("DQRNetworkManager::GetFriends: Found %i 2-way friendships\n", friends->Size);
return friends;}
// If data for game settings exists returns FriendsOfFriends value, otherwise returns truebool DQRNetworkManager::IsSessionFriendsOfFriends(MXSM::MultiplayerSession^ session){ // Default to true, don't want to incorrectly prevent joining bool friendsOfFriends = true;
// We retrieve the game session data later too, shouldn't really duplicate this void *gameSessionData = malloc( sizeof(GameSessionData)); memset(gameSessionData, 0, sizeof(GameSessionData));
bool result = GetGameSessionData(session, gameSessionData);
if (result) { friendsOfFriends = app.GetGameHostOption(((GameSessionData *)gameSessionData)->m_uiGameHostSettings, eGameHostOption_FriendsOfFriends); }
free(gameSessionData);
return friendsOfFriends;}
// Parses custom json data from session and populates game session data param, return true if parse succeededbool DQRNetworkManager::GetGameSessionData(MXSM::MultiplayerSession^ session, void *gameSessionData){ Platform::String ^gameSessionDataJson = session->SessionProperties->SessionCustomPropertiesJson; if( gameSessionDataJson ) { try { Windows::Data::Json::JsonObject^ customParam = Windows::Data::Json::JsonObject::Parse(gameSessionDataJson); Windows::Data::Json::JsonValue^ customValue = customParam->GetNamedValue(L"GameSessionData"); Platform::String ^customValueString = customValue->GetString(); if( customValueString ) { base64_decode( customValueString, (unsigned char *)gameSessionData, sizeof(GameSessionData) ); return true; } } catch (Platform::COMException^ ex) { LogCommentWithError( L"Custom GameSessionData parameter Parse/GetNamedValue failed", ex->HResult ); } }
return false;}