#requires -Version 5 <# .SYNOPSIS Refreshes pack.lock.json by querying Modrinth + CurseForge for the latest compatible version of each mod listed below. .DESCRIPTION THIS SCRIPT MUTATES pack.lock.json. Run only when you intentionally want to bump versions (and remember to update the server to match). For non-mutating "what's available?" reports, run Check-Updates.ps1. .PARAMETER PackName .PARAMETER PackVersion .PARAMETER MinecraftVersion .PARAMETER LoaderType .PARAMETER LoaderVersion Override pack metadata. Defaults read from existing lockfile if present. #> [CmdletBinding()] param( [string]$PackName = "", [string]$PackVersion = "", [string]$MinecraftVersion = "1.21.1", [string]$LoaderType = "neoforge", [string]$LoaderVersion = "" ) $ErrorActionPreference = 'Stop' $ProgressPreference = 'SilentlyContinue' $here = Split-Path -Parent $MyInvocation.MyCommand.Path $lockPath = Join-Path $here '..\pack\pack.lock.json' # Read existing lock for defaults $existing = if (Test-Path $lockPath) { Get-Content $lockPath -Raw | ConvertFrom-Json } else { $null } if (-not $PackName -and $existing) { $PackName = $existing.name } if (-not $PackVersion -and $existing) { $PackVersion = $existing.version } if (-not $LoaderVersion -and $existing) { $LoaderVersion = $existing.loader.version } if (-not $PackName) { $PackName = "Brass and Sigil" } if (-not $PackVersion) { $PackVersion = "0.6.0" } if (-not $LoaderVersion) { $LoaderVersion = "21.1.228" } # --------------------------------------------------------------------------- # Mod definitions: edit here when adding/removing mods. allowBeta=true allows # fallback to beta if no release exists. # --------------------------------------------------------------------------- $modrinthMods = @( @{ slug = "create"; allowBeta = $false } @{ slug = "create-aeronautics"; allowBeta = $false } @{ slug = "sable"; allowBeta = $false } @{ slug = "create-big-cannons"; allowBeta = $false } @{ slug = "create-tfmg"; allowBeta = $false } @{ slug = "distanthorizons"; allowBeta = $true } @{ slug = "sodium"; allowBeta = $false } @{ slug = "iris"; allowBeta = $false } @{ slug = "modernfix"; allowBeta = $false } @{ slug = "ferrite-core"; allowBeta = $false } @{ slug = "architectury-api"; allowBeta = $false } @{ slug = "rhino"; allowBeta = $true } @{ slug = "rpl"; allowBeta = $false } @{ slug = "kubejs"; allowBeta = $false } @{ slug = "jei"; allowBeta = $true } @{ slug = "jade"; allowBeta = $false } @{ slug = "chunky"; allowBeta = $false } ) # CurseForge mods aren't auto-resolved (no public API without a key). # Update fileId/version/filename here when bumping. $curseforgeMods = @( @{ slug = "ftb-library" version = "2101.1.31" fileId = "7746959" path = "mods/ftb-library-neoforge-2101.1.31.jar" url = "https://mediafilez.forgecdn.net/files/7746/959/ftb-library-neoforge-2101.1.31.jar" } @{ slug = "ftb-teams" version = "2101.1.9" fileId = "7369021" path = "mods/ftb-teams-neoforge-2101.1.9.jar" url = "https://mediafilez.forgecdn.net/files/7369/21/ftb-teams-neoforge-2101.1.9.jar" } @{ slug = "ftb-chunks" version = "2101.1.14" fileId = "7608681" path = "mods/ftb-chunks-neoforge-2101.1.14.jar" url = "https://mediafilez.forgecdn.net/files/7608/681/ftb-chunks-neoforge-2101.1.14.jar" } ) Add-Type -AssemblyName System.Web function Invoke-Modrinth { param([string]$Path) Invoke-RestMethod -Uri "https://api.modrinth.com/v2$Path" -Headers @{ 'User-Agent' = 'BrassAndSigil-Launcher/0.1 (matt@sijbers.uk)' } } function Get-LatestModrinthVersion { param([string]$Slug, [bool]$AllowBeta) $encGv = [System.Web.HttpUtility]::UrlEncode('["' + $MinecraftVersion + '"]') $encL = [System.Web.HttpUtility]::UrlEncode('["' + $LoaderType + '"]') $versions = Invoke-Modrinth ("/project/$Slug/version?game_versions=$encGv" + "&" + "loaders=$encL") if (-not $versions -or $versions.Count -eq 0) { return $null } if ($AllowBeta) { # Take the newest version regardless of stability (Modrinth orders newest first). return $versions[0] } # Stable-only: latest release version, or null if there's no release at all. return $versions | Where-Object { $_.version_type -eq 'release' } | Select-Object -First 1 } $lockedMods = @() $missing = @() Write-Host "" Write-Host "Querying Modrinth for $($modrinthMods.Count) mods (MC $MinecraftVersion / $LoaderType)..." Write-Host "" foreach ($mod in $modrinthMods) { try { $version = Get-LatestModrinthVersion -Slug $mod.slug -AllowBeta $mod.allowBeta } catch { Write-Warning " [error] $($mod.slug) - $($_.Exception.Message)" $missing += $mod.slug continue } if (-not $version) { Write-Warning " [missing] $($mod.slug) - no compatible version" $missing += $mod.slug continue } $primary = $version.files | Where-Object { $_.primary -eq $true } | Select-Object -First 1 if (-not $primary) { $primary = $version.files[0] } $lockedMods += [ordered]@{ source = "modrinth" slug = $mod.slug versionId = $version.id version = $version.version_number path = "mods/$($primary.filename)" url = $primary.url sha1 = $primary.hashes.sha1 size = $primary.size } $tag = if ($version.version_type -eq 'beta') { '[beta] ' } else { '[release]' } Write-Host (" $tag $($mod.slug.PadRight(22)) $($version.version_number.PadRight(28))") } Write-Host "" Write-Host "Hashing $($curseforgeMods.Count) CurseForge mods (manual entries)..." $tmpDir = Join-Path $env:TEMP "brassandsigil-cf-cache" if (-not (Test-Path $tmpDir)) { New-Item -ItemType Directory -Path $tmpDir | Out-Null } foreach ($cf in $curseforgeMods) { $fname = Split-Path -Leaf $cf.path $tmpFile = Join-Path $tmpDir $fname try { if (-not (Test-Path $tmpFile)) { Invoke-WebRequest -Uri $cf.url -OutFile $tmpFile -UseBasicParsing -Headers @{ 'User-Agent' = 'BrassAndSigil-Launcher/0.1 (matt@sijbers.uk)' } } $sha1 = (Get-FileHash -Algorithm SHA1 -Path $tmpFile).Hash.ToLowerInvariant() $size = (Get-Item $tmpFile).Length $lockedMods += [ordered]@{ source = "curseforge" slug = $cf.slug fileId = $cf.fileId version = $cf.version path = $cf.path url = $cf.url sha1 = $sha1 size = $size } Write-Host (" [cforge] $($cf.slug.PadRight(22)) $($cf.version)") } catch { Write-Warning " [error] $fname - $($_.Exception.Message)" $missing += $fname } } $lock = [ordered]@{ '$schema' = "Brass-and-Sigil pack.lock.json - generated, do not edit by hand unless you know what you are doing" name = $PackName version = $PackVersion minecraft = $MinecraftVersion loader = [ordered]@{ type = $LoaderType; version = $LoaderVersion } lockedAt = (Get-Date -Format 'o') mods = $lockedMods } $json = $lock | ConvertTo-Json -Depth 10 [System.IO.File]::WriteAllText($lockPath, $json, [System.Text.UTF8Encoding]::new($false)) Write-Host "" Write-Host "Lockfile written: $lockPath" Write-Host (" Total mods: {0}" -f $lockedMods.Count) if ($missing.Count -gt 0) { Write-Warning "Missing: $($missing -join ', ')" } Write-Host "" Write-Host "Run Build-Pack.ps1 next to generate the manifest."