Player Data Storage Interface

处理基于云端数据管理的界面

阅读时间14分钟

开发者可以利用 Epic在线服务 (EOS)的 玩家数据存储(PDS)界面 将加密、用户专属、游戏专属的数据保存到云服务器。用户可以在登录的任意设备上访问通过此界面保存的数据。玩家数据存储界面支持任意文件格式,常见用途为游戏存档和回放数据。

通过平台界面函数 EOS_Platform_GetPlayerDataStorageInterface 获取一个 EOS_HPlayerDataStorage 句柄,即可访问玩家数据存储界面。所有玩家数据存储界面函数均需将此句柄作为首个参数。必须确保 EOS_HPlatform 句柄可以更新(tick),以便在请求完成时触发回调。

要使用此界面,必须用以下额外参数对EOS平台进行初始化:

  • const char* EncryptionKey:这个64个字符的十六进制字符串将编译一个256位密钥,EOS将使用此密钥来加密用户数据。Epic的后端服务器不保存此密钥。如不存在密钥,从玩家数据存储界面调用文件管理函数将引起失败,并返回 EOS_PlayerDataStorage_EncryptionKeyNotSet 错误代码。

  • const char* CacheDirectory:这是界面在数据文件传输时用于临时存储的本地目录的完整路径。这可以是任何路径,通常为系统的临时目录或游戏数据的目录。如指定的目录不存在,EOS将尝试创建目录。多个用户和产品可以使用相同目录,EOS会确保其不会冲突。如果目录存在问题,调用文件管理函数将引起失败,并返回 EOS_CacheDirectoryMissingEOS_CacheDirectoryInvalid 错误代码。请参阅EOS平台文档了解各个平台在文件夹的使用上有哪些限制。

文件管理

文件名格式

EOS中的文件名称采用以下格式:Directory0/Directory1/DirectoryN/Filename.Extension

“Directory”和“Extension”部分是选项项。“/”必须在每个目录名称后出现,但不能出现在其他位置,包括作为开头或末尾的字符。文件名中可以出现以下字符:

  • 所有数字类型的ASCII字符
  • !
  • -
  • _
  • +
  • .
  • '
  • (
  • )

文件名不能超过 EOS_PLAYERDATASTORAGE_FILENAME_MAX_LENGTH_BYTES (64)个字符。

查询文件

你可以通过查询EOS来获取保存在云端的一个或所有文件的信息。任意查询类型都将返回关于文件或其找到文件的元数据,包括文件的名称、数据大小(以字节为单位、未加密)及MD5散列。在访问或修改文件之前,你可以缓存此信息的副本。注意,由于文件以加密形式保存在后端,因此会为加密文件提供一些元数据(例如文件大小和哈希)。元数据不会完全匹配你传递给API的文件的对应值。

请务必在查询的回调函数中从缓存中复制可能需要的任何信息。无法保证此数据的生存周期会超过回调函数的持续时间。这点在运行多个查询时尤其重要,因为每次成功的查询都可能更改缓存的内容。

查询所有文件

要获取所有文件的相关信息,以 EOS_PlayerDataStorage_QueryFileListOptions 数据结构调用 EOS_PlayerDataStorage_QueryFileList。此数据结构初始化如下:

属性
ApiVersionEOS_PLAYERDATASTORAGE_QUERYFILELISTOPTIONS_API_LATEST
LocalUserId请求文件数据的本地用户的 EOS_ProductUserId

完成时,EOS将用一个 EOS_PlayerDataStorage_QueryFileListCallbackInfo 数据界面运行你的回调函数(类型为 EOS_PlayerDataStorage_OnQueryFileListCompleteCallback)。如调用成功,数据结构将包含找到文件的数量,以及EOS缓存中可用文件的相关信息。

查询单个文件

如果只需要单个文件的信息,且了解此文件的名称,可以调用 EOS_PlayerDataStorage_QueryFile 函数,传入一个初始化如下的 EOS_PlayerDataStorage_QueryFileOptions 结构:

属性
ApiVersionEOS_PLAYERDATASTORAGE_QUERYFILEOPTIONS_API_LATEST
LocalUserId请求文件数据的本地用户的 EOS_ProductUserId
Filename正在查询文件的文件名

操作完成时,EOS将用一个 EOS_PlayerDataStorage_QueryFileCallbackInfo 结构运行你的回调(类型为 EOS_PlayerDataStorage_OnQueryFileCompleteCallback)。如调用成功,EOS将在缓存中保存你文件的相关数据。

