<# .SYNOPSIS Collects DayZ server + client logs (and the mod's live config) for debugging the YALootBoxes mod, then bundles them into one timestamped .zip that is easy to hand off. .DESCRIPTION Copies recent .RPT / .ADM / .log / .mdmp / crash logs from the server profile folder and the client log folder, plus the auto-generated profiles\YALootBoxes\*.json config. Produces: - server\ , client\ : the raw collected files - server\YALootBoxes_config\*.json: the mod's live config - YALootBoxes_filtered.txt : every "[YALootBoxes]" line, by file - summary.txt : machine info + ERROR/WARN counts + file list ...all inside YALootBoxes_logs_.zip on your Desktop. Handles files locked by a running server (shared-read copy). .EXAMPLE powershell -ExecutionPolicy Bypass -File .\Collect-YALootBoxesLogs.ps1 .EXAMPLE # widen the window to the last 48 hours powershell -ExecutionPolicy Bypass -File .\Collect-YALootBoxesLogs.ps1 -SinceHours 48 #> [CmdletBinding()] param( [string]$ServerProfile = 'C:\Program Files (x86)\Steam\steamapps\common\DayZServer\profiles', [string]$ClientLogs = 'C:\Users\drasi\AppData\Local\DayZ', [string]$OutDir = "$env:USERPROFILE\Desktop", [int] $SinceHours = 12, [string]$Tag = '[YALootBoxes]' ) $ErrorActionPreference = 'Stop' $stamp = Get-Date -Format 'yyyyMMdd_HHmmss' $stageRoot = Join-Path $env:TEMP "YALB_logs_$stamp" $stageServer = Join-Path $stageRoot 'server' $stageClient = Join-Path $stageRoot 'client' New-Item -ItemType Directory -Force -Path $stageServer, $stageClient | Out-Null $patterns = '*.RPT', '*.log', '*.ADM', '*.mdmp', 'crash_*.log', 'error.log', 'script*.log' $cutoff = (Get-Date).AddHours(-$SinceHours) # Copy a file even if a running server holds it open (shared read). function Copy-Locked { param([string]$Src, [string]$Dst) try { $in = [System.IO.File]::Open($Src, 'Open', 'Read', 'ReadWrite') try { $out = [System.IO.File]::Create($Dst) try { $in.CopyTo($out) } finally { $out.Dispose() } } finally { $in.Dispose() } return $true } catch { Write-Warning "Could not copy '$Src': $($_.Exception.Message)" return $false } } function Collect { param([string]$Source, [string]$Stage, [string]$Label) if (-not (Test-Path $Source)) { Write-Warning "$Label path not found: $Source" return @() } $files = Get-ChildItem -Path $Source -Recurse -File -Include $patterns -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -ge $cutoff } # Always include the newest .RPT even if it predates the cutoff window. if (-not ($files | Where-Object { $_.Extension -eq '.RPT' })) { $newestRpt = Get-ChildItem -Path $Source -Recurse -File -Filter '*.RPT' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1 if ($newestRpt) { $files = @($files) + $newestRpt } } $collected = @() foreach ($f in ($files | Sort-Object FullName -Unique)) { $dst = Join-Path $Stage $f.Name if (Test-Path $dst) { $dst = Join-Path $Stage ('{0}_{1}{2}' -f $f.BaseName, $f.LastWriteTime.ToString('HHmmss'), $f.Extension) } if (Copy-Locked $f.FullName $dst) { $collected += [pscustomobject]@{ Source = $Label; Name = $f.Name; Size = $f.Length; Modified = $f.LastWriteTime } } } return $collected } # --- collect server + client logs --- $serverFiles = @(Collect -Source $ServerProfile -Stage $stageServer -Label 'server') $clientFiles = @(Collect -Source $ClientLogs -Stage $stageClient -Label 'client') # --- grab the mod's live generated config ($profile:YALootBoxes\*.json) --- $modCfg = Join-Path $ServerProfile 'YALootBoxes' if (Test-Path $modCfg) { $cfgStage = Join-Path $stageServer 'YALootBoxes_config' New-Item -ItemType Directory -Force -Path $cfgStage | Out-Null Get-ChildItem $modCfg -Filter '*.json' -File -ErrorAction SilentlyContinue | ForEach-Object { Copy-Locked $_.FullName (Join-Path $cfgStage $_.Name) | Out-Null } } else { Write-Warning "Mod config folder not found (mod may not have run yet): $modCfg" } $all = $serverFiles + $clientFiles # --- extract only the mod-tagged lines into one file --- $filterPath = Join-Path $stageRoot 'YALootBoxes_filtered.txt' $sb = New-Object System.Text.StringBuilder $textFiles = Get-ChildItem -Path $stageServer, $stageClient -Recurse -File | Where-Object { $_.Extension -in '.RPT', '.log', '.ADM' } foreach ($tf in $textFiles) { $hits = Select-String -Path $tf.FullName -SimpleMatch -Pattern $Tag -ErrorAction SilentlyContinue if ($hits) { [void]$sb.AppendLine("===== $($tf.Name) ($($hits.Count) line(s)) =====") foreach ($h in $hits) { [void]$sb.AppendLine(('{0,6}: {1}' -f $h.LineNumber, $h.Line.Trim())) } [void]$sb.AppendLine('') } } if ($sb.Length -eq 0) { [void]$sb.AppendLine("No '$Tag' lines found in the collected logs.") } Set-Content -Path $filterPath -Value $sb.ToString() -Encoding UTF8 # --- summary --- $errCount = (Select-String -Path $filterPath -SimpleMatch -Pattern '[ERROR]' -ErrorAction SilentlyContinue).Count $warnCount = (Select-String -Path $filterPath -SimpleMatch -Pattern '[WARN]' -ErrorAction SilentlyContinue).Count $lines = @( 'YALootBoxes log bundle' "Generated: $(Get-Date -Format 'u')" "Machine: $env:COMPUTERNAME User: $env:USERNAME" "Window: last $SinceHours hour(s) Tag: $Tag" "Server src: $ServerProfile" "Client src: $ClientLogs" '' "Mod-tagged ERROR lines: $errCount" "Mod-tagged WARN lines: $warnCount" '' 'Files collected:' ) if ($all) { foreach ($f in $all) { $lines += (' [{0}] {1} ({2:N0} bytes, {3:yyyy-MM-dd HH:mm})' -f $f.Source, $f.Name, $f.Size, $f.Modified) } } else { $lines += ' (none - check the paths or widen -SinceHours)' } Set-Content -Path (Join-Path $stageRoot 'summary.txt') -Value $lines -Encoding UTF8 # --- zip + cleanup --- $zip = Join-Path $OutDir "YALootBoxes_logs_$stamp.zip" if (Test-Path $zip) { Remove-Item $zip -Force } Compress-Archive -Path (Join-Path $stageRoot '*') -DestinationPath $zip Remove-Item $stageRoot -Recurse -Force Write-Host '' Write-Host "Collected $($all.Count) log file(s)." -ForegroundColor Green Write-Host "Mod-tagged lines -> ERROR: $errCount WARN: $warnCount" Write-Host "Bundle: $zip" -ForegroundColor Cyan Write-Host 'Send me that .zip (summary.txt + YALootBoxes_filtered.txt inside are the key files).'