Commit Graph

34 Commits

Author SHA1 Message Date
Matt 3dc0767a24 pack: tune Waystones config + bump to 0.16.1
Ships an override of waystones-common.toml under pack/overrides/
defaultconfigs/ so first-run installs (clients + fresh servers) get the
intended defaults. NeoForge auto-copies defaultconfigs/<file> into
config/<file> on first run only -- player-tweaked configs after that
are preserved across pack updates.

The only override vs mod-default in this commit:
- spawnInVillages: REGULAR -> DISABLED

Everything else stays at mod default, which already gives the security
posture we want:
- defaultVisibility = ACTIVATION  -> each player must physically visit
  a waystone before they can teleport TO it. Solves the "player B
  teleports into player A's base" concern without a hard per-player
  placement cap (which v21.x dropped).
- restrictedWaystones = [PLAYER]  -> only owners can edit their own
  waystones.
- allowedVisibilities = []        -> no player UI can create global
  waystones; OPs use the /waystone command for server-public ones.

Wild waystones (in the open world, not villages) stay on -- they're
public-good fast travel, not tied to bases.
2026-05-23 12:55:18 +00:00
Matt c996d4edff Merge pull request 'Add Waystones + Balm' (#10) from feature/add-waystones into main 2026-05-23 12:53:27 +00:00
Matt b080dfe325 pack: add Waystones + Balm, bump to 0.16.0
Waystones 21.1.33: fast-travel teleport network.
Balm 21.0.58: required common lib for Waystones (and other BlayTheNinth mods).

Config will be tweaked in a follow-up after the server generates its
default config files. Intended config:
- worldgen village waystones DISABLED
- maxWaystonesPerPlayer = 1
- defaultPrivacy = Private (player-placed)
- OP-placed global waystones via /waystone command
2026-05-23 12:53:26 +00:00
Matt ce28a44e89 Merge pull request 'Add Check-Deps pre-flight (mods.toml ground truth)' (#9) from feature/check-deps-preflight into main 2026-05-23 11:55:38 +00:00
Matt 40dd06b8cf scripts: add Check-Deps pre-flight (mods.toml ground truth)
Adds scripts/Check-Deps.ps1 and wires it into Deploy-Brass.ps1 as a
pre-flight gate when Stage includes Pack. The script parses each lockfile
jar's META-INF/neoforge.mods.toml directly -- the same source the loader
reads at startup -- and refuses to deploy if any [[dependencies]] block
with type=required (default) names a modId that nobody in the pack
provides.

Handles three real edge cases discovered during initial run:

1. Jar-in-jar bundled deps: Create ships Ponder, Registrate, Flywheel
   embedded under META-INF/jarjar/. The script recurses into those and
   counts their modIds as present, since the loader auto-loads them.

2. Language-provider jars (Kotlin for Forge) have no standard mods.toml
   -- they declare via a different mechanism. Tiny slugToImplicitModId
   override map (currently 1 entry) covers them.

3. Non-mod lockfile entries (shaderpacks under shaderpacks/, configs
   under config/, etc.) are skipped explicitly by path prefix check.

Vanilla deps (minecraft, neoforge, forge, fabric, java, javafml, etc.)
are pre-seeded as present.

Jars cached at /tmp/bns-check-deps-cache/<sha1>.jar; second-run cost is
~1.5s. First run downloads ~150MB.

The script would have caught the v0.15.0 -> v0.15.1 incident: Slice &
Dice's jar declares kotlinforforge required in mods.toml even though
Modrinth's dep field only lists Create. The Modrinth-only check (earlier
draft) wouldn't have helped; the mods.toml-based check does.
2026-05-23 11:55:38 +00:00
Matt 06d6a5ef45 Merge pull request 'Hotfix: add Kotlin for Forge (Slice & Dice dep)' (#8) from fix/kotlin-for-forge-dep into main 2026-05-22 21:00:38 +00:00
Matt 4ea919432e pack: add Kotlin for Forge 5.11.0, bump to 0.15.1
Hotfix for 0.15.0: Slice & Dice declares 'kotlinforforge >= 5.8' as a
hard dependency and the server crashed on load without it. Adding KFF
satisfies the dep. 6.9 MB, both sides.
2026-05-22 21:00:37 +00:00
Matt 6ccd83da5f Merge pull request 'Add Farmer's Delight + Slice & Dice' (#7) from feature/add-farmers-delight into main 2026-05-22 20:58:38 +00:00
Matt 89c41dfe4d pack: add Farmer's Delight + Slice & Dice, bump to 0.15.0
- Farmer's Delight 1.3.2 (3.2 MB): cooking expansion -- new crops
  (tomato, onion, rice, cabbage), cooking pot, stove, cutting board,
  knife, ~50+ food recipes. Both sides required.
- Create Slice & Dice 4.2.4 (407 KB): FD<->Create automation bridge.
  Adds a Slicer machine (Mechanical Press/Mixer-style) that automates
  the knife/cutting-board step so Create kinetic networks can feed FD
  prep. Loader-shared jar (forge+neoforge from one file).

Both tagged "both". Brings live mod count to 41.
2026-05-22 20:58:37 +00:00
Matt 8b599a7da7 Merge pull request 'Add AppleSkin + Mouse Tweaks' (#6) from feature/add-appleskin-mousetweaks into main 2026-05-22 20:49:14 +00:00
Matt 6143ddc111 pack: add AppleSkin + Mouse Tweaks, bump to 0.14.0
Two client-leaning QoL mods.

- AppleSkin 3.0.9: food + saturation tooltips. Modrinth side=optional both;
  tagged "both" so server still installs (harmless, ~75KB).
- Mouse Tweaks 2.26.1: shift-drag, scroll-transfer, auto-refill, click-and-
  drag stacking in inventory UI. Modrinth lists server=unsupported, so
  explicitly tagged side=client -- this is the first new mod that exercises
  the server-side filter from the 0.10.0 schema migration: server's sync
  should skip downloading it.
2026-05-22 20:49:14 +00:00
Matt a29c0e3cab Merge pull request 'Add Spark profiler 1.10.124' (#5) from feature/add-spark into main 2026-05-22 20:46:13 +00:00
Matt 3bcf8bc4f8 pack: add Spark profiler 1.10.124 + bump to 0.13.0
Best-in-class MC profiler. Server side: /spark sampler start, /spark
heap, /spark tps. Optional client side too. ~3.5MB, both sides optional
per Modrinth -- tagged "both" so launcher syncs it too.
2026-05-22 20:46:12 +00:00
Matt 2e40732e69 Merge pull request 'Add Almost Unified 1.4.2' (#4) from feature/add-almost-unified into main 2026-05-22 20:34:32 +00:00
Matt 987fedd7e4 pack: add Almost Unified 1.4.2 + bump to 0.12.0
Preemptive insurance against duplicate-item proliferation when more
metals-producing mods are added (e.g. when Power Grid lands, or any
future Mekanism/Thermal/etc. addition). Today's pack only has TFMG as
the metals provider so AU is largely a no-op now, but ships with sane
default tag preferences that activate automatically as duplicates appear.

Tiny mod (~290 KB jar), client-optional/server-required per Modrinth.
2026-05-22 20:34:32 +00:00
Matt 8d0c05bd8a Merge pull request 'Add Simple Voice Chat 2.6.17' (#3) from feature/add-simple-voice-chat into main 2026-05-22 20:23:14 +00:00
Matt 95966d2d14 pack: add Simple Voice Chat 2.6.17 + bump to 0.11.0
Adds proximity voice chat to the modpack. Server uses UDP/24454 by default
(firewall rule added separately).

Modrinth lists both sides as "optional" -- players without it can still
join, just no voice. Tagged "both" in the lockfile so server + launcher
both install it.
2026-05-22 20:23:07 +00:00
Matt 839d93f242 Add explicit client/server side field to manifest (#2) 2026-05-22 15:15:03 +00:00
Matt db4534e427 Add local deploy mode to Deploy-Brass.ps1 (#1) 2026-05-22 15:14:44 +00:00
Matt 4ee4a2bb43 scripts: make Build-Pack and Deploy-Brass cross-platform
Two small fixes that surface when running the deploy pipeline on a
non-Windows host (we now do this on glados via the Telegram bridge):

- Build-Pack.ps1: fall back to launcher/ModpackLauncher.csproj <Version>
  when Get-Item.VersionInfo.FileVersion returns empty. Linux PowerShell
  can't parse PE FileVersion from a Windows .exe, so the previous code
  threw "set <Version> in ModpackLauncher.csproj and republish" -- but
  the csproj <Version> *was* set, just not readable from a foreign PE.
  Appends ".0" so the embedded value still matches the 4-part form the
  Windows compile bakes in.

- Deploy-Brass.ps1: prefer rsync where available, fall back to robocopy
  otherwise. robocopy is Windows-only and bails on Linux/macOS hosts.
2026-05-22 14:23:09 +00:00
Matt 4594cb9c00 Add explicit client/server side field to manifest
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
2026-05-22 14:17:43 +00:00
Matt f280e107f3 Add 'local' deploy mode to Deploy-Brass.ps1
When ServerSshHost = 'local' the server-binary stage skips scp/ssh and
does an in-place Copy-Item + chmod + Move-Item on the local filesystem.
Pre-flight pgrep also runs locally. Allows running the deploy script on
the server itself (glados in our case) without setting up ssh-to-self.

Other deploy modes (remote scp/ssh) unchanged.
2026-05-22 14:08:02 +00:00
Matt Sijbers e0b9278da4 launcher: bump to 0.4.8 (test artifact for the self-update flow shipped in 0.4.7) 2026-05-20 22:09:56 +01:00
Matt Sijbers f311429bc2 launcher: in-place self-update via download + swap + restart
Adds Services/SelfUpdateService.cs implementing the Chrome-style pattern:
  1. Download new exe to "<current>.new"
  2. Rename running exe to "<current>.old" (NTFS allows MoveFile on a
     running exe -- it resolves processes by handle, not by path)
  3. Rename ".new" to canonical path
  4. Spawn the new exe with our argv
  5. Environment.Exit(0)
  6. Next start of the new exe runs CleanupAfterUpdate() in App.OFIC()
     which deletes the leftover ".old"

UI: the existing "Download" banner button is now "Install update". Click
runs the self-update flow with a percent/MB progress label; any failure
(network, AV write-block, dir not writable) falls back to opening the
URL in the browser so users always have a path forward.

Bumps to 0.4.7. Also fixes a stale fallback URL that pointed at
sijbers.uk/pack/... -- now correctly points at bns.sijbers.uk/launcher/.
2026-05-20 22:07:03 +01:00
Matt Sijbers ed70ff52e9 Deploy-Brass: extend manifest verification to all fields
The previous check covered version, launcherVersion/Url, and a single
sampled self-hosted URL. Expanded to cover every field that has ever
been a foot-gun:

  Required (must always be present and match pack.lock.json):
    name, version, minecraft.version, loader.type, loader.version,
    launcherVersion, launcherUrl

  Optional but consistency-checked: if pack.lock.json declares the
  field, the manifest MUST also have it (and defaultServer must include
  a non-empty ip). Catches the case where someone removes the field
  from pack.lock or Build-Pack drops it silently:
    panelUrl, defaultShader, defaultServer

  files[]: every entry validated for path/url/sha1/size shape; every
  self-hosted (non-CDN) url checked for the expected host. Catches
  partial-update bugs where some URLs migrate hosts but others don't.

URL host checks use the configured $ManifestPublicUrl host as the
source of truth; CDN-hosted mods (modrinth/forgecdn/curseforge) are
intentionally exempt.
2026-05-20 21:37:55 +01:00
Matt Sijbers 4e31f5ab1a Deploy-Brass: prevent silent loss of launcher metadata in manifest
Two safeguards so a -Stage Pack run never strips launcherVersion/
launcherUrl from the deployed manifest (the original bug that left
old launchers unable to see upgrade prompts):

  1. Always pass -LauncherExePath to Build-Pack.ps1 when a local
     publish exists, regardless of stage. Previously this only fired
     for stages that included Launcher, so Pack-only deploys regressed
     the manifest to a version with no launcher metadata.

  2. New post-deploy verification step fetches the published manifest
     and asserts: pack version matches lockfile, launcherVersion +
     launcherUrl present, all self-hosted URLs use the configured
     manifest host. Throws on any mismatch.
2026-05-20 21:35:39 +01:00
Matt Sijbers 480205a630 Build-Pack: default new URLs to bns.sijbers.uk (manifest + launcher paths) 2026-05-20 21:32:46 +01:00
Matt Sijbers 683c2c19ff feat(panel): edit memoryMB from the Settings modal
Adds GET/POST /api/daemon/config (safe subset of server-config.json --
just memoryMB for now) and a "Resources" section in the existing Settings
modal. The Save and Save+restart buttons now POST both /api/server/settings
and /api/daemon/config; restartRequired is OR'd across the two endpoints.

Future fields like backupKeep / backupSchedule / bluemapDir can be added
by extending DAEMON_FIELDS in settings.js + the validation block in
RunCommand.cs. Sensitive fields (passwords, manifestUrl, ports) are
intentionally kept off this endpoint.
2026-05-20 21:25:47 +01:00
Matt Sijbers 9bf3cc69b0 launcher: bump to 0.4.6 + point manifestUrl at bns.sijbers.uk 2026-05-19 22:42:11 +01:00
Matt Sijbers bf53b65706 feat(auth): first-run password setup via web panel
When `webPassword` is null and the daemon starts headless (systemd, piped
SSH), no longer auto-generate a random password. Instead:
  - Boot normally with the gate denying everything except /api/auth/setup
  - Panel UI eagerly probes new /api/auth/state on load and renders a
    first-run setup overlay (password + confirm) when needsSetup=true
  - POST /api/auth/setup writes the chosen password and issues the auth
    cookie in the same response, so the operator lands logged in

Interactive TTY behaviour (prompt at the console) is unchanged. The gate
middleware is now registered unconditionally so first-run mode is still
locked-down instead of wide-open.
2026-05-18 23:20:27 +01:00
Matt Sijbers 372b5090cd Build-Tweaks: validate every jar before declaring success
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.
2026-05-09 22:33:52 +01:00
Matt Sijbers 62c88d4895 fix(deploy): publish manifest.json on Pack-only deploys
Stage 5 of Deploy-Brass.ps1 was gated only on \$shouldRunLauncher, so a
"-Stage Pack" run regenerated the manifest locally + mirrored
pack/overrides/ to the share, but never copied the new manifest.json
itself. Result: tweak jars/configs landed on the share, but clients
fetching the (still-old) manifest never knew about the new SHA-1s and
skipped the re-sync. Caught when fixing the brassandsigil_tweaks jar:
the public manifest stayed at 0.9.2 even though local was 0.9.3.

Split into two stages -- launcher exe stays gated on \$shouldRunLauncher,
manifest.json now publishes whenever \$shouldRunPack (so any Pack, All,
or Launcher deploy includes it).
2026-05-09 22:32:01 +01:00
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
Matt Sijbers a1331212cb Initial commit: Brass & Sigil monorepo
Self-hosted Minecraft modpack distribution + administration system.

- launcher/  Avalonia 12 desktop client; single-file win-x64 publish.
             Microsoft auth via XboxAuthNet, manifest+SHA-1 mod sync,
             portable install path, sidecar settings.
- server/    brass-sigil-server daemon (.NET 8, linux-x64). Wraps the
             MC subprocess, embedded Kestrel admin panel with cookie
             auth + rate limiting, RCON bridge, scheduled backups,
             BlueMap CLI integration with player markers + skin proxy,
             friend-side whitelist request flow, world wipe with seed
             selection (keep current / random / custom).
- pack/      pack.lock.json (Modrinth + manual CurseForge entries),
             data-only tweak source under tweaks/, build outputs in
             overrides/ (gitignored).
- scripts/   Build-Pack / Build-Tweaks / Update-Pack / Check-Updates
             plus Deploy-Brass.ps1 unified one-shot deploy with
             version-bump pre-flight and daemon-state detection.
2026-05-05 00:19:05 +01:00