检查缓存文件信息

EOS获取并缓存一个或多个文件的信息之后,可以用文件名调用 EOS_PlayerDataStorage_CopyFileMetadataByFilename、或以缓存中文件的零基索引来调用 EOS_PlayerDataStorage_CopyFileMetadataAtIndex,以获取特定文件上的信息。如果需要了解缓存中的文件数量,可以调用 EOS_PlayerDataStorage_GetFileMetadataCount。不再需要此信息的副本之后,可调用 EOS_PlayerDataStorage_FileMetadata_Release 来释放其使用的内存。

查询EOS关于单个文件的信息后,EOS_PlayerDataStorage_GetFileMetadataCount 可能显示出缓存保存着多个文件。之前查询的结果仍留在缓存中时便可能出现此结果。在此类情况中,最佳方法是检查 EOS_PlayerDataStorage_QueryFileCallbackInfo 中的 ResultCodeEOS_Success,并调用 EOS_PlayerDataStorage_CopyFileMetadataByFilename 访问其信息。

文件的元数据结构如下所示:

属性
ApiVersionEOS_PLAYERDATASTORAGE_FILEMETADATA_API_LATEST
FileSizeBytes文件的总大小,以字节为单位(采用加密形式,可以稍大于预期)
MD5Hash整个文件的MD5哈希,采用十六进制(加密文件的哈希)
Filename文件名称
LastModifiedTime文件最后一次保存时的日期和时间。时间戳使用POSIX/Unix 格式。
UnencryptedDataSizeBytesThe total size of the data (payload) in bytes (in its unencrypted original form).

访问并修改文件

EOS支持从云端异步读取、写入并删除文件。读取和写入为流送操作,EOS将提供柄,游戏可将其用于检查流送进程,或者进行节流,或者干预。

读取文件

要从云端(或本地缓存)调用已知名称的文件,调用 EOS_PlayerDataStorage_ReadFile。需要传入一个 EOS_PlayerDataStorage_ReadFileOptions,其初始化如下:

属性
ApiVersionEOS_PLAYERDATASTORAGE_READFILEOPTIONS_API_LATEST
LocalUserId读取请求文件的本地用户的帐户ID
Filename要读取的文件的名称
ReadChunkLengthBytes在单次 EOS_PlayerDataStorage_OnReadFileDataCallback 调用中药读取的最大数据量
ReadFileDataCallback在数据进入时对其进行处理、可提早停止传输的回调函数
FileTransferProgressCallback告知传输进度的可选回调函数;设置后将被调用至少一次

此函数返回一个 EOS_HPlayerDataStorageFileTransferRequest,其可用于检查传输的进度、获取文件名、或取消传输。在传输期间,调用 EOS_PlayerDataStorageFileTransferRequest_GetFileRequestState 可轮询其当前状态、调用 EOS_PlayerDataStorageFileTransferRequest_GetFilename 可检查正在传输文件的命名、调用 EOS_PlayerDataStorageFileTransferRequest_CancelRequest 可将其取消(不会产生错误)。如果希望从EOS获得更新,而不是直接轮询传输,可在开始传输时提供一个有效的 EOS_PlayerDataStorage_OnFileTransferProgressCallback 函数(和 FileTransferProgressCallback 参数一样),EOS将定期对其进行调用(利用一个 EOS_PlayerDataStorage_FileTransferProgressCallbackInfo 参数),告知文件传输的进度。如果选择此方法,你必将接收到对回调函数的至少一个调用。使用回调函数并不会妨碍柄的使用,但这二者作用相差无几,多数情况下都不需要同时使用。

文件块进入时,你的 ReadFileDataCallback(类型为 EOS_PlayerDataStorage_OnReadFileDataCallback)将随类型为 EOS_PlayerDataStorage_ReadFileDataCallbackInfo 的参数运行。此结构包含来自文件的实际数据的一个块和一个变量(说明这是否为最后一个文件块)。其还拥有一个枚举返回类型 EOS_PlayerDataStorage_EReadResult,可用来告知EOS继续传输、由于出错而终止、或是取消而不报错。

ReadFileDataCallback 只会从主SDK线程(用于执行SDK每帧更新逻辑的线程)调用,而且每帧只调用一次。这就是为何必须为 ReadChunkLengthBytes设置正确的数值。如果把区块大小设置得过低,则会降低用户的读取效率。我们推荐使用4096的倍数,因为这通常符合系统的内存分页大小。你可以通过将 ReadChunkLengthBytes 与SDK的更新频率相乘,估计出最大读取速度。例如,4096 * 30相当于每秒120千字节的读取速度。另一方面,如果使用过大的区块大小,将导致SDK在内部分配更多内存。为了留下合理的数据足迹,你应该选择一个最符合你需要的数值。

