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 "<="}) {
Write-Output "Copying from cloud: $($PSItem.InputObject.Name)"
Copy-Item -Path $PSItem.InputObject.FullName -Destination $minecraftLocalSave -Container -Recurse
}
( {$PSItem.SideIndicator -eq "=>"}) {
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."
}
}
}