mirror of
https://github.com/EngineHub/WorldEdit.git
synced 2025-03-07 13:48:00 +08:00
Add force-deletion utilities for cleaning up dirs
Windows sucks.
This commit is contained in:
parent
624e416e9b
commit
176418bad1
@ -0,0 +1,23 @@
|
||||
package com.sk89q.worldedit.util;
|
||||
|
||||
public class ShutdownHook<V> implements AutoCloseable {
|
||||
|
||||
private final Thread hook;
|
||||
private final V value;
|
||||
|
||||
public ShutdownHook(Thread hook, V value) {
|
||||
this.hook = hook;
|
||||
this.value = value;
|
||||
|
||||
Runtime.getRuntime().addShutdownHook(hook);
|
||||
}
|
||||
|
||||
public V getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
Runtime.getRuntime().removeShutdownHook(hook);
|
||||
}
|
||||
}
|
@ -1,30 +1,34 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.io.file;
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.io.file;
|
||||
|
||||
import com.sk89q.worldedit.util.ShutdownHook;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class SafeFiles {
|
||||
|
||||
@ -64,6 +68,90 @@ private static String dropSlash(String name) {
|
||||
return name.substring(0, name.length() - 1);
|
||||
}
|
||||
|
||||
public static ShutdownHook<Path> tryHardToDeleteDirOnExit(Path directory) {
|
||||
Thread hook = new Thread(() -> {
|
||||
try {
|
||||
tryHardToDeleteDir(directory);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
});
|
||||
return new ShutdownHook<>(hook, directory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively uses {@link #tryHardToDelete(Path)} to cleanup directories before deleting them.
|
||||
*
|
||||
* @param directory the directory to delete
|
||||
* @throws IOException if an error occurs trying to delete the directory
|
||||
*/
|
||||
public static void tryHardToDeleteDir(Path directory) throws IOException {
|
||||
if (!Files.isDirectory(directory)) {
|
||||
if (!Files.exists(directory)) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new IOException(directory + " is not a directory");
|
||||
}
|
||||
try (Stream<Path> files = Files.list(directory)) {
|
||||
for (Iterator<Path> iter = files.iterator(); iter.hasNext(); ) {
|
||||
Path next = iter.next();
|
||||
if (Files.isDirectory(next)) {
|
||||
tryHardToDeleteDir(next);
|
||||
} else {
|
||||
tryHardToDelete(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
tryHardToDelete(directory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to delete a path. If it fails the first time, uses an implementation detail to try
|
||||
* and make it possible to delete the path, and then tries again. If that fails, throws an
|
||||
* {@link IOException} with both errors.
|
||||
*
|
||||
* @param path the path to delete
|
||||
* @throws IOException if the path could not be deleted after multiple attempts
|
||||
*/
|
||||
public static void tryHardToDelete(Path path) throws IOException {
|
||||
IOException suppressed = tryDelete(path);
|
||||
if (suppressed == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This is copied from Ant (see org.apache.tools.ant.util.FileUtils.tryHardToDelete).
|
||||
// It mentions that there is a bug in the Windows JDK implementations that this is a valid
|
||||
// workaround for. I've been unable to find a definitive reference to this bug.
|
||||
// The thinking is that if this is good enough for Ant, it's good enough for us.
|
||||
System.gc();
|
||||
try {
|
||||
Thread.sleep(10);
|
||||
} catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
IOException suppressed2 = tryDelete(path);
|
||||
if (suppressed2 == null) {
|
||||
return;
|
||||
}
|
||||
IOException ex = new IOException("Failed to delete " + path, suppressed2);
|
||||
ex.addSuppressed(suppressed);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static IOException tryDelete(Path path) {
|
||||
try {
|
||||
Files.deleteIfExists(path);
|
||||
if (Files.exists(path)) {
|
||||
return new IOException(path + " still exists after deleting");
|
||||
}
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
private SafeFiles() {
|
||||
}
|
||||
}
|
||||
|
@ -47,9 +47,11 @@
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.util.Direction;
|
||||
import com.sk89q.worldedit.util.Location;
|
||||
import com.sk89q.worldedit.util.ShutdownHook;
|
||||
import com.sk89q.worldedit.util.SideEffect;
|
||||
import com.sk89q.worldedit.util.SideEffectSet;
|
||||
import com.sk89q.worldedit.util.TreeGenerator.TreeType;
|
||||
import com.sk89q.worldedit.util.io.file.SafeFiles;
|
||||
import com.sk89q.worldedit.world.AbstractWorld;
|
||||
import com.sk89q.worldedit.world.RegenOptions;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
@ -94,9 +96,7 @@
|
||||
import net.minecraft.world.gen.feature.Feature;
|
||||
import net.minecraft.world.level.ServerWorldProperties;
|
||||
import net.minecraft.world.level.storage.LevelStorage;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@ -113,7 +113,6 @@
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
@ -302,14 +301,9 @@ public boolean regenerate(Region region, EditSession editSession, RegenOptions o
|
||||
|
||||
private void doRegen(Region region, EditSession editSession, RegenOptions options) throws Exception {
|
||||
Path tempDir = Files.createTempDirectory("WorldEditWorldGen");
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
try {
|
||||
FileUtils.deleteDirectory(tempDir.toFile());
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}));
|
||||
LevelStorage levelStorage = LevelStorage.create(tempDir);
|
||||
try (LevelStorage.Session session = levelStorage.createSession("WorldEditTempGen")) {
|
||||
try (ShutdownHook<Path> ignored = SafeFiles.tryHardToDeleteDirOnExit(tempDir);
|
||||
LevelStorage.Session session = levelStorage.createSession("WorldEditTempGen")) {
|
||||
ServerWorld originalWorld = (ServerWorld) getWorld();
|
||||
long seed = options.getSeed().orElse(originalWorld.getSeed());
|
||||
AccessorLevelProperties levelProperties = (AccessorLevelProperties)
|
||||
@ -355,7 +349,7 @@ private void doRegen(Region region, EditSession editSession, RegenOptions option
|
||||
levelProperties.setGeneratorOptions(originalOpts);
|
||||
}
|
||||
} finally {
|
||||
FileUtils.deleteDirectory(tempDir.toFile());
|
||||
SafeFiles.tryHardToDeleteDir(tempDir);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,9 +47,11 @@
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.util.Direction;
|
||||
import com.sk89q.worldedit.util.Location;
|
||||
import com.sk89q.worldedit.util.ShutdownHook;
|
||||
import com.sk89q.worldedit.util.SideEffect;
|
||||
import com.sk89q.worldedit.util.SideEffectSet;
|
||||
import com.sk89q.worldedit.util.TreeGenerator.TreeType;
|
||||
import com.sk89q.worldedit.util.io.file.SafeFiles;
|
||||
import com.sk89q.worldedit.world.AbstractWorld;
|
||||
import com.sk89q.worldedit.world.RegenOptions;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
@ -312,14 +314,9 @@ public boolean regenerate(Region region, EditSession editSession, RegenOptions o
|
||||
|
||||
private void doRegen(Region region, EditSession editSession, RegenOptions options) throws Exception {
|
||||
Path tempDir = Files.createTempDirectory("WorldEditWorldGen");
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
try {
|
||||
FileUtils.deleteDirectory(tempDir.toFile());
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}));
|
||||
SaveFormat levelStorage = SaveFormat.func_237269_a_(tempDir);
|
||||
try (SaveFormat.LevelSave session = levelStorage.func_237274_c_("WorldEditTempGen")) {
|
||||
try (ShutdownHook<Path> ignored = SafeFiles.tryHardToDeleteDirOnExit(tempDir);
|
||||
SaveFormat.LevelSave session = levelStorage.func_237274_c_("WorldEditTempGen")) {
|
||||
ServerWorld originalWorld = (ServerWorld) getWorld();
|
||||
long seed = options.getSeed().orElse(originalWorld.getSeed());
|
||||
ServerWorldInfo levelProperties =
|
||||
@ -367,7 +364,7 @@ private void doRegen(Region region, EditSession editSession, RegenOptions option
|
||||
levelProperties.field_237343_c_ = originalOpts;
|
||||
}
|
||||
} finally {
|
||||
FileUtils.deleteDirectory(tempDir.toFile());
|
||||
SafeFiles.tryHardToDeleteDir(tempDir);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user