文件传输结束后,EOS将以一个 EOS_PlayerDataStorage_ReadFileCallbackInfo 参数运行 EOS_PlayerDataStorage_OnReadFileCompleteCallback 回调函数。此参数将说明成功或失败,并包含文件名。

EOS可以将文件缓存到本地,加快之后的读取操作。这些文件将使用EOS平台初始化期间提供的密钥进行加密。发送到 ReadFileDataCallback 函数的数据块不会被加密。

以下时间线图表展示了假想的读取操作流程:

写入文件

要写入已知命名的文件,调用 EOS_PlayerDataStorage_WriteFile。需要传入一个 EOS_PlayerDataStorage_WriteFileOptions,其初始化如下:

属性
ApiVersionEOS_PLAYERDATASTORAGE_WRITEFILEOPTIONS_API_LATEST
LocalUserId将文件写出至云端的本地用户的账户ID
Filename要写入的文件的名称
ChunkLengthBytes单次tick中写入文件的最大数据量
WriteFileDataCallback将数据块提供到EOS的回调函数
FileTransferProgressCallback告知传输进度的可选回调函数(类型为 EOS_PlayerDataStorage_OnFileTransferProgressCallback);设置后将被调用至少一次

此函数返回一个 EOS_HPlayerDataStorageFileTransferRequest,其可用于检查传输的进度、获取文件名、或取消传输。在传输期间,调用 EOS_PlayerDataStorageFileTransferRequest_GetFileRequestState 可轮询其当前状态、调用 EOS_PlayerDataStorageFileTransferRequest_GetFilename 可检查正在传输文件的命名、调用 EOS_PlayerDataStorageFileTransferRequest_CancelRequest 可将其取消(不会产生错误)。如果希望从EOS获得更新,而不是直接轮询传输,可在开始传输时提供一个有效的 EOS_PlayerDataStorage_OnFileTransferProgressCallback 函数(和 FileTransferProgressCallback 参数一样),EOS将定期对其进行调用(利用一个 EOS_PlayerDataStorage_FileTransferProgressCallbackInfo 参数),告知文件传输的进度。如果选择此方法,你必将接收到对回调函数的至少一个调用。使用回调函数并不会妨碍柄的使用,但这二者作用相差无几,多数情况下都不需要同时使用。

EOS要将文件的下个块发送至云端时,你的 WriteFileDataCallback(类型为 EOS_PlayerDataStorage_OnWriteFileDataCallback)将以一个类型为 EOS_PlayerDataStorage_WriteFileDataCallbackInfo 的参数运行,一个 指针指向外出数据的缓冲,一个 uint32_t 指针将说明数据的大小。EOS_PlayerDataStorage_WriteFileDataCallbackInfo 拥有一个名为 DataBufferLengthBytes 的变量,其说明可放入缓冲的最大字节数。此回调函数拥有一个枚举返回类型 EOS_PlayerDataStorage_EWriteResult,可用来告知EOS继续传输、由于出错而终止、或是取消而不报错。

WriteFileDataCallback 只会从主SDK线程(用于执行SDK每帧更新逻辑的线程)调用,而且每帧只调用一次。这就是为何必须为 ReadChunkLengthBytes设置正确的数值。如果把区块大小设置得过低,则会降低用户的读取效率。我们推荐使用4096的倍数,因为这通常符合系统的内存分页大小。你可以通过将 ReadChunkLengthBytes 与SDK的更新频率相乘,估计出最大读取速度。例如,4096 * 30相当于每秒120千字节的读取速度。另一方面,如果使用过大的区块大小,将导致SDK在内部分配更多内存。为了留下合理的数据足迹,你应该选择一个最符合你需要的数值。

文件传输结束后,EOS将以一个 EOS_PlayerDataStorage_WriteFileCallbackInfo 参数运行 EOS_PlayerDataStorage_OnWriteFileCompleteCallback 回调函数。此参数将说明成功或失败,并包含文件名。为避免数据丢失,必须运行此回调,才能允许用户退出游戏或关闭主机。

复制文件

如果要复制文件,无需手动读写文件的完整数据内容即可进行复制。用包含以下值的 EOS_PlayerDataStorage_DuplicateFileOptions 调用 EOS_PlayerDataStorage_DuplicateFile

