4594cb9c00
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
97 lines
3.8 KiB
PowerShell
97 lines
3.8 KiB
PowerShell
#requires -Version 7
|
|
<#
|
|
.SYNOPSIS
|
|
Populate the `side` field on each mod entry in pack.lock.json by querying
|
|
Modrinth's project metadata. Run once after schema migration; thereafter,
|
|
Update-Pack.ps1 should set side automatically when adding new mods.
|
|
|
|
.DESCRIPTION
|
|
Modrinth returns `client_side` and `server_side` per project, with values
|
|
`required` / `optional` / `unsupported` / `unknown`. We map:
|
|
server_side = unsupported -> side = "client" (skip on server)
|
|
client_side = unsupported -> side = "server" (skip on launcher)
|
|
both required/optional/unknown -> side = "both" (install everywhere)
|
|
|
|
Conservative: only marks a mod side-restricted when the OTHER side is
|
|
explicitly `unsupported`. Anything ambiguous stays "both".
|
|
|
|
CurseForge mods can't be queried (no API key here) -- left at "both" by
|
|
default, manual edit if needed.
|
|
|
|
.PARAMETER LockPath
|
|
pack.lock.json to update in place. Defaults to ../pack/pack.lock.json.
|
|
|
|
.PARAMETER DryRun
|
|
Print proposed changes without writing the file.
|
|
#>
|
|
|
|
[CmdletBinding()]
|
|
param(
|
|
[string]$LockPath = $(Join-Path (Split-Path -Parent $MyInvocation.MyCommand.Path) '..\pack\pack.lock.json'),
|
|
[switch]$DryRun
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
$ProgressPreference = 'SilentlyContinue'
|
|
|
|
$LockPath = (Resolve-Path $LockPath).Path
|
|
$json = Get-Content $LockPath -Raw | ConvertFrom-Json -Depth 20
|
|
|
|
if (-not $json.mods) { throw "pack.lock.json has no 'mods' array." }
|
|
|
|
function Get-ModrinthSide {
|
|
param([string]$Slug)
|
|
$proj = Invoke-RestMethod -Uri "https://api.modrinth.com/v2/project/$Slug" `
|
|
-Headers @{ 'User-Agent' = 'BrassAndSigil-Launcher/0.1 (matt@sijbers.uk)' }
|
|
$cs = $proj.client_side; $ss = $proj.server_side
|
|
if ($ss -eq 'unsupported' -and $cs -ne 'unsupported') { return 'client' }
|
|
elseif ($cs -eq 'unsupported' -and $ss -ne 'unsupported') { return 'server' }
|
|
else { return 'both' }
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host "Bootstrapping side field on $($json.mods.Count) mods..."
|
|
Write-Host ""
|
|
|
|
$changes = 0
|
|
foreach ($mod in $json.mods) {
|
|
$existing = if ($mod.PSObject.Properties.Name -contains 'side') { $mod.side } else { $null }
|
|
if ($mod.source -ne 'modrinth') {
|
|
# CurseForge or other: default to 'both' if missing, leave alone otherwise.
|
|
if (-not $existing) {
|
|
$mod | Add-Member -NotePropertyName 'side' -NotePropertyValue 'both' -Force
|
|
Write-Host (" [{0}] {1,-26} {2}" -f $mod.source.PadRight(7).Substring(0,7), $mod.slug, 'both (default; non-modrinth)')
|
|
$changes++
|
|
}
|
|
continue
|
|
}
|
|
try {
|
|
$side = Get-ModrinthSide -Slug $mod.slug
|
|
} catch {
|
|
Write-Warning (" $($mod.slug) lookup failed: $($_.Exception.Message) -- leaving as 'both'")
|
|
$side = 'both'
|
|
}
|
|
if ($existing -ne $side) {
|
|
$mod | Add-Member -NotePropertyName 'side' -NotePropertyValue $side -Force
|
|
Write-Host (" [{0}] {1,-26} {2}" -f $mod.source.PadRight(7).Substring(0,7), $mod.slug, $side)
|
|
$changes++
|
|
} else {
|
|
Write-Host (" [{0}] {1,-26} {2} (unchanged)" -f $mod.source.PadRight(7).Substring(0,7), $mod.slug, $side) -ForegroundColor DarkGray
|
|
}
|
|
Start-Sleep -Milliseconds 100 # be polite to Modrinth's API
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host "$changes mod(s) updated."
|
|
|
|
if ($DryRun) {
|
|
Write-Host "DryRun: not writing $LockPath." -ForegroundColor DarkGray
|
|
exit 0
|
|
}
|
|
|
|
# Round-trip via JSON: Modrinth-returned ConvertFrom-Json objects don't always
|
|
# round-trip with consistent property order, so we serialise with sorted keys
|
|
# at the mod level to keep the lockfile diff minimal.
|
|
$json | ConvertTo-Json -Depth 20 | Set-Content -Path $LockPath -Encoding utf8
|
|
Write-Host "Wrote $LockPath" -ForegroundColor Green
|