Files
UnrealEngine/Engine/Plugins/Online/OnlineSubsystemSteam/Source/Private/OnlineUserCloudInterfaceSteam.cpp
2025-05-18 13:04:45 +08:00

460 lines
15 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "OnlineUserCloudInterfaceSteam.h"
#include "Misc/ScopeLock.h"
#include "OnlineSubsystemSteam.h"
FString FOnlineAsyncTaskSteamEnumerateUserFiles::ToString() const
{
return FString::Printf(TEXT("FOnlineAsyncTaskSteamEnumerateUserFiles bWasSuccessful:%d UserId:%s"),
WasSuccessful(), *UserId->ToDebugString());
}
void FOnlineAsyncTaskSteamEnumerateUserFiles::Tick()
{
bIsComplete = true;
bWasSuccessful = false;
if (SteamRemoteStorage())
{
if (SteamUser()->BLoggedOn() && SteamUser()->GetSteamID() == *UserId)
{
//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_CLOUD(Warning, TEXT("Can only enumerate cloud files for logged in user."));
}
}
else
{
UE_LOG_ONLINE_CLOUD(Warning, TEXT("Steam remote storage API disabled."));
}
}
void FOnlineAsyncTaskSteamEnumerateUserFiles::TriggerDelegates()
{
FOnlineAsyncTaskSteam::TriggerDelegates();
IOnlineUserCloudPtr UserCloudInterface = Subsystem->GetUserCloudInterface();
UserCloudInterface->TriggerOnEnumerateUserFilesCompleteDelegates(bWasSuccessful, *UserId);
}
FString FOnlineAsyncTaskSteamReadUserFile::ToString() const
{
return FString::Printf(TEXT("FOnlineAsyncTaskSteamReadUserFile bWasSuccessful:%d UserId:%s FileName:%s"),
WasSuccessful(), *UserId->ToDebugString(), *FileName);
}
void FOnlineAsyncTaskSteamReadUserFile::Tick()
{
// Going to be complete no matter what
bIsComplete = true;
if (SteamRemoteStorage() && FileName.Len() > 0)
{
if (SteamUser()->BLoggedOn() && SteamUser()->GetSteamID() == *UserId)
{
// 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_CLOUD(Warning, TEXT("Requested file %s has invalid size %d."), *FileName, FileSize);
}
}
else
{
UE_LOG_ONLINE_CLOUD(Warning, TEXT("Can only read cloud files for logged in user."));
}
}
else
{
UE_LOG_ONLINE_CLOUD(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;
}
}
}
}
void FOnlineAsyncTaskSteamReadUserFile::TriggerDelegates()
{
FOnlineAsyncTaskSteam::TriggerDelegates();
IOnlineUserCloudPtr UserCloudInterface = Subsystem->GetUserCloudInterface();
UserCloudInterface->TriggerOnReadUserFileCompleteDelegates(bWasSuccessful, *UserId, FileName);
}
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_CLOUD(Warning, TEXT("Failed to write file to Steam cloud \"%s\"."), *InFileToWrite);
}
}
else
{
UE_LOG_ONLINE_CLOUD(Warning, TEXT("File too large %d to write to Steam cloud."), InContents.Num());
}
}
else
{
UE_LOG_ONLINE_CLOUD(Warning, TEXT("Can only write cloud files for logged in user."));
}
}
else
{
UE_LOG_ONLINE_CLOUD(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;
}
FString FOnlineAsyncTaskSteamWriteUserFile::ToString() const
{
return FString::Printf(TEXT("FOnlineAsyncTaskSteamWriteUserFile bWasSuccessful:%d UserId:%s FileName:%s"),
WasSuccessful(), *UserId->ToDebugString(), *FileName);
}
void FOnlineAsyncTaskSteamWriteUserFile::Tick()
{
// Going to be complete no matter what
bIsComplete = true;
if (WriteUserFile(*UserId, FileName, Contents))
{
bWasSuccessful = true;
}
// Done with this copy of the data regardless
Contents.Empty();
}
void FOnlineAsyncTaskSteamWriteUserFile::TriggerDelegates()
{
FOnlineAsyncTaskSteam::TriggerDelegates();
IOnlineUserCloudPtr UserCloudInterface = Subsystem->GetUserCloudInterface();
UserCloudInterface->TriggerOnWriteUserFileCompleteDelegates(bWasSuccessful, *UserId, FileName);
}
FOnlineUserCloudSteam::~FOnlineUserCloudSteam()
{
if (SteamSubsystem)
{
SteamSubsystem->ClearUserCloudFiles();
}
}
bool FOnlineUserCloudSteam::GetFileContents(const FUniqueNetId& UserId, const FString& FileName, TArray<uint8>& FileContents)
{
FScopeLock ScopeLock(&SteamSubsystem->UserCloudDataLock);
// Search for the specified file and return the raw data
FSteamUserCloudData* UserCloudData = SteamSubsystem->GetUserCloudEntry(UserId);
if (UserCloudData)
{
FCloudFile* SteamCloudFile = UserCloudData->GetFileData(FileName);
if (SteamCloudFile && SteamCloudFile->AsyncState == EOnlineAsyncTaskState::Done && SteamCloudFile->Data.Num() > 0)
{
FileContents = SteamCloudFile->Data;
return true;
}
}
return false;
}
bool FOnlineUserCloudSteam::ClearFiles(const FUniqueNetId& UserId)
{
FScopeLock ScopeLock(&SteamSubsystem->UserCloudDataLock);
// Search for the specified file
FSteamUserCloudData* UserCloudData = SteamSubsystem->GetUserCloudEntry(UserId);
if (UserCloudData)
{
return UserCloudData->ClearFiles();
}
return true;
}
bool FOnlineUserCloudSteam::ClearFile(const FUniqueNetId& UserId, const FString& FileName)
{
FScopeLock ScopeLock(&SteamSubsystem->UserCloudDataLock);
// Search for the specified file
FSteamUserCloudData* UserCloudData = SteamSubsystem->GetUserCloudEntry(UserId);
if (UserCloudData)
{
return UserCloudData->ClearFileData(FileName);
}
return true;
}
void FOnlineUserCloudSteam::EnumerateUserFiles(const FUniqueNetId& UserId)
{
SteamSubsystem->QueueAsyncTask(new FOnlineAsyncTaskSteamEnumerateUserFiles(SteamSubsystem, FUniqueNetIdSteam::Cast(UserId)));
}
void FOnlineUserCloudSteam::GetUserFileList(const FUniqueNetId& UserId, TArray<FCloudFileHeader>& UserFiles)
{
FScopeLock ScopeLock(&SteamSubsystem->UserCloudDataLock);
FSteamUserCloudData* UserMetadata = SteamSubsystem->GetUserCloudEntry(UserId);
UserFiles = UserMetadata->CloudMetadata;
}
bool FOnlineUserCloudSteam::ReadUserFile(const FUniqueNetId& UserId, const FString& FileName)
{
FScopeLock ScopeLock(&SteamSubsystem->UserCloudDataLock);
// Create or get the current entry for this file
FSteamUserCloudData* UserCloud = SteamSubsystem->GetUserCloudEntry(UserId);
if (UserCloud && FileName.Len() > 0)
{
FCloudFile* UserCloudFile = UserCloud->GetFileData(FileName, true);
UserCloudFile->AsyncState = EOnlineAsyncTaskState::InProgress;
SteamSubsystem->QueueAsyncTask(new FOnlineAsyncTaskSteamReadUserFile(SteamSubsystem, FUniqueNetIdSteam::Cast(UserId), FileName));
return true;
}
return false;
}
bool FOnlineUserCloudSteam::WriteUserFile(const FUniqueNetId& UserId, const FString& FileName, TArray<uint8>& FileContents, bool bCompressBeforeUpload)
{
FScopeLock ScopeLock(&SteamSubsystem->UserCloudDataLock);
// Create or get the current entry for this file
FSteamUserCloudData* UserCloud = SteamSubsystem->GetUserCloudEntry(UserId);
if (UserCloud && FileName.Len() > 0)
{
FCloudFile* UserCloudFile = UserCloud->GetFileData(FileName, true);
UserCloudFile->AsyncState = EOnlineAsyncTaskState::InProgress;
SteamSubsystem->QueueAsyncTask(new FOnlineAsyncTaskSteamWriteUserFile(SteamSubsystem, FUniqueNetIdSteam::Cast(UserId), FileName, FileContents));
return true;
}
return false;
}
void FOnlineUserCloudSteam::CancelWriteUserFile(const FUniqueNetId& UserId, const FString& FileName)
{
// Not implemented
}
bool FOnlineUserCloudSteam::DeleteUserFile(const FUniqueNetId& UserId, const FString& FileName, bool bShouldCloudDelete, bool bShouldLocallyDelete)
{
SteamSubsystem->QueueAsyncTask(new FOnlineAsyncTaskSteamDeleteUserFile(SteamSubsystem, FUniqueNetIdSteam::Cast(UserId), FileName, bShouldCloudDelete, bShouldLocallyDelete));
return true;
}
bool FOnlineUserCloudSteam::RequestUsageInfo(const FUniqueNetId& UserId)
{
// Not implemented
return false;
}
void FOnlineUserCloudSteam::DumpCloudState(const FUniqueNetId& UserId)
{
uint64 TotalBytes, TotalAvailable;
if (SteamRemoteStorage()->GetQuota(&TotalBytes, &TotalAvailable) == false)
{
TotalBytes = TotalAvailable = 0;
}
UE_LOG_ONLINE_CLOUD(Verbose, TEXT("Steam Disk Quota: %" UINT64_FMT " / %" UINT64_FMT), TotalAvailable, TotalBytes);
UE_LOG_ONLINE_CLOUD(Verbose, TEXT("Game does %shave cloud storage enabled."), SteamRemoteStorage()->IsCloudEnabledForApp() ? TEXT("") : TEXT("NOT "));
UE_LOG_ONLINE_CLOUD(Verbose, TEXT("User does %shave cloud storage enabled."), SteamRemoteStorage()->IsCloudEnabledForAccount() ? TEXT("") : TEXT("NOT "));
}
void FOnlineUserCloudSteam::DumpCloudFileState(const FUniqueNetId& UserId, const FString& FileName)
{
if (FileName.Len() > 0)
{
UE_LOG_ONLINE_CLOUD(Log, TEXT("Cloud File State file %s:"), *FileName);
{
FScopeLock ScopeLock(&SteamSubsystem->UserCloudDataLock);
FSteamUserCloudData* UserCloud = SteamSubsystem->GetUserCloudEntry(UserId);
FCloudFileHeader* FileMetadata = UserCloud->GetFileMetadata(FileName);
if (FileMetadata)
{
UE_LOG_ONLINE_CLOUD(Log, TEXT("\tMeta: FileName:%s DLName:%s FileSize:%d Hash:%s"), *FileMetadata->FileName, *FileMetadata->DLName, FileMetadata->FileSize, *FileMetadata->Hash);
}
else
{
UE_LOG_ONLINE_CLOUD(Log, TEXT("\tNo metadata found!"));
}
FCloudFile* FileData = UserCloud->GetFileData(FileName);
if (FileData)
{
UE_LOG_ONLINE_CLOUD(Log, TEXT("\tFileCache: FileName:%s State:%s CacheSize:%d"), *FileData->FileName, EOnlineAsyncTaskState::ToString(FileData->AsyncState), FileData->Data.Num());
}
else
{
UE_LOG_ONLINE_CLOUD(Log, TEXT("\tNo cache entry found!"));
}
}
int32 FileSize = SteamRemoteStorage()->GetFileSize(TCHAR_TO_UTF8(*FileName));
UE_LOG_ONLINE_CLOUD(Log, TEXT("\tSteam: FileName:%s Size:%d Exists:%s Persistent:%s"),
*FileName, FileSize,
SteamRemoteStorage()->FileExists(TCHAR_TO_UTF8(*FileName)) ? TEXT("Y") : TEXT("N"),
SteamRemoteStorage()->FilePersisted(TCHAR_TO_UTF8(*FileName)) ? TEXT("Y") : TEXT("N"));
}
}
FString FOnlineAsyncTaskSteamDeleteUserFile::ToString() const
{
return FString::Printf(TEXT("FOnlineAsyncTaskSteamDeleteUserFile bWasSuccessful:%d UserId:%s FileName:%s"),
WasSuccessful(), *UserId->ToDebugString(), *FileName);
}
void FOnlineAsyncTaskSteamDeleteUserFile::Tick()
{
bWasSuccessful = false;
if (SteamRemoteStorage() && FileName.Len() > 0)
{
if (SteamUser()->BLoggedOn() && SteamUser()->GetSteamID() == *UserId)
{
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_CLOUD(Warning, TEXT("Can only delete cloud files for logged in user."));
}
}
else
{
UE_LOG_ONLINE_CLOUD(Warning, TEXT("Steam remote storage API disabled."));
}
bIsComplete = true;
}
void FOnlineAsyncTaskSteamDeleteUserFile::TriggerDelegates()
{
FOnlineAsyncTaskSteam::TriggerDelegates();
IOnlineUserCloudPtr UserCloudInterface = Subsystem->GetUserCloudInterface();
UserCloudInterface->TriggerOnDeleteUserFileCompleteDelegates(bWasSuccessful, *UserId, FileName);
}