스팀웍스에 게임을 등록한 상태고, 프로젝트와의 설정이 완료된 상태 이후에 작업을 해야 한다.
스팀 클라우드는 기본적으로 파일이름과 콘텐츠의 짝으로 이루어져 있다. 그렇기 때문에 파일이름을 중복으로 하면 잘못된 데이터로 덮어 쓰이기 때문에 파일이름을 굉장히 조심히 다뤄야 한다.
스팀 클라우드의 단점은 스팀웍스에서 게임 운영자가 유저들의 전체적인 데이터를 볼 수 없는 것 같다. (만약 찾으신 분 계시면 댓글로 알려주세요) 유저가 본인에게 저장된 데이터 파일은 아래의 주소에서 확인이 가능하다.
Sign In
store.steampowered.com
개발했던 게임의 경로로 가 보니 아래의 그림과 같이 데이터가 저장 돼 있음을 알 수 있었다.

우선, Steamworks에서 Application -> SteamCloud로 이동하면 아래와 같은 그림이 나온다.

파일의 개수와 허용된 용량의 크기가 나오고 적절한 값으로 지정해 주면 된다.
스팀 클라우드는 언리얼에서 OnlineSubsystem -> IOnlineUserCloudPtr -> FOnlineUserCloudSteam -> SteamRemoteStorage의 로직으로 구현 돼 있다. 데이터를 Write 할 때는 비동기가 아니지만, Read 할 때는 데이터를 비동기로 읽어온다.
언리얼에서 제공하는 스팀 클라우드의 인터페이스 함수 기능들이다. 함수들만 따로 넣었다.
위치는 Engine\Plugins\Online\OnlineSubsystem\Public\Interfaces\OnlineUserCloudInterface.h 이다
virtual bool GetFileContents(const FUniqueNetId& UserId, const FString& FileName, TArray<uint8>& FileContents) = 0;
virtual bool ClearFiles(const FUniqueNetId& UserId) = 0;
virtual bool ClearFile(const FUniqueNetId& UserId, const FString& FileName) = 0;
virtual void EnumerateUserFiles(const FUniqueNetId& UserId) = 0;
virtual void GetUserFileList(const FUniqueNetId& UserId, TArray<FCloudFileHeader>& UserFiles) = 0;
virtual bool ReadUserFile(const FUniqueNetId& UserId, const FString& FileName) = 0;
virtual bool WriteUserFile(const FUniqueNetId& UserId, const FString& FileName, TArray<uint8>& FileContents, bool bCompressBeforeUpload = false) = 0;
virtual void CancelWriteUserFile(const FUniqueNetId& UserId, const FString& FileName) = 0;
virtual bool DeleteUserFile(const FUniqueNetId& UserId, const FString& FileName, bool bShouldCloudDelete, bool bShouldLocallyDelete) = 0;
virtual bool RequestUsageInfo(const FUniqueNetId& UserId) = 0;
virtual void DumpCloudState(const FUniqueNetId& UserId) = 0;
virtual void DumpCloudFileState(const FUniqueNetId& UserId, const FString& FileName) = 0;
이번 포스트에서는 Write, Read, Get만 만들어서 수행 해 보겠다.
스팀 클라우드에 대한 코드는 GameInstance에 작성해서 테스트해 보고 본인의 구성에 맞는 위치에 옮겨서 작업하면 될 것 같다.
1. SteamCloud Write
bool APiratesControllerBase::WriteUserFile_Test(const FString& FileName, const FString& FileContents, bool bCompressBeforeUpload)
{
if (UMyPiratesLandFunctionLibrary::IsSubsystemSteam())
{
FString GameSave = FileContents;
TArray<uint8> CompressedBytes;
FArchiveSaveCompressedProxy Compressor(CompressedBytes, NAME_Zlib);
Compressor << GameSave;
Compressor.Flush();
Compressor.Close();
if (const IOnlineSubsystem* OnlineSubsystem = IOnlineSubsystem::Get())
{
if (GetPlayerUniqueNetId().IsValid())
{
return OnlineSubsystem->GetUserCloudInterface()->WriteUserFile(*GetPlayerUniqueNetId().Get(), FileName, CompressedBytes, bCompressBeforeUpload);
}
}
return false;
}
return false;
}
FileName은 스팀 클라우드에 저장할 때 데이터를 가져올 Key값으로 생각하면 되고 FileContents는 그 Key값에 대응되는 데이터라고 생각하면 된다. WriteUserFile 함수를 사용할 때 데이터를 넘겨주는 방식이 TArray<uint8> 형식이어서 시리얼라이제이션은 FArchiveSaveCompressedProxy을 이용했다. 만약 저장하고 싶은 데이터가 Key값 대비 여러 개 인경우 구조체로 만들어서 해 주면 된다.
위와 같이 코드를 구성하고 엔진에서

