Files
2026-09-28 16:52:30 +00:00

131 lines
5.2 KiB
PowerShell

<#
.SYNOPSIS
Collects every mod currently enabled in tModLoader (Windows) into one folder,
ready to copy into /opt/terraria/<instance>/data/ on the Linux server.
.DESCRIPTION
Output layout (mirrors the server's data/ folder):
<Out>\Mods\*.tmod
<Out>\Mods\enabled.json (only lists mods that were actually found)
<Out>\ModConfigs\*.json
.tmod files are taken from Steam's Workshop folder (all Steam libraries are
searched), falling back to Documents\My Games\Terraria\tModLoader\Mods for
mods you installed manually or built yourself.
.PARAMETER Out
Where to put the export. Default: Desktop\tmod-export
.PARAMETER TmlDir
tModLoader's save folder. Default: Documents\My Games\Terraria\tModLoader
For the preview branch use ...\My Games\Terraria\tModLoader-preview
.PARAMETER MaxVersion
Optional. Your tModLoader version as year.month (e.g. 2025.3), shown at the
bottom-left of the tModLoader main menu. Workshop builds made for a newer
tModLoader than this are skipped.
.EXAMPLE
powershell -ExecutionPolicy Bypass -File .\export-tmod-mods.ps1
powershell -ExecutionPolicy Bypass -File .\export-tmod-mods.ps1 -MaxVersion 2025.3
#>
param(
[string]$Out = (Join-Path ([Environment]::GetFolderPath('Desktop')) 'tmod-export'),
[string]$TmlDir = (Join-Path ([Environment]::GetFolderPath('MyDocuments')) 'My Games\Terraria\tModLoader'),
[version]$MaxVersion
)
$ErrorActionPreference = 'Stop'
# --- Which mods are enabled? ------------------------------------------------
$enabledJson = Join-Path $TmlDir 'Mods\enabled.json'
if (-not (Test-Path $enabledJson)) {
throw "Can't find $enabledJson. Has tModLoader been run? For the preview branch pass -TmlDir ...\tModLoader-preview"
}
$enabled = @(Get-Content $enabledJson -Raw | ConvertFrom-Json)
if ($enabled.Count -eq 0) { throw "No mods are enabled in $enabledJson" }
# --- Find Steam Workshop folders for tModLoader (app 1281930) ----------------
$steamPath = (Get-ItemProperty 'HKCU:\Software\Valve\Steam' -ErrorAction SilentlyContinue).SteamPath
if (-not $steamPath) { $steamPath = 'C:\Program Files (x86)\Steam' }
$libraries = @($steamPath)
$vdf = Join-Path $steamPath 'steamapps\libraryfolders.vdf'
if (Test-Path $vdf) {
$libraries += Select-String -Path $vdf -Pattern '"path"\s+"([^"]+)"' |
ForEach-Object { $_.Matches[0].Groups[1].Value -replace '\\\\', '\' }
}
$workshopDirs = @($libraries |
ForEach-Object { Join-Path $_ 'steamapps\workshop\content\1281930' } |
Where-Object { Test-Path $_ } |
ForEach-Object { (Resolve-Path $_).Path } |
Sort-Object -Unique)
if ($workshopDirs.Count -eq 0) { Write-Warning "No Steam Workshop folder for tModLoader found; only local mods will be exported." }
function Get-FolderVersion([string]$dir) {
# Workshop items keep builds in folders named after the tModLoader version, e.g. 2024.3
$v = $null
if ([version]::TryParse((Split-Path $dir -Leaf), [ref]$v)) { return $v }
return $null
}
# --- Collect ------------------------------------------------------------------
$modsOut = Join-Path $Out 'Mods'
New-Item -ItemType Directory -Force -Path $modsOut | Out-Null
$exported = @()
$missing = @()
foreach ($mod in $enabled) {
$candidates = @()
foreach ($w in $workshopDirs) {
$candidates += @(Get-ChildItem -Path $w -Recurse -File -Filter "$mod.tmod" -ErrorAction SilentlyContinue)
}
$pick = $null
if ($candidates.Count -gt 0) {
$withVer = $candidates | ForEach-Object {
[pscustomobject]@{ File = $_; Version = (Get-FolderVersion $_.DirectoryName) }
}
# Newest version folder that isn't newer than -MaxVersion
$best = @($withVer |
Where-Object { $_.Version -and (-not $MaxVersion -or $_.Version -le $MaxVersion) } |
Sort-Object Version -Descending)
if ($best.Count -gt 0) {
$pick = $best[0].File
} else {
# Build stored at the item's top level (no version folder)
$root = @($withVer | Where-Object { -not $_.Version } |
Sort-Object { $_.File.LastWriteTime } -Descending)
if ($root.Count -gt 0) { $pick = $root[0].File }
}
}
if (-not $pick) {
$local = Join-Path $TmlDir "Mods\$mod.tmod"
if (Test-Path $local) { $pick = Get-Item $local }
}
if ($pick) {
Copy-Item $pick.FullName $modsOut -Force
$exported += $mod
Write-Host (" {0,-28} {1}" -f $mod, $pick.FullName)
} else {
$missing += $mod
Write-Warning "$mod is enabled but no .tmod file was found; skipped."
}
}
# enabled.json listing only what we exported (a missing mod would stop the server loading)
$json = ConvertTo-Json -InputObject @($exported)
[IO.File]::WriteAllText((Join-Path $modsOut 'enabled.json'), $json, (New-Object Text.UTF8Encoding $false))
# Mod settings (server-side configs matter; client-side ones are harmless)
$cfgSrc = Join-Path $TmlDir 'ModConfigs'
if (Test-Path $cfgSrc) {
$cfgOut = Join-Path $Out 'ModConfigs'
New-Item -ItemType Directory -Force -Path $cfgOut | Out-Null
Copy-Item (Join-Path $cfgSrc '*') $cfgOut -Recurse -Force
}
Write-Host ""
Write-Host "Exported $($exported.Count) mod(s) to $Out"
if ($missing.Count -gt 0) { Write-Warning "Missing: $($missing -join ', ')" }