属性
ApiVersionEOS_PLAYERDATASTORAGE_DUPLICATEFILEOPTIONS_API_LATEST
LocalUserId授权操作的本地用户的账户ID;必须为原始文件的拥有者
SourceFilename要复制的现有文件的名称
DestinationFilename复制文件所需的名称

完成时,EOS将以一个 EOS_PlayerDataStorage_DuplicateFileCallbackInfo 参数运行 EOS_PlayerDataStorage_OnDuplicateFileCompleteCallback 函数。如用户未拥有原始文件、或存储空间不足,则文件复制将失败。如 DestinationFilename 识别到已存在的文件,则文件将被副本所替代。

  • 原有文件不存在。
  • 用户不持有原有文件。
  • 用户储存空间不够。

如果 DestinationFilename 标识了一个已经存在的文件,则该文件会被副本替换。

删除文件

用一个初始化如下的 EOS_PlayerDataStorage_DeleteFileOptions 调用 EOS_PlayerDataStorage_DeleteFileOptions 即可删除文件:

属性
ApiVersionEOS_PLAYERDATASTORAGE_DELETEFILEOPTIONS_API_LATEST
LocalUserId授权文件删除的本地用户的账户ID;必须为文件的拥有者
Filename要删除的文件的名称

完成时,EOS将运行回调(类型为 EOS_PlayerDataStorage_OnDeleteFileCompleteCallback)并为其传递一个类型为 EOS_PlayerDataStorage_DeleteFileCallbackInfo 的参数。如用户并非文件的拥有者,文件删除操作可能失败。

数据缓存与加密

玩家数据存储界面会将文件数据和元数据缓存到客户端系统上的CacheDirectory文件夹中。EOS在进行读写操作时将尽量使用此数据,而非从云端进行流送,并使用MD5校验和测试来防止数据损坏,并确定本地缓存文件何时与云端版本完全一致。在此情况下读写操作可在单次回调循环中完成。任何成功的读写操作都将更新缓存,而删除则会清除缓存文件并移除其元数据。EOS在关闭时不会清理缓存,所以可以在之后的会话中重复使用缓存文件。

玩家数据储存文件会固定加密保存,使用EOS平台初始化时提供的密钥。加密密钥本身未保存在云端,因此我们无法使用密钥来访问你的数据。

文件解密工具

你可以使用文件解密工具(File Decryption Tool)来创建新文件或解密作品存储玩家数据存储 中的数据。

用法限制

玩家数据存储界面可以让使用EOS的开发者将加密的、用户特定的、游戏特定的数据保存到云服务器上。用户可以在任何可以登录的设备上访问通过该界面存储的数据。玩家数据存储界面支持任何文件格式;常用案例包括保存游戏数据和重播数据。

该服务对存储数量和使用率都有限制。节流限制(Throttling)会强制执行使用率限制,而服务可以删除文件以便强制执行存储空间限制。有关节流限制、使用配额和最佳实践的信息,请参阅服务使用限制

| 使用类型|限制 | | --- | --- | | 读取或写入(Read or write)|每分钟1000次请求| | 单个文件的最大尺寸(见下文说明) | 200 MB | | 自动删除的文件大小(Auto-delete file size)|400 MB| | 单个用户的总存储空间(Total storage space per user)|400 MB | | 每个用户的最大文件数(Maximum files per user) | 1000 |

如果用户试图上传的内容超过任意一个存储空间限制,上传操作就会失败。一次性传输太多数据不会导致错误,但用户将无法上传其他文件,直到该文件被删除。在这种状态下,用户仍然可以检索元数据和删除文件。如果某个文件超过了自动删除文件的大小,服务会在上传后立即将其删除。

玩家数据存储 vs EGS 云存储

Epic提供两种不同的云存储服务:启动器云存储和EOS玩家数据存储。前者使用范围最广,可以在Epic游戏商城中作为游戏配置的一部分进行设置。不过,这两种服务之间存在一些差异,而这会对使用哪种服务产生影响。

  1. 启动器云存储在Epic Games启动器中与游戏关联。所以假如玩家在启动器之外启动游戏,游戏就只能访问本地版本的存储数据。
  2. 启动器的云存储在配置完毕后完全由Epic负责处理
  3. EOS玩家数据存储要求玩家必须在线,并且完全由游戏端通过EOS界面来驱动。这就使得数据能在不同平台上共享,这一点是它和启动器云存储的区别。