Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions cli/beamable.common/Runtime/Content/ClientManifest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Linq;
using System.Runtime.CompilerServices;
using UnityEngine;
// ReSharper disable InconsistentNaming

namespace Beamable.Common.Content
{
Expand All @@ -27,6 +28,11 @@ public class ClientManifest
/// </summary>
public List<ClientContentInfo> entries = new List<ClientContentInfo>();

/// <summary>
/// The unique identifier for this manifest.
/// </summary>
public Optional<string> uid = null;

/// <summary>
/// Use a <see cref="ContentQuery"/> to filter the <see cref="entries"/> and get a new <see cref="ClientManifest"/>.
/// This method will not mutate the <i>current</i> <see cref="ClientManifest"/>. Instead, it allocates a new one.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
// This file generated by a copy-operation from another project.
// Edits to this file will be overwritten by the build process.

// This file generated by a copy-operation from another project.
// Edits to this file will be overwritten by the build process.
using Beamable.Common.Api.Content;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using UnityEngine;
// ReSharper disable InconsistentNaming

namespace Beamable.Common.Content
{
Expand All @@ -30,6 +31,11 @@ public class ClientManifest
/// </summary>
public List<ClientContentInfo> entries = new List<ClientContentInfo>();

/// <summary>
/// The unique identifier for this manifest.
/// </summary>
public Optional<string> uid = null;

/// <summary>
/// Use a <see cref="ContentQuery"/> to filter the <see cref="entries"/> and get a new <see cref="ClientManifest"/>.
/// This method will not mutate the <i>current</i> <see cref="ClientManifest"/>. Instead, it allocates a new one.
Expand Down
29 changes: 18 additions & 11 deletions client/Packages/com.beamable/Editor/ContentService/ContentBaker.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Beamable;
using Beamable.Api;
using Beamable.Api.Autogenerated.Content;
using Beamable.Common;
using Beamable.Common.Api;
using Beamable.Common.BeamCli.Contracts;
Expand Down Expand Up @@ -182,18 +183,24 @@ private static bool Bake(ContentDataInfo[] contentData,
return true;
}

private static Promise<ClientManifest> RequestClientManifest(IBeamableRequester requester)
private static async Promise<ClientManifest> RequestClientManifest(IBeamableRequester requester)
{
string url = $"/basic/content/manifest/public?id={ContentConfiguration.Instance.RuntimeManifestID}";
return requester.Request(Method.GET, url, null, true, ClientManifest.ParseCSV, true).Recover(ex =>
{
if (ex is PlatformRequesterException err && err.Status == 404)
{
return new ClientManifest {entries = new List<ClientContentInfo>()};
}

throw ex;
});
string id = ContentConfiguration.Instance.RuntimeManifestID;
var api = new ContentApi(requester);
var checksumInfo = await api.GetManifestChecksum(id);
string url = $"/basic/content/manifest/public?uid={checksumInfo.uid}";
var manifest = await requester.Request(Method.GET, url, null, true, ClientManifest.ParseCSV, true)
.Recover(ex =>
{
if (ex is PlatformRequesterException err && err.Status == 404)
{
return new ClientManifest {entries = new List<ClientContentInfo>()};
}

throw ex;
});
manifest.uid = checksumInfo.uid;
return manifest;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using UnityEngine;
Expand All @@ -34,13 +33,15 @@ namespace Beamable.Content
/// </summary>
public class ManifestSubscription : PlatformSubscribable<ClientManifest, ClientManifest>
{
private readonly IDependencyProvider _provider;

/// <summary>
/// Every content manifest has an ID. Usually, a game will only have a single content manifest called "global", but it is possible to add more.
/// </summary>
public string ManifestID { get; } = "global";

private readonly IBeamableFilesystemAccessor _filesystemAccessor;
private readonly IManifestResolver _manifestResolver;
private readonly Api.Autogenerated.Content.IContentApi _contentApi;

private readonly bool _omitTags;

private ClientManifest _latestManifiest;
Expand All @@ -49,6 +50,7 @@ public class ManifestSubscription : PlatformSubscribable<ClientManifest, ClientM

private readonly Dictionary<string, ClientContentInfo> _contentIdTable =
new Dictionary<string, ClientContentInfo>();
private readonly ClientManifest _bakedManifest;

/// <summary>
/// This will be removed in a future release. Please do not use.
Expand All @@ -58,9 +60,13 @@ public class ManifestSubscription : PlatformSubscribable<ClientManifest, ClientM

public ManifestSubscription(IDependencyProvider provider,
string manifestID,
bool omitTags = false) : base(provider, "content")
bool omitTags = false,
ClientManifest bakedManifest = null) : base(provider, "content")
{
_provider = provider;
_filesystemAccessor = provider.GetService<IBeamableFilesystemAccessor>();
_contentApi = provider.GetService<Api.Autogenerated.Content.IContentApi>();
_manifestResolver = provider.GetService<IManifestResolver>();
_bakedManifest = bakedManifest;
ManifestID = manifestID;
_omitTags = omitTags;
}
Expand Down Expand Up @@ -138,7 +144,34 @@ protected override string CreateRefreshUrl(string scope)

protected override Promise<ClientManifest> ExecuteRequest(IBeamableRequester requester, string url)
{
return _provider.GetService<IManifestResolver>().ResolveManifest(requester, url, this);
// There could be some use cases for game makers where they would want to opt out of manifest caching.
// Define symbol `BEAMABLE_OPTOUT_MANIFEST_FILE_CACHING` allows them to bypass manifest filesystem caching.
#if BEAMABLE_OPTOUT_MANIFEST_FILE_CACHING
return _manifestResolver.ResolveManifest(requester, url, this);
#else
return _contentApi.GetManifestChecksum(ManifestID).FlatMap(checksumResponse =>
{
if (!checksumResponse.uid.HasValue)
{
return _manifestResolver.ResolveManifest(requester, url, this);
}

string uid = checksumResponse.uid.Value;
if (TryGetCachedManifest(uid, out var cachedManifest))
{
return Promise<ClientManifest>.Successful(cachedManifest);
}

string downloadUrl = url + $"&uid={uid}";
return _manifestResolver
.ResolveManifest(requester, downloadUrl, this)
.Map(manifest =>
{
SaveCachedManifest(uid, manifest);
return manifest;
});
});
#endif
}

protected override void OnRefresh(ClientManifest data)
Expand All @@ -152,6 +185,55 @@ protected override void OnRefresh(ClientManifest data)

_manifestPromise.CompleteSuccess(new Unit());
}

private bool TryGetCachedManifest(string uid, out ClientManifest manifest)
{
manifest = null;
try
{
if (_bakedManifest != null && _bakedManifest.uid.TryGet(out var bakedUid) && uid.Equals(bakedUid))
{
manifest = _bakedManifest;
return manifest != null;
}
string path = GetManifestPath(uid);
if (!File.Exists(path)) return false;

string json = File.ReadAllText(path);
manifest = JsonUtility.FromJson<ClientManifest>(json);
manifest.uid = uid;
return manifest != null;
}
catch (Exception ex)
{
Debug.LogWarning($"Failed to load cached manifest: {ex.Message}");
return false;
}
}

private void SaveCachedManifest(string uid, ClientManifest manifest)
{
try
{
string path = GetManifestPath(uid);
string dir = Path.GetDirectoryName(path);
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);

string json = JsonUtility.ToJson(manifest);
File.WriteAllText(path, json);
}
catch (Exception ex)
{
Debug.LogError($"Failed to save cached manifest: {ex.Message}");
}
}

private string GetManifestPath(string uid)
{
var pid = Beam.RuntimeConfigProvider.Pid;
var cid = Beam.RuntimeConfigProvider.Cid;
return $"{_filesystemAccessor.GetPersistentDataPathWithoutTrailingSlash()}/{pid}-{cid}/content/manifests/{ManifestID}_{uid}.json";
}
}

/// <summary>
Expand Down Expand Up @@ -469,7 +551,7 @@ private void AddSubscriber(string manifestID)
if (Subscribables.ContainsKey(manifestID))
return;

Subscribables.Add(manifestID, new ManifestSubscription(_provider, manifestID, _config.OmitContentManifestTags));
Subscribables.Add(manifestID, new ManifestSubscription(_provider, manifestID, _config.OmitContentManifestTags, BakedManifest));
Subscribables[manifestID].Subscribe(cb => { });
}

Expand Down
Loading