プレイヤー固有のデータを格納および取得する

Rajen Kishna、Epic Games テクニカル アカウント マネージャー |
2021年12月7日
前回の EOS 入門編シリーズの記事では、Title Storage を使用してゲーム固有のデータをクラウドから取得する方法について説明しました。その記事では Title StoragePlayer Data Storage の違いについて説明しましたので、今回は Player Data Storage を使用してプレイヤー データをクラウドに保存したり、クラウドから取得したりしてみます。Player Data Storage はプレイヤーに権限があるということを認識することが重要です。そのため、改ざんが不正につながる可能性がある状況 (たとえば、インベントリ管理など) では使用しないでください。この記事では、次の内容を扱います。
 

Player Data Storage のクラウド セーブと Epic Games Store のクラウド セーブの比較

Player Data Storage について説明する前に、 クラウド セーブ という Epic Games Store (EGS) の関連する機能について説明しておきましょう。EGS クラウド セーブは Epic Games Store 用に直接構成されている機能で、Epic Games Launcher を介して管理するため、API 実装を必要としません。

どちらの実装もクラウド内のプレイヤー セーブ データを格納するために使用することができますが、Player Data Storage はゲームが配布されるストアに関係なく、どのプラットフォームのいかなるファイルも格納および取得するために使用することができるため、より柔軟性があります。Player Data Storage を使用して、すべてのプレイヤー用にクロスセーブ機能を実装することができます。この差異についての詳細は、 こちらのドキュメント を参照してください。

クライアント ポリシーを変更する

