Skip to main content

Player BlobData

1. Overview

PGOS provides player blob data service to help developers use binary large object in game. Game developers need to customize the player blob information on the portal first (blob key name, access permissions, etc.), and then they can upload/download these blobs in the game. The maximum size of a single blob is 100MB.

After completing this tutorial, you will understand the following content:

  • How to customize player blob data template on the web portal and view player blob data.
  • How to query, download, and update player blob data in the game.

2. Customize BlobData Template

Before accessing player blob data, the game must customize the player blob information on the portal. Log in to the portal, access your title region, and then go to Players > BlobData Template:

image-20221124112712026

The above screenshot shows an example: This template has two keys, meaning each player in the game will have these two blob items independently. The game can customize blob items based on its gameplay needs.

2.1 Add/Remove BlobData Items

  • Use the Add Item button to create a new item and then fill in the following details:

    • Key: The key name of the item, composed of letters, numbers, and underscores. It must start with a letter and cannot exceed 128 characters.
    • Access permissions: Define access permissions for item.
  • Use the Remove Item button to remove the selected data item. Once a data item is removed, it fails to access the item. Make sure the relevant code logic is modified at the same time.

2.2 BlobData Item Access Permissions

The access permissions management of blob data is the same as that of key-value data, view key-value data access permissions here for details.

3. View Player BlobData

Log in to the portal, access your title region, and then go to Players > Query:

image-20221116192538298

Click on the player ID to view the player detail:

image-20221116192920640

4. Using Player BlobData

4.1 For Game Client

4.1.1 Query Player Blob

Developers can specify player_id and blob_key to query player's blob information. A player's Game Client can only query its own non-Server-Only permission blobs, and other players' Client-Public permission blobs.

API prototype:

/**
* Get the player blob data information of the specified key.
*
* @param PlayerId The player to query.
* @param BlobKey The blob key to query.
* @param ResultCallback The result callback after the API execution ends, and it will be called in the GAME THREAD.
*/
void GetPBDInfo(
const FString& PlayerId,
const FString& BlobKey,
TFunction<void(const FPgosResult& Ret, const FPlayerBlobDataInfo* Data)> ResultCallback) const;

