372b5090cd
Adds three structural assertions that run after the zip is built: 1. No backslash separators in any entry name. NeoForge needs "META-INF/neoforge.mods.toml" to be at exactly that forward-slash path; PowerShell 5.1's [ZipFile]::CreateFromDirectory() puts "META-INF\neoforge.mods.toml" instead, which the loader silently rejects. The current build code uses CreateEntry with explicit forward slashes, but this guard fires if anyone reverts to the simpler-looking CreateFromDirectory. 2. META-INF/neoforge.mods.toml exists at the canonical path. Without it, NeoForge skips the jar with a bland "not a valid mod file" warning that's easy to miss in a 500-line game log. 3. The modId declared in the embedded TOML matches the source folder's modId. Catches the case where a tweak folder is renamed but its neoforge.mods.toml isn't updated, which would otherwise ship a jar whose declared identity differs from its filename. Each tweak jar's build line now tags "[validated]" so a casual reader of the log sees that the post-build checks ran. Failure raises a specific exception with the offending entries listed, so the build fails loudly at the source instead of producing a pack that mysteriously doesn't apply its tweaks at runtime.
167 lines
7.2 KiB
PowerShell
167 lines
7.2 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() }
|
|
|
|
# ── Post-build validation ────────────────────────────────────────────────
|
|
# Catch structural problems BEFORE the jar gets bundled into the manifest
|
|
# and shipped to clients. NeoForge silently rejects invalid jars with a
|
|
# bland "not a valid mod file" warning that's easy to miss in a 500-line
|
|
# game log; this fails the build loudly with a specific reason instead.
|
|
$verify = [System.IO.Compression.ZipFile]::OpenRead($jarPath)
|
|
try {
|
|
$names = @($verify.Entries | ForEach-Object { $_.FullName })
|
|
|
|
# 1. No backslashes in entry names. PowerShell 5.1's CreateFromDirectory
|
|
# leaks Windows path separators into ZIP entries, which makes
|
|
# NeoForge fail to find META-INF/neoforge.mods.toml. We build with
|
|
# explicit CreateEntry above, but if anyone "simplifies" the zip
|
|
# path back to CreateFromDirectory, this guard fires.
|
|
$badSep = $names | Where-Object { $_ -match '\\' }
|
|
if ($badSep) {
|
|
throw ("$jarName has entries with backslash separators (NeoForge will reject it):`n - " + ($badSep -join "`n - "))
|
|
}
|
|
|
|
# 2. META-INF/neoforge.mods.toml must be at the canonical path.
|
|
if (-not ($names -contains 'META-INF/neoforge.mods.toml')) {
|
|
throw "$jarName is missing META-INF/neoforge.mods.toml at the canonical forward-slash path. Found: $($names -join ', ')"
|
|
}
|
|
|
|
# 3. modId from the embedded toml must match the jar's filename, so a
|
|
# misnamed source folder doesn't ship a jar whose manifest declares
|
|
# a different mod (which loads silently under the wrong identity).
|
|
$tomlEntry = $verify.GetEntry('META-INF/neoforge.mods.toml')
|
|
$reader = New-Object System.IO.StreamReader($tomlEntry.Open())
|
|
try { $tomlContent = $reader.ReadToEnd() } finally { $reader.Dispose() }
|
|
$tomlModId = [regex]::Match($tomlContent, '(?m)^\s*modId\s*=\s*"([^"]+)"').Groups[1].Value
|
|
if ($tomlModId -ne $meta.ModId) {
|
|
throw "$jarName modId mismatch: source folder advertises '$($meta.ModId)' but built jar's neoforge.mods.toml says '$tomlModId'."
|
|
}
|
|
} finally { $verify.Dispose() }
|
|
|
|
$size = (Get-Item $jarPath).Length
|
|
Write-Host (" built {0,-40} {1,8:N0} bytes [validated]" -f $jarName, $size)
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host "Tweak jars are in: $OutputRoot"
|