すべてのサービスと同様に、クライアント ポリシーに適切なアクションを追加する必要があります。
  1. デベロッパー ポータル (https://dev.epicgames.com/portal/) にログインします。
  2. 左側のメニューで [Your Product (あなたの製品)] > [Product Settings (製品の設定)] に移動し、[製品の設定] 画面にある [Clients (クライアント)] タブをクリックします。
  3. 使用するクライアント ポリシーの横にある [...] をクリックし、[Details (詳細)] をクリックします。
  4. [Features (機能)] まで下にスクロールし、[Player Data Storage] の横にあるトグル ボタンをクリックします。
  5. 「アクセス」、「削除」、および「コピー」の機能をすべて実装するため、これらのアクションの横にあるボックスをオンにします。
  6. [Save & Exit (保存して終了)] をクリックして確定します。
Developer Portal Client Policy Player Data Storage
Player Data Storage クライアント ポリシーで許可されている機能とアクション

ファイルをクエリしてファイル メタデータを取得する

Title Storage に関する前回の記事を読んでいない場合は、「EncryptionKey と CacheDirectory を SDK の初期化に追加する」セクションを参照し、ファイルを暗号化してキャッシュする方法について学習してください。学習できたら、クエリ ファイルを実装するところから開始します。
 
  1. 「Views」フォルダに「PlayerDataStorageView」という名前で、次の内容の新規ユーザー コントロールを作成します。

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>

    <StackPanel Grid.Column="1">
        <Button Width="100" Height="23" Margin="2" Content="Query files" Command="{Binding PlayerDataStorageQueryFileList}" />
        <Button Width="100" Height="23" Margin="2" Content="Upload file" Command="{Binding PlayerDataStorageWriteFile}" />
        <StackPanel Orientation="Horizontal">
            <Button Width="100" Height="23" Margin="2" Content="Download file" Command="{Binding PlayerDataStorageReadFile}" />
            <Button Width="100" Height="23" Margin="2" Content="Duplicate file" Command="{Binding PlayerDataStorageDuplicateFile}" />
        </StackPanel>
        <Button Width="100" Height="23" Margin="2" Content="Delete file" Command="{Binding PlayerDataStorageDeleteFile}" />
    </StackPanel>

    <ListView x:Name="PlayerDataStorageFilesListView" Grid.Column="0" Margin="2" ItemsSource="{Binding PlayerDataStorageFiles}" SelectedItem="{Binding SelectedPlayerDataStorageFile}" SelectionChanged="PlayerDataStorageFilesListView_SelectionChanged">
        <ListView.View>
            <GridView>
                <GridViewColumn Header="Filename" Width="150" DisplayMemberBinding="{Binding Filename}">
                    <GridViewColumn.HeaderContainerStyle>
                        <Style TargetType="{x:Type GridViewColumnHeader}">
                            <Setter Property="HorizontalContentAlignment" Value="Left" />
                        </Style>
                    </GridViewColumn.HeaderContainerStyle>
                </GridViewColumn>
                <GridViewColumn Header="LastModifiedTime" Width="175" DisplayMemberBinding="{Binding LastModifiedTime}">
                    <GridViewColumn.HeaderContainerStyle>
                        <Style TargetType="{x:Type GridViewColumnHeader}">
                            <Setter Property="HorizontalContentAlignment" Value="Left" />
                        </Style>
                    </GridViewColumn.HeaderContainerStyle>
                </GridViewColumn>
                <GridViewColumn Header="FileSizeBytes" Width="75" DisplayMemberBinding="{Binding FileSizeBytes}">
                    <GridViewColumn.HeaderContainerStyle>
                        <Style TargetType="{x:Type GridViewColumnHeader}">
                            <Setter Property="HorizontalContentAlignment" Value="Left" />
                        </Style>
                    </GridViewColumn.HeaderContainerStyle>
                </GridViewColumn>
                <GridViewColumn Header="UnencryptedDataSizeBytes" Width="150" DisplayMemberBinding="{Binding UnencryptedDataSizeBytes}">
                    <GridViewColumn.HeaderContainerStyle>
                        <Style TargetType="{x:Type GridViewColumnHeader}">
                            <Setter Property="HorizontalContentAlignment" Value="Left" />
                        </Style>
                    </GridViewColumn.HeaderContainerStyle>
                </GridViewColumn>
            </GridView>
        </ListView.View>
    </ListView>
</Grid>

 
  • ファイルが表示される [ListView] とファイルをクエリ、アップロード、ダウンロード、複製、および削除するためのボタンがある簡単な UI があります。
 
  1. 「PlayerDataStorageView.xaml.cs」を開いて DataContext とプレースホルダー イベント ハンドラーを設定します。

public partial class PlayerDataStorageView :UserControl
{
    public PlayerDataStorageViewModel ViewModel { get { return ViewModelLocator.PlayerDataStorage; } }

    public PlayerDataStorageView()
    {
        InitializeComponent();
        DataContext = ViewModel;
    }

    private void PlayerDataStorageFilesListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
    }
}

 
  1. PlayerDataStorageViewModel.cs クラスを「ViewModels」フォルダに追加します。

public class PlayerDataStorageViewModel :BindableBase
{
    private ObservableCollection<FileMetadata> _playerDataStorageFiles;
    public ObservableCollection<FileMetadata> PlayerDataStorageFiles
    {
        get { return _playerDataStorageFiles; }
        set { SetProperty(ref _playerDataStorageFiles, value); }
    }

    private FileMetadata _selectedPlayerDataStorageFile;
    public FileMetadata SelectedPlayerDataStorageFile
    {
        get { return _selectedPlayerDataStorageFile; }
        set { SetProperty(ref _selectedPlayerDataStorageFile, value); }
    }
}

 
  1. 「ViewModelLocator.cs」で PlayerDataStorageViewModel への参照を追加します。

private static PlayerDataStorageViewModel _playerDataStorage;
public static PlayerDataStorageViewModel PlayerDataStorage
{
    get { return _playerDataStorage ??= new PlayerDataStorageViewModel(); }
}

 
  1. PlayerDataStorageService.cs クラスを「Services」フォルダに追加して、クエリのロジックを保持します。