struct FPlayerBlobDataInfo
{
/**
* A downloadable blob url, valid for one hour after the information is obtained.
* To download it, you can use the download function provided by the PGOS Utility module or use the download function implemented by yourself.
*/
FString url;
/** The blob version number, an auto-incrementing number. */
int32 version = 0;
/** The UTC timestamp(in seconds) when the blob was updated. */
int64 last_modified_ts = 0;
/** Size of the blob (in bytes). */
int64 blob_size = 0;
/** MD5 of the blob. */
FString blob_md5;
};

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto PlayerBlobData = IPgosSDKCpp::Get().GetClientPlayerBlobDataAPI();
if (PlayerBlobData)
{

FString PlayerId = TEXT("33816459");
FString BlobKey = TEXT("GameSave");
PlayerBlobData->GetPBDInfo(PlayerId, BlobKey, [](const FPgosResult& Ret, const FPlayerBlobDataInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("GetPBDInfo Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("GetPBDInfo Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

4.1.2 Download Player Blob

When you get the blob download url from GetPBDInfo API, you can use your own http downloader to download it, or use the http downloader API provided by PGOS Utility. The following is the example code to download blob using the PGOS Utility API.

❗ Note: The blob URL obtained from the GetPBDInfo API is only valid for about an hour.

API prototype:

/**
* Download the specified URL using the http protocol.
*
* @param Url The url to download.
* @param SaveCallback This callback function gets called by PGOS as soon as there is data received that needs to be saved, and it may be called with zero bytes data if the transferred file is empty. It will be called in an ASYNCHRONOUS CHILD THREAD.
* @param ResultCallback The result callback after the API execution ends, and it will be called in the GAME THREAD.
*/
void HttpDownloadBuff(
const FString& Url,
TFunction<void(const FPgosBuffer* data)> SaveCallback,
TFunction<void(const FPgosResult& Ret)> ResultCallback) const;

/**
* Download the specified URL to local file using the http protocol.
*
* @param Url The url to download.
* @param SavePath File path to write the data to.
* @param ProgressCallback Data transfer progress callback, which will be called in an ASYNCHRONOUS CHILD THREAD.
* @param ResultCallback The result callback after the API execution ends, and it will be called in the GAME THREAD.
*/
void HttpDownloadFile(
const FString& Url,
const FString& SavePath,
TFunction<void(const FHttpDownloadProgressInfo* data)> ProgressCallback,
TFunction<void(const FPgosResult& Ret)> ResultCallback) const;

Example Code:

  • HttpDownloadBuff

    #include "PgosSDKCpp.h"

    void SomeUObjectClass::SomeFunction()
    {
    auto PlayerBlobData = IPgosSDKCpp::Get().GetClientPlayerBlobDataAPI();
    if (PlayerBlobData)
    {
    FString PlayerId = TEXT("33816459");
    FString BlobKey = TEXT("GameSave");
    PlayerBlobData->GetPBDInfo(PlayerId, BlobKey, [this](const FPgosResult& Ret, const FPlayerBlobDataInfo* Data) {
    if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
    {
    UE_LOG(LogTemp, Log, TEXT("GetPBDInfo Success"));
    DownloadBlobBuffer(Data->url);
    }
    });
    }
    }

    void SomeUObjectClass::DownloadBlobBuffer(const FString& URL)
    {
    auto spBufferHolder = std::make_shared<std::string>();

    auto SaveCallback = [spBufferHolder](const FPgosBuffer* data) {
    spBufferHolder->append(data->buffer_ptr, (size_t)data->buffer_len);
    UE_LOG(LogTemp, Log, TEXT("Http Download SaveCallback: %llu bytes."), data->buffer_len);
    };

    auto Utility = IPgosSDKCpp::Get().GetClientUtilityAPI();
    if (Utility)
    {
    Utility->HttpDownloadBuff(URL, SaveCallback, [](const FPgosResult& Ret) {
    if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
    {
    UE_LOG(LogTemp, Log, TEXT("HttpDownloadBuff Success"));
    // Process the data in spBufferHolder ...
    }
    else
    {
    UE_LOG(LogTemp, Log, TEXT("HttpDownloadBuff Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
    }
    });
    }
    }
  • DownloadBlobFile

    #include "PgosSDKCpp.h"

    void SomeUObjectClass::SomeFunction()
    {
    auto PlayerBlobData = IPgosSDKCpp::Get().GetClientPlayerBlobDataAPI();
    if (PlayerBlobData)
    {
    FString PlayerId = TEXT("33816459");
    FString BlobKey = TEXT("GameSave");
    PlayerBlobData->GetPBDInfo(PlayerId, BlobKey, [this](const FPgosResult& Ret, const FPlayerBlobDataInfo* Data) {
    if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
    {
    UE_LOG(LogTemp, Log, TEXT("GetPBDInfo Success"));
    DownloadBlobFile(Data->url);
    }
    });
    }
    }

    void SomeUObjectClass::DownloadBlobFile(const FString& URL)
    {
    FString ProjectSavedPath = FPaths::ConvertRelativePathToFull(FPaths::ProjectSavedDir());
    FString SavePath = ProjectSavedPath + "/GameSave.blob";

    auto ProgressCallback = [](const FHttpDownloadProgressInfo* data) {
    UE_LOG(LogTemp, Log, TEXT("Http Download ProgressCallback: %llu bytes."), data->transfered_size);
    };

    auto Utility = IPgosSDKCpp::Get().GetClientUtilityAPI();
    if (Utility)
    {
    Utility->HttpDownloadFile(URL, SavePath, ProgressCallback, [](const FPgosResult& Ret) {
    if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
    {
    UE_LOG(LogTemp, Log, TEXT("HttpDownloadFile Success"));
    }
    else
    {
    UE_LOG(LogTemp, Log, TEXT("HttpDownloadFile Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
    }
    });
    }
    }

❗ Note: the SaveCallback and the ProgressCallback are callbacks being called in an ASYNCHRONOUS CHILD THREAD.

4.1.3 Update My Player Blob

Developers can update a memory buffer (UpdateMyPBDFromBuff) or a local file (UpdateMyPBDFromFile) to the player blob. It should be noted that using UpdateMyPBDFromBuff API will create a copy of blob data internally, if the buffer is very large, this may lead to high memory usage. So for the blob buffer with large size, consider using the UpdateMyPBDFromFile API instead. When updating, the developer can monitor the progress of the update. A player's Game Client can only update its own Client-Writable permission blobs.

API prototype:

/**
* Update blob data of the current player from a memory buffer.
* The function will create a copy of blob data internally, if the buffer is very large, this may lead to high memory usage.
* So for the blob buffer with large size, consider using the UpdateMyPBDFromFile API instead.
*
* @param BlobKey The blob key to update.
* @param BlobBuff Blob data buffer to set. If the buffer address pointer is nullptr, the blob data will be reset to 0 bytes.
* @param ProgressCallback Data transfer progress callback, which will be called in an ASYNCHRONOUS CHILD THREAD.
* @param ResultCallback The result callback after the API execution ends, and it will be called in the GAME THREAD.
*/
void UpdateMyPBDFromBuff(
const FString& BlobKey,
const FPgosBuffer& BlobBuff,
TFunction<void(const FUpdatePBDProgressInfo* data)> ProgressCallback,
TFunction<void(const FPgosResult& Ret)> ResultCallback) const;

/**
* Update blob data of the current player from a local file.
*
* @param BlobKey The blob key to update.
* @param FilePath The full path to the file to upload. If it is not a UE project (FString), please pass in a full local path (not in utf8 format).
* @param ProgressCallback Data transfer progress callback, which will be called in an ASYNCHRONOUS CHILD THREAD.
* @param ResultCallback The result callback after the API execution ends, and it will be called in the GAME THREAD.
*/
void UpdateMyPBDFromFile(
const FString& BlobKey,
const FString& FilePath,
TFunction<void(const FUpdatePBDProgressInfo* data)> ProgressCallback,
TFunction<void(const FPgosResult& Ret)> ResultCallback) const;

Example Code:

  • UpdateMyPBDFromBuff

    #include "PgosSDKCpp.h"

    void SomeUObjectClass::SomeFunction()
    {
    auto PlayerBlobData = IPgosSDKCpp::Get().GetClientPlayerBlobDataAPI();
    if (PlayerBlobData)
    {
    FString BlobKey = TEXT("GameSave");
    std::string buffer("game save content...");
    FPgosBuffer GameSaveBuffer;
    GameSaveBuffer.buffer_ptr = (const char*)buffer.c_str();
    GameSaveBuffer.buffer_len = (int64)buffer.size();

    auto ProgressCallback = [](const FUpdatePBDProgressInfo* data) {
    UE_LOG(LogTemp, Log, TEXT("UpdateMyPBDFromBuff ProgressCallback: player_id=%s, blob_key=%s, transfered_size=%llu, new_blob_size=%llu."), *data->player_id, *data->blob_key, data->transfered_size, data->new_blob_size);
    };

    PlayerBlobData->UpdateMyPBDFromBuff(BlobKey, GameSaveBuffer, ProgressCallback, [](const FPgosResult& Ret) {
    if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
    {
    UE_LOG(LogTemp, Log, TEXT("UpdateMyPBDFromBuff Success"));
    }
    else
    {
    UE_LOG(LogTemp, Log, TEXT("UpdateMyPBDFromBuff Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
    }
    });
    }
    }
  • UpdateMyPBDFromFile

    #include "PgosSDKCpp.h"

    void SomeUObjectClass::SomeFunction()
    {
    auto PlayerBlobData = IPgosSDKCpp::Get().GetClientPlayerBlobDataAPI();
    if (PlayerBlobData)
    {
    FString BlobKey = TEXT("GameSave");
    FString ProjectSavedPath = FPaths::ConvertRelativePathToFull(FPaths::ProjectSavedDir());
    FString FilePath = ProjectSavedPath + "/GameSave.blob";

    auto ProgressCallback = [](const FUpdatePBDProgressInfo* data) {
    UE_LOG(LogTemp, Log, TEXT("UpdatePBDFromFile ProgressCallback: player_id=%s, blob_key=%s, transfered_size=%llu, new_blob_size=%llu."), *data->player_id, *data->blob_key, data->transfered_size, data->new_blob_size);
    };

    PlayerBlobData->UpdateMyPBDFromFile(BlobKey, FilePath, ProgressCallback, [](const FPgosResult& Ret) {
    if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
    {
    UE_LOG(LogTemp, Log, TEXT("UpdateMyPBDFromFile Success"));
    }
    else
    {
    UE_LOG(LogTemp, Log, TEXT("UpdateMyPBDFromFile Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
    }
    });
    }
    }

4.1.4 Cancel Update

Developers can cancel ongoing blob updates when needed.

API prototype:

/**
* Cancel the blob data update operation of the current player.
*
* @param BlobKey The blob key to cancel the update.
* @param ResultCallback The result callback after the API execution ends, and it will be called in the GAME THREAD.
*/
void CancelMyPBDUpdate(
const FString& BlobKey,
TFunction<void(const FPgosResult& Ret)> ResultCallback) const;

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto PlayerBlobData = IPgosSDKCpp::Get().GetClientPlayerBlobDataAPI();
if (PlayerBlobData)
{
FString BlobKey = TEXT("GameSave");
PlayerBlobData->CancelMyPBDUpdate(BlobKey, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("CancelMyPBDUpdate Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("CancelMyPBDUpdate Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

When the cancel operation is completed, the ResultCallback will be run in the game thread.

Note: cancel operation will return success even if the specified update task does not exist.

4.2 For Game Server

4.2.1 Query Player Blob

Developers can specify player_id and blob_key to query player's blob information. Game Server has access permission to all player blobs.

API prototype:

/**
* Get the player blob data information of the specified key.
*
* @param PlayerId The player to query.
* @param BlobKey The blob key to query.
* @param ResultCallback The result callback after the API execution ends, and it will be called in the GAME THREAD.
*/
void GetPBDInfo(
const FString& PlayerId,
const FString& BlobKey,
TFunction<void(const FPgosResult& Ret, const FPlayerBlobDataInfo* Data)> ResultCallback) const;

struct FPlayerBlobDataInfo
{
/**
* A downloadable blob url, valid for one hour after the information is obtained.
* To download it, you can use the download function provided by the PGOS Utility module or use the download function implemented by yourself.
*/
FString url;
/** The blob version number, an auto-incrementing number. */
int32 version = 0;
/** The UTC timestamp(in seconds) when the blob was updated. */
int64 last_modified_ts = 0;
/** Size of the blob (in bytes). */
int64 blob_size = 0;
/** MD5 of the blob. */
FString blob_md5;
};

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto PlayerBlobData = IPgosSDKCpp::Get().GetServerPlayerBlobDataAPI();
if (PlayerBlobData)
{

FString PlayerId = TEXT("33816459");
FString BlobKey = TEXT("GameSave");
PlayerBlobData->GetPBDInfo(PlayerId, BlobKey, [](const FPgosResult& Ret, const FPlayerBlobDataInfo* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("GetPBDInfo Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("GetPBDInfo Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

4.2.2 Download Player Blob

When you get the blob download url from GetPBDInfo API, you can use your own http downloader to download it, or use the http downloader API provided by PGOS Utility. The following is the example code to download blob using the PGOS Utility API.

❗ Note: The blob URL obtained from the GetPBDInfo API is only valid for about an hour.

API prototype:

/**
* Download the specified URL using the http protocol.
*
* @param Url The url to download.
* @param SaveCallback This callback function gets called by PGOS as soon as there is data received that needs to be saved, and it may be called with zero bytes data if the transferred file is empty. It will be called in an ASYNCHRONOUS CHILD THREAD.
* @param ResultCallback The result callback after the API execution ends, and it will be called in the GAME THREAD.
*/
void HttpDownloadBuff(
const FString& Url,
TFunction<void(const FPgosBuffer* data)> SaveCallback,
TFunction<void(const FPgosResult& Ret)> ResultCallback) const;

/**
* Download the specified URL to local file using the http protocol.
*
* @param Url The url to download.
* @param SavePath File path to write the data to.
* @param ProgressCallback Data transfer progress callback, which will be called in an ASYNCHRONOUS CHILD THREAD.
* @param ResultCallback The result callback after the API execution ends, and it will be called in the GAME THREAD.
*/
void HttpDownloadFile(
const FString& Url,
const FString& SavePath,
TFunction<void(const FHttpDownloadProgressInfo* data)> ProgressCallback,
TFunction<void(const FPgosResult& Ret)> ResultCallback) const;

Example Code:

  • HttpDownloadBuff

    #include "PgosSDKCpp.h"

    void SomeUObjectClass::SomeFunction()
    {
    auto PlayerBlobData = IPgosSDKCpp::Get().GetServerPlayerBlobDataAPI();
    if (PlayerBlobData)
    {
    FString PlayerId = TEXT("33816459");
    FString BlobKey = TEXT("GameSave");
    PlayerBlobData->GetPBDInfo(PlayerId, BlobKey, [this](const FPgosResult& Ret, const FPlayerBlobDataInfo* Data) {
    if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
    {
    UE_LOG(LogTemp, Log, TEXT("GetPBDInfo Success"));
    DownloadBlobBuffer(Data->url);
    }
    });
    }
    }

    void SomeUObjectClass::DownloadBlobBuffer(const FString& URL)
    {
    auto spBufferHolder = std::make_shared<std::string>();

    auto SaveCallback = [spBufferHolder](const FPgosBuffer* data) {
    spBufferHolder->append(data->buffer_ptr, (size_t)data->buffer_len);
    UE_LOG(LogTemp, Log, TEXT("Http Download SaveCallback: %llu bytes."), data->buffer_len);
    };

    auto Utility = IPgosSDKCpp::Get().GetServerUtilityAPI();
    if (Utility)
    {
    Utility->HttpDownloadBuff(URL, SaveCallback, [](const FPgosResult& Ret) {
    if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
    {
    UE_LOG(LogTemp, Log, TEXT("HttpDownloadBuff Success"));
    // Process the data in spBufferHolder ...
    }
    else
    {
    UE_LOG(LogTemp, Log, TEXT("HttpDownloadBuff Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
    }
    });
    }
    }
  • DownloadBlobFile

    #include "PgosSDKCpp.h"

    void SomeUObjectClass::SomeFunction()
    {
    auto PlayerBlobData = IPgosSDKCpp::Get().GetServerPlayerBlobDataAPI();
    if (PlayerBlobData)
    {
    FString PlayerId = TEXT("33816459");
    FString BlobKey = TEXT("GameSave");
    PlayerBlobData->GetPBDInfo(PlayerId, BlobKey, [this](const FPgosResult& Ret, const FPlayerBlobDataInfo* Data) {
    if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
    {
    UE_LOG(LogTemp, Log, TEXT("GetPBDInfo Success"));
    DownloadBlobFile(Data->url);
    }
    });
    }
    }

    void SomeUObjectClass::DownloadBlobFile(const FString& URL)
    {
    FString ProjectSavedPath = FPaths::ConvertRelativePathToFull(FPaths::ProjectSavedDir());
    FString SavePath = ProjectSavedPath + "/GameSave.blob";

    auto ProgressCallback = [](const FHttpDownloadProgressInfo* data) {
    UE_LOG(LogTemp, Log, TEXT("Http Download ProgressCallback: %llu bytes."), data->transfered_size);
    };

    auto Utility = IPgosSDKCpp::Get().GetServerUtilityAPI();
    if (Utility)
    {
    Utility->HttpDownloadFile(URL, SavePath, ProgressCallback, [](const FPgosResult& Ret) {
    if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
    {
    UE_LOG(LogTemp, Log, TEXT("HttpDownloadFile Success"));
    }
    else
    {
    UE_LOG(LogTemp, Log, TEXT("HttpDownloadFile Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
    }
    });
    }
    }

❗ Note: the SaveCallback and the ProgressCallback are callbacks being called in an ASYNCHRONOUS CHILD THREAD.

4.2.3 Update Player Blob

Developers can update a memory buffer (UpdatePBDFromBuff) or a local file (UpdatePBDFromFile) to the player blob. It should be noted that using UpdatePBDFromBuff API will create a copy of blob data internally, if the buffer is very large, this may lead to high memory usage. So for the blob buffer with large size, consider using the UpdatePBDFromFile API instead. When updating, the developer can monitor the progress of the update. Game Server has update permissions to all player blobs.

API prototype:

/**
* Update the player blob data from a memory buffer.
* The function will create a copy of blob data internally, if the buffer is very large, this may lead to high memory usage.
* So for the blob buffer with large size, consider using the UpdatePBDFromFile API instead.
*
* @param PlayerId The player to update blob data.
* @param BlobKey The blob key to update.
* @param BlobBuff Blob data buffer to set. If the buffer address pointer is nullptr, the blob data will be reset to 0 bytes.
* @param ProgressCallback Data transfer progress callback, which will be called in an ASYNCHRONOUS CHILD THREAD.
* @param ResultCallback The result callback after the API execution ends, and it will be called in the GAME THREAD.
*/
void UpdatePBDFromBuff(
const FString& PlayerId,
const FString& BlobKey,
const FPgosBuffer& BlobBuff,
TFunction<void(const FUpdatePBDProgressInfo* data)> ProgressCallback,
TFunction<void(const FPgosResult& Ret)> ResultCallback) const;

/**
* Update the player blob data from a local file.
*
* @param PlayerId The player to update blob data.
* @param BlobKey The blob key to update.
* @param FilePath The full path to the file to upload. If it is not a UE project (FString), please pass in a full local path (not in utf8 format).
* @param ProgressCallback Data transfer progress callback, which will be called in an ASYNCHRONOUS CHILD THREAD.
* @param ResultCallback The result callback after the API execution ends, and it will be called in the GAME THREAD.
*/
void UpdatePBDFromFile(
const FString& PlayerId,
const FString& BlobKey,
const FString& FilePath,
TFunction<void(const FUpdatePBDProgressInfo* data)> ProgressCallback,
TFunction<void(const FPgosResult& Ret)> ResultCallback) const;

Example Code:

  • UpdatePBDFromBuff

    #include "PgosSDKCpp.h"

    void SomeUObjectClass::SomeFunction()
    {
    auto PlayerBlobData = IPgosSDKCpp::Get().GetServerPlayerBlobDataAPI();
    if (PlayerBlobData)
    {
    FString PlayerId = TEXT("33816459");
    FString BlobKey = TEXT("GameSave");
    std::string buffer("game save content...");
    FPgosBuffer GameSaveBuffer;
    GameSaveBuffer.buffer_ptr = (const char*)buffer.c_str();
    GameSaveBuffer.buffer_len = (int64)buffer.size();

    auto ProgressCallback = [](const FUpdatePBDProgressInfo* data) {
    UE_LOG(LogTemp, Log, TEXT("UpdatePBDFromBuff ProgressCallback: player_id=%s, blob_key=%s, transfered_size=%llu, new_blob_size=%llu."), *data->player_id, *data->blob_key, data->transfered_size, data->new_blob_size);
    };

    PlayerBlobData->UpdatePBDFromBuff(PlayerId, BlobKey, GameSaveBuffer, ProgressCallback, [](const FPgosResult& Ret) {
    if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
    {
    UE_LOG(LogTemp, Log, TEXT("UpdatePBDFromBuff Success"));
    }
    else
    {
    UE_LOG(LogTemp, Log, TEXT("UpdatePBDFromBuff Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
    }
    });
    }
    }
  • UpdatePBDFromFile

    #include "PgosSDKCpp.h"

    void SomeUObjectClass::SomeFunction()
    {
    auto PlayerBlobData = IPgosSDKCpp::Get().GetServerPlayerBlobDataAPI();
    if (PlayerBlobData)
    {
    FString PlayerId = TEXT("33816459");
    FString BlobKey = TEXT("GameSave");
    FString ProjectSavedPath = FPaths::ConvertRelativePathToFull(FPaths::ProjectSavedDir());
    FString FilePath = ProjectSavedPath + "/GameSave.blob";
    auto ProgressCallback = [](const FUpdatePBDProgressInfo* data) {
    UE_LOG(LogTemp, Log, TEXT("UpdatePBDFromFile ProgressCallback: player_id=%s, blob_key=%s, transfered_size=%llu, new_blob_size=%llu."), *data->player_id, *data->blob_key, data->transfered_size, data->new_blob_size);
    };

    PlayerBlobData->UpdatePBDFromFile(PlayerId, BlobKey, FilePath, ProgressCallback, [](const FPgosResult& Ret) {
    if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
    {
    UE_LOG(LogTemp, Log, TEXT("UpdatePBDFromFile Success"));
    }
    else
    {
    UE_LOG(LogTemp, Log, TEXT("UpdatePBDFromFile Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
    }
    });
    }
    }

4.2.4 Cancel Update

Developers can cancel ongoing blob updates when needed.

API prototype:

/**
* Cancel the update operation of the player blob data.
*
* @param PlayerId The player to be cancelled from updating blob data.
* @param BlobKey The blob key to cancel the update.
* @param ResultCallback The result callback after the API execution ends, and it will be called in the GAME THREAD.
*/
void CancelPBDUpdate(
const FString& PlayerId,
const FString& BlobKey,
TFunction<void(const FPgosResult& Ret)> ResultCallback) const;

Example Code:

#include "PgosSDKCpp.h"

void SomeUObjectClass::SomeFunction()
{
auto PlayerBlobData = IPgosSDKCpp::Get().GetServerPlayerBlobDataAPI();
if (PlayerBlobData)
{
FString PlayerId = TEXT("33816459");
FString BlobKey = TEXT("GameSave");
PlayerBlobData->CancelPBDUpdate(PlayerId, BlobKey, [](const FPgosResult& Ret) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("CancelPBDUpdate Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("CancelPBDUpdate Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}

When the cancel operation is completed, the ResultCallback will be run in the game thread.

Note: cancel operation will return success even if the specified update task does not exist.

5. Key Error Handling

Error CodeRelevant APIHandling Suggestion
kBackendKeyNotExistGetPBDInfo (Client)
GetPBDInfo (Server)
UpdateMyPBDFromBuff
UpdateMyPBDFromFile
UpdatePBDFromBuff
UpdatePBDFromFile
blob key not exist. Check the key whether it's defined on the portal.
kBackendReqPlayerIDIsInvalidGetPBDInfo (Client)
GetPBDInfo (Server)
UpdatePBDFromBuff
UpdatePBDFromFile
request player_id is invalid.
kBackendBlobItemNotExistGetPBDInfo (Client)
GetPBDInfo (Server)
blob data not initialized. This means that the blob has never been updated before.
kBackendClientInaccessibleKeyGetPBDInfo (Client)key is inaccessible by client. This means that the access permission of the key is server-only or the key you specified is a non-client-public key belonging to other players. Check the key access permission on the portal.
kBackendBlobNotOwnerGetPBDInfo (Client)
the key can only be accessed by its owner. This means that the key you specified is a non-client-public key belonging to other players. Check the key access permission on the portal.
kSdkCreateFileFailedHttpDownloadFile (Client)
HttpDownloadFile (Server)
failed to create the file, please ensure that the path is correct and you have the appropriate permissions.
kSdkOpenTheUploadFileFailedUpdateMyPBDFromFile
UpdatePBDFromFile
failed to open the file to be uploaded, please check whether the file is readable.
kSdkUploadCanceledByAnotherCallUpdateMyPBDFromBuff
UpdateMyPBDFromFile
UpdatePBDFromBuff
UpdatePBDFromFile
the upload was canceled by another API call.
kSdkLastOperationNotCompletedUpdateMyPBDFromBuff
UpdateMyPBDFromFile
UpdatePBDFromBuff
UpdatePBDFromFile
the last homogeneous operation (same player id and key) has not been completed. To perform the current operation, please wait or cancel the last operation.
kBackendClientUnwritableKeyUpdateMyPBDFromBuff
UpdateMyPBDFromFile
key is un-writable by client. Check the key access permission on the portal.
kSdkNetworkErrorAll Network APInetwork error, check error msg for detail.