Skip to content

Commit 04414f8

Browse files
committed
feat: align SDKs with "better files" backend changes
- Add revision handling for files - Add optional `key` parameter to FileStream upload overloads - Add 19 PlayMode tests covering key-based upload/upsert, key-based lookup/delete, file revisions (by ID and by key), and response field verification
1 parent 03f5eb2 commit 04414f8

5 files changed

Lines changed: 1059 additions & 36 deletions

File tree

Runtime/Client/LootLockerEndPoints.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,14 @@ public class LootLockerEndPoints
7474
public static EndPointClass uploadPlayerFile = new EndPointClass("player/files", LootLockerHTTPMethod.UPLOAD_FILE);
7575
public static EndPointClass updatePlayerFile = new EndPointClass("/player/files/{0}", LootLockerHTTPMethod.UPDATE_FILE);
7676
public static EndPointClass deletePlayerFile = new EndPointClass("/player/files/{0}", LootLockerHTTPMethod.DELETE);
77+
public static EndPointClass listPlayerFileRevisions = new EndPointClass("player/files/{0}/revisions", LootLockerHTTPMethod.GET);
78+
public static EndPointClass getPlayerFileRevision = new EndPointClass("player/files/{0}/revisions/{1}", LootLockerHTTPMethod.GET);
79+
public static EndPointClass promotePlayerFileRevision = new EndPointClass("player/files/{0}/revisions/{1}/current", LootLockerHTTPMethod.POST);
80+
public static EndPointClass getPlayerFileByKey = new EndPointClass("player/files/key/{0}", LootLockerHTTPMethod.GET);
81+
public static EndPointClass listPlayerFileRevisionsByKey = new EndPointClass("player/files/key/{0}/revisions", LootLockerHTTPMethod.GET);
82+
public static EndPointClass getPlayerFileRevisionByKey = new EndPointClass("player/files/key/{0}/revisions/{1}", LootLockerHTTPMethod.GET);
83+
public static EndPointClass promotePlayerFileRevisionByKey = new EndPointClass("player/files/key/{0}/revisions/{1}/current", LootLockerHTTPMethod.POST);
84+
public static EndPointClass deletePlayerFileByKey = new EndPointClass("player/files/key/{0}", LootLockerHTTPMethod.DELETE);
7785

7886
// Player Progressions
7987
[Header("Player Progressions")]

Runtime/Game/LootLockerSDKManager.cs

