bool FOnlineSubsystemSteam::Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar) { if (FOnlineSubsystemImpl::Exec(InWorld, Cmd, Ar)) { return true; } bool bWasHandled = false; if (FParse::Command(&Cmd, TEXT("DELETECLOUDFILES"))) { IOnlineUserCloudPtr UserCloud = GetUserCloudInterface(); FUniqueNetIdSteam SteamId(SteamUser()->GetSteamID()); FOnEnumerateUserFilesCompleteDelegate Delegate = FOnEnumerateUserFilesCompleteDelegate::CreateStatic(&DeleteFromEnumerateUserFilesComplete); GPerCloudDeleteFromEnumerateUserFilesCompleteDelegateHandles.Add(UserCloud.Get(), UserCloud->AddOnEnumerateUserFilesCompleteDelegate_Handle(Delegate)); UserCloud->EnumerateUserFiles(SteamId); bWasHandled = true; } else if (FParse::Command(&Cmd, TEXT("SYNCLOBBIES"))) { if (SessionInterface.IsValid()) { SessionInterface->SyncLobbies(); bWasHandled = true; } } return bWasHandled; }
TSharedPtr<const FUniqueNetId> FOnlineIdentitySteam::CreateUniquePlayerId(uint8* Bytes, int32 Size) { if (Bytes && Size == sizeof(uint64)) { uint64* RawUniqueId = (uint64*)Bytes; CSteamID SteamId(*RawUniqueId); if (SteamId.IsValid()) { return MakeShareable(new FUniqueNetIdSteam(SteamId)); } } return NULL; }
void FOnlineAsyncTaskSteamDeleteUserFile::Tick() { bWasSuccessful = false; if (SteamRemoteStorage() && FileName.Len() > 0) { CSteamID SteamId(*(uint64*)UserId.GetBytes()); if (SteamUser()->BLoggedOn() && SteamUser()->GetSteamID() == SteamId) { bool bCloudDeleteSuccess = true; if (bShouldCloudDelete) { // Remove the cloud flag, the file remains safely available on the local machine bCloudDeleteSuccess = SteamRemoteStorage()->FileForget(TCHAR_TO_UTF8(*FileName)); } bool bLocalDeleteSuccess = true; if (bShouldLocallyDelete) { bLocalDeleteSuccess = false; // Only clear the tables if we're permanently deleting the file // Need to make sure nothing async is happening first (this is a formality as nothing in Steam actually is) IOnlineUserCloudPtr UserCloud = Subsystem->GetUserCloudInterface(); if (UserCloud->ClearFile(UserId, FileName)) { // Permanent delete bLocalDeleteSuccess = SteamRemoteStorage()->FileDelete(TCHAR_TO_UTF8(*FileName)); Subsystem->ClearUserCloudMetadata(UserId, FileName); } } bWasSuccessful = bCloudDeleteSuccess && bLocalDeleteSuccess; } else { UE_LOG_ONLINE(Warning, TEXT("Can only delete cloud files for logged in user.")); } } else { UE_LOG_ONLINE(Warning, TEXT("Steam remote storage API disabled.")); } bIsComplete = true; }
void FOnlineAsyncTaskSteamEnumerateUserFiles::Tick() { bIsComplete = true; bWasSuccessful = false; if (SteamRemoteStorage()) { CSteamID SteamId(*(uint64*)UserId.GetBytes()); if (SteamUser()->BLoggedOn() && SteamUser()->GetSteamID() == SteamId) { //SteamSubsystem->GetUserCloudInterface()->DumpCloudState(UserId); FScopeLock ScopeLock(&Subsystem->UserCloudDataLock); // Get or create the user metadata entry and empty it FSteamUserCloudData* UserMetadata = Subsystem->GetUserCloudEntry(UserId); UserMetadata->CloudMetadata.Empty(); // Fill in the metadata entries const int32 FileCount = (int32) SteamRemoteStorage()->GetFileCount(); for (int32 FileIdx = 0; FileIdx < FileCount; FileIdx++) { int32 FileSize = 0; const char *FileName = SteamRemoteStorage()->GetFileNameAndSize(FileIdx, &FileSize); new (UserMetadata->CloudMetadata) FCloudFileHeader(UTF8_TO_TCHAR(FileName), UTF8_TO_TCHAR(FileName), int32(FileSize)); //SteamSubsystem->GetUserCloudInterface()->DumpCloudFileState(UserId, FileName); } bWasSuccessful = true; } else { UE_LOG_ONLINE(Warning, TEXT("Can only enumerate cloud files for logged in user.")); } } else { UE_LOG_ONLINE(Warning, TEXT("Steam remote storage API disabled.")); } }
bool FOnlineAchievementsSteam::ResetAchievements(const FUniqueNetId& PlayerId) { if (!bHaveConfiguredAchievements) { // we don't have achievements UE_LOG_ONLINE(Warning, TEXT("Steam achievements have not been configured in .ini")); return false; } FUniqueNetIdSteam SteamId(PlayerId); // check if this is our player (cannot report for someone else) if (SteamUser() == NULL || SteamUser()->GetSteamID() != SteamId) { // we don't have achievements UE_LOG_ONLINE(Warning, TEXT("Cannot clear Steam achievements for non-local player %s"), *PlayerId.ToString()); return false; } const TArray<FOnlineAchievement> * PlayerAch = PlayerAchievements.Find(SteamId); if (NULL == PlayerAch) { // achievements haven't been read for a player UE_LOG_ONLINE(Warning, TEXT("Steam achievements have not been read for player %s"), *PlayerId.ToString()); return false; } const int32 AchNum = PlayerAch->Num(); for (int32 AchIdx = 0; AchIdx < AchNum; ++AchIdx) { SteamUserStats()->ClearAchievement(TCHAR_TO_UTF8(*(*PlayerAch)[ AchIdx ].Id)); } // TODO: provide a separate method to just store stats? StatsInt->FlushLeaderboards(FName(TEXT("UNUSED"))); return true; };
/** * Update the backend with the currently defined settings * * @param World current running world instance * @param SessionName name of session to update published settings */ void UpdatePublishedSettings(UWorld* World, FNamedOnlineSession* Session) { ISteamGameServer* SteamGameServerPtr = SteamGameServer(); check(SteamGameServerPtr); // Copy the current settings so we can remove the ones used for well defined search parameters FOnlineSessionSettings TempSessionSettings = Session->SessionSettings; // Server name FString ServerName = Session->OwningUserName; SteamGameServerPtr->SetServerName(TCHAR_TO_UTF8(*ServerName)); // Max user slots reported int32 NumTotalSlots = Session->SessionSettings.NumPublicConnections + Session->SessionSettings.NumPrivateConnections; SteamGameServerPtr->SetMaxPlayerCount(NumTotalSlots); // Region setting FString Region(TEXT("")); SteamGameServerPtr->SetRegion(TCHAR_TO_UTF8(*Region)); // @TODO ONLINE Password protected or not SteamGameServerPtr->SetPasswordProtected(false); // Dedicated server or not SteamGameServerPtr->SetDedicatedServer(Session->SessionSettings.bIsDedicated ? true : false); // Map name FString MapName; if (TempSessionSettings.Get(SETTING_MAPNAME, MapName) && !MapName.IsEmpty()) { SteamGameServerPtr->SetMapName(TCHAR_TO_UTF8(*MapName)); } TempSessionSettings.Remove(SETTING_MAPNAME); // Bot Count int32 BotCount = 0; if (TempSessionSettings.Get(SETTING_NUMBOTS, BotCount)) { SteamGameServerPtr->SetBotPlayerCount(BotCount); } TempSessionSettings.Remove(SETTING_NUMBOTS); // Update all the players names/scores if (World) { AGameState const* const GameState = World->GameState; if (GameState) { for (int32 PlayerIdx=0; PlayerIdx < GameState->PlayerArray.Num(); PlayerIdx++) { APlayerState const* const PlayerState = GameState->PlayerArray[PlayerIdx]; if (PlayerState && PlayerState->UniqueId.IsValid()) { CSteamID SteamId(*(uint64*)PlayerState->UniqueId->GetBytes()); SteamGameServerPtr->BUpdateUserData(SteamId, TCHAR_TO_UTF8(*PlayerState->PlayerName), PlayerState->Score); } } } } // Get the advertised session settings out as Steam key/value pairs FSteamSessionKeyValuePairs AdvertisedKeyValuePairs; GetServerKeyValuePairsFromSession(Session, AdvertisedKeyValuePairs); if (Session->SessionInfo.IsValid()) { FOnlineSessionInfoSteam* SessionInfo = (FOnlineSessionInfoSteam*)(Session->SessionInfo.Get()); GetServerKeyValuePairsFromSessionInfo(SessionInfo, AdvertisedKeyValuePairs); } FString SessionFlags = GetSessionFlagsAsString(Session->SessionSettings); AdvertisedKeyValuePairs.Add(STEAMKEY_SESSIONFLAGS, SessionFlags); FString SessionBuildUniqueId = GetBuildIdAsSteamKey(Session->SessionSettings); GetServerKeyValuePairsFromSessionSettings(TempSessionSettings, AdvertisedKeyValuePairs, EOnlineDataAdvertisementType::ViaOnlineService); FSteamSessionKeyValuePairs AuxKeyValuePairs; GetServerKeyValuePairsFromSessionSettings(TempSessionSettings, AuxKeyValuePairs, EOnlineDataAdvertisementType::ViaPingOnly); FSteamSessionKeyValuePairs TempKeyValuePairs; GetServerKeyValuePairsFromSessionSettings(TempSessionSettings, TempKeyValuePairs, EOnlineDataAdvertisementType::ViaOnlineServiceAndPing); AdvertisedKeyValuePairs.Append(TempKeyValuePairs); AuxKeyValuePairs.Append(TempKeyValuePairs); FString GameTagsString, GameDataString; // Start the game tags with the build id so search results can early out GameTagsString = SessionBuildUniqueId; // Create the properly formatted Steam string (ie key:value,key:value,key) for GameTags/GameData FSteamSessionKeyValuePairs::TConstIterator It(AdvertisedKeyValuePairs); if (It) { UE_LOG_ONLINE(Verbose, TEXT("Master Server Data (%s, %s)"), *It.Key(), *It.Value()); FString NewKey = FString::Printf(TEXT("%s:%s"), *It.Key(), *It.Value()); if (GameTagsString.Len() + NewKey.Len() < k_cbMaxGameServerTags) { GameTagsString = GameTagsString + "," + NewKey; } else { UE_LOG_ONLINE(Warning, TEXT("Server setting %s overflows Steam SetGameTags call"), *NewKey); } if (NewKey.Len() < k_cbMaxGameServerGameData) { GameDataString = NewKey; } else { UE_LOG_ONLINE(Warning, TEXT("Server setting %s overflows Steam SetGameData call"), *NewKey); } ++It; } for (; It; ++It) { UE_LOG_ONLINE(Verbose, TEXT("Master Server Data (%s, %s)"), *It.Key(), *It.Value()); FString NewKey = FString::Printf(TEXT(",%s:%s"), *It.Key(), *It.Value()); if (GameTagsString.Len() + NewKey.Len() < k_cbMaxGameServerTags) { GameTagsString += NewKey; } else { UE_LOG_ONLINE(Warning, TEXT("Server setting %s overflows Steam SetGameTags call"), *NewKey); } if (GameDataString.Len() + NewKey.Len() < k_cbMaxGameServerGameData) { GameDataString += NewKey; } else { UE_LOG_ONLINE(Warning, TEXT("Server setting %s overflows Steam SetGameData call"), *NewKey); } } // Small and searchable game tags (returned in initial server query structure) if (GameTagsString.Len() > 0 && GameTagsString.Len() < k_cbMaxGameServerTags) { UE_LOG_ONLINE(Verbose, TEXT("SetGameTags(%s)"), *GameTagsString); SteamGameServerPtr->SetGameTags(TCHAR_TO_UTF8(*GameTagsString)); } // Large and searchable game data (never returned) if (GameDataString.Len() > 0 && GameDataString.Len() < k_cbMaxGameServerGameData) { UE_LOG_ONLINE(Verbose, TEXT("SetGameData(%s)"), *GameDataString); SteamGameServerPtr->SetGameData(TCHAR_TO_UTF8(*GameDataString)); } // @TODO ONLINE - distinguish between server side keys (SetGameData()) and client side keys (SetKeyValue()) // Set the advertised filter keys (these can not be filtered at master-server level, only client side) SteamGameServerPtr->ClearAllKeyValues(); // Key value pairs sent as rules (requires secondary RulesRequest call) for (FSteamSessionKeyValuePairs::TConstIterator AdvKeyIt(AdvertisedKeyValuePairs); AdvKeyIt; ++AdvKeyIt) { UE_LOG_ONLINE(Verbose, TEXT("Aux Server Data (%s, %s)"), *AdvKeyIt.Key(), *AdvKeyIt.Value()); SteamGameServerPtr->SetKeyValue(TCHAR_TO_UTF8(*AdvKeyIt.Key()), TCHAR_TO_UTF8(*AdvKeyIt.Value())); } // Key value pairs sent as rules (requires secondary RulesRequest call) for (FSteamSessionKeyValuePairs::TConstIterator AuxKeyIt(AuxKeyValuePairs); AuxKeyIt; ++AuxKeyIt) { UE_LOG_ONLINE(Verbose, TEXT("Aux Server Data (%s, %s)"), *AuxKeyIt.Key(), *AuxKeyIt.Value()); SteamGameServerPtr->SetKeyValue(TCHAR_TO_UTF8(*AuxKeyIt.Key()), TCHAR_TO_UTF8(*AuxKeyIt.Value())); } }
void FOnlineAchievementsSteam::WriteAchievements(const FUniqueNetId& PlayerId, FOnlineAchievementsWriteRef& WriteObject, const FOnAchievementsWrittenDelegate& Delegate) { if (!bHaveConfiguredAchievements) { // we don't have achievements UE_LOG_ONLINE(Warning, TEXT("Steam achievements have not been configured in .ini")); WriteObject->WriteState = EOnlineAsyncTaskState::Failed; Delegate.ExecuteIfBound(PlayerId, false); return; } FUniqueNetIdSteam SteamId(PlayerId); // check if this is our player (cannot report for someone else) if (SteamUser() == NULL || SteamUser()->GetSteamID() != SteamId) { // we don't have achievements UE_LOG_ONLINE(Warning, TEXT("Cannot report Steam achievements for non-local player %s"), *PlayerId.ToString()); WriteObject->WriteState = EOnlineAsyncTaskState::Failed; Delegate.ExecuteIfBound(PlayerId, false); return; } const TArray<FOnlineAchievement> * PlayerAch = PlayerAchievements.Find(SteamId); if (NULL == PlayerAch) { // achievements haven't been read for a player UE_LOG_ONLINE(Warning, TEXT("Steam achievements have not been read for player %s"), *PlayerId.ToString()); WriteObject->WriteState = EOnlineAsyncTaskState::Failed; Delegate.ExecuteIfBound(PlayerId, false); return; } const int32 AchNum = PlayerAch->Num(); for (FStatPropertyArray::TConstIterator It(WriteObject->Properties); It; ++It) { const FString AchievementId = It.Key().ToString(); UE_LOG_ONLINE(Verbose, TEXT("WriteObject AchievementId: '%s'"), *AchievementId); for (int32 AchIdx = 0; AchIdx < AchNum; ++AchIdx) { if ((*PlayerAch)[ AchIdx ].Id == AchievementId) { // do not unlock it now, but after a successful write #if !UE_BUILD_SHIPPING float Value = 0.0f; It.Value().GetValue(Value); if (Value <= 0.0f) { UE_LOG_ONLINE(Verbose, TEXT("Resetting achievement '%s'"), *AchievementId); SteamUserStats()->ClearAchievement(TCHAR_TO_UTF8(*AchievementId)); } else { #endif // !UE_BUILD_SHIPPING UE_LOG_ONLINE(Verbose, TEXT("Setting achievement '%s'"), *AchievementId); SteamUserStats()->SetAchievement(TCHAR_TO_UTF8(*AchievementId)); #if !UE_BUILD_SHIPPING } #endif // !UE_BUILD_SHIPPING break; } } } StatsInt->WriteAchievementsInternal(SteamId, WriteObject, Delegate); };
void FOnlineAsyncTaskSteamReadUserFile::Tick() { // Going to be complete no matter what bIsComplete = true; if (SteamRemoteStorage() && FileName.Len() > 0) { CSteamID SteamId(*(uint64*)UserId.GetBytes()); if (SteamUser()->BLoggedOn() && SteamUser()->GetSteamID() == SteamId) { // Currently don't support greater than 1 chunk const int32 FileSize = SteamRemoteStorage()->GetFileSize(TCHAR_TO_UTF8(*FileName)); if (FileSize >= 0 && FileSize <= k_unMaxCloudFileChunkSize) { FScopeLock ScopeLock(&Subsystem->UserCloudDataLock); // Create or get the current entry for this file FSteamUserCloudData* UserCloud = Subsystem->GetUserCloudEntry(UserId); if (UserCloud) { FCloudFile* UserCloudFile = UserCloud->GetFileData(FileName, true); check(UserCloudFile); // Allocate and read in the file UserCloudFile->Data.Empty(FileSize); UserCloudFile->Data.AddUninitialized(FileSize); if (SteamRemoteStorage()->FileRead(TCHAR_TO_UTF8(*FileName), UserCloudFile->Data.GetData(), FileSize) == FileSize) { bWasSuccessful = true; } else { UserCloudFile->Data.Empty(); } } } else { UE_LOG_ONLINE(Warning, TEXT("Requested file %s has invalid size %d."), *FileName, FileSize); } } else { UE_LOG_ONLINE(Warning, TEXT("Can only read cloud files for logged in user.")); } } else { UE_LOG_ONLINE(Warning, TEXT("Steam remote storage API disabled.")); } { FScopeLock ScopeLock(&Subsystem->UserCloudDataLock); FSteamUserCloudData* UserCloud = Subsystem->GetUserCloudEntry(UserId); if (UserCloud) { FCloudFile* UserCloudFileData = UserCloud->GetFileData(FileName); if (UserCloudFileData) { UserCloudFileData->AsyncState = bWasSuccessful ? EOnlineAsyncTaskState::Done : EOnlineAsyncTaskState::Failed; } } } }
bool FOnlineAsyncTaskSteamWriteUserFile::WriteUserFile(const FUniqueNetId& InUserId, const FString& InFileToWrite, const TArray<uint8>& InContents) { bool bSuccess = false; if (InFileToWrite.Len() > 0 && InContents.Num() > 0) { if (SteamRemoteStorage() && FileName.Len() > 0) { CSteamID SteamId(*(uint64*)InUserId.GetBytes()); if (SteamUser()->BLoggedOn() && SteamUser()->GetSteamID() == SteamId) { // Currently don't support greater than 1 chunk if (InContents.Num() < k_unMaxCloudFileChunkSize) { if (SteamRemoteStorage()->FileWrite(TCHAR_TO_UTF8(*InFileToWrite), InContents.GetData(), InContents.Num())) { FScopeLock ScopeLock(&Subsystem->UserCloudDataLock); FSteamUserCloudData* UserCloud = Subsystem->GetUserCloudEntry(InUserId); if (UserCloud) { // Update the metadata table to reflect this write (might be new entry) FCloudFileHeader* UserCloudFileMetadata = UserCloud->GetFileMetadata(InFileToWrite, true); check(UserCloudFileMetadata); UserCloudFileMetadata->FileSize = SteamRemoteStorage()->GetFileSize(TCHAR_TO_UTF8(*InFileToWrite)); UserCloudFileMetadata->Hash = FString(TEXT("0")); // Update the file table to reflect this write FCloudFile* UserCloudFileData = UserCloud->GetFileData(InFileToWrite, true); check(UserCloudFileData); UserCloudFileData->Data = InContents; bSuccess = true; } } else { UE_LOG_ONLINE(Warning, TEXT("Failed to write file to Steam cloud \"%s\"."), *InFileToWrite); } } else { UE_LOG_ONLINE(Warning, TEXT("File too large %d to write to Steam cloud."), InContents.Num()); } } else { UE_LOG_ONLINE(Warning, TEXT("Can only write cloud files for logged in user.")); } } else { UE_LOG_ONLINE(Warning, TEXT("Steam remote storage API disabled.")); } } { FScopeLock ScopeLock(&Subsystem->UserCloudDataLock); FSteamUserCloudData* UserCloud = Subsystem->GetUserCloudEntry(InUserId); if (UserCloud) { FCloudFile* UserCloudFileData = UserCloud->GetFileData(InFileToWrite, true); check(UserCloudFileData); UserCloudFileData->AsyncState = bSuccess ? EOnlineAsyncTaskState::Done : EOnlineAsyncTaskState::Failed; } } //SteamSubsystem->GetUserCloudInterface()->DumpCloudFileState(InUserId, InFileToWrite); return bSuccess; }