Files
Mykhailo Ivanchenko 238aa7809c install.ps1: ask which drive to install to
Lists local fixed drives with free space and lets the player pick one by
number or letter. Enter keeps the previous location on the system drive
(%USERPROFILE%\mc-lan-party), so existing installs are still found;
other drives install to <drive>:\mc-lan-party. Warns under 3 GB free.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 13:12:05 +02:00

134 lines
7.2 KiB
PowerShell

# LAN party bootstrap for Windows: installs Fjord Launcher Unlocked (DRM-free
# Prism fork), seeds a persistent offline account, drops in the pre-built
# modpack instance, and launches straight into it on the LAN server.
#
# Hosted from Gitea (git.mivanchenko.de), not a local http.server — see HOSTING.md.
# Participants run: irm https://git.mivanchenko.de/mivanchenko/mc-lan-party/raw/branch/main/install.ps1 | iex
# ---- EDIT ME before hosting ----
$HostUrl = "https://git.mivanchenko.de/mivanchenko/mc-lan-party/raw/branch/main" # for this script + install.sh (small text files)
$ModpackUrl = "https://minecraft.mivanchenko.de/modpack.zip" # served directly by Caddy (static/ mount), NOT Gitea — modpack.zip is well over Gitea's 50MB upload cap
$InstanceName = "LANParty"
$ServerAddr = "minecraft.mivanchenko.de:25565" # always-on homelab box, same address for prep and the actual party
# ---------------------------------
$ErrorActionPreference = "Stop" # fail loudly instead of printing an error and silently continuing
# ---- pick the install drive (default: the system drive, under the user profile like before) ----
$SystemDrive = $env:SystemDrive # e.g. "C:"
$Drives = @(Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" | Sort-Object DeviceID) # local fixed disks only
Write-Host "Where should the LAN party Minecraft be installed? (needs ~3 GB)"
for ($i = 0; $i -lt $Drives.Count; $i++) {
$d = $Drives[$i]
$free = [math]::Round($d.FreeSpace / 1GB, 1)
$label = if ($d.VolumeName) { " ($($d.VolumeName))" } else { "" }
$default = if ($d.DeviceID -eq $SystemDrive) { " [default]" } else { "" }
Write-Host (" {0}) {1}{2} - {3} GB free{4}" -f ($i + 1), $d.DeviceID, $label, $free, $default)
}
$DriveChoice = $null
while (-not $DriveChoice) {
$answer = Read-Host "Drive number or letter [$SystemDrive]"
if ([string]::IsNullOrWhiteSpace($answer)) { $answer = $SystemDrive }
$answer = $answer.Trim()
if ($answer -match '^\d+$' -and [int]$answer -ge 1 -and [int]$answer -le $Drives.Count) {
$DriveChoice = $Drives[[int]$answer - 1]
} else {
$letter = $answer.TrimEnd(':', '\').ToUpper() + ":"
$DriveChoice = $Drives | Where-Object { $_.DeviceID -eq $letter } | Select-Object -First 1
}
if (-not $DriveChoice) { Write-Host " '$answer' is not one of the drives above, try again." -ForegroundColor Yellow }
}
if ($DriveChoice.FreeSpace -lt 3GB) {
Write-Host "==> Warning: only $([math]::Round($DriveChoice.FreeSpace / 1GB, 1)) GB free on $($DriveChoice.DeviceID), the install may run out of space." -ForegroundColor Yellow
}
# System drive keeps the old location so existing installs are found again; other drives get a top-level folder
if ($DriveChoice.DeviceID -eq $SystemDrive) {
$InstallDir = Join-Path $env:USERPROFILE "mc-lan-party"
} else {
$InstallDir = Join-Path "$($DriveChoice.DeviceID)\" "mc-lan-party"
}
New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null
Set-Location $InstallDir
Write-Host "==> Setting up in $InstallDir"
$release = Invoke-RestMethod -Uri "https://api.github.com/repos/hero-persson/FjordLauncherUnlocked/releases/latest"
$asset = $release.assets | Where-Object { $_.name -match "Windows-MSVC-Portable-.*\.zip$" } | Select-Object -First 1
if (-not $asset) {
$asset = $release.assets | Where-Object { $_.name -match "Windows-MinGW-w64-Portable-.*\.zip$" } | Select-Object -First 1
}
if (-not $asset) { throw "Could not find a Windows portable build in the latest Fjord Launcher Unlocked release." }
Write-Host "==> Downloading $($asset.name)"
Invoke-WebRequest -Uri $asset.browser_download_url -OutFile "FjordLauncher.zip"
Expand-Archive -Path "FjordLauncher.zip" -DestinationPath "app" -Force
$LauncherBin = Get-ChildItem -Path "app" -Filter "fjordlauncher.exe" -Recurse | Select-Object -First 1 -ExpandProperty FullName
if (-not $LauncherBin) { throw "fjordlauncher.exe not found after extracting the portable build." }
# ---- offline account: compute Mojang-style offline UUID so playerdata persists across launches ----
$Nick = Read-Host "Minecraft nickname [$env:USERNAME]"
if ([string]::IsNullOrWhiteSpace($Nick)) { $Nick = $env:USERNAME }
function Get-OfflineUuid([string]$name) {
$md5 = [System.Security.Cryptography.MD5]::Create()
$bytes = $md5.ComputeHash([System.Text.Encoding]::UTF8.GetBytes("OfflinePlayer:$name"))
$bytes[6] = ($bytes[6] -band 0x0f) -bor 0x30
$bytes[8] = ($bytes[8] -band 0x3f) -bor 0x80
$hex = -join ($bytes | ForEach-Object { $_.ToString("x2") })
return "{0}-{1}-{2}-{3}-{4}" -f $hex.Substring(0,8), $hex.Substring(8,4), $hex.Substring(12,4), $hex.Substring(16,4), $hex.Substring(20,12)
}
$Uuid = Get-OfflineUuid $Nick
$md5 = [System.Security.Cryptography.MD5]::Create()
$clientTokenBytes = $md5.ComputeHash([System.Text.Encoding]::UTF8.GetBytes("ClientToken:$Nick"))
$ClientToken = -join ($clientTokenBytes | ForEach-Object { $_.ToString("x2") })
$Iat = [int][double]::Parse((Get-Date -UFormat %s))
$accounts = @{
accounts = @(
@{
active = $true
type = "Offline"
profile = @{ id = $Uuid; name = $Nick; capes = @(); skin = @{ id = ""; url = ""; variant = "" } }
entitlement = @{ canPlayMinecraft = $true; ownsMinecraft = $true }
ygg = @{ token = "0"; extra = @{ clientToken = $ClientToken; userName = $Nick }; iat = $Iat }
}
)
formatVersion = 3
}
$accounts | ConvertTo-Json -Depth 6 | Set-Content -Path "accounts.json" -Encoding UTF8
Write-Host "==> Offline account '$Nick' ready (UUID $Uuid)"
# ---- modpack instance ----
New-Item -ItemType Directory -Force -Path "instances" | Out-Null
if (-not (Test-Path (Join-Path "instances" $InstanceName))) {
Write-Host "==> Downloading modpack from $ModpackUrl"
try {
Invoke-WebRequest -Uri $ModpackUrl -OutFile "modpack.zip"
Expand-Archive -Path "modpack.zip" -DestinationPath "instances" -Force
} catch {
Write-Host "==> FAILED to fetch/extract the modpack from $ModpackUrl" -ForegroundColor Red
Write-Host " $($_.Exception.Message)" -ForegroundColor Red
Write-Host " (Fjord Launcher itself installed fine at $InstallDir\app — this just means modpack.zip isn't being served yet.)"
exit 1
}
Write-Host "==> Modpack installed to instances\$InstanceName"
} else {
Write-Host "==> Instance already present, skipping download"
}
# Desktop shortcut so the game is findable after closing it, without re-running this script
$ShortcutPath = Join-Path ([Environment]::GetFolderPath("Desktop")) "LAN Party Minecraft.lnk"
$WshShell = New-Object -ComObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut($ShortcutPath)
$Shortcut.TargetPath = $LauncherBin
$Shortcut.Arguments = "--dir `"$InstallDir`" --launch $InstanceName --profile `"$Nick`" --server $ServerAddr"
$Shortcut.WorkingDirectory = Split-Path $LauncherBin
$Shortcut.IconLocation = $LauncherBin
$Shortcut.Save()
Write-Host "==> Desktop shortcut created: $ShortcutPath (double-click this next time instead of re-running the install script)"
Write-Host "==> Launching into $InstanceName as $Nick, joining $ServerAddr"
& $LauncherBin --dir $InstallDir --launch $InstanceName --profile $Nick --server $ServerAddr