public static class PlayerDataStorageService
{
    public static void QueryFileList()
    {
        var queryFileListOptions = new QueryFileListOptions()
        {
            LocalUserId = ProductUserId.FromString(ViewModelLocator.Main.ProductUserId)
        };

        ViewModelLocator.Main.StatusBarText = "Querying player data storage file list...";

        App.Settings.PlatformInterface.GetPlayerDataStorageInterface()
.QueryFileList(queryFileListOptions, null, (QueryFileListCallbackInfo queryFileListCallbackInfo) =>
        {
            Debug.WriteLine($"QueryFileList {queryFileListCallbackInfo.ResultCode}");

            if (queryFileListCallbackInfo.ResultCode == Result.Success)
            {
                for (uint i = 0; i < queryFileListCallbackInfo.FileCount; i++)
                {
                    var copyFileMetadataAtIndexOptions = new CopyFileMetadataAtIndexOptions()
                    {
                        Index = i,
                        LocalUserId = ProductUserId.FromString(ViewModelLocator.Main.ProductUserId)
                    };
                    var result = App.Settings.PlatformInterface.GetPlayerDataStorageInterface()
.CopyFileMetadataAtIndex(copyFileMetadataAtIndexOptions, out var metadata);

                    if (result == Result.Success)
                    {
                        ViewModelLocator.PlayerDataStorage.PlayerDataStorageFiles
.Add(metadata);
                    }
                }
            }

            ViewModelLocator.Main.StatusBarText = string.Empty;
        });
    }
}

 
  • このシリーズにある他の記事の内容に従って進めている場合は、次のパターンに見覚えがあるでしょう。[Options (オプション)] 構造体を初期化して PlayerDataStorage.QueryFileList に渡します。
  • 次にこのパターンを繰り返し、返されたファイルごとに PlayerDataStorage.CopyFileMetadataAtIndex を呼び出し、すべてのメタデータを取得します。
 
  1. PlayerDataStorageQueryFileListCommand.cs クラスを「Commands」フォルダに追加します。

public class PlayerDataStorageQueryFileListCommand :CommandBase
{
    public override bool CanExecute(object parameter)
    {
        return !string.IsNullOrWhiteSpace(ViewModelLocator.Main.ProductUserId);
    }

    public override void Execute(object parameter)
    {
        ViewModelLocator.PlayerDataStorage.PlayerDataStorageFiles = new ObservableCollection<FileMetadata>();
        PlayerDataStorageService.QueryFileList();
    }
}

 
  1. 「PlayerDataStorageViewModel.cs」を開き、次のコマンドを宣言してインスタンス化します。

public PlayerDataStorageQueryFileListCommand PlayerDataStorageQueryFileList { get; set; }

public PlayerDataStorageViewModel()
{
    PlayerDataStorageQueryFileList = new PlayerDataStorageQueryFileListCommand();
}

 
  1. Connect インターフェースを使用して正常にログインした後にのみクエリを行えるように、次の行を「ViewModelLocator.cs」にある RaiseConnectCanExecuteChanged() メソッドに追加します。

PlayerDataStorage.PlayerDataStorageQueryFileList.RaiseCanExecuteChanged();
 
  1. 最後に、「MainWindow.xaml:」にある TabControl に PlayerDataStorageView を追加します。

<TabItem x:Name="PlayerDataStorage" Header="Player Data Storage">
    <views:PlayerDataStorageView />
</TabItem>


これで、アプリを実行して認証後に Player Data Storage ファイルをクエリすると、ログに次の内容が表示されます。

[Warning] LogEOSPlayerDataStorage - Querying file failed, got 0 results.
QueryFileList NotFound


これはまだファイルをアップロードしていないために表示されるものです。それでは、次に進みましょう。

ファイルをアップロードする

ファイルをアップロードするためのロジックは状況によって異なる可能性がありますが、次のサンプルでこの API を実装する方法の概念を確認することができます。
 
  1. 次のメソッドを「PlayerDataStorageService.cs」に追加します。

