Add explicit client/server side field to manifest
Bake each file's "side" (client / server / both) into manifest.json at build time so both the launcher and the server filter deterministically and offline. This replaces the launcher's previous "download everything" default and lets the server short-circuit its runtime Modrinth lookup when a file already has an authoritative tag. Schema: - ManifestFile.Side: "client" | "server" | "both" (null = "both" for backward compat with manifests pre-dating this field) - Files marked "both" omit the field entirely to keep the JSON tight Code changes: - launcher Manifest.cs + ManifestSyncService: filter out "server" files in both the prune+download path and FindMissingFiles - server Manifest.cs + ManifestSync: drop "client" files outright; only fall back to the existing runtime Modrinth lookup for files with no explicit side (legacy/un-tagged mods stay protected) - server LockedMod: side field propagates into manifest at build time - scripts/Build-Pack.ps1: propagate side from lockfile to manifest - scripts/Bootstrap-Sides.ps1: one-off populator; queries Modrinth's client_side/server_side per project, conservatively marks restricted only when one side is "unsupported", leaves ambiguous cases as "both" - pack/pack.lock.json: bootstrap-populated sides (3 client, 5 server, rest both); CurseForge mods default to "both" pending manual review; version bumped 0.9.3 -> 0.10.0 since clients must re-sync Compatibility: - Old launcher + new manifest: ignores unknown "side", downloads all - New launcher + old manifest: side null -> "both", installs all - Old server + new manifest: same -- runtime Modrinth lookup still works - New server + old manifest: side null -> runtime lookup, same behaviour
This commit is contained in:
@@ -38,12 +38,20 @@ public sealed class ManifestSync
|
||||
progress?.Report($"Pack: {manifest.Name} v{manifest.Version}");
|
||||
Directory.CreateDirectory(serverDir);
|
||||
|
||||
// Resolve which mods are server-side.
|
||||
var skipSlugs = await ResolveServerSideSkipListAsync(manifest, ct);
|
||||
// Build-time tagging is authoritative: anything marked "client" is dropped
|
||||
// outright, anything marked "server" or "both" is kept. We only fall back
|
||||
// to the runtime Modrinth lookup for files with NO explicit side -- this
|
||||
// keeps un-tagged (or CurseForge-only) mods safe and matches pre-side
|
||||
// behaviour for them.
|
||||
var needsLookup = manifest.Files.Where(f => string.IsNullOrEmpty(f.Side)).ToList();
|
||||
var skipSlugs = needsLookup.Count > 0
|
||||
? await ResolveServerSideSkipListAsync(
|
||||
new Manifest { Files = needsLookup }, ct)
|
||||
: new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// Build the filtered list of files to keep on the server.
|
||||
var keepFiles = manifest.Files
|
||||
.Where(f => !ShouldSkipFile(f.Path, skipSlugs))
|
||||
.Where(f => !IsClientOnly(f) && (!string.IsNullOrEmpty(f.Side) || !ShouldSkipFile(f.Path, skipSlugs)))
|
||||
.ToList();
|
||||
var skippedCount = manifest.Files.Count - keepFiles.Count;
|
||||
|
||||
@@ -108,6 +116,9 @@ public sealed class ManifestSync
|
||||
|| name.Equals(slug, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
private static bool IsClientOnly(ManifestFile f)
|
||||
=> string.Equals(f.Side, "client", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>Walk the manifest's mod URLs; for Modrinth ones, look up server_side; build a skip set.</summary>
|
||||
private async Task<HashSet<string>> ResolveServerSideSkipListAsync(Manifest manifest, CancellationToken ct)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user