Powershell for syncing Minecraft save files My code in full plus the task scheduler definition: https://gist.github.com/DaneWeber/0c5e7978bd3927734173e3afdc3d6338

This is my first non-trivial PowerShell script. This was a pretty major learning experience. I only went down this path once I discovered that xcopy wouldn't be sufficient.

My Ask: What do I not know that I don't know? That is, what about this strikes you as weird, clunky, or "not the PowerShell way"?

Problem I am solving: I want Minecraft worlds sync'ed across two (or more) computers. It doesn't seem like I should have to pay for an additional service to accomplish this.

Other solutions attempted:


Symlink/Junction of the Minecraft minecraftWorlds folder with one in OneDrive. Minecraft seemed to be unable/unwilling to see the data.
xcopy /d to update files. The problem is that Minecraft renames the files every save, so each save results in another large file, growing out of control.


Solution approach:


If any world exists in one location (local or cloud) but not the other: copy it.
If one location's world contains a file newer than all other files in the other location, replace the entire world with the newer one. (You have to be careful not to consider folders, since they have a modified date of their creation, even when you copy a folder.)
In case the script runs while the cloud service is still downloading data, copy files from the cloud if there are ones missing locally.


The script:

$minecraftSaveFolder = "minecraftWorlds"
$minecraftLocalPath = $env:LOCALAPPDATA + "\Packages\Microsoft.MinecraftUWP_8wekyb3d8bbwe\LocalState\games\com.mojang"
$minecraftLocalSave = $minecraftLocalPath + "\" + $minecraftSaveFolder
$minecraftCloudSave = $PSScriptRoot + "\MinecraftBackup"

$cloudWorlds = Get-ChildItem $minecraftCloudSave
$localWorlds = Get-ChildItem $minecraftLocalSave

$saves = Compare-Object $cloudWorlds $localWorlds -IncludeEqual

switch ($saves) {
  ( {$PSItem.SideIndicator -eq "&lt;="}) {
    Write-Output "Copying from cloud: $($PSItem.InputObject.Name)"
    Copy-Item -Path $PSItem.InputObject.FullName -Destination $minecraftLocalSave -Container -Recurse
  }
  ( {$PSItem.SideIndicator -eq "=&gt;"}) {
    Write-Output "Copying to cloud: $($PSItem.InputObject.Name)"
    Copy-Item -Path $PSItem.InputObject.FullName -Destination $minecraftCloudSave -Container -Recurse
  }
  ( {$PSItem.SideIndicator -eq "=="}) {
    $cloudLatest = (( Get-ChildItem ( $minecraftCloudSave + "\" + $PSItem.InputObject) -Recurse -File ).LastWriteTime | Measure-Object -Maximum)
    $localLatest = (( Get-ChildItem ( $minecraftLocalSave + "\" + $PSItem.InputObject) -Recurse -File ).LastWriteTime | Measure-Object -Maximum)
    if ($cloudLatest.Maximum -eq $localLatest.Maximum) {
      if ($cloudLatest.Count -gt $localLatest.Count) {
        Write-Output "Adding files from cloud: $($PSItem.InputObject.Name)"
        xcopy ($minecraftCloudSave + "\" + $PSItem.InputObject) $minecraftLocalSave /d /e /c /i /h /y
      }
      else {
        Write-Output "Already synchronized: $($PSItem.InputObject.Name)"
      }
    }
    elseif ($cloudLatest.Maximum -gt $localLatest.Maximum) {
      $world = $PSItem.InputObject.Name
      Write-Output "Over-writing local with $world"
      $old = Rename-Item -Path "$minecraftLocalSave\$world" -NewName ($world + "_old") -PassThru
      Copy-Item -Path "$minecraftCloudSave\$world" -Destination $minecraftLocalSave -Container -Recurse
      Remove-Item -Recurse -Force -Path $old
    }
    elseif ($cloudLatest.Maximum -lt $localLatest.Maximum) {
      $world = $PSItem.InputObject.Name
      Write-Output "Updating cloud with $world"
      $old = Rename-Item -Path "$minecraftCloudSave\$world" -NewName ($world + "_old") -PassThru
      Copy-Item -Path "$minecraftLocalSave\$world" -Destination $minecraftCloudSave -Container -Recurse
      Remove-Item -Recurse -Force -Path $old
    }
    else {
      Write-Error "Something went wrong with the folder comparison."
    }
  }
}
Looks fine for me. As a note - instead of using Write-Output for display purposes use Write-Host cmdlet. 

In a nutshell, Write-Host writes to the console itself. Think of it as a MsgBox in VBScript. Write-Output, on the other hand, writes to the pipeline, so the next command can accept it as its input. You are not required to use Write-Output in order to write objects, as Write-Output is implicitly called for you.