public static void WriteFile(OpenFileDialog openFileDialog)
{
    var bytesWritten = 0;

    var writeFileOptions = new WriteFileOptions()
    {
        LocalUserId = ProductUserId.FromString(ViewModelLocator.Main.ProductUserId),
        Filename = openFileDialog.SafeFileName,
        ChunkLengthBytes = 10485760,
        WriteFileDataCallback = (WriteFileDataCallbackInfo writeFileDataCallbackInfo, out byte[] buffer) =>
        {
            using var fs = new FileStream($"{openFileDialog.FileName}", FileMode.Open, FileAccess.Read);
            if (fs.Length > bytesWritten)
            {
                var readBytes = new byte[System.Math.Min(writeFileDataCallbackInfo.DataBufferLengthBytes, fs.Length)];
                fs.Seek(bytesWritten, SeekOrigin.Begin);
                bytesWritten += fs.Read(readBytes, 0, (int)System.Math.Min(writeFileDataCallbackInfo
.DataBufferLengthBytes, fs.Length));
                buffer = readBytes;
            }
            else
            {
                buffer = new byte[0];
                return WriteResult.CompleteRequest;
            }
            return WriteResult.ContinueWriting;
        },
        FileTransferProgressCallback = (FileTransferProgressCallbackInfo fileTransferProgressCallbackInfo) =>
        {
            var percentComplete = (double)fileTransferProgressCallbackInfo.BytesTransferred / (double)fileTransferProgressCallbackInfo.TotalFileSizeBytes * 100;
            ViewModelLocator.Main.StatusBarText = $"Downloading file <{fileTransferProgressCallbackInfo.Filename}> ({System.Math.Ceiling(percentComplete)}%)...";
        }
    };

    ViewModelLocator.Main.StatusBarText = $"Uploading file <{writeFileOptions.Filename}> (creating buffer)...";

    var fileTransferRequest = App.Settings.PlatformInterface
.GetPlayerDataStorageInterface().WriteFile(writeFileOptions, null, (WriteFileCallbackInfo writeFileCallbackInfo) =>
    {
        Debug.WriteLine($"WriteFile {writeFileCallbackInfo.ResultCode}");

        if (writeFileCallbackInfo.ResultCode == Result.Success)
        {
            ViewModelLocator.PlayerDataStorage
.PlayerDataStorageQueryFileList.Execute(null);
            Debug.WriteLine($"Successfully uploaded {writeFileCallbackInfo.Filename}.");
            ViewModelLocator.Main.StatusBarText = string.Empty;
        }
        else
        {
            Debug.WriteLine($"Error uploading {writeFileCallbackInfo.Filename}:{writeFileCallbackInfo.ResultCode}.");
            ViewModelLocator.Main.StatusBarText = string.Empty;
        }    
    });

    if (fileTransferRequest == null)
    {
        Debug.WriteLine("Error uploading file:bad handle");
        ViewModelLocator.Main.StatusBarText = string.Empty;
    }
}

 
  • このパターンは、Title Storage のファイルを読み込むために実装したものに似ています。WriteFileOptions を初期化します。これにより、ファイルのアップロードおよびレポートの進捗についてのロジックをチャンクに保持します。
  • PlayerDataStorage.WriteFile を使用して実際のアップロードを開始し、「Success」以外の結果を監視します。これは、次に説明する 使用上の制限事項 に達していることを示している場合があります。
 
  1. PlayerDataStorageWriteFileCommand.cs クラスを「Commands」フォルダに追加します。

public class PlayerDataStorageWriteFileCommand :CommandBase
{
    public override bool CanExecute(object parameter)
    {
        return !string.IsNullOrWhiteSpace(ViewModelLocator.Main.ProductUserId);
    }

    public override void Execute(object parameter)
    {
        OpenFileDialog openFileDialog = new OpenFileDialog();
        if (openFileDialog.ShowDialog() == true)
        {
            PlayerDataStorageService.WriteFile(openFileDialog);
        }
    }
}

 
  1. 「PlayerDataStorageViewModel.cs」を開き、新しいコマンドを宣言してインスタンス化します。

