Compare commits
11 Commits
6ccd83da5f
...
0bdc48c86f
| Author | SHA1 | Date | |
|---|---|---|---|
| 0bdc48c86f | |||
| 02e50f2fe5 | |||
| 50007e1109 | |||
| d4dfcb1e11 | |||
| 3dc0767a24 | |||
| c996d4edff | |||
| b080dfe325 | |||
| ce28a44e89 | |||
| 40dd06b8cf | |||
| 06d6a5ef45 | |||
| 4ea919432e |
+2
-1
@@ -55,8 +55,9 @@ CLAUDE.md
|
||||
|
||||
# ─── Build artifacts that get regenerated ─────────────────────────────────
|
||||
# Tweak jars rebuilt by scripts/Build-Tweaks.ps1
|
||||
pack/overrides/
|
||||
pack/overrides/mods/
|
||||
# manifest.json regenerated by scripts/Build-Pack.ps1 -- produced at scripts/
|
||||
# (default OutputPath) and copied to the deploy share by Deploy-Brass.ps1
|
||||
scripts/manifest.json
|
||||
pack/manifest.json
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
[general]
|
||||
#List of waystone origins that should prevent others from editing. PLAYER is special in that it allows only edits by the owner of the waystone.
|
||||
restrictedWaystones = ["PLAYER"]
|
||||
#Set to "GLOBAL" to have newly placed or found waystones be global by default.
|
||||
#Allowed Values: ACTIVATION, GLOBAL, SHARD_ONLY, ORANGE_SHARESTONE, MAGENTA_SHARESTONE, LIGHT_BLUE_SHARESTONE, YELLOW_SHARESTONE, LIME_SHARESTONE, PINK_SHARESTONE, GRAY_SHARESTONE, LIGHT_GRAY_SHARESTONE, CYAN_SHARESTONE, PURPLE_SHARESTONE, BLUE_SHARESTONE, BROWN_SHARESTONE, GREEN_SHARESTONE, RED_SHARESTONE, BLACK_SHARESTONE
|
||||
defaultVisibility = "ACTIVATION"
|
||||
#Add "GLOBAL" to allow every player to create global waystones.
|
||||
allowedVisibilities = []
|
||||
#The time in ticks that it takes to use a warp stone. This is the charge-up time when holding right-click.
|
||||
warpStoneUseTime = 32
|
||||
#The time in ticks that it takes to use a warp plate. This is the time the player has to stand on top for.
|
||||
warpPlateUseTime = 15
|
||||
#The time in ticks it takes to use a scroll. This is the charge-up time when holding right-click.
|
||||
scrollUseTime = 32
|
||||
|
||||
[inventoryButton]
|
||||
#Set to 'NONE' for no inventory button. Set to 'NEAREST' for an inventory button that teleports to the nearest waystone. Set to 'ANY' for an inventory button that opens the waystone selection menu. Set to a waystone name for an inventory button that teleports to a specifically named waystone.
|
||||
inventoryButton = ""
|
||||
#The x position of the inventory button in the inventory.
|
||||
inventoryButtonX = 58
|
||||
#The y position of the inventory button in the inventory.
|
||||
inventoryButtonY = 60
|
||||
#The y position of the inventory button in the creative menu.
|
||||
creativeInventoryButtonX = 88
|
||||
#The y position of the inventory button in the creative menu.
|
||||
creativeInventoryButtonY = 33
|
||||
|
||||
[worldGen]
|
||||
#Set to 'DEFAULT' to only generate the normally textured waystones. Set to 'MOSSY' or 'SANDY' to generate all as that variant. Set to 'BIOME' to make the style depend on the biome it is generated in.
|
||||
#Allowed Values: DEFAULT, MOSSY, SANDY, BLACKSTONE, DEEPSLATE, END_STONE, BIOME
|
||||
wildWaystoneStyle = "BIOME"
|
||||
#Approximate chunk distance between wild waystones being generated. Set to 0 to disable generation.
|
||||
chunksBetweenWildWaystones = 25
|
||||
#List of dimensions that wild waystones are allowed to spawn in. If left empty, all dimensions except those in wildWaystonesDimensionDenyList will be able to spawn waystones, provided the biomes are in the `has_structure/waystone` tag.
|
||||
wildWaystonesDimensionAllowList = ["minecraft:the_nether", "minecraft:overworld", "minecraft:the_end"]
|
||||
#List of dimensions that wild waystones are not allowed to spawn in, even if the biome is in the `has_structure/waystone` tag. Only used if wildWaystonesDimensionAllowList is empty.
|
||||
wildWaystonesDimensionDenyList = []
|
||||
#Set to 'PRESET_FIRST' to first use names from the nameGenerationPresets. Set to 'PRESET_ONLY' to use only those custom names. Set to 'MIXED' to have some waystones use custom names, and others random names.
|
||||
#Allowed Values: PRESET_FIRST, RANDOM_ONLY, PRESET_ONLY, MIXED
|
||||
nameGenerationMode = "PRESET_FIRST"
|
||||
#The template to use when generating new names. Supported placeholders are {Biome} (english biome name) and {MrPork} (the default name generator).
|
||||
nameGenerationTemplate = "{MrPork}"
|
||||
#These names will be used for the PRESET name generation mode. See the nameGenerationMode option for more info.
|
||||
nameGenerationPresets = []
|
||||
#Set to REGULAR to have waystones spawn in some villages. Set to FREQUENT to have waystones spawn in most villages. Set to DISABLED to disable waystone generation in villages. Waystones will only spawn in vanilla or supported villages.
|
||||
#Allowed Values: DISABLED, REGULAR, FREQUENT
|
||||
spawnInVillages = "DISABLED"
|
||||
|
||||
[teleports]
|
||||
#Set to false to simply disable all xp costs. See warpRequirements for more fine-grained control.
|
||||
enableCosts = true
|
||||
#Set to false to simply disable all cooldowns. See warpRequirements for more fine-grained control.
|
||||
enableCooldowns = true
|
||||
#List of warp requirements with comma-separated parameters in parentheses. Conditions can be defined as comma-separated list in square brackets. Will be applied in order.
|
||||
warpRequirements = ["[is_not_interdimensional] scaled_add_xp_cost(distance, 0.01)", "[is_interdimensional] add_xp_cost(27)", "[source_is_warp_plate] multiply_xp_cost(0)", "[target_is_global] multiply_xp_cost(0)", "min_xp_cost(0)", "max_xp_cost(27)", "[source_is_inventory_button] add_cooldown(inventory_button, 300)"]
|
||||
#Set to ENABLED to have nearby pets teleport with you. Set to SAME_DIMENSION to have nearby pets teleport with you only if you're not changing dimensions. Set to DISABLED to disable.
|
||||
#Allowed Values: ENABLED, SAME_DIMENSION, DISABLED
|
||||
transportPets = "DISABLED"
|
||||
#Set to ENABLED to have leashed mobs teleport with you. Set to SAME_DIMENSION to have leashed mobs teleport with you only if you're not changing dimensions. Set to DISABLED to disable.
|
||||
#Allowed Values: ENABLED, SAME_DIMENSION, DISABLED
|
||||
transportLeashed = "ENABLED"
|
||||
#List of entities that cannot be teleported, either as pet, leashed, or on warp plates.
|
||||
entityDenyList = ["minecraft:wither"]
|
||||
#Set to true to enable warp modifier items for applying status effects on teleports.
|
||||
enableModifiers = true
|
||||
|
||||
[client]
|
||||
#If enabled, the text overlay on waystones will no longer always render at full brightness.
|
||||
disableTextGlow = false
|
||||
|
||||
[compatibility]
|
||||
#If enabled, JourneyMap waypoints will be created for each activated waystone.
|
||||
journeyMap = true
|
||||
#If enabled, JourneyMap waypoints will only be created if the mod 'JourneyMap Integration' is not installed
|
||||
preferJourneyMapIntegrationMod = true
|
||||
#If enabled, Waystones will add markers for waystones and sharestones to Dynmap.
|
||||
dynmap = true
|
||||
|
||||
[blueMap]
|
||||
#Controls whether Waystones' BlueMap integration should be loaded
|
||||
enabled = true
|
||||
#If enabled, waystones will be tracked as markers on BlueMap
|
||||
includeWaystones = true
|
||||
#If enabled, sharestones will be tracked as markers on BlueMap.
|
||||
includeSharestones = true
|
||||
#If enabled, undiscovered waystones will be tracked as markers on BlueMap.
|
||||
includeUndiscoveredWaystones = false
|
||||
+34
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "Brass-and-Sigil pack.lock.json - generated, do not edit by hand unless you know what you are doing",
|
||||
"name": "Brass and Sigil",
|
||||
"version": "0.15.0",
|
||||
"version": "0.16.3",
|
||||
"minecraft": "1.21.1",
|
||||
"loader": {
|
||||
"type": "neoforge",
|
||||
@@ -448,6 +448,39 @@
|
||||
"sha1": "13d8835813b3851d4818c59ec8f7124e4a826c14",
|
||||
"size": 406699,
|
||||
"side": "both"
|
||||
},
|
||||
{
|
||||
"source": "modrinth",
|
||||
"slug": "kotlin-for-forge",
|
||||
"versionId": "NrSebcsG",
|
||||
"version": "5.11.0",
|
||||
"path": "mods/kotlinforforge-5.11.0-all.jar",
|
||||
"url": "https://cdn.modrinth.com/data/ordsPcFz/versions/NrSebcsG/kotlinforforge-5.11.0-all.jar",
|
||||
"sha1": "4d83e8a8e8cb06b306d2cea1e903680fe513f1f3",
|
||||
"size": 6869790,
|
||||
"side": "both"
|
||||
},
|
||||
{
|
||||
"source": "modrinth",
|
||||
"slug": "balm",
|
||||
"versionId": "3XiinKbh",
|
||||
"version": "21.0.58+neoforge-1.21.1",
|
||||
"path": "mods/balm-neoforge-1.21.1-21.0.58.jar",
|
||||
"url": "https://cdn.modrinth.com/data/MBAkmtvl/versions/3XiinKbh/balm-neoforge-1.21.1-21.0.58.jar",
|
||||
"sha1": "47b6dbe51693d5a2b3eac15c6322f357de365f15",
|
||||
"size": 701868,
|
||||
"side": "both"
|
||||
},
|
||||
{
|
||||
"source": "modrinth",
|
||||
"slug": "waystones",
|
||||
"versionId": "PXntW965",
|
||||
"version": "21.1.33+neoforge-1.21.1",
|
||||
"path": "mods/waystones-neoforge-1.21.1-21.1.33.jar",
|
||||
"url": "https://cdn.modrinth.com/data/LOpKHB2A/versions/PXntW965/waystones-neoforge-1.21.1-21.1.33.jar",
|
||||
"sha1": "488bc70db3730f209e65cbda04c36dbc3918c448",
|
||||
"size": 878265,
|
||||
"side": "both"
|
||||
}
|
||||
],
|
||||
"defaultServer": {
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
#requires -Version 7
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Audit pack.lock.json for missing required mod dependencies by reading
|
||||
each jar's mods.toml (and any jar-in-jar bundles) directly. Loader-truth,
|
||||
not Modrinth-truth.
|
||||
|
||||
.DESCRIPTION
|
||||
Each NeoForge/Forge mod declares its real dependencies in
|
||||
META-INF/neoforge.mods.toml (or META-INF/mods.toml) inside the jar.
|
||||
This is what the loader actually checks at startup; the Modrinth
|
||||
dependency field is metadata authors fill in (or don't).
|
||||
|
||||
The script also recurses into META-INF/jarjar/ bundles. Big mods like
|
||||
Create ship their deps (Registrate, Flywheel, Ponder) embedded as
|
||||
jar-in-jar -- the loader auto-loads them, so their modIds count as
|
||||
"present" without explicit lockfile entries.
|
||||
|
||||
Slug filter: only entries under "mods/" are treated as mod jars.
|
||||
Shaderpacks and other managed-but-non-mod files are skipped.
|
||||
|
||||
Language-provider jars (e.g. Kotlin for Forge) ship without a standard
|
||||
mods.toml; their modIds are added via a tiny override map.
|
||||
|
||||
Jars are cached in /tmp/bns-check-deps-cache/<sha1>.jar so subsequent
|
||||
runs are instant.
|
||||
|
||||
.PARAMETER LockPath
|
||||
.PARAMETER CacheDir
|
||||
.PARAMETER WarnOnly
|
||||
Print missing deps but exit 0. Deploy-Brass.ps1 calls without it.
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$LockPath = $(Join-Path (Split-Path -Parent $MyInvocation.MyCommand.Path) '..\pack\pack.lock.json'),
|
||||
[string]$CacheDir = '/tmp/bns-check-deps-cache',
|
||||
[switch]$WarnOnly
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
|
||||
$LockPath = (Resolve-Path $LockPath).Path
|
||||
$lock = Get-Content $LockPath -Raw | ConvertFrom-Json -Depth 20
|
||||
if (-not $lock.mods) { throw "pack.lock.json has no 'mods' array." }
|
||||
if (-not (Test-Path $CacheDir)) { New-Item -ItemType Directory -Path $CacheDir | Out-Null }
|
||||
|
||||
# Vanilla / loader IDs are always satisfied -- they're never modpack mods.
|
||||
$vanillaIds = @('minecraft','neoforge','forge','fabric','fabricloader','quilt','java','javafml','lowcodefml')
|
||||
|
||||
# Language-provider / library jars that ship without a standard mods.toml.
|
||||
# Map their lockfile slug to the modId they register at runtime. Add entries
|
||||
# here as new ones are encountered.
|
||||
$slugToImplicitModId = @{
|
||||
'kotlin-for-forge' = 'kotlinforforge'
|
||||
}
|
||||
|
||||
Add-Type -AssemblyName System.IO.Compression
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
|
||||
function Get-JarPath {
|
||||
param($mod)
|
||||
$cached = Join-Path $CacheDir ("$($mod.sha1).jar")
|
||||
if (Test-Path $cached) { return $cached }
|
||||
Write-Host " downloading $($mod.slug) ..." -ForegroundColor DarkGray
|
||||
try { Invoke-WebRequest -Uri $mod.url -OutFile $cached -TimeoutSec 60 }
|
||||
catch { throw "Failed to download $($mod.url): $($_.Exception.Message)" }
|
||||
return $cached
|
||||
}
|
||||
|
||||
# Read a specific entry's bytes from an already-open ZipArchive into memory.
|
||||
function Read-ZipEntry {
|
||||
param([System.IO.Compression.ZipArchive]$Zip, [string]$EntryName)
|
||||
$entry = $Zip.GetEntry($EntryName)
|
||||
if (-not $entry) { return $null }
|
||||
$ms = New-Object System.IO.MemoryStream
|
||||
$s = $entry.Open()
|
||||
try { $s.CopyTo($ms) } finally { $s.Dispose() }
|
||||
return $ms.ToArray()
|
||||
}
|
||||
|
||||
function Read-ZipEntryText {
|
||||
param([System.IO.Compression.ZipArchive]$Zip, [string]$EntryName)
|
||||
$entry = $Zip.GetEntry($EntryName)
|
||||
if (-not $entry) { return $null }
|
||||
$reader = New-Object System.IO.StreamReader($entry.Open())
|
||||
try { return $reader.ReadToEnd() }
|
||||
finally { $reader.Dispose() }
|
||||
}
|
||||
|
||||
# Tiny TOML reader -- only the bits we need (sections + key="value" lines).
|
||||
function Parse-ModsToml {
|
||||
param([string]$Text)
|
||||
$modIds = @()
|
||||
$deps = @()
|
||||
$section = $null
|
||||
$current = $null
|
||||
function Flush-Record {
|
||||
param($refModIds, $refDeps, $sec, $cur)
|
||||
if (-not $cur) { return }
|
||||
if ($sec -eq 'mods' -and $cur.modId) {
|
||||
$refModIds.Value += $cur.modId
|
||||
} elseif ($sec -like 'dep:*' -and $cur.modId) {
|
||||
$refDeps.Value += [pscustomobject]@{
|
||||
parent = $sec.Substring(4)
|
||||
modId = $cur.modId
|
||||
type = $cur.type
|
||||
mandatory = $cur.mandatory
|
||||
versionRange = $cur.versionRange
|
||||
}
|
||||
}
|
||||
}
|
||||
$modIdsRef = [ref]$modIds
|
||||
$depsRef = [ref]$deps
|
||||
foreach ($raw in ($Text -split "`n")) {
|
||||
$line = $raw.Trim()
|
||||
if (-not $line -or $line.StartsWith('#')) { continue }
|
||||
if ($line.StartsWith('[[')) {
|
||||
Flush-Record $modIdsRef $depsRef $section $current
|
||||
$current = $null
|
||||
if ($line -match '^\[\[mods\]\]') {
|
||||
$section = 'mods'; $current = @{}
|
||||
} elseif ($line -match '^\[\[dependencies\.([^\]]+)\]\]') {
|
||||
$section = "dep:$($Matches[1])"; $current = @{}
|
||||
} else {
|
||||
$section = $null
|
||||
}
|
||||
continue
|
||||
}
|
||||
if (-not $current) { continue }
|
||||
if ($line -match '^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.+?)\s*$') {
|
||||
$k = $Matches[1]; $v = $Matches[2].Trim()
|
||||
if ($v.StartsWith('"') -and $v.EndsWith('"')) { $v = $v.Substring(1, $v.Length - 2) }
|
||||
$current[$k] = $v
|
||||
}
|
||||
}
|
||||
Flush-Record $modIdsRef $depsRef $section $current
|
||||
return [pscustomobject]@{ ModIds = $modIdsRef.Value; Deps = $depsRef.Value }
|
||||
}
|
||||
|
||||
function Is-Required {
|
||||
param($dep)
|
||||
if ($dep.type) { return ($dep.type -eq 'required' -or $dep.type -eq 'mandatory') }
|
||||
if ($dep.mandatory) { return ($dep.mandatory -eq 'true') }
|
||||
return $true # NeoForge default
|
||||
}
|
||||
|
||||
# Recursively walk a jar (and its META-INF/jarjar/ embedded jars) collecting
|
||||
# every modId declared by mods.toml inside. Used to satisfy deps that ship
|
||||
# bundled (e.g. ponder/registrate/flywheel inside Create).
|
||||
function Collect-Jar-Identity {
|
||||
param([string]$JarPath)
|
||||
$modIds = @()
|
||||
$deps = @()
|
||||
$zip = [System.IO.Compression.ZipFile]::OpenRead($JarPath)
|
||||
try {
|
||||
# 1) This jar's own mods.toml (if any).
|
||||
$toml = Read-ZipEntryText -Zip $zip -EntryName 'META-INF/neoforge.mods.toml'
|
||||
if (-not $toml) { $toml = Read-ZipEntryText -Zip $zip -EntryName 'META-INF/mods.toml' }
|
||||
if ($toml) {
|
||||
$parsed = Parse-ModsToml -Text $toml
|
||||
$modIds += $parsed.ModIds
|
||||
$deps += $parsed.Deps
|
||||
}
|
||||
# 2) jar-in-jar bundles -- recursively extract + collect modIds (we only
|
||||
# need their modIds; their deps are the parent's problem to satisfy).
|
||||
$jarjarEntries = $zip.Entries | Where-Object {
|
||||
$_.FullName -like 'META-INF/jarjar/*.jar'
|
||||
}
|
||||
foreach ($e in $jarjarEntries) {
|
||||
$tmpInner = [System.IO.Path]::GetTempFileName() + '.jar'
|
||||
$s = $e.Open()
|
||||
try {
|
||||
$fs = [System.IO.File]::Open($tmpInner, [System.IO.FileMode]::Create)
|
||||
try { $s.CopyTo($fs) } finally { $fs.Dispose() }
|
||||
} finally { $s.Dispose() }
|
||||
$inner = Collect-Jar-Identity -JarPath $tmpInner
|
||||
$modIds += $inner.ModIds # bundled deps count as present
|
||||
# Don't propagate bundled deps -- they're satisfied within this jar
|
||||
Remove-Item $tmpInner -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
} finally {
|
||||
$zip.Dispose()
|
||||
}
|
||||
return [pscustomobject]@{ ModIds = ($modIds | Select-Object -Unique); Deps = $deps }
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Auditing $($lock.mods.Count) lockfile entries (parsing each jar's mods.toml + jar-in-jar bundles)..."
|
||||
Write-Host ""
|
||||
|
||||
$presentModIds = New-Object 'System.Collections.Generic.HashSet[string]' ([System.StringComparer]::OrdinalIgnoreCase)
|
||||
foreach ($v in $vanillaIds) { [void]$presentModIds.Add($v) }
|
||||
|
||||
# parent modId -> list of dep records (so the missing report blames a modId, not a slug)
|
||||
$jarDepsByParentModId = @{}
|
||||
$skipped = @() # informational
|
||||
|
||||
$count = 0
|
||||
foreach ($m in $lock.mods) {
|
||||
$count++
|
||||
# Only consider entries that live under mods/ -- shaderpacks, configs etc.
|
||||
# are managed by the launcher but aren't mods.
|
||||
if (-not ($m.path -like 'mods/*')) {
|
||||
$skipped += "$($m.slug): not under mods/ ($($m.path))"
|
||||
continue
|
||||
}
|
||||
# Language-provider override (kotlin-for-forge etc.)
|
||||
if ($slugToImplicitModId.ContainsKey($m.slug)) {
|
||||
[void]$presentModIds.Add($slugToImplicitModId[$m.slug])
|
||||
}
|
||||
try {
|
||||
$jar = Get-JarPath -mod $m
|
||||
$ident = Collect-Jar-Identity -JarPath $jar
|
||||
foreach ($id in $ident.ModIds) {
|
||||
[void]$presentModIds.Add($id)
|
||||
}
|
||||
foreach ($id in $ident.ModIds) {
|
||||
$jarDepsByParentModId[$id] = $ident.Deps
|
||||
}
|
||||
if (-not $ident.ModIds -and -not $slugToImplicitModId.ContainsKey($m.slug)) {
|
||||
$skipped += "$($m.slug): no modId discovered (no mods.toml; consider adding to override map)"
|
||||
}
|
||||
} catch {
|
||||
$skipped += "$($m.slug): $($_.Exception.Message)"
|
||||
}
|
||||
if ($count % 10 -eq 0) { Write-Host " ...processed $count/$($lock.mods.Count)" -ForegroundColor DarkGray }
|
||||
}
|
||||
|
||||
# Pass 2: check each parent's required deps against the present set.
|
||||
$missing = @{}
|
||||
foreach ($parentId in $jarDepsByParentModId.Keys) {
|
||||
foreach ($dep in $jarDepsByParentModId[$parentId]) {
|
||||
if (-not (Is-Required $dep)) { continue }
|
||||
if (-not $presentModIds.Contains($dep.modId)) {
|
||||
if (-not $missing.ContainsKey($dep.modId)) {
|
||||
$missing[$dep.modId] = @{ parents = @() }
|
||||
}
|
||||
$missing[$dep.modId].parents += $parentId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 60)
|
||||
if ($missing.Count -eq 0) {
|
||||
Write-Host "All required deps satisfied. ($($presentModIds.Count) mod IDs in pack, incl. bundled jar-in-jar)" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "MISSING REQUIRED DEPS: $($missing.Count) unique" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
foreach ($depId in ($missing.Keys | Sort-Object)) {
|
||||
$parents = ($missing[$depId].parents | Sort-Object -Unique) -join ', '
|
||||
Write-Host " $depId" -ForegroundColor Red
|
||||
Write-Host " needed by: $parents" -ForegroundColor DarkGray
|
||||
}
|
||||
}
|
||||
|
||||
if ($skipped.Count -gt 0) {
|
||||
Write-Host ""
|
||||
Write-Host "Informational (skipped / unanalysable):" -ForegroundColor DarkGray
|
||||
foreach ($s in $skipped) { Write-Host " - $s" -ForegroundColor DarkGray }
|
||||
}
|
||||
|
||||
if ($missing.Count -gt 0 -and -not $WarnOnly) {
|
||||
throw "Missing required dependencies. Add them to pack/pack.lock.json (or pass -WarnOnly to bypass)."
|
||||
}
|
||||
@@ -118,6 +118,17 @@ if (($shouldRunPack -or $shouldRunLauncher) -and -not $DryRun -and -not $Force)
|
||||
}
|
||||
}
|
||||
|
||||
# ─── Pre-flight: required-dependency check ─────────────────────────────────
|
||||
# Walks each Modrinth mod in pack.lock.json, fetches its 'required'
|
||||
# dependencies, and refuses to deploy if any aren't already in the lockfile.
|
||||
# Catches the failure mode where adding a mod that hard-deps on (say)
|
||||
# kotlinforforge crash-loops the daemon at startup. Only runs when pack
|
||||
# content is changing; -Force skips it (rarely needed).
|
||||
if ($shouldRunPack -and -not $DryRun -and -not $Force) {
|
||||
Write-Host "Pre-flight: validating Modrinth dependency graph..." -ForegroundColor DarkGray
|
||||
& (Join-Path $here 'Check-Deps.ps1')
|
||||
}
|
||||
|
||||
# ─── Pre-flight: was the daemon already running? ───────────────────────────
|
||||
# We don't auto-stop it (kicks active players to fix a problem that isn't
|
||||
# actually broken -- the atomic swap is safe with the daemon running). But
|
||||
|
||||
Reference in New Issue
Block a user