I have been tackling this for 20 days now, with little to no documentation and i decided to share this with every one so that you dont have to go throught the same thing.
- Get a session token
- Call GetMatch
- Order the information you got
- Get your players playfabID's
- set your developer secret key
- call update player statistics
Before you do all that you need to change a setting in unreal so that you can actualy make calls please refer to this thread.
https://community.playfab.com/articles/43928/compatibility-issue-discovered-with-ue425-and-play.html?fbclid=IwAR0XVm_jdxU095-gTpLlzxWMaN_MZPfAEshiKDE_0Sv4H6QMg_E8hlyJve4
first all the includes you are going to need:
#include "PlayFab.h" #include "PlayFabCommon.h" #include "Core/PlayFabClientDataModels.h" #include "Core/PlayFabError.h" #include "Core/PlayFabMultiplayerAPI.h" #include "Core/PlayFabAuthenticationAPI.h" #include "Core/PlayFabServerAPI.h" #include "Core/PlayFabAdminAPI.h" #include "Core/PlayFabProfilesAPI.h"
How to get the session token :
std::unordered_map<std::string, std::string> config = Microsoft::Azure::Gaming::GSDK::getConfigSettings(); //Retrieve a particular configuration value auto it = config.find(Microsoft::Azure::Gaming::GSDK::SESSION_ID_KEY); if (it != config.end()) { std::string sessionIDraw = config[Microsoft::Azure::Gaming::GSDK::SESSION_ID_KEY]; sessionID = FString(sessionIDraw.c_str()); //Set settings PlayFab::PlayFabSettings::SetTitleId(FString("XXX"));//not sure if needed, didnt test without //get Token PlayFabAuthenticationPtr authenticationAPI = IPlayFabModuleInterface::Get().GetAuthenticationAPI(); TSharedPtr<FJsonObject> EntityItems = MakeShareable(new FJsonObject); TSharedPtr<FJsonObject> Entity = MakeShareable(new FJsonObject); ////make json EntityItems->SetField(TEXT("id"), MakeShareable(new FJsonValueString(FString("42C9E")))); EntityItems->SetField(TEXT("Type"), MakeShareable(new FJsonValueString(FString("title")))); EntityItems->SetField(TEXT("TypeString"), MakeShareable(new FJsonValueString(FString("")))); Entity->SetField(TEXT("Entity"), MakeShareable(new FJsonValueObject(EntityItems))); PlayFab::AuthenticationModels::FGetEntityTokenRequest tokenRequest = PlayFab::AuthenticationModels::FGetEntityTokenRequest(Entity); authenticationAPI->GetEntityToken(tokenRequest, PlayFab::UPlayFabAuthenticationAPI::FGetEntityTokenDelegate::CreateUObject(this, &AArenaGameMode::OnGetTokenSuccess), PlayFab::FPlayFabErrorDelegate::CreateUObject(this, &AArenaGameMode::OnGetTokenFailed));
The json needs to be done like that, for some reason the request doesnt get filled properly when you try to acess and fill the members directly in the request.
Now onto the Get match, if getting the token is successfull we get the match in the success callback:
PlayFab::PlayFabSettings::SetEntityToken(result.EntityToken); //Get match PlayFabMultiplayerPtr multiplayerAPI = IPlayFabModuleInterface::Get().GetMultiplayerAPI(); PlayFab::MultiplayerModels::FGetMatchRequest request; request.EscapeObject = false; request.MatchId = sessionID; request.QueueName = "QuickPlay5"; request.ReturnMemberAttributes = true; //PlayFab::FPlayFabCppRequestCommon::AuthenticationContext x = PlayFab::FPlayFabCppRequestCommon::AuthenticationContext(); UE_LOG(LogTemp, Warning, TEXT("getting match")); UE_LOG(LogTemp, Warning, TEXT("%s"), *request.toJSONString()); //LogPlayFabCpp: Error: You must call GetEntityToken API Method before calling this function. multiplayerAPI->GetMatch(request, PlayFab::UPlayFabMultiplayerAPI::FGetMatchDelegate::CreateUObject(this, &AArenaGameMode::OnGetMatchSuccess), PlayFab::FPlayFabErrorDelegate::CreateUObject(this, &AArenaGameMode::OnGetMatchFailed));
Here you can make the request and acess the members directly, i dont know why i think it had to do with unreal smart pointers and references, if any one that knows more about that please let me know in the answers, so if the get match is sucessfull you are gonna get a success callback where you can order ther info from all the players:
for (int32 x = 0; x < result.Members.Num()/*result.Members.Num()*/; x++) { playersInfo[x].team = result.Members[x].TeamId; //get mmr FString atributes = result.Members[x].Attributes->toJSONString(); // TSharedPtr<FJsonObject> JsonObj = MakeShareable(new FJsonObject()); TSharedRef<TJsonReader<TCHAR>> JsonReader = TJsonReaderFactory<TCHAR>::Create(*result.Members[0].Attributes.Get()->toJSONString()); FString mmrString; if(FJsonSerializer::Deserialize(JsonReader, JsonObj)) { UE_LOG(LogTemp, Warning, TEXT("deserialized sucessfully")); TSharedPtr<FJsonObject> tempObj = JsonObj->GetObjectField("DataObject"); mmrString = tempObj->GetStringField("MMR"); } int mmr = FCString::Atoi(*mmrString); playersInfo[x].MMR = mmr; //----- //get player ID playersInfo[x].entityID = result.Members[x].Entity.Id; //--------- if (result.Members[x].TeamId == FString("team1")) { GetGameState<AProjectArenaGameState>()->Team1MMR += mmr; } else { GetGameState<AProjectArenaGameState>()->Team2MMR += mmr; } }
At this point you should have a struct with all the info from the match for all players, after that we need to get the playfab ID for the players so that we can call the function that updates the statistic because it requires a playfab ID, we do it in the login, which is the unreal function that first gets called when a player tries to connect.
APlayerController* AArenaGameMode::Login(UPlayer* NewPlayer, ENetRole InRemoteRole, const FString& Portal, const FString& Options, const FUniqueNetIdRepl& UniqueId, FString& ErrorMessage) { if(playfab)//playfab { if (UGameplayStatics::HasOption(Options, FString("ID"))) { UE_LOG(LogTemp, Warning, TEXT("in ID section")); playerID = UGameplayStatics::ParseOption(Options, FString("ID")); PlayFabProfilesPtr profilesAPI = IPlayFabModuleInterface::Get().GetProfilesAPI(); TSharedPtr<FJsonObject> EntityItems = MakeShareable(new FJsonObject); TSharedPtr<FJsonObject> Entity = MakeShareable(new FJsonObject); ////make json EntityItems->SetField(TEXT("id"), MakeShareable(new FJsonValueString(playerID))); EntityItems->SetField(TEXT("Type"), MakeShareable(new FJsonValueString(FString("title_player_account")))); EntityItems->SetField(TEXT("TypeString"), MakeShareable(new FJsonValueString(FString("")))); Entity->SetField(TEXT("Entity"), MakeShareable(new FJsonValueObject(EntityItems))); PlayFab::ProfilesModels::FGetEntityProfileRequest profileRequest = PlayFab::ProfilesModels::FGetEntityProfileRequest(Entity); UE_LOG(LogTemp, Warning, TEXT("Get player profile!!!!!!")); profilesAPI->GetProfile(profileRequest, PlayFab::UPlayFabProfilesAPI::FGetProfileDelegate::CreateUObject(this, &AArenaGameMode::OnGetProfileSuccess), PlayFab::FPlayFabErrorDelegate::CreateUObject(this, &AArenaGameMode::OnGetProfileFailed)); } } return Super::Login(NewPlayer, InRemoteRole, Portal, Options, FUniqueNetIdRepl(), ErrorMessage); }
If the callback is sucessfull we are going to need to know which player requested that "GetProfile" so in order to identify the players we connect with the entity token ID without that we wouldnt know which player called that function and we would have the value but we woulnt know who which player it belonged to so make sure you connect with a identity token ID in the server parameters.
Sucess callback looks like so:
for (int32 x = 0; x < playersInfo.Num()/*result.Members.Num()*/; x++) { //entity ID that we connect with that comes in get match //entity ID in the response if (playersInfo[x].entityID == result.Profile.Get()->Entity.Get()->Id) { playersInfo[x].playfabID = result.Profile.Get()->Lineage.Get()->MasterPlayerAccountId; //the actual playfab ID needed } }
After we have all this info, all that is left is updating the MMR bu first you gota set ur secret key:
IPlayFabCommonModuleInterface::Get().SetDeveloperSecretKey(FString("yourkeyherexxxxx")); //key found on your dashboard PlayFabServerPtr serverAPI = IPlayFabModuleInterface::Get().GetServerAPI();
After that just make the call when the game ends:
PlayFab::ServerModels::FUpdatePlayerStatisticsRequest statisticRequest; TArray<PlayFab::ServerModels::FStatisticUpdate> statisticsArray; PlayFab::ServerModels::FStatisticUpdate statistic; statistic.StatisticName = FString("MatchMakingRating"); statistic.Value = 10; statisticsArray.Add(statistic); statisticRequest.Statistics = statisticsArray; statisticRequest.PlayFabId = playersInfo[x].playfabID; serverAPI->UpdatePlayerStatistics(statisticRequest, PlayFab::UPlayFabServerAPI::FUpdatePlayerStatisticsDelegate::CreateUObject(this, &AArenaGameMode::OnStatisticUpdateSuccess), PlayFab::FPlayFabErrorDelegate::CreateUObject(this, &AArenaGameMode::OnStatisticUpdateFailed)); }
.H callbacks:
void OnGetMatchSuccess(const PlayFab::MultiplayerModels::FGetMatchResult & result); void OnGetMatchFailed(const PlayFab::FPlayFabCppError & error); void OnGetTokenSuccess(const PlayFab::AuthenticationModels::FGetEntityTokenResponse & result); void OnGetTokenFailed(const PlayFab::FPlayFabCppError & error); void OnStatisticUpdateSuccess(const PlayFab::ServerModels::FUpdatePlayerStatisticsResult & result); void OnStatisticUpdateFailed(const PlayFab::FPlayFabCppError & error); void OnGetProfileSuccess(const PlayFab::ProfilesModels::FGetEntityProfileResponse & result); void OnGetProfileFailed(const PlayFab::FPlayFabCppError & error);
Thats it!
Please keep in mind that this might have some bugs and is probably not the best code in the world, i am not even sure if this is the correct or intended way to do things, i just know that it works and it gave me a lot of trouble to figure all of this out. I just hope this helps some one.
If any Playfab dev sees this and knows of a better way to do things please let us know, i am just trying to fill a gap in the documentation (god knows we need it haha).
Also if this helped you please buy me a beer if you ever see me.