Radicade Public Code

This commit is contained in:
JogmansonReal
2026-10-16 23:33:13 -07:00
commit 55175acdf6
832 changed files with 363261 additions and 0 deletions
+81
View File
@@ -0,0 +1,81 @@
using System.Text.Json;
using Microsoft.AspNetCore.Mvc;
using RadicadeServer.API.Studio;
namespace RadicadeServer.CDN
{
/// <summary>
/// Step one of the client's two-step bundle download. The client asks
/// <c>/rooms/unity_assets/{assetId}/{target}/{version}</c> and is handed a file name, then
/// ranges over that file at <c>/data/{Filename}</c> (and, for a subroom's bundle, under the
/// CDN's own <c>/unity/{Filename}</c> alias).
///
/// The id arrives in two shapes and both have to be answered. A Studio manifest id is 40
/// alphanumerics and its baked bundles are named in the manifest's own metadata. A legacy
/// bundle is stored under its file name (or that name minus <c>.assetbundle</c>), and that
/// name is what the subroom's <c>UnityAssetId</c> carries - every imported room, the built-in
/// rooms and every room bound from the admin panel. Answering only the 40-character shape
/// leaves every legacy bundle unresolvable, and the client reports one 400 as
/// "Error downloading room assets" for the whole room.
/// </summary>
[Route("rooms/")]
public class UnityAssetsCDN : Controller
{
[HttpGet("unity_assets/{assetId}/{target}/{version}")]
public async Task<IActionResult> GetUnityAsset(string assetId, int target, int version)
{
return await ServeMetadataAsync(assetId, target, version);
}
[HttpGet("unity_assets/{assetId}")]
public async Task<IActionResult> GetUnityAsset(string assetId)
{
return await ServeMetadataAsync(assetId, 0, 1);
}
private async Task<IActionResult> ServeMetadataAsync(string assetId, int target, int version)
{
if (!CdnStorageNames.IsServable(assetId) || target < 0 || target > byte.MaxValue || version < 0)
return BadRequest();
string? filename = null;
string? rejectedDependencies = null;
if (StudioUnityAssetRegistry.IsValidAssetId(assetId))
{
var asset = await StudioUnityAssetRegistry.ReadAsync(assetId, HttpContext.RequestAborted);
var baked = asset?.BakedUnityAssets.FirstOrDefault(x => x.Target == target && x.Version == version);
if (baked != null)
{
filename = baked.Filename;
rejectedDependencies = baked.RejectedDependenciesFilename;
}
else if (asset != null)
{
// The manifest exists, so the file on disk under this id is that JSON
// manifest and not a bundle. A target/version it does not name is a 404:
// handing the manifest over as the bundle would have the client parse
// metadata as an asset bundle.
return NotFound();
}
}
// Reached for every legacy id, and for a 40-character id with no manifest behind it:
// the bundle is the file stored under the name the subroom points at, with or without
// the .assetbundle suffix an import appends.
filename ??= CdnStorageNames.Resolve(assetId);
if (filename == null) return NotFound();
var body = JsonSerializer.Serialize(new
{
UnityAssetId = assetId,
Target = target,
Version = version,
Filename = filename,
RejectedDependenciesFilename = rejectedDependencies
});
return Content(body, "application/json");
}
}
}