public PlayerDataStorageQueryFileListCommand PlayerDataStorageQueryFileList { get; set; }
public PlayerDataStorageWriteFileCommand PlayerDataStorageWriteFile { get; set; }

public PlayerDataStorageViewModel()
{
    PlayerDataStorageQueryFileList = new PlayerDataStorageQueryFileListCommand();
    PlayerDataStorageWriteFile = new PlayerDataStorageWriteFileCommand();
}

 
  1. 最後に、「ViewModelLocator.cs」を開いて次を RaiseConnectCanExecuteChanged() メソッドに追加します。

PlayerDataStorage.PlayerDataStorageWriteFile.RaiseCanExecuteChanged();

これで、アプリを実行してファイルをアップロードすることができるようになりました (後述の使用上の制限事項を考慮してください)。アップロードすると、コードによってクエリがトリガーされ、新しいファイルが [ListView] に表示されます。
 
App Player Data Storage Added Files
Player Data Storage にアップロードされたファイル

Developer Portal を開いて [あたなの製品] > [ゲームのサービス] > [プレイヤーデータのストレージ] に移動し、アプリの UI から PUID をコピーしてプレイヤーを調べることもできます。次の図は、このユーザーのファイル リストと、現在の消費量および最大消費量が表示されています。
 
Developer Portal Player Data Storage Added Files
Epic Games Developer Portal にアップロードされたファイル

ファイルを読み取る

次のステップは、Title Storage で行った内容と同じように、クラウドのファイルを読み取ります。こちらのロジックはほぼ同じです。
 
  1. 次のメソッドを「PlayerDataStorageService.cs」に追加します。

public static void ReadFile(FileMetadata fileMetadata)
{
    var readFileOptions = new ReadFileOptions()
    {
        LocalUserId = ProductUserId.FromString(ViewModelLocator.Main.ProductUserId),
        Filename = fileMetadata.Filename,
        ReadChunkLengthBytes = 1048576,
        ReadFileDataCallback = (ReadFileDataCallbackInfo readFileDataCallbackInfo) =>
        {
            using var fs = new FileStream($"{App.Settings.CacheDirectory}{readFileDataCallbackInfo.Filename}", FileMode.Append, FileAccess.Write);
            fs.Write(readFileDataCallbackInfo.DataChunk, 0, readFileDataCallbackInfo.DataChunk.Length);
            return ReadResult.ContinueReading;
        },
        FileTransferProgressCallback = (FileTransferProgressCallbackInfo fileTransferProgressCallbackInfo) =>
        {
            var percentComplete = (double)fileTransferProgressCallbackInfo.BytesTransferred / (double)fileTransferProgressCallbackInfo.TotalFileSizeBytes * 100;
            ViewModelLocator.Main.StatusBarText = $"Downloading file <{fileTransferProgressCallbackInfo.Filename}> ({System.Math.Ceiling(percentComplete)}%)...";
        }
    };

    ViewModelLocator.Main.StatusBarText = $"Downloading file <{readFileOptions.Filename}> (creating buffer)...";

    var fileTransferRequest = App.Settings.PlatformInterface
.GetPlayerDataStorageInterface().ReadFile(readFileOptions, null, (ReadFileCallbackInfo readFileCallbackInfo) =>
    {
        Debug.WriteLine($"ReadFile {readFileCallbackInfo.ResultCode}");

        if (readFileCallbackInfo.ResultCode == Result.Success)
        {
            Debug.WriteLine($"Successfully downloaded {readFileCallbackInfo.Filename} to {App.Settings.CacheDirectory}.");
            ViewModelLocator.Main.StatusBarText = string.Empty;
        }
    });

    if (fileTransferRequest == null)
    {
        Debug.WriteLine("Error downloading file:bad handle");
        ViewModelLocator.Main.StatusBarText = string.Empty;
    }
}

 
  • ReadFileOptions を初期化します。これにより、ファイルのダウンロードおよびレポートの進捗についてのロジックをチャンクに保持します。
  • PlayerDataStorage.ReadFile を使用して構成済みの CacheDirectory へのダウンロードを開始します。
 
  1. PlayerDataStorageReadFileCommand.cs クラスを「Commands」フォルダに追加します。

