Files
brass-and-sigil/scripts/Build-Tweaks.ps1
T
Matt Sijbers bbba58d1dd fix: tweak jars use forward-slash entry names; bump pack to 0.9.3
NeoForge's mod scanner rejected brassandsigil_tweaks-1.0.0.jar because
PowerShell 5.1's [ZipFile]::CreateFromDirectory() writes Windows-native
path separators into ZIP entry names on Windows. Entries came out as
"META-INF\neoforge.mods.toml" instead of the spec-required forward-slash
form, so the loader couldn't find the manifest and silently dropped the
jar with "not a valid mod file".

Build-Tweaks.ps1 now opens the archive in 'Create' mode and writes each
file as an explicit ZipArchiveEntry whose name is built from the relative
path with backslashes replaced by forward slashes. Verified the rebuilt
jar lists "META-INF/neoforge.mods.toml" etc.

Pack version 0.9.2 -> 0.9.3 so launchers cached at 0.9.2 see "pack
changed" and re-sync the new tweak jar (their bytes differ; SHA-1 in the
manifest will reflect that).
2026-05-09 22:30:49 +01:00

131 lines
4.9 KiB
PowerShell

#requires -Version 5
<#
.SYNOPSIS
Builds every tweak source folder under ..\pack\tweaks\ into a data-only
mod jar and drops it into ..\pack\overrides\mods\.
.DESCRIPTION
Each subfolder of ..\pack\tweaks\ is treated as one self-contained
"data-only" NeoForge mod. Its modId, version, and display name come from
the folder's META-INF\neoforge.mods.toml. The script:
1. Reads modId + version from neoforge.mods.toml.
2. Zips the folder contents (data\, META-INF\, pack.mcmeta) into a jar
named "<modId>-<version>.jar".
3. Removes any older jars for the same modId from the output folder so
stale versions don't get bundled into the manifest.
Run this before Build-Pack.ps1, or let Build-Pack.ps1 invoke it for you.
.PARAMETER TweaksRoot
Folder containing tweak source subfolders. Defaults to ..\pack\tweaks\.
.PARAMETER OutputRoot
Where built jars go. Defaults to ..\pack\overrides\mods\. This path
becomes Build-Pack.ps1's -LocalPackSource (one level up).
#>
[CmdletBinding()]
param(
[string]$TweaksRoot = "",
[string]$OutputRoot = ""
)
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
if (-not $TweaksRoot) { $TweaksRoot = Join-Path $here '..\pack\tweaks' }
if (-not $OutputRoot) { $OutputRoot = Join-Path $here '..\pack\overrides\mods' }
if (-not (Test-Path $TweaksRoot)) {
Write-Host "No tweaks folder at $TweaksRoot -- nothing to build."
return
}
if (-not (Test-Path $OutputRoot)) {
New-Item -ItemType Directory -Path $OutputRoot -Force | Out-Null
}
# Read modId + version out of a neoforge.mods.toml. We do this with a tiny regex
# parser instead of a full TOML library because we only need two scalars and we
# control the file format. Lines like: modId = "foo" version = "1.2.3"
function Get-ModMeta {
param([string]$TomlPath)
if (-not (Test-Path $TomlPath)) {
throw "Missing $TomlPath -- every tweak folder needs META-INF\neoforge.mods.toml."
}
$content = Get-Content $TomlPath -Raw
$modIdMatch = [regex]::Match($content, '(?m)^\s*modId\s*=\s*"([^"]+)"')
$versionMatch = [regex]::Match($content, '(?m)^\s*version\s*=\s*"([^"]+)"')
if (-not $modIdMatch.Success) { throw "Could not parse modId from $TomlPath" }
if (-not $versionMatch.Success) { throw "Could not parse version from $TomlPath" }
return [pscustomobject]@{
ModId = $modIdMatch.Groups[1].Value
Version = $versionMatch.Groups[1].Value
}
}
Add-Type -AssemblyName System.IO.Compression.FileSystem
$tweakDirs = Get-ChildItem -Path $TweaksRoot -Directory
if ($tweakDirs.Count -eq 0) {
Write-Host "No tweak folders found under $TweaksRoot."
return
}
Write-Host ""
Write-Host "Building tweak jars from $TweaksRoot"
Write-Host ""
foreach ($dir in $tweakDirs) {
$tomlPath = Join-Path $dir.FullName 'META-INF\neoforge.mods.toml'
$meta = Get-ModMeta -TomlPath $tomlPath
$jarName = "$($meta.ModId)-$($meta.Version).jar"
$jarPath = Join-Path $OutputRoot $jarName
# Wipe stale jars for this modId so old versions don't get bundled into
# the manifest alongside the new one.
Get-ChildItem -Path $OutputRoot -Filter "$($meta.ModId)-*.jar" -ErrorAction SilentlyContinue |
Where-Object { $_.Name -ne $jarName } |
ForEach-Object {
Write-Host (" removing stale: {0}" -f $_.Name)
Remove-Item $_.FullName -Force
}
if (Test-Path $jarPath) { Remove-Item $jarPath -Force }
# NB: PowerShell 5.1's [ZipFile]::CreateFromDirectory() writes Windows-
# native path separators (\) into ZIP entry names on Windows, producing
# entries like "META-INF\neoforge.mods.toml" instead of the spec-required
# "META-INF/neoforge.mods.toml". NeoForge's mod scanner then can't find
# the manifest and silently rejects the jar with "not a valid mod file".
# We build the archive manually so we control the entry name format.
# 'Create' string maps to ZipArchiveMode.Create -- PowerShell 5.1 doesn't
# auto-load the enum type, so the literal string form is more portable.
$zip = [System.IO.Compression.ZipFile]::Open($jarPath, 'Create')
try {
$sourceLen = $dir.FullName.Length
Get-ChildItem -Path $dir.FullName -Recurse -File | ForEach-Object {
$relPath = $_.FullName.Substring($sourceLen).TrimStart('\','/').Replace('\','/')
$entry = $zip.CreateEntry($relPath, [System.IO.Compression.CompressionLevel]::Optimal)
$entryStream = $entry.Open()
try {
$bytes = [System.IO.File]::ReadAllBytes($_.FullName)
$entryStream.Write($bytes, 0, $bytes.Length)
} finally { $entryStream.Dispose() }
}
} finally { $zip.Dispose() }
$size = (Get-Item $jarPath).Length
Write-Host (" built {0,-40} {1,8:N0} bytes" -f $jarName, $size)
}
Write-Host ""
Write-Host "Tweak jars are in: $OutputRoot"