2
\$\begingroup\$

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."
 }
 }
}
asked Sep 10, 2018 at 17:38
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

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.

answered Sep 25, 2018 at 9:59
\$\endgroup\$
2
  • \$\begingroup\$ Hi Kirill, you should explain why using Write-Host is a better solution as this is pretty much the goal of CodeReview. \$\endgroup\$ Commented Sep 25, 2018 at 13:28
  • \$\begingroup\$ @IEatBagels, fair enough \$\endgroup\$ Commented Sep 25, 2018 at 13:33

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.