public class PlayerDataStorageReadFileCommand :CommandBase
{
    public override bool CanExecute(object parameter)
    {
        return ViewModelLocator.PlayerDataStorage.SelectedPlayerDataStorageFile != null;
    }

    public override void Execute(object parameter)
    {
        PlayerDataStorageService.ReadFile(ViewModelLocator
.PlayerDataStorage.SelectedPlayerDataStorageFile);
    }
}

 
  1. 「PlayerDataStorageViewModel.cs」を開き、新しいコマンドを宣言してインスタンス化します。

public PlayerDataStorageQueryFileListCommand PlayerDataStorageQueryFileList { get; set; }
public PlayerDataStorageWriteFileCommand PlayerDataStorageWriteFile { get; set; }
public PlayerDataStorageReadFileCommand PlayerDataStorageReadFile { get; set; }

public PlayerDataStorageViewModel()
{
    PlayerDataStorageQueryFileList = new PlayerDataStorageQueryFileListCommand();
    PlayerDataStorageWriteFile = new PlayerDataStorageWriteFileCommand();
    PlayerDataStorageReadFile = new PlayerDataStorageReadFileCommand();
}

 
  1. 最後に、「PlayerDataStorageView.xaml.cs」を開き、次を PlayerDataStorageFilesListView_SelectionChanged() メソッドに追加します。

ViewModel.PlayerDataStorageReadFile.RaiseCanExecuteChanged();

これで、ファイルをクエリすると、[ListView] にあるファイルの 1 つを選択できるようになり、[Download file] ボタンをクリックするとクラウドからファイルを取得することができます。ダウンロードが完了したことを示すメッセージが [Debug] ウィンドウに表示されます。

ReadFile Success
Successfully downloaded test_file.txt to C:\Users\<User>\AppData\Local\Temp\.

ファイルを複製する

場合により、ダウンロードやアップロードを行わずにプレイヤーのストレージ スペースへのファイルの複製が必要になることがあります。そのような時に複製 API が役立ちます。
 
  1. 次のメソッドを「PlayerDataStorageService.cs」に追加します。

public static void DuplicateFile(FileMetadata fileMetadata)
{
    var duplicateFileOptions = new DuplicateFileOptions()
    {
        LocalUserId = ProductUserId.FromString(ViewModelLocator.Main.ProductUserId),
        SourceFilename = fileMetadata.Filename,
        DestinationFilename = $"{fileMetadata.Filename}_(copy)"
    };

    ViewModelLocator.Main.StatusBarText = $"Copying <{duplicateFileOptions.SourceFilename}> as <{duplicateFileOptions.DestinationFilename}>...";

    App.Settings.PlatformInterface.GetPlayerDataStorageInterface()
.DuplicateFile(duplicateFileOptions, null, (DuplicateFileCallbackInfo duplicateFileCallbackInfo) =>
    {
        Debug.WriteLine($"DuplicateFile {duplicateFileCallbackInfo.ResultCode}");

        if (duplicateFileCallbackInfo.ResultCode == Result.Success)
        {
            ViewModelLocator.PlayerDataStorage
.PlayerDataStorageQueryFileList.Execute(null);
            ViewModelLocator.Main.StatusBarText = "Successfully copied file.";
        }
        else
        {
            Debug.WriteLine("Copying file failed:" + duplicateFileCallbackInfo.ResultCode);
            ViewModelLocator.Main.StatusBarText = string.Empty;
        }
    });
}

 
  • PlayerDataStorage.DuplicateFile API がソース ファイルおよび宛先ファイルを入力として受け取ると、ファイルがクラウドにコピーされます。
 
  1. PlayerDataStorageDuplicateFileCommand.cs クラスを「Commands」フォルダに追加します。

