mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2025-01-24 01:01:50 +01:00
Handle watching for region files even if the region folder doesnt yet exist on load time
This commit is contained in:
parent
ad3c8fec60
commit
e7b3bcb625
@ -67,6 +67,8 @@ public void run() {
|
||||
while (!closed)
|
||||
this.watchService.take().forEach(this::updateRegion);
|
||||
} catch (WatchService.ClosedException ignore) {
|
||||
} catch (IOException e) {
|
||||
Logger.global.logError("Exception trying to watch map '" + map.getId() + "' for updates.", e);
|
||||
} catch (InterruptedException iex) {
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
|
@ -31,8 +31,10 @@
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.file.WatchService;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.FileAttribute;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class FileHelper {
|
||||
|
||||
@ -114,4 +116,27 @@ public static void copy(URL source, Path target) throws IOException {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses file-watchers on the path-parent to wait until a specific file or folder exists
|
||||
*/
|
||||
public static boolean awaitExistence(Path path, long timeout, TimeUnit unit) throws IOException, InterruptedException {
|
||||
if (Files.exists(path)) return true;
|
||||
|
||||
long endTime = System.currentTimeMillis() + unit.toMillis(timeout);
|
||||
|
||||
Path parent = path.toAbsolutePath().normalize().getParent();
|
||||
if (parent == null) throw new IOException("No parent directory exists that can be watched.");
|
||||
if (!awaitExistence(parent, timeout, unit)) return false;
|
||||
|
||||
try (WatchService watchService = parent.getFileSystem().newWatchService()) {
|
||||
parent.register(watchService, StandardWatchEventKinds.ENTRY_CREATE);
|
||||
while (!Files.exists(path)) {
|
||||
long now = System.currentTimeMillis();
|
||||
if (now >= endTime) return false;
|
||||
watchService.poll(endTime - now, TimeUnit.MILLISECONDS).reset();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,6 +27,7 @@
|
||||
import lombok.experimental.StandardException;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@ -41,7 +42,7 @@ public interface WatchService<T> extends AutoCloseable {
|
||||
* @throws ClosedException If the watch-service is closed
|
||||
*/
|
||||
@Nullable
|
||||
List<T> poll();
|
||||
List<T> poll() throws IOException;
|
||||
|
||||
/**
|
||||
* Retrieves and consumes the next batch of events,
|
||||
@ -49,7 +50,7 @@ public interface WatchService<T> extends AutoCloseable {
|
||||
* @throws ClosedException If the watch-service is closed, or it is closed while waiting for the next event
|
||||
* @throws InterruptedException If interrupted while waiting
|
||||
*/
|
||||
@Nullable List<T> poll(long timeout, TimeUnit unit) throws InterruptedException;
|
||||
@Nullable List<T> poll(long timeout, TimeUnit unit) throws IOException, InterruptedException;
|
||||
|
||||
/**
|
||||
* Retrieves and consumes the next batch of events,
|
||||
@ -57,7 +58,7 @@ public interface WatchService<T> extends AutoCloseable {
|
||||
* @throws ClosedException If the watch-service is closed, or it is closed while waiting for the next event
|
||||
* @throws InterruptedException If interrupted while waiting
|
||||
*/
|
||||
List<T> take() throws InterruptedException;
|
||||
List<T> take() throws IOException, InterruptedException;
|
||||
|
||||
/**
|
||||
* Thrown when the WatchService is closed or gets closed when polling or while waiting for events
|
||||
|
@ -51,6 +51,7 @@
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -162,6 +163,7 @@ private Region getRegion(Vector2i pos) {
|
||||
|
||||
@Override
|
||||
public Collection<Vector2i> listRegions() {
|
||||
if (!Files.exists(regionFolder)) return Collections.emptyList();
|
||||
try (Stream<Path> stream = Files.list(regionFolder)) {
|
||||
return stream
|
||||
.map(file -> {
|
||||
|
@ -25,34 +25,31 @@
|
||||
package de.bluecolored.bluemap.core.world.mca;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import de.bluecolored.bluemap.core.util.FileHelper;
|
||||
import de.bluecolored.bluemap.core.util.WatchService;
|
||||
import de.bluecolored.bluemap.core.world.mca.region.RegionType;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.ClosedWatchServiceException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardWatchEventKinds;
|
||||
import java.nio.file.WatchKey;
|
||||
import java.nio.file.*;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class MCAWorldRegionWatchService implements WatchService<Vector2i> {
|
||||
|
||||
private final Path regionFolder;
|
||||
private final java.nio.file.WatchService watchService;
|
||||
private boolean initialized;
|
||||
|
||||
public MCAWorldRegionWatchService(Path regionFolder) throws IOException {
|
||||
this.regionFolder = regionFolder;
|
||||
this.watchService = regionFolder.getFileSystem().newWatchService();
|
||||
regionFolder.register(this.watchService,
|
||||
StandardWatchEventKinds.ENTRY_CREATE,
|
||||
StandardWatchEventKinds.ENTRY_MODIFY,
|
||||
StandardWatchEventKinds.ENTRY_DELETE
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<Vector2i> poll() {
|
||||
public @Nullable List<Vector2i> poll() throws IOException {
|
||||
if (!ensureInitialization()) return null;
|
||||
try {
|
||||
WatchKey key = watchService.poll();
|
||||
if (key == null) return null;
|
||||
@ -63,9 +60,17 @@ public MCAWorldRegionWatchService(Path regionFolder) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<Vector2i> poll(long timeout, TimeUnit unit) throws InterruptedException {
|
||||
public @Nullable List<Vector2i> poll(long timeout, TimeUnit unit) throws IOException, InterruptedException {
|
||||
long endTime = System.currentTimeMillis() + unit.toMillis(timeout);
|
||||
|
||||
FileHelper.awaitExistence(regionFolder, timeout, unit);
|
||||
if (!ensureInitialization()) return null;
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
if (now >= endTime) return null;
|
||||
|
||||
try {
|
||||
WatchKey key = watchService.poll(timeout, unit);
|
||||
WatchKey key = watchService.poll(endTime - now, TimeUnit.MILLISECONDS);
|
||||
if (key == null) return null;
|
||||
return processWatchKey(key);
|
||||
} catch (ClosedWatchServiceException e) {
|
||||
@ -74,7 +79,10 @@ public MCAWorldRegionWatchService(Path regionFolder) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Vector2i> take() throws InterruptedException {
|
||||
public List<Vector2i> take() throws IOException, InterruptedException {
|
||||
while (!ensureInitialization())
|
||||
FileHelper.awaitExistence(regionFolder, 1, TimeUnit.HOURS);
|
||||
|
||||
try {
|
||||
WatchKey key = watchService.take();
|
||||
return processWatchKey(key);
|
||||
@ -83,6 +91,20 @@ public List<Vector2i> take() throws InterruptedException {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
private synchronized boolean ensureInitialization() throws IOException {
|
||||
if (initialized) return true;
|
||||
if (!Files.exists(regionFolder)) return false;
|
||||
|
||||
regionFolder.register(this.watchService,
|
||||
StandardWatchEventKinds.ENTRY_CREATE,
|
||||
StandardWatchEventKinds.ENTRY_MODIFY,
|
||||
StandardWatchEventKinds.ENTRY_DELETE
|
||||
);
|
||||
initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
watchService.close();
|
||||
|
@ -101,8 +101,8 @@ public void renderMaps(BlueMapService blueMap, boolean watch, Predicate<TileStat
|
||||
watcher.start();
|
||||
mapUpdateServices.add(watcher);
|
||||
} catch (IOException ex) {
|
||||
Logger.global.logError("Failed to create file-watcher for map: " + map.getId() +
|
||||
" (This map might not automatically update)", ex);
|
||||
Logger.global.logError("Failed to create update-watcher for map: " + map.getId() +
|
||||
" (This means the map might not automatically update)", ex);
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
Logger.global.logWarning("Update-watcher for map '" + map.getId() + "' is not supported for the world-type." +
|
||||
" (This means the map might not automatically update)");
|
||||
|
Loading…
Reference in New Issue
Block a user