Lines changed: 173 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4080,8 +4080,9 @@ public static void GetAllPlayerFiles(int playerId, Action<LootLockerPlayerFilesR
40804080
/// <param name="filePurpose">Purpose of the file, example: savefile/config</param>
40814081
/// <param name="isPublic">Should this file be viewable by other players?</param>
40824082
/// <param name="onComplete">onComplete Action for handling the response of type LootLockerPlayerFile</param>
4083+
/// <param name="key">Optional key for upsert behavior. If a file with this key already exists, it will be updated.</param>
40834084
/// <param name="forPlayerWithUlid">Optional : Execute the request for the specified player. If not supplied, the default player will be used.</param>
4084-
public static void UploadPlayerFile(string pathToFile, string filePurpose, bool isPublic, Action<LootLockerPlayerFile> onComplete, string forPlayerWithUlid = null)
4085+
public static void UploadPlayerFile(string pathToFile, string filePurpose, bool isPublic, Action<LootLockerPlayerFile> onComplete, string key = null, string forPlayerWithUlid = null)
40854086
{
40864087
if (!CheckInitialized(false, forPlayerWithUlid))
40874088
{
@@ -4095,6 +4096,10 @@ public static void UploadPlayerFile(string pathToFile, string filePurpose, bool
40954096
{ "public", isPublic.ToString().ToLower() }
40964097
};
40974098

4099+
if (!string.IsNullOrEmpty(key))
4100+
{
4101+
body.Add("key", key);
4102+
}
40984103

40994104
var fileBytes = new byte[] { };
41004105
try
@@ -4125,7 +4130,7 @@ public static void UploadPlayerFile(string pathToFile, string filePurpose, bool
41254130
/// <param name="forPlayerWithUlid">Optional : Execute the request for the specified player. If not supplied, the default player will be used.</param>
41264131
public static void UploadPlayerFile(string pathToFile, string filePurpose, Action<LootLockerPlayerFile> onComplete, string forPlayerWithUlid = null)
41274132
{
4128-
UploadPlayerFile(pathToFile, filePurpose, false, onComplete, forPlayerWithUlid);
4133+
UploadPlayerFile(pathToFile, filePurpose, false, onComplete, null, forPlayerWithUlid);
41294134
}
41304135

41314136
/// @ingroup PlayerFiles
@@ -4136,8 +4141,9 @@ public static void UploadPlayerFile(string pathToFile, string filePurpose, Actio
41364141
/// <param name="filePurpose">Purpose of the file, example: savefile/config</param>
41374142
/// <param name="isPublic">Should this file be viewable by other players?</param>
41384143
/// <param name="onComplete">onComplete Action for handling the response of type LootLockerPlayerFile</param>
4144+
/// <param name="key">Optional key for upsert behavior. If a file with this key already exists, it will be updated.</param>
41394145
/// <param name="forPlayerWithUlid">Optional : Execute the request for the specified player. If not supplied, the default player will be used.</param>
4140-
public static void UploadPlayerFile(FileStream fileStream, string filePurpose, bool isPublic, Action<LootLockerPlayerFile> onComplete, string forPlayerWithUlid = null)
4146+
public static void UploadPlayerFile(FileStream fileStream, string filePurpose, bool isPublic, Action<LootLockerPlayerFile> onComplete, string key = null, string forPlayerWithUlid = null)
41414147
{
41424148
if (!CheckInitialized(false, forPlayerWithUlid))
41434149
{
@@ -4151,6 +4157,11 @@ public static void UploadPlayerFile(FileStream fileStream, string filePurpose, b
41514157
{ "public", isPublic.ToString().ToLower() }
41524158
};
41534159

4160+
if (!string.IsNullOrEmpty(key))
4161+
{
4162+
body.Add("key", key);
4163+
}
4164+
41544165
var fileBytes = new byte[fileStream.Length];
41554166
try
41564167
{
@@ -4176,10 +4187,11 @@ public static void UploadPlayerFile(FileStream fileStream, string filePurpose, b
41764187
/// <param name="fileStream">Filestream to upload</param>
41774188
/// <param name="filePurpose">Purpose of the file, example: savefile/config</param>
41784189
/// <param name="onComplete">onComplete Action for handling the response of type LootLockerPlayerFile</param>
4190+
/// <param name="key">Optional key for upsert behavior. If a file with this key already exists, it will be updated.</param>
41794191
/// <param name="forPlayerWithUlid">Optional : Execute the request for the specified player. If not supplied, the default player will be used.</param>
4180-
public static void UploadPlayerFile(FileStream fileStream, string filePurpose, Action<LootLockerPlayerFile> onComplete, string forPlayerWithUlid = null)
4192+
public static void UploadPlayerFile(FileStream fileStream, string filePurpose, Action<LootLockerPlayerFile> onComplete, string key = null, string forPlayerWithUlid = null)
41814193
{
4182-
UploadPlayerFile(fileStream, filePurpose, false, onComplete, forPlayerWithUlid);
4194+
UploadPlayerFile(fileStream, filePurpose, false, onComplete, key, forPlayerWithUlid);
41834195
}
41844196

41854197
/// @ingroup PlayerFiles
@@ -4191,8 +4203,9 @@ public static void UploadPlayerFile(FileStream fileStream, string filePurpose, A
41914203
/// <param name="filePurpose">Purpose of the file, example: savefile/config</param>
41924204
/// <param name="isPublic">Should this file be viewable by other players?</param>
41934205
/// <param name="onComplete">onComplete Action for handling the response of type LootLockerPlayerFile</param>
4206+
/// <param name="key">Optional key for upsert behavior. If a file with this key already exists, it will be updated.</param>
41944207
/// <param name="forPlayerWithUlid">Optional : Execute the request for the specified player. If not supplied, the default player will be used.</param>
4195-
public static void UploadPlayerFile(byte[] fileBytes, string fileName, string filePurpose, bool isPublic, Action<LootLockerPlayerFile> onComplete, string forPlayerWithUlid = null)
4208+
public static void UploadPlayerFile(byte[] fileBytes, string fileName, string filePurpose, bool isPublic, Action<LootLockerPlayerFile> onComplete, string key = null, string forPlayerWithUlid = null)
41964209
{
41974210
if (!CheckInitialized(false, forPlayerWithUlid))
41984211
{
@@ -4206,6 +4219,11 @@ public static void UploadPlayerFile(byte[] fileBytes, string fileName, string fi
42064219
{ "public", isPublic.ToString().ToLower() }
42074220
};
42084221

4222+
if (!string.IsNullOrEmpty(key))
4223+
{
4224+
body.Add("key", key);
4225+
}
4226+
42094227
LootLockerServerRequest.UploadFile(forPlayerWithUlid, LootLockerEndPoints.uploadPlayerFile, fileBytes, Path.GetFileName(fileName), "multipart/form-data", body,
42104228
onComplete: (serverResponse) =>
42114229
{
@@ -4224,7 +4242,7 @@ public static void UploadPlayerFile(byte[] fileBytes, string fileName, string fi
42244242
/// <param name="forPlayerWithUlid">Optional : Execute the request for the specified player. If not supplied, the default player will be used.</param>
42254243
public static void UploadPlayerFile(byte[] fileBytes, string fileName, string filePurpose, Action<LootLockerPlayerFile> onComplete, string forPlayerWithUlid = null)
42264244
{
4227-
UploadPlayerFile(fileBytes, fileName, filePurpose, false, onComplete, forPlayerWithUlid);
4245+
UploadPlayerFile(fileBytes, fileName, filePurpose, false, onComplete, null, forPlayerWithUlid);
42284246
}
42294247

42304248
/// @ingroup PlayerFiles
@@ -4345,6 +4363,154 @@ public static void DeletePlayerFile(int fileId, Action<LootLockerResponse> onCom
43454363

43464364
LootLockerServerRequest.CallAPI(forPlayerWithUlid, endpoint, LootLockerHTTPMethod.DELETE, onComplete: (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); });
43474365
}
4366+
4367+
/// @ingroup PlayerFiles
4368+
/// <summary>
4369+
/// List all revisions for a player file.
4370+
/// </summary>
4371+
/// <param name="fileId">Id of the file.</param>
4372+
/// <param name="onComplete">onComplete Action for handling the response of type LootLockerPlayerFileRevisionsResponse</param>
4373+
/// <param name="forPlayerWithUlid">Optional : Execute the request for the specified player. If not supplied, the default player will be used.</param>
4374+
public static void GetPlayerFileRevisions(int fileId, Action<LootLockerPlayerFileRevisionsResponse> onComplete, string forPlayerWithUlid = null)
4375+
{
4376+
if (!CheckInitialized(false, forPlayerWithUlid))
4377+
{
4378+
onComplete?.Invoke(LootLockerResponseFactory.SDKNotInitializedError<LootLockerPlayerFileRevisionsResponse>(forPlayerWithUlid));
4379+
return;
4380+
}
4381+
4382+
LootLockerAPIManager.ListPlayerFileRevisions(forPlayerWithUlid, fileId, onComplete);
4383+
}
4384+
4385+
/// @ingroup PlayerFiles
4386+
/// <summary>
4387+
/// Get a specific revision of a player file by its revision id.
4388+
/// </summary>
4389+
/// <param name="fileId">Id of the file.</param>
4390+
/// <param name="revisionId">The ULID of the revision to retrieve.</param>
4391+
/// <param name="onComplete">onComplete Action for handling the response of type LootLockerPlayerFileContent</param>
4392+
/// <param name="forPlayerWithUlid">Optional : Execute the request for the specified player. If not supplied, the default player will be used.</param>
4393+
public static void GetPlayerFileRevision(int fileId, string revisionId, Action<LootLockerPlayerFileContent> onComplete, string forPlayerWithUlid = null)
4394+
{
4395+
if (!CheckInitialized(false, forPlayerWithUlid))
4396+
{
4397+
onComplete?.Invoke(LootLockerResponseFactory.SDKNotInitializedError<LootLockerPlayerFileContent>(forPlayerWithUlid));
4398+
return;
4399+
}
4400+
4401+
LootLockerAPIManager.GetPlayerFileRevision(forPlayerWithUlid, fileId, revisionId, onComplete);
4402+
}
4403+
4404+
/// @ingroup PlayerFiles
4405+
/// <summary>
4406+
/// Promote a specific revision to be the current (active) revision of a player file.
4407+
/// </summary>
4408+
/// <param name="fileId">Id of the file.</param>
4409+
/// <param name="revisionId">The ULID of the revision to promote.</param>
4410+
/// <param name="onComplete">onComplete Action for handling the response of type LootLockerResponse</param>
4411+
/// <param name="forPlayerWithUlid">Optional : Execute the request for the specified player. If not supplied, the default player will be used.</param>
4412+
public static void PromotePlayerFileRevision(int fileId, string revisionId, Action<LootLockerResponse> onComplete, string forPlayerWithUlid = null)
4413+
{
4414+
if (!CheckInitialized(false, forPlayerWithUlid))
4415+
{
4416+
onComplete?.Invoke(LootLockerResponseFactory.SDKNotInitializedError<LootLockerResponse>(forPlayerWithUlid));
4417+
return;
4418+
}
4419+
4420+
LootLockerAPIManager.PromotePlayerFileRevision(forPlayerWithUlid, fileId, revisionId, onComplete);
4421+
}
4422+
4423+
/// @ingroup PlayerFiles
4424+
/// <summary>
4425+
/// Get a player file by its key.
4426+
/// </summary>
4427+
/// <param name="key">The key of the file.</param>
4428+
/// <param name="onComplete">onComplete Action for handling the response of type LootLockerPlayerFile</param>
4429+
/// <param name="forPlayerWithUlid">Optional : Execute the request for the specified player. If not supplied, the default player will be used.</param>
4430+
public static void GetPlayerFileByKey(string key, Action<LootLockerPlayerFile> onComplete, string forPlayerWithUlid = null)
4431+
{
4432+
if (!CheckInitialized(false, forPlayerWithUlid))
4433+
{
4434+
onComplete?.Invoke(LootLockerResponseFactory.SDKNotInitializedError<LootLockerPlayerFile>(forPlayerWithUlid));
4435+
return;
4436+
}
4437+
4438+
LootLockerAPIManager.GetPlayerFileByKey(forPlayerWithUlid, key, onComplete);
4439+
}
4440+
4441+
/// @ingroup PlayerFiles
4442+
/// <summary>
4443+
/// List all revisions for a player file identified by its key.
4444+
/// </summary>
4445+
/// <param name="key">The key of the file.</param>
4446+
/// <param name="onComplete">onComplete Action for handling the response of type LootLockerPlayerFileRevisionsResponse</param>
4447+
/// <param name="forPlayerWithUlid">Optional : Execute the request for the specified player. If not supplied, the default player will be used.</param>
4448+
public static void GetPlayerFileRevisionsByKey(string key, Action<LootLockerPlayerFileRevisionsResponse> onComplete, string forPlayerWithUlid = null)
4449+
{
4450+
if (!CheckInitialized(false, forPlayerWithUlid))
4451+
{
4452+
onComplete?.Invoke(LootLockerResponseFactory.SDKNotInitializedError<LootLockerPlayerFileRevisionsResponse>(forPlayerWithUlid));
4453+
return;
4454+
}
4455+
4456+
LootLockerAPIManager.ListPlayerFileRevisionsByKey(forPlayerWithUlid, key, onComplete);
4457+
}
4458+
4459+
/// @ingroup PlayerFiles
4460+
/// <summary>
4461+
/// Get a specific revision of a player file by its key and revision id.
4462+
/// </summary>
4463+
/// <param name="key">The key of the file.</param>
4464+
/// <param name="revisionId">The ULID of the revision to retrieve.</param>
4465+
/// <param name="onComplete">onComplete Action for handling the response of type LootLockerPlayerFileContent</param>
4466+
/// <param name="forPlayerWithUlid">Optional : Execute the request for the specified player. If not supplied, the default player will be used.</param>
4467+
public static void GetPlayerFileRevisionByKey(string key, string revisionId, Action<LootLockerPlayerFileContent> onComplete, string forPlayerWithUlid = null)
4468+
{
4469+
if (!CheckInitialized(false, forPlayerWithUlid))
4470+
{
4471+
onComplete?.Invoke(LootLockerResponseFactory.SDKNotInitializedError<LootLockerPlayerFileContent>(forPlayerWithUlid));
4472+
return;
4473+
}
4474+
4475+
LootLockerAPIManager.GetPlayerFileRevisionByKey(forPlayerWithUlid, key, revisionId, onComplete);
4476+
}
4477+
4478+
/// @ingroup PlayerFiles
4479+
/// <summary>
4480+
/// Promote a specific revision to be the current (active) revision of a player file identified by its key.
4481+
/// </summary>
4482+
/// <param name="key">The key of the file.</param>
4483+
/// <param name="revisionId">The ULID of the revision to promote.</param>
4484+
/// <param name="onComplete">onComplete Action for handling the response of type LootLockerResponse</param>
4485+
/// <param name="forPlayerWithUlid">Optional : Execute the request for the specified player. If not supplied, the default player will be used.</param>
4486+
public static void PromotePlayerFileRevisionByKey(string key, string revisionId, Action<LootLockerResponse> onComplete, string forPlayerWithUlid = null)
4487+
{
4488+
if (!CheckInitialized(false, forPlayerWithUlid))
4489+
{
4490+
onComplete?.Invoke(LootLockerResponseFactory.SDKNotInitializedError<LootLockerResponse>(forPlayerWithUlid));
4491+
return;
4492+
}
4493+
4494+
LootLockerAPIManager.PromotePlayerFileRevisionByKey(forPlayerWithUlid, key, revisionId, onComplete);
4495+
}
4496+
4497+
/// @ingroup PlayerFiles
4498+
/// <summary>
4499+
/// Delete a player file by its key.
4500+
/// </summary>
4501+
/// <param name="key">The key of the file to delete.</param>
4502+
/// <param name="onComplete">onComplete Action for handling the response of type LootLockerResponse</param>
4503+
/// <param name="forPlayerWithUlid">Optional : Execute the request for the specified player. If not supplied, the default player will be used.</param>
4504+
public static void DeletePlayerFileByKey(string key, Action<LootLockerResponse> onComplete, string forPlayerWithUlid = null)
4505+
{
4506+
if (!CheckInitialized(false, forPlayerWithUlid))
4507+
{
4508+
onComplete?.Invoke(LootLockerResponseFactory.SDKNotInitializedError<LootLockerResponse>(forPlayerWithUlid));
4509+
return;
4510+
}
4511+
4512+
LootLockerAPIManager.DeletePlayerFileByKey(forPlayerWithUlid, key, onComplete);
4513+
}
43484514
#endregion
43494515

43504516
#region Player progressions

0 commit comments

Comments
 (0)