# wDayzManager v0.2 # Copyright (c) 2024 Vladislav Salikov aka W0LF aka 'dreamforce' # https://github.com/dreamforceinc # Required module: PSIni # Installation: Install-Module -Name PsIni [CmdletBinding()] param ( [Parameter()] [int]$param = 0 ) [bool]$update = $param [bool]$noDelete = $false # For tests - don't delete logs ############################################################################################################################################################### [string]$myName = "wDayzManager" [string]$steamScript = $myName + ".txt" [string]$iniFile = $myName + ".ini" if (-not (Test-Path -Path $iniFile)) { Write-Warning "Can't find file '$iniFile', creating new..." New-Item -ItemType File -Path $iniFile -Value "; $($iniFile)" | Out-Null $content = " [Config] serverLocation=Z:\Servers\DayzServer profilesLocation=profiles steamCMD=Z:\SteamCMD steamUser=SupaMegaGamer appidGame=221100 appidServer=223350 rotateLogs=1 daysAmount=7 rotatedLogsFolderName=RotatedLogs becFolderName=BEC atFolderName=VPPAdminTools ; Server updating settings: ; D = Daily - Updates are checked daily at the hours specified in parameter 'updateHour' (0 - 23). In this case, parameter 'updateDay' is ignored. ; W = Weekly - Updates are checked weekly at the hours specified in parameter 'updateHour' (0 - 23). Parameter 'updateDay' specifies the day of the week (0 - 6, 0 = Sunday). ; M = Monthly - Updates are checked monthly at the hours specified in parameter 'updateHour' (0 - 23). Parameter 'updateDay' specifies the day of the month (1 - 31. Be careful with February!). ; Any other value disables the updates. updatePeriod=W updateDay=1 updateHour=4 [ClientMods] @CF=1559212036 @Namalsk_Island=2288339650 @Namalsk_Survival=2288336145 [ServerMods] [Server-1] serverPort=2302 serverConfig=serverDZ.cfg serverExeName=DayZServer_x64.exe serverCPU=2 startupClientMods=@CF;@Namalsk_Island;@Namalsk_Survival startupServerMods= additionalParams= -name=Server ; Mission name for Namalsk Mod ; This is the name of the Namalsk mod mission. It can be 'Hardcore' or 'Regular', i.e. the names of folders in the '@Namalsk_Survival\Extras\' directory. ; If you are not using Namalsk mod, this parameter is ignored. namalskMission=Hardcore [Server-2] serverPort=2402 serverConfig=serverDZ.cfg serverExeName=DayZServer_x64.exe serverCPU=2 startupClientMods=@CF startupServerMods= additionalParams= -name=Server -AdminLog -FilePatching namalskMission= " Add-Content -Path $iniFile -Value $content -Encoding UTF8 $content = $null Exit } $date = Get-Date [int]$currentDayOfWeek = ([datetime]$date).DayOfWeek [int]$currentDay = ([datetime]$date).Day [int]$currentHour = ([datetime]$date).Hour $date2 = Get-Date -Format 's' | ForEach-Object { $_ -replace 'T', ' ' } Write-Host "$($date2) Starting wDayzManager script" Write-Host "update = $($update), noDelete = $($noDelete)" $ini = $config = $servers = $clientMods = $serverMods = $steamProcess = $null $modNI = $modNS = $namalskMission = $pathNamalskMission = $null $dayzProcess = $becProcess = $instance = $profilesLocation = $null $becLocation = $rotatedLogsFolderName = $atFolderName = $serverLocation = $null $steamCMD = $steamUser = $appidServer = $appidGame = $rotateLogs = $daysAmount = $null $serverPort = $serverConfig = $serverExeName = $serverCPU = $null $startupClientMods = $startupServerMods = $startupMods = $startupParams = $null $serverProfile = $additionalParams = $dayzArguments = $dayz = $bec = $becArguments = $null $logsName = $atLogsLocation = $becLogsLocation = $rotatedLogsLocation = $null $currentPath = Get-Location $ini = (Get-IniContent -FilePath $iniFile) $config = $ini.Config [string]$serverLocation = $config.serverLocation [string]$profilesLocation = $config.profilesLocation [string]$steamCMD = $config.steamCMD [string]$steamUser = $config.steamUser [int]$appidGame = $config.appidGame [int]$appidServer = $config.appidServer [int]$updateDay = $config.updateDay [int]$updateHour = $config.updateHour [string]$updatePeriod = $config.updatePeriod [bool]$rotateLogs = [int]$config.rotateLogs $clientMods = New-Object 'Collections.Generic.List[Tuple[string,string]]' foreach ($mod in $ini.ClientMods.GetEnumerator()) { if ($mod.Key -notlike "Comment*" ) { $clientMods.Add([Tuple]::Create($mod.Key, $mod.Value)) } } $serverMods = New-Object 'Collections.Generic.List[Tuple[string,string]]' foreach ($mod in $ini.ServerMods.GetEnumerator()) { if ($mod.Key -notlike "Comment*" ) { $serverMods.Add([Tuple]::Create($mod.Key, $mod.Value)) } } switch ($updatePeriod) { D { if ($currentHour -eq $updateHour) { $update = $true } } W { if (($currentDayOfWeek -eq $updateDay) -and ($currentHour -eq $updateHour)) { $update = $true } } M { if (($currentDay -eq $updateDay) -and ($currentHour -eq $updateHour)) { $update = $true } } Default { $update = $param } } if ($update) { If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { Write-Warning "You do not have Administrator rights to run this script!`nPlease re-run this script as an Administrator!" Exit } Write-Host "Start updating server..." if (-not (Test-Path $steamCMD)) { Write-Error "Can't find folder '$steamCMD'!" Exit } # Making Steam script $arr = @() $arr += "@NoPromptForPassword 1" $arr += "@ShutdownOnFailedCommand 1" $arr += "force_install_dir `"$($serverLocation)`"" $arr += "login $($steamUser)" $arr += "app_update $($appidServer) validate" $clientMods | ForEach-Object { $arr += "workshop_download_item $($appidGame) $($_.Item2) validate" } $serverMods | ForEach-Object { $arr += "workshop_download_item $($appidGame) $($_.Item2) validate" } $arr += "quit" # Saving Steam script "// $($steamScript) - Steam script" | Out-File -FilePath "$($currentPath)\$($steamScript)" -Encoding Ascii $arr | Out-File -FilePath "$($currentPath)\$($steamScript)" -Encoding Ascii -Append # Run Steam script Write-Host "Run Steam script from `"$($currentPath)\$($steamScript)`"" $steamProcess = Start-Process -FilePath "$($steamCMD)\steamcmd.exe" -ArgumentList "+runscript $($currentPath)\$($steamScript) +quit" -PassThru -Wait -NoNewWindow if ($steamProcess.ExitCode -ne 0) { Write-Error "$_ failed with error code $($steamProcess.ExitCode)" Exit } Write-Host # Making symbolic links to mods and copying mod's keys Write-Host "Making symbolic links to mods:" $clientMods | ForEach-Object { New-Item -ItemType SymbolicLink -Path "$($serverLocation)\$($_.Item1)" -Target "$($serverLocation)\steamapps\workshop\content\$($appidGame)\$($_.Item2)" -Force Copy-Item -Path "$($serverLocation)\$($_.Item1)\Keys\*.bikey" -Destination "$($serverLocation)\keys\" -Force -ErrorAction SilentlyContinue } $serverMods | ForEach-Object { New-Item -ItemType SymbolicLink -Path "$($serverLocation)\$($_.Item1)" -Target "$($serverLocation)\steamapps\workshop\content\$($appidGame)\$($_.Item2)" -Force Copy-Item -Path "$($serverLocation)\$($_.Item1)\Keys\*.bikey" -Destination "$($serverLocation)\keys\" -Force -ErrorAction SilentlyContinue } Start-Sleep -Seconds 1 Write-Host # Patching Namalsk [bool]$isNamalskIslandPresent = $false [bool]$isNamalskSurvivalPresent = $false $clientMods.Item1 | ForEach-Object { if ($_ -like "*namalsk*" -and $_ -like "*island*") { $isNamalskIslandPresent = $true $modNI = $_ Write-Host "Patching Namalsk Island server meta.cpp file ..." $whatFind_NI = "2288339650" $replaceWith_NI = "2289456201" $content = Get-Content -Path "$($serverLocation)\$($modNI)\meta.cpp" $content = $content -replace $whatFind_NI, $replaceWith_NI Set-Content -Path "$($serverLocation)\$($modNI)\meta.cpp" -Value $content } if ($_ -like "*namalsk*" -and $_ -like "*survival*") { $isNamalskSurvivalPresent = $true $modNS = $_ Write-Host "Patching Namalsk Survival server meta.cpp file ..." $whatFind_NS = "2288336145" $replaceWith_NS = "2289461232" $content = Get-Content -Path "$($serverLocation)\$($modNS)\meta.cpp" $content = $content -replace $whatFind_NS, $replaceWith_NS Set-Content -Path "$($serverLocation)\$($modNS)\meta.cpp" -Value $content } } # # Customizing servers # # Overwriting default messages.xml from Namalsk by custom file # if (Test-Path -Path "$($serverLocation)\.customs") { # Copy-Item -Path "$($serverLocation)\.customs\messages.xml" -Destination "$($serverLocation)\mpmissions\$($nPath2)\db\" # } Write-Host "End of updating server." Start-Sleep -Seconds 1 } Write-Host if ($rotateLogs) { $daysAmount = [Math]::Abs($config.daysAmount) if (-not ([string]::IsNullOrEmpty($config.rotatedLogsFolderName))) { $rotatedLogsFolderName = $config.rotatedLogsFolderName Write-Host "Removing old wDayzManager logs..." $fileList = $null $fileList = Get-ChildItem -Path $currentPath\*.log -Recurse | Where-Object { $_.LastWriteTime.Date -lt $date.AddDays( -3) } # Write-Host $fileList -Separator "`n" foreach ($file in $fileList) { Remove-Item -Path $file Write-Host "Removed $($file)!" } Write-Host "Total: $($fileList.Count)" Write-Host } else { Write-Error "The 'rotatedLogsFolderName' parameter not set!" Exit } } if (-not ([string]::IsNullOrEmpty($config.becFolderName))) { $becFolderName = $config.becFolderName $becLocation = "$($serverLocation)\$($becFolderName)" } if (-not ([string]::IsNullOrEmpty($config.atFolderName))) { $atFolderName = $config.atFolderName } $servers += $ini.Keys.Where({ $_ -Match "^[Ss]erver[\s_-]?[0-9]+$" }) $processID = 0 $dayzProcess = [Object[]]::new($servers.Count) $dayzArguments = @() if ($becLocation) { $becProcess = [Object[]]::new($servers.Count) } foreach ($srv in $servers) { $instance = ${srv} $serverPort = $ini.${srv}.serverPort $serverConfig = $ini.${srv}.serverConfig $serverExeName = $ini.${srv}.serverExeName $serverCPU = $ini.${srv}.serverCPU $startupClientMods = $ini.${srv}.startupClientMods $startupServerMods = $ini.${srv}.startupServerMods $additionalParams = $ini.${srv}.additionalParams $cm = $startupClientMods -Split ';' if ($cm -contains $modNS) { $namalskMission = $ini.${srv}.namalskMission if (($namalskMission -eq "regular") -or ($namalskMission -eq "hardcore")) { $pathNamalskMission = "$($namalskMission)\$($namalskMission).namalsk" $namalskMission += ".namalsk" if (-not (Test-Path -Path "$($serverLocation)\mpmissions\$($namalskMission)\")) { Write-Host "Copying $($namalskMission)..." Copy-Item -Path "$($serverLocation)\$($modNS)\Extras\$($pathNamalskMission)\" -Destination "$($serverLocation)\mpmissions\" -Recurse -Force Start-Sleep -Seconds 1 } } else { Write-Error "Error in Namalsk mission definition: '$($namalskMission)'" Exit } } # Checks for profile location if ([System.IO.Path]::IsPathRooted($profilesLocation)) { $serverProfile = "$($profilesLocation)\$($instance)" } else { $serverProfile = "$($serverLocation)\$($profilesLocation)\$($instance)" } $serverProfile = [System.IO.Path]::GetFullPath($serverProfile) Write-Host "serverProfile: $($serverProfile)" $startupMods = @("`"-mod=$($startupClientMods)`"", "`"-serverMod=$($startupServerMods)`"") $startupParams = @("`"-profiles=$($serverProfile)`"", "`"-config=$($serverProfile)\$($serverConfig)`"", "-port=$($serverPort)", "-cpuCount=$($serverCPU)") $dayzArguments = $startupParams + $startupMods + $additionalParams $dayz = "`"$($serverLocation)\$($serverExeName)`"" $bec = "`"$($becLocation)\bec.exe`"" $becArguments = "-f `"$($instance).cfg`" --dsc" # Rotate logs if ($rotateLogs) { if ($rotatedLogsFolderName) { $rotatedLogsLocation = "$($serverProfile)\$($rotatedLogsFolderName)" } if ($becLocation) { $becLogsLocation = "$($becLocation)\Log\$($instance)" } if ($atFolderName) { $atLogsLocation = "$($serverProfile)\$($atFolderName)" $logsName = Get-ChildItem -Path $atLogsLocation -Directory -Filter "Log*" $atLogsLocation += '\' + $logsName } If (!(Test-Path -PathType Container $rotatedLogsLocation)) { New-Item -ItemType Directory -Path $rotatedLogsLocation | Out-Null Write-Host "Created new folder: $($rotatedLogsLocation)" } if ($noDelete) { $destDelDir = "$($rotatedLogsLocation)\DeletedLogs" If (!(Test-Path -PathType Container $destDelDir)) { New-Item -ItemType Directory -Path $destDelDir | Out-Null Write-Host "Created new folder: $($destDelDir)" } } #region ### DayZ Game ### Write-Host "Rotating DAYZ logs..." $fileList = $null $fileList = Get-Item -Path $serverProfile\*.rpt, $serverProfile\*.log, $serverProfile\*.adm, $serverProfile\*.mdmp | Where-Object { $_.LastWriteTime.Date -lt $date.Date } # Write-Host $fileList -Separator "`n" Write-Host "Moving DayZ logs to $($rotatedLogsLocation):" foreach ($file in $fileList) { Move-Item -Path $file -Destination $rotatedLogsLocation -Force Write-Host "Moved $($file) to $($rotatedLogsLocation)" } Write-Host "Total: $($fileList.Count)" $fileList = Get-Item -Path $rotatedLogsLocation\*.rpt, $rotatedLogsLocation\*.log, $rotatedLogsLocation\*.adm, $rotatedLogsLocation\*.mdmp | Where-Object { $_.LastWriteTime -lt $date.AddDays( - ($daysAmount)) } # Write-Host $fileList -Separator "`n" Write-Host "Removing DayZ logs:" foreach ($file in $fileList) { if ($noDelete) { Move-Item -Path $file -Destination $destDelDir -Force Write-Host "Moved $($file) to $($destDelDir)" } else { Remove-Item -Path $file Write-Host "Removed $($file)!" } } Write-Host "Total: $($fileList.Count)" Write-Host #endregion #region ### BEC ### if ($becLocation) { $becDestDir = "$($rotatedLogsLocation)\$($becFolderName)" If (!(Test-Path -PathType Container $becDestDir)) { New-Item -ItemType Directory -Path $becDestDir | Out-Null Write-Host "Created new folder: $($becDestDir)" } Write-Host "Rotating BEC logs..." $fileList = $null $fileList = Get-ChildItem -Path $becLogsLocation\*.log -Recurse | Where-Object { $_.LastWriteTime.Date -lt $date.Date } # Write-Host $fileList -Separator "`n" Write-Host "Moving BEC logs to $($becDestDir):" foreach ($file in $fileList) { Move-Item -Path $file -Destination $becDestDir -Force Write-Host "Moved $($file) to $($becDestDir)" } Write-Host "Total: $($fileList.Count)" $fileList = Get-Item -Path $becDestDir\*.log | Where-Object { $_.LastWriteTime -lt $date.AddDays( - ($daysAmount)) } # Write-Host $fileList -Separator "`n" Write-Host "Removing BEC logs:" foreach ($file in $fileList) { if ($noDelete) { Move-Item -Path $file -Destination $destDelDir -Force Write-Host "Moved $($file) to $($destDelDir)" } else { Remove-Item -Path $file Write-Host "Removed $($file)!" } } Write-Host "Total: $($fileList.Count)" Write-Host } #endregion #region ### Admin Tool ### if ($atLogsLocation) { $atDestDir = "$($rotatedLogsLocation)\$($atFolderName)" If (!(Test-Path -PathType Container $atDestDir)) { New-Item -ItemType Directory -Path $atDestDir | Out-Null Write-Host "Created new folder: $($atDestDir)" } Write-Host "Rotating AdminTool logs..." $fileList = $null $fileList = Get-ChildItem -Path $atLogsLocation\*.txt, $atLogsLocation\*.log -Recurse | Where-Object { $_.LastWriteTime.Date -lt $date.Date } # Write-Host $fileList -Separator "`n" Write-Host "Moving AdminTool logs to $($atDestDir):" foreach ($file in $fileList) { Move-Item -Path $file -Destination $atDestDir -Force Write-Host "Moved $($file) to $($atDestDir)" } Write-Host "Total: $($fileList.Count)" $fileList = Get-Item -Path $atDestDir\*.txt, $atDestDir\*.log | Where-Object { $_.LastWriteTime -lt $date.AddDays( - ($daysAmount)) } # Write-Host $fileList -Separator "`n" Write-Host "Removing AdminTool logs:" foreach ($file in $fileList) { if ($noDelete) { Move-Item -Path $file -Destination $destDelDir -Force Write-Host "Moved $($file) to $($destDelDir)" } else { Remove-Item -Path $file Write-Host "Removed $($file)!" } } Write-Host "Total: $($fileList.Count)" Write-Host } #endregion } # Write-Host " instance: $($instance)" # Write-Host " serverProfile: $($serverProfile)" # Write-Host " serverPort: $($serverPort)" # Write-Host " serverConfig: $($serverConfig)" # Write-Host " serverExeName: $($serverExeName)" # Write-Host " serverCPU: $($serverCPU)" # Write-Host " startupClientMods: $($startupClientMods)" # Write-Host " startupServerMods: $($startupServerMods)" # Write-Host " startupMods: $($startupMods)" # Write-Host " startupParams: $($startupParams)" # Write-Host " additionalParams: $($additionalParams)" # Write-Host " pathNamalskMission: $($pathNamalskMission)" # Write-Host " namalskMission: $($namalskMission)" # Write-Host # Write-Host " dayz: $($dayz)" # Write-Host " dayzArguments: $($dayzArguments)" # Write-Host # Write-Host " becLocation: $($becLocation)" # Write-Host " bec: $($bec)" # Write-Host " becArguments: $($becArguments)" # Write-Host # Write-Host " atFolderName: $($atFolderName)" # Write-Host # Write-Host " atLogsLocation: $($atLogsLocation)" # Write-Host " becLogsLocation: $($becLogsLocation)" # Write-Host # Write-Host " rotateLogs: $($rotateLogs)" # Write-Host " destDelDir: $($destDelDir)" # Write-Host " becDestDir: $($becDestDir)" # Write-Host " atDestDir: $($atDestDir)" # Write-Host # Write-Host "rotatedLogsFolderName: $($rotatedLogsFolderName)" # Write-Host " rotatedLogsLocation: $($rotatedLogsLocation)" # Write-Host " daysAmount: $($daysAmount)" # Write-Host " noDelete: $($noDelete)" # Starting servers Set-Location -Path "$($serverLocation)" $dayzProcess[$processID] = Start-Process -FilePath $dayz -ArgumentList $dayzArguments -PassThru # Write-Host "$($dayzProcess[$processID]) = Start-Process -FilePath $($dayz) -ArgumentList $($dayzArguments) -PassThru" # $dayzProcess[$processID] # Starting BEC if ($becLocation) { Set-Location -Path "$($becLocation)" $becProcess[$processID] = Start-Process -FilePath $bec -ArgumentList $becArguments -PassThru # Write-Host "$($becProcess[$processID]) = Start-Process -FilePath $($bec) -ArgumentList $($becArguments) -PassThru" # $becProcess[$processID] } $processID++ Write-Host Start-Sleep -Seconds 1 } Write-Host "End of wDayzManager script"