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.
This commit is contained in:
Matt Sijbers
2026-05-05 00:19:05 +01:00
commit a1331212cb
99 changed files with 12640 additions and 0 deletions
+156
View File
@@ -0,0 +1,156 @@
using System.Formats.Tar;
using System.IO.Compression;
using System.Runtime.InteropServices;
namespace BrassAndSigil.Server.Services;
/// <summary>
/// Downloads + extracts Adoptium Temurin JRE 21 to server/java/. Used as a fallback
/// when system Java is missing or too old. Adoptium's API gives us a stable
/// platform-keyed download URL without needing API keys or auth.
/// </summary>
public sealed class JavaInstaller
{
private static readonly HttpClient _http = new() { Timeout = TimeSpan.FromMinutes(15) };
public string GetJavaInstallDir(string serverDir) => Path.Combine(serverDir, "java");
public string GetJavaInstallDir(string serverDir, int majorVersion) =>
Path.Combine(serverDir, "java" + majorVersion);
/// <summary>If a previous install put a java executable under serverDir/java/, return its path.</summary>
public string? FindBundledJava(string serverDir)
{
var javaDir = GetJavaInstallDir(serverDir);
if (!Directory.Exists(javaDir)) return null;
var exe = OperatingSystem.IsWindows() ? "java.exe" : "java";
return Directory.EnumerateFiles(javaDir, exe, SearchOption.AllDirectories).FirstOrDefault();
}
/// <summary>Find a Java install for a specific major version (e.g. javaXX/jdk-XX*/bin/java).</summary>
public string? FindBundledJava(string serverDir, int majorVersion)
{
var javaDir = GetJavaInstallDir(serverDir, majorVersion);
if (!Directory.Exists(javaDir)) return null;
var exe = OperatingSystem.IsWindows() ? "java.exe" : "java";
return Directory.EnumerateFiles(javaDir, exe, SearchOption.AllDirectories).FirstOrDefault();
}
public Task<string?> InstallJre21Async(string serverDir, IProgress<string>? progress, CancellationToken ct)
=> InstallJreAsync(21, serverDir, GetJavaInstallDir(serverDir), progress, ct);
/// <summary>
/// Download + extract Adoptium Temurin JRE for a specific major version into
/// <paramref name="installDir"/>. Used by BlueMap to get JRE 25 alongside the
/// JRE 21 we use for Minecraft.
/// </summary>
public async Task<string?> InstallJreAsync(int majorVersion, string serverDir, string installDir,
IProgress<string>? progress, CancellationToken ct)
{
var javaDir = installDir;
Directory.CreateDirectory(javaDir);
var (url, archiveName, isZip) = PickAdoptiumDownload(majorVersion);
if (url is null)
{
progress?.Report($"[err] No supported Adoptium binary for {RuntimeInformation.OSDescription} {RuntimeInformation.OSArchitecture}.");
return null;
}
var archivePath = Path.Combine(javaDir, archiveName!);
progress?.Report($"Downloading Adoptium Temurin JRE 21 ({(isZip ? "zip" : "tar.gz")})...");
try
{
using (var resp = await _http.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, ct))
{
resp.EnsureSuccessStatusCode();
await using var src = await resp.Content.ReadAsStreamAsync(ct);
await using var dst = File.Create(archivePath);
await src.CopyToAsync(dst, ct);
}
}
catch (Exception ex)
{
progress?.Report($" [err] Download failed: {ex.Message}");
return null;
}
progress?.Report($" Downloaded {new FileInfo(archivePath).Length:N0} bytes");
progress?.Report("Extracting...");
try
{
if (isZip)
{
ZipFile.ExtractToDirectory(archivePath, javaDir, overwriteFiles: true);
}
else
{
await using var fs = File.OpenRead(archivePath);
await using var gzip = new GZipStream(fs, CompressionMode.Decompress);
await TarFile.ExtractToDirectoryAsync(gzip, javaDir, overwriteFiles: true, ct);
}
}
catch (Exception ex)
{
progress?.Report($" [err] Extract failed: {ex.Message}");
return null;
}
try { File.Delete(archivePath); } catch { /* best-effort */ }
var javaExe = FindBundledJava(serverDir);
if (javaExe is null)
{
progress?.Report(" [err] Extracted, but couldn't locate bin/java in the result.");
return null;
}
// On Linux/macOS, make sure java is executable. TarFile preserves mode bits in
// most setups, but be defensive.
if (!OperatingSystem.IsWindows())
{
try
{
File.SetUnixFileMode(javaExe,
UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute |
UnixFileMode.GroupRead | UnixFileMode.GroupExecute |
UnixFileMode.OtherRead | UnixFileMode.OtherExecute);
}
catch { }
}
progress?.Report($"Java {majorVersion} ready: {javaExe}");
return javaExe;
}
private static (string? Url, string? ArchiveName, bool IsZip) PickAdoptiumDownload(int majorVersion)
{
// Adoptium API picks the latest GA release matching our os/arch.
// Docs: https://api.adoptium.net/q/swagger-ui/
var arch = RuntimeInformation.OSArchitecture switch
{
Architecture.X64 => "x64",
Architecture.Arm64 => "aarch64",
_ => null
};
if (arch is null) return (null, null, false);
if (OperatingSystem.IsWindows())
{
return ($"https://api.adoptium.net/v3/binary/latest/{majorVersion}/ga/windows/{arch}/jre/hotspot/normal/eclipse",
$"jre{majorVersion}.zip", true);
}
if (OperatingSystem.IsLinux())
{
return ($"https://api.adoptium.net/v3/binary/latest/{majorVersion}/ga/linux/{arch}/jre/hotspot/normal/eclipse",
$"jre{majorVersion}.tar.gz", false);
}
if (OperatingSystem.IsMacOS())
{
return ($"https://api.adoptium.net/v3/binary/latest/{majorVersion}/ga/mac/{arch}/jre/hotspot/normal/eclipse",
$"jre{majorVersion}.tar.gz", false);
}
return (null, null, false);
}
}