#if !DISABLE_PLAYFABENTITY_API && !DISABLE_PLAYFABCLIENT_API
using PlayFab;
using PlayFab.Internal;
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
public class EntityFileExample : MonoBehaviour
{
public string entityId; // Id representing the logged in player
public string entityType; // entityType representing the logged in player
private readonly Dictionary<string, string> _entityFileJson = new Dictionary<string, string>();
private readonly Dictionary<string, string> _tempUpdates = new Dictionary<string, string>();
public string ActiveUploadFileName;
public string NewFileName;
public int GlobalFileLock = 0; // Kind of cheap and simple way to handle this kind of lock
void OnSharedFailure(PlayFabError error)
{
Debug.LogError(error.GenerateErrorReport());
GlobalFileLock -= 1;
}
void OnGUI()
{
if (!PlayFabClientAPI.IsClientLoggedIn() && GUI.Button(new Rect(0, 0, 100, 30), "Login"))
Login();
if (PlayFabClientAPI.IsClientLoggedIn() && GUI.Button(new Rect(0, 0, 100, 30), "LogOut"))
PlayFabClientAPI.ForgetAllCredentials();
if (PlayFabClientAPI.IsClientLoggedIn() && GUI.Button(new Rect(100, 0, 100, 30), "(re)Load Files"))
LoadAllFiles();
if (PlayFabClientAPI.IsClientLoggedIn())
{
// Display existing files
_tempUpdates.Clear();
var index = 0;
foreach (var each in _entityFileJson)
{
GUI.Label(new Rect(100 * index, 60, 100, 30), each.Key);
var tempInput = _entityFileJson[each.Key];
var tempOutput = GUI.TextField(new Rect(100 * index, 90, 100, 30), tempInput);
if (tempInput != tempOutput)
_tempUpdates[each.Key] = tempOutput;
if (GUI.Button(new Rect(100 * index, 120, 100, 30), "Save " + each.Key))
UploadFile(each.Key);
index++;
}
// Apply any changes
foreach (var each in _tempUpdates)
_entityFileJson[each.Key] = each.Value;
// Add a new file
NewFileName = GUI.TextField(new Rect(100 * index, 60, 100, 30), NewFileName);
if (GUI.Button(new Rect(100 * index, 90, 100, 60), "Create " + NewFileName))
UploadFile(NewFileName);
}
}
void Login()
{
var request = new PlayFab.ClientModels.LoginWithCustomIDRequest
{
CustomId = SystemInfo.deviceUniqueIdentifier,
CreateAccount = true
};
PlayFabClientAPI.LoginWithCustomID(request, OnLogin, OnSharedFailure);
}
void OnLogin(PlayFab.ClientModels.LoginResult result)
{
entityId = result.EntityToken.Entity.Id;
entityType = result.EntityToken.Entity.Type;
}
void LoadAllFiles()
{
if (GlobalFileLock != 0)
throw new Exception("This example overly restricts file operations for safety. Careful consideration must be made when doing multiple file operations in parallel to avoid conflict.");
GlobalFileLock += 1; // Start GetFiles
var request = new PlayFab.DataModels.GetFilesRequest { Entity = new PlayFab.DataModels.EntityKey { Id = entityId, Type = entityType } };
PlayFabDataAPI.GetFiles(request, OnGetFileMeta, OnSharedFailure);
}
void OnGetFileMeta(PlayFab.DataModels.GetFilesResponse result)
{
Debug.Log("Loading " + result.Metadata.Count + " files");
_entityFileJson.Clear();
foreach (var eachFilePair in result.Metadata)
{
_entityFileJson.Add(eachFilePair.Key, null);
GetActualFile(eachFilePair.Value);
}
GlobalFileLock -= 1; // Finish GetFiles
}
void GetActualFile(PlayFab.DataModels.GetFileMetadata fileData)
{
GlobalFileLock += 1; // Start Each SimpleGetCall
PlayFabHttp.SimpleGetCall(fileData.DownloadUrl,
result => { _entityFileJson[fileData.FileName] = Encoding.UTF8.GetString(result); GlobalFileLock -= 1; }, // Finish Each SimpleGetCall
error => { Debug.Log(error); }
);
}
void UploadFile(string fileName)
{
if (GlobalFileLock != 0)
throw new Exception("This example overly restricts file operations for safety. Careful consideration must be made when doing multiple file operations in parallel to avoid conflict.");
ActiveUploadFileName = fileName;
GlobalFileLock += 1; // Start InitiateFileUploads
var request = new PlayFab.DataModels.InitiateFileUploadsRequest
{
Entity = new PlayFab.DataModels.EntityKey { Id = entityId, Type = entityType },
FileNames = new List<string> { ActiveUploadFileName },
};
PlayFabDataAPI.InitiateFileUploads(request, OnInitFileUpload, OnInitFailed);
}
void OnInitFailed(PlayFabError error)
{
if (error.Error == PlayFabErrorCode.EntityFileOperationPending)
{
// This is an error you should handle when calling InitiateFileUploads, but your resolution path might vary
GlobalFileLock += 1; // Start AbortFileUploads
var request = new PlayFab.DataModels.AbortFileUploadsRequest
{
Entity = new PlayFab.DataModels.EntityKey { Id = entityId, Type = entityType },
FileNames = new List<string> { ActiveUploadFileName },
};
PlayFabDataAPI.AbortFileUploads(request, (result) => { GlobalFileLock -= 1; UploadFile(ActiveUploadFileName); }, OnSharedFailure); GlobalFileLock -= 1; // Finish AbortFileUploads
GlobalFileLock -= 1; // Failed InitiateFileUploads
}
else
OnSharedFailure(error);
}
void OnInitFileUpload(PlayFab.DataModels.InitiateFileUploadsResponse response)
{
string payloadStr;
if (!_entityFileJson.TryGetValue(ActiveUploadFileName, out payloadStr))
payloadStr = "{}";
var payload = Encoding.UTF8.GetBytes(payloadStr);
GlobalFileLock += 1; // Start SimplePutCall
PlayFabHttp.SimplePutCall(response.UploadDetails[0].UploadUrl,
payload,
FinalizeUpload,
error => { Debug.Log(error); }
);
GlobalFileLock -= 1; // Finish InitiateFileUploads
}
void FinalizeUpload(byte[] body) // body is unused in this example
{
GlobalFileLock += 1; // Start FinalizeFileUploads
var request = new PlayFab.DataModels.FinalizeFileUploadsRequest
{
Entity = new PlayFab.DataModels.EntityKey { Id = entityId, Type = entityType },
FileNames = new List<string> { ActiveUploadFileName },
};
PlayFabDataAPI.FinalizeFileUploads(request, OnUploadSuccess, OnSharedFailure);
GlobalFileLock -= 1; // Finish SimplePutCall
}
void OnUploadSuccess(PlayFab.DataModels.FinalizeFileUploadsResponse result)
{
Debug.Log("File upload success: " + ActiveUploadFileName);
GlobalFileLock -= 1; // Finish FinalizeFileUploads
}
}
#endif
この例の詳細
GlobalFileLockは、ファイルの衝突を回避するための簡易的な方法で、この例のために特別に設計されています。- 独立したファイル アクションでは、問題は発生しません。
- 各ファイル アクションには多くの手順と複数の API 呼び出しが必要なので、同じファイルに対して同時に複数の方法でアクセスしようとしないでください。
- 注意深く行う場合は、ロック メカニズムは必要ありません。
- 複雑な操作を行う場合は、ロック メカニズムがはるかに複雑になる可能性があります。
OnGUIは、スクリプト内で Unity GUI を完全に構築するための古い、しかし非常に密度の高い方法です。- 皆さんの GUI ははるかに優れており、ゲーム固有のものになるでしょう。
- すべての PlayFab 機能は、最初 にログインまたは認証を必要とします。
LoadAllFiles()は文字通りの動作をします。現在サインインしているエンティティに対して、PlayFab に保存されたすべてのファイルを読み込みます。- この関数には複数の手順が必要です:
- PlayFab にファイルの場所を問い合わせ、
- その後、それらを個別にダウンロードします。
- この関数には複数の手順が必要です:
UploadFile(string fileName)は、エンティティのためにファイルをサービスに保存します。- 簡単のため、この例では 1 つずつファイルを保存していますが、セットとしてアトミックにファイルをアップロードすることもできます。
- この操作の手順は次のとおりです:
- アトミックなアップロード操作を初期化し、
- すべてのファイルをアップロードし、
- アトミックなアップロード操作を確定します。
- エンティティは、アトミックなアップロード操作が正常に確定されるまで、ファイル アップロードが完了したとは見なさず、他の呼び出し元にも変更を反映しません。
Game Manager とエンティティ
Game Manager では、プレイヤーのオブジェクトとファイルを操作できます。プレイヤーの概要は、タイトル プレイヤーとマスター プレイヤー アカウントの両方の情報を表示するように更新されました。

関連機能
エンティティ オブジェクトとファイルは、いくつかの PlayFab 機能で使用され、それぞれ異なるエンティティ タイプにスコープされています:| 機能 | エンティティ タイプ | ユース ケース |
|---|---|---|
| ゲーム サーバー構成 | game_server | サーバー側の設定とサーバー権威データ |
| タイトル データ | namespace, title | すべてのプレイヤー間で共有されるグローバルなキー/値の構成 |
| プレイヤー データ | title_player_account, master_player_account, character | プレイヤーごとのプロファイル、進行状況、および設定のストレージ |
| グループ | group | プレイヤー グループまたはギルド内で共有されるデータ |
