<# MidgardPlus installer - Odin the Red https://midgardplus.odinthered.dev Installs BepInEx (Valheim pack), Jotunn and MidgardPlus into your Valheim folder. Run: irm https://midgardplus.odinthered.dev/install.ps1 | iex Nothing here runs as admin, nothing touches the registry, and nothing is uninstalled. Every file it writes lives inside your Valheim folder. To undo it, delete BepInEx, winhttp.dll, doorstop_config.ini and doorstop_libs from that folder. Prefer a window with proper controls for all 29 settings? There is a launcher: https://midgardplus.odinthered.dev/MidgardPlusLauncher.exe #> $ErrorActionPreference = 'Stop' [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $BepInEx = 'https://thunderstore.io/package/download/denikson/BepInExPack_Valheim/5.4.2350/' $Jotunn = 'https://thunderstore.io/package/download/ValheimModding/Jotunn/2.30.0/' # Resolved from Thunderstore at run time rather than pinned, so this script cannot go stale # and start handing out an old build the way a hardcoded URL does. $ModApi = 'https://thunderstore.io/api/experimental/package/OdinTheRed/MidgardPlus/' $ModFallback = 'https://thunderstore.io/package/download/OdinTheRed/MidgardPlus/' function Say($msg, $colour = 'Gray') { Write-Host $msg -ForegroundColor $colour } Say '' Say ' MidgardPlus installer' Yellow Say ' by Odin the Red' DarkYellow Say '' # ---------------------------------------------------------------- find Valheim function Find-Valheim { # The usual place first. $guess = Join-Path ${env:ProgramFiles(x86)} 'Steam\steamapps\common\Valheim' if (Test-Path (Join-Path $guess 'valheim.exe')) { return $guess } # Otherwise ask Steam where its libraries are. $steam = $null foreach ($key in @('HKCU:\Software\Valve\Steam', 'HKLM:\SOFTWARE\WOW6432Node\Valve\Steam')) { try { $p = (Get-ItemProperty -Path $key -ErrorAction Stop).SteamPath if ($p) { $steam = $p.Replace('/', '\'); break } } catch { } } if (-not $steam) { return $null } $candidates = @(Join-Path $steam 'steamapps\common\Valheim') $vdf = Join-Path $steam 'steamapps\libraryfolders.vdf' if (Test-Path $vdf) { foreach ($line in Get-Content $vdf) { if ($line -match '"path"\s+"(.+?)"') { $lib = $matches[1].Replace('\\', '\') $candidates += (Join-Path $lib 'steamapps\common\Valheim') } } } foreach ($c in $candidates) { if (Test-Path (Join-Path $c 'valheim.exe')) { return $c } } return $null } $game = Find-Valheim if (-not $game) { Say ' Could not find Valheim automatically.' Red Say '' $game = Read-Host ' Paste the folder containing valheim.exe' $game = $game.Trim('"').Trim() if (-not (Test-Path (Join-Path $game 'valheim.exe'))) { Say ' No valheim.exe in that folder. Nothing was changed.' Red return } } Say " Valheim: $game" Green Say '' # ---------------------------------------------------------------- helpers $work = Join-Path $env:TEMP ("midgardplus-" + [Guid]::NewGuid().ToString('N').Substring(0, 8)) New-Item -ItemType Directory -Path $work -Force | Out-Null function Fetch($url, $name) { $out = Join-Path $work $name Say " Downloading $name ..." Invoke-WebRequest -Uri $url -OutFile $out -UseBasicParsing return $out } # Extracts a zip, turning any "\" in an entry name into "/". # # A Thunderstore package built on Windows can store paths with backslashes, which the ZIP # spec does not allow. Left alone that yields one file literally named "plugins\Mod.dll" # instead of a plugins folder, and nothing ever loads. function Unpack($zip, $name) { Add-Type -AssemblyName System.IO.Compression.FileSystem $dir = Join-Path $work ($name + '-x') New-Item -ItemType Directory -Path $dir -Force | Out-Null $archive = [IO.Compression.ZipFile]::OpenRead($zip) try { foreach ($entry in $archive.Entries) { $rel = $entry.FullName.Replace('\', '/').TrimStart('/') if (-not $rel) { continue } $full = Join-Path $dir ($rel -replace '/', '\') if ($rel.EndsWith('/') -or -not $entry.Name) { New-Item -ItemType Directory -Path $full -Force | Out-Null continue } $parent = Split-Path $full -Parent if ($parent) { New-Item -ItemType Directory -Path $parent -Force | Out-Null } [IO.Compression.ZipFileExtensions]::ExtractToFile($entry, $full, $true) } } finally { $archive.Dispose() } return $dir } try { # ------------------------------------------------------------ newest version $midUrl = $ModFallback $midVer = 'latest' try { $meta = Invoke-RestMethod -Uri $ModApi -UseBasicParsing if ($meta.latest.download_url) { $midUrl = $meta.latest.download_url $midVer = $meta.latest.version_number } } catch { Say ' Could not reach the Thunderstore API; taking the newest release instead.' DarkGray } $bepZip = Fetch $BepInEx 'BepInExPack_Valheim.zip' $jotZip = Fetch $Jotunn 'Jotunn.zip' $midZip = Fetch $midUrl 'MidgardPlus.zip' Say '' Say ' Installing ...' # --- BepInEx: the pack nests everything under BepInExPack_Valheim\ $bepDir = Unpack $bepZip 'bep' $inner = Join-Path $bepDir 'BepInExPack_Valheim' $src = if (Test-Path $inner) { $inner } else { $bepDir } Copy-Item -Path (Join-Path $src '*') -Destination $game -Recurse -Force Say ' BepInEx' DarkGray $plugins = Join-Path $game 'BepInEx\plugins' New-Item -ItemType Directory -Path $plugins -Force | Out-Null # --- Jotunn: plugins\Jotunn.dll $jotDir = Unpack $jotZip 'jot' $jotDll = Get-ChildItem -Path $jotDir -Filter 'Jotunn.dll' -Recurse | Select-Object -First 1 if (-not $jotDll) { throw 'Jotunn.dll was not in the download.' } Copy-Item $jotDll.FullName -Destination $plugins -Force Say ' Jotunn' DarkGray # --- MidgardPlus: its own folder, so uninstalling is one delete $midDir = Unpack $midZip 'mid' $midDll = Get-ChildItem -Path $midDir -Filter 'MidgardPlus.dll' -Recurse | Select-Object -First 1 if (-not $midDll) { throw 'MidgardPlus.dll was not in the download.' } $target = Join-Path $plugins 'MidgardPlus' New-Item -ItemType Directory -Path $target -Force | Out-Null Copy-Item $midDll.FullName -Destination $target -Force Say " MidgardPlus $midVer" DarkGray Say '' Say ' Done.' Green Say '' Say ' Start Valheim once, then edit your settings here:' Gray Say " $game\BepInEx\config\midgardplus.cfg" Yellow Say '' Say ' Or use the launcher, which gives every setting a proper control:' Gray Say ' https://midgardplus.odinthered.dev/MidgardPlusLauncher.exe' Yellow Say '' Say ' Everything starts at vanilla, so nothing changes until you turn it on.' DarkGray Say '' } catch { Say '' Say " Install failed: $($_.Exception.Message)" Red Say ' Nothing was left half-written outside your Valheim folder.' DarkGray Say ' Manual download: https://midgardplus.odinthered.dev' Gray Say '' } finally { Remove-Item $work -Recurse -Force -ErrorAction SilentlyContinue }