public class PlayerDataStorageDuplicateFileCommand :CommandBase
{
    public override bool CanExecute(object parameter)
    {
        return ViewModelLocator.PlayerDataStorage.SelectedPlayerDataStorageFile != null;
    }

    public override void Execute(object parameter)
    {
        PlayerDataStorageService.DuplicateFile(ViewModelLocator
.PlayerDataStorage.SelectedPlayerDataStorageFile);
    }
}

 
  1. 「PlayerDataStorageViewModel.cs」を開き、新しいコマンドを宣言してインスタンス化します。

public PlayerDataStorageQueryFileListCommand PlayerDataStorageQueryFileList { get; set; }
public PlayerDataStorageWriteFileCommand PlayerDataStorageWriteFile { get; set; }
public PlayerDataStorageReadFileCommand PlayerDataStorageReadFile { get; set; }
public PlayerDataStorageDuplicateFileCommand PlayerDataStorageDuplicateFile { get; set; }

public PlayerDataStorageViewModel()
{
    PlayerDataStorageQueryFileList = new PlayerDataStorageQueryFileListCommand();
    PlayerDataStorageWriteFile = new PlayerDataStorageWriteFileCommand();
    PlayerDataStorageReadFile = new PlayerDataStorageReadFileCommand();
    PlayerDataStorageDuplicateFile = new PlayerDataStorageDuplicateFileCommand();
}

 
  1. 最後に、「PlayerDataStorageView.xaml.cs」を開き、次を PlayerDataStorageFilesListView_SelectionChanged() メソッドに追加します。

ViewModel.PlayerDataStorageDuplicateFile.RaiseCanExecuteChanged();

[ListView] のファイルを選択して [Duplicate file] ボタンをクリックすると、同じ名前に「_(copy)」が付いた新しいファイルが表示されます。
 
App Player Data Storage Duplicated File
Player Data Storage のクラウド ストレージに複製されたファイル

ファイルを削除する

最後に実装するのは、ファイルを削除する機能です。
 
  1. 次のメソッドを「PlayerDataStorageService.cs」に追加します。

public static void DeleteFile(FileMetadata fileMetadata)
{
    var deleteFileOptions = new DeleteFileOptions()
    {
        LocalUserId = ProductUserId.FromString(ViewModelLocator.Main.ProductUserId),
        Filename = fileMetadata.Filename
    };

    ViewModelLocator.Main.StatusBarText = $"Deleting <{deleteFileOptions.Filename}>...";

    App.Settings.PlatformInterface.GetPlayerDataStorageInterface()
.DeleteFile(deleteFileOptions, null, (DeleteFileCallbackInfo deleteFileCallbackInfo) =>
    {
        Debug.WriteLine($"DeleteFile {deleteFileCallbackInfo.ResultCode}");

        if (deleteFileCallbackInfo.ResultCode == Result.Success)
        {
            ViewModelLocator.PlayerDataStorage
.PlayerDataStorageQueryFileList.Execute(null);
            ViewModelLocator.Main.StatusBarText = "Successfully deleted file.";
        }
        else
        {
            Debug.WriteLine("Deleting file failed:" + deleteFileCallbackInfo.ResultCode);
            ViewModelLocator.Main.StatusBarText = string.Empty;
        }
    });
}

 
  • PlayerDataStorage.DeleteFile API は非常に簡単です。ファイル名を入力として受け取ると、ファイルが完全に削除されます。
 
  1. PlayerDataStorageDeleteFileCommand.cs クラスを「Commands」フォルダに追加します。

public class PlayerDataStorageDeleteFileCommand :CommandBase
{
    public override bool CanExecute(object parameter)
    {
        return ViewModelLocator.PlayerDataStorage.SelectedPlayerDataStorageFile != null;
    }