위와 같이 블루프린트를 만든다. 일단 FileName(Key에 해당)값은 CloudTest로 넣었다.
2. SteamCloud Read
스팀 클라우드에서는 데이터를 가져올 때 두 가지 단계가 존재한다. 우선 데이터를 Read 하고, 그 데이터를 Get 하여서 실제로 저장된 데이터를 사용 가능하다.
bool APiratesControllerBase::ReadUserFile_Test(const FString& FileName, const FDelegateReadUserFileCompleted& ReadUserFileCompleted)
{
if (const IOnlineSubsystem* OnlineSubsystem = IOnlineSubsystem::Get())
{
if (GetPlayerUniqueNetId().IsValid())
{
OnlineSubsystem->GetUserCloudInterface()->ClearOnReadUserFileCompleteDelegates(this);
OnlineSubsystem->GetUserCloudInterface()->OnReadUserFileCompleteDelegates.AddUObject(this, &APiratesControllerBase::ReadUserFileCompleted, ReadUserFileCompleted);
return OnlineSubsystem->GetUserCloudInterface()->ReadUserFile(*GetPlayerUniqueNetId().Get(), FileName);
}
}
return false;
}
void APiratesControllerBase::ReadUserFileCompleted(bool bSuccessful, const FUniqueNetId& UserNetId, const FString& FileName, FDelegateReadUserFileCompleted ReadUserFileCompleted)
{
ReadUserFileCompleted.ExecuteIfBound(FileName);
}
코드에서 특별한 것은 없고, 함수 매개변수 마지막에는 델리게이트를 PayLoad로 사용하기 위해 넣어주었다. 블루프린트에서 함수를 호출하고 리턴을 받아서 사용하기 위해 넣었다. Read가 끝나면 OnReadUserFileCompleteDelegates와 연결된 함수가 호출되는데, 여기서 블루프린트에 바인딩된 함수를 호출하게 했다.
3. SteamCloudContent Get
bool APiratesControllerBase::GetFileContents_Test(const FString& FileName, TArray<uint8>& FileContents)
{
if (const IOnlineSubsystem* OnlineSubsystem = IOnlineSubsystem::Get())
{
if (GetPlayerUniqueNetId().IsValid())
{
return OnlineSubsystem->GetUserCloudInterface()->GetFileContents(*GetPlayerUniqueNetId().Get(), FileName, FileContents);
}
}
return false;
}
FString APiratesControllerBase::FileContentsToStringArray(const TArray<uint8>& FileContents)
{
FString temp;
if (FileContents.Num() < 1)
{
return temp;
}
FArchiveLoadCompressedProxy Decompressor(FileContents, NAME_Zlib);
Decompressor << temp;
Decompressor.Flush();
Decompressor.Close();
return temp;
}
Read가 성공적으로 됐으면 이제 콘텐츠를 Get 하면 된다. 파일이름을 받아와서 파일콘텐츠에 넣어서 반환하게 구현 돼 있다. 아래의 FileContentsToStringArray는 이전에 Write 할 때 스팀에 TArray<uint8> 형식으로 보냈기 때문에 같은 형식으로 받아서 다시 String으로 바꿔주는 함수이다. FArchiveLoadCompressedProxy를 이용해서 디시리얼라이제이션 해 줘서 String으로 받아주게 했다.
블루프린트에서는 아래의 그림과 같이 호출하게 했다.

4. 테스트
이제 구성은 어느 정도 됐으니 패키징하여 실제로 잘 구현이 됐는지 호출해 보자.
패키징이 다 됐다면 스팀을 로그인한 상태로 게임을 실행시켜야 한다. 그렇지 않으면 스팀 클라우드에 데이터가 저장되지 않는다. 게임을 실행시키면 라이브러리에 초록색으로 실행 중이라고 나오게 되는데 마우스 오른쪽 클릭 -> 속성 -> 일반 -> Steam Cloud에 게임 계속 저장을 On 한 상태여야 한다.

게임을 실행시키고 먼저 Write를 해 보자.
블루프린트에서 작성한 WriteTest를 해 보자. `를 누르면 아래의 그림과 같이 커맨드 창이 나오고,
ke * WriteTest [쓰고 싶은 문자] 를 쓰면 이 전에이미 하드코딩해 두었던 FileName값(CloudTest)에 Write_SteamCloud값이 저장될 것이다.

그다음 데이터를 Read 해 보자. 이미 위의 블루프린트에서 데이터 Read와 Get을 하나로 묶어뒀기 때문에 Read 하는 순간 오류가 없다면 Get까지 볼 수 있다.


위의 그림과 같이 저장된 데이터를 받아올 수 있다. ke * WriteTest [원하는 정보]를 수행하면 원하는 정보로 계속 수정 저장된다.
'UnrealEngine > Steam' 카테고리의 다른 글
| UE4 스팀 유저간 P2P 메시지 보내기 (0) | 2024.02.23 |
|---|