    public override void Execute(object parameter)
    {
        PlayerDataStorageService.DeleteFile(ViewModelLocator
.PlayerDataStorage.SelectedPlayerDataStorageFile);
    }
}

 
  1. 「PlayerDataStorageViewModel.cs」を開き、新しいコマンドを宣言してインスタンス化します。

public PlayerDataStorageQueryFileListCommand PlayerDataStorageQueryFileList { get; set; }
public PlayerDataStorageWriteFileCommand PlayerDataStorageWriteFile { get; set; }
public PlayerDataStorageReadFileCommand PlayerDataStorageReadFile { get; set; }
public PlayerDataStorageDuplicateFileCommand PlayerDataStorageDuplicateFile { get; set; }
public PlayerDataStorageDeleteFileCommand PlayerDataStorageDeleteFile { get; set; }

public PlayerDataStorageViewModel()
{
    PlayerDataStorageQueryFileList = new PlayerDataStorageQueryFileListCommand();
    PlayerDataStorageWriteFile = new PlayerDataStorageWriteFileCommand();
    PlayerDataStorageReadFile = new PlayerDataStorageReadFileCommand();
    PlayerDataStorageDuplicateFile = new PlayerDataStorageDuplicateFileCommand();
    PlayerDataStorageDeleteFile = new PlayerDataStorageDeleteFileCommand();
}

 
  1. 最後に、「PlayerDataStorageView.xaml.cs」を開き、次を PlayerDataStorageFilesListView_SelectionChanged() メソッドに追加します。

ViewModel.PlayerDataStorageDeleteFile.RaiseCanExecuteChanged();

これで、[ListView] にあるファイルを選択してから [Delete file] ボタンを使用すると、ファイルが削除できるようになりました。

使用上の制限事項

Player Data Storage には、すべてのユーザーに対して信頼性と可用性を保証するために設定された使用上の制限事項があります。この記事の作成時点では次の制限事項があります (最新情報については こちらのドキュメント を参照してください)。
  • 読み取りまたは書き込みリクエスト数: 1000 リクエスト / 分
  • 1 ファイルの最大サイズ: 200 MB
  • ユーザーごとの合計ストレージ スペース: 400 MB
  • ユーザーごとの最大ファイル数: 1000 ファイル

1 ファイルの最大サイズである 200 MB を超えるファイルをアップロードしようとすると、 PlayerDataStorage.WriteFile の呼び出しが失敗し、PlayerDataStorageFileSizeTooLarge の結果が返されます。ユーザーの合計ストレージ スペースが 400 MB を超えると、PlayerDataStorage.WriteFile への追加の呼び出しが失敗し、そのユーザーが制限された状態であることを示す PlayerDataStorageUserThrottled の結果が返されます。このユーザーのストレージ スペースは、ストレージ スペースの利用が 400 MB 未満になるようにファイルが削除されるまで、制限された状態が維持されます。制限されてはいますが、その間もファイルのクエリ、ファイル メタデータの取得、およびファイルの削除は行うことができます。

コードを入手する

この記事に記載しているコードは次の場所から取得できます。GitHub リポジトリに記載されている使用方法に従って、ダウンロードしたコードを設定してください。
Title Storage と Player Data Storage の違いについて説明したので、次の記事では、Player Data Storage を使用してプレイヤー固有のデータの格納と取得を実装します。

このシリーズの記事の一覧については、「シリーズの記事リスト」を参照してください。フィードバックや質問がある場合は、コミュニティ フォーラムにアクセスしてお知らせください。

    デベロッパーみなさんの成功が、Epic の成功です

    Epic は、オープンで統合されたゲーム コミュニティを大切にしています。オンライン サービスをすべての人に無料で提供することにより、より多くのデベロッパーがそれぞれのプレイヤー コミュニティにサービスを提供できるようにすることが目標です。