mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2025-02-13 11:01:55 +01:00
Move region-file watch service into World interface
This commit is contained in:
parent
20aa0a72f5
commit
3db6833fc6
@ -29,23 +29,20 @@
|
||||
import de.bluecolored.bluemap.common.rendermanager.WorldRegionRenderTask;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.map.BmMap;
|
||||
import de.bluecolored.bluemap.core.util.FileHelper;
|
||||
import de.bluecolored.bluemap.core.world.World;
|
||||
import de.bluecolored.bluemap.core.world.mca.MCAWorld;
|
||||
import de.bluecolored.bluemap.core.world.mca.region.RegionType;
|
||||
import de.bluecolored.bluemap.core.util.WatchService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.ClosedWatchServiceException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class RegionFileWatchService extends Thread {
|
||||
public class MapUpdateService extends Thread {
|
||||
|
||||
private final BmMap map;
|
||||
private final RenderManager renderManager;
|
||||
private final WatchService watchService;
|
||||
private final WatchService<Vector2i> watchService;
|
||||
|
||||
private volatile boolean closed;
|
||||
|
||||
@ -53,25 +50,12 @@ public class RegionFileWatchService extends Thread {
|
||||
|
||||
private final Map<Vector2i, TimerTask> scheduledUpdates;
|
||||
|
||||
public RegionFileWatchService(RenderManager renderManager, BmMap map) throws IOException {
|
||||
public MapUpdateService(RenderManager renderManager, BmMap map) throws IOException {
|
||||
this.renderManager = renderManager;
|
||||
this.map = map;
|
||||
this.closed = false;
|
||||
this.scheduledUpdates = new HashMap<>();
|
||||
|
||||
World world = map.getWorld();
|
||||
if (!(world instanceof MCAWorld)) throw new UnsupportedOperationException("world-type is not supported");
|
||||
Path folder = ((MCAWorld) world).getRegionFolder();
|
||||
FileHelper.createDirectories(folder);
|
||||
|
||||
this.watchService = folder.getFileSystem().newWatchService();
|
||||
folder.register(this.watchService,
|
||||
StandardWatchEventKinds.ENTRY_CREATE,
|
||||
StandardWatchEventKinds.ENTRY_MODIFY,
|
||||
StandardWatchEventKinds.ENTRY_DELETE
|
||||
);
|
||||
|
||||
Logger.global.logDebug("Created region-file watch-service for map '" + map.getId() + "' at '" + folder + "'.");
|
||||
this.watchService = map.getWorld().createRegionWatchService();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -81,24 +65,8 @@ public void run() {
|
||||
Logger.global.logDebug("Started watching map '" + map.getId() + "' for updates...");
|
||||
|
||||
try {
|
||||
while (!closed) {
|
||||
WatchKey key = this.watchService.take();
|
||||
|
||||
for (WatchEvent<?> event : key.pollEvents()) {
|
||||
WatchEvent.Kind<?> kind = event.kind();
|
||||
|
||||
if (kind == StandardWatchEventKinds.OVERFLOW) continue;
|
||||
|
||||
Object fileObject = event.context();
|
||||
if (!(fileObject instanceof Path)) continue;
|
||||
Path file = (Path) fileObject;
|
||||
|
||||
String regionFileName = file.toFile().getName();
|
||||
updateRegion(regionFileName);
|
||||
}
|
||||
|
||||
if (!key.reset()) return;
|
||||
}
|
||||
while (!closed)
|
||||
this.watchService.take().forEach(this::updateRegion);
|
||||
} catch (ClosedWatchServiceException ignore) {
|
||||
} catch (InterruptedException iex) {
|
||||
Thread.currentThread().interrupt();
|
||||
@ -111,36 +79,25 @@ public void run() {
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void updateRegion(String regionFileName) {
|
||||
if (RegionType.forFileName(regionFileName) == null) return;
|
||||
private synchronized void updateRegion(Vector2i regionPos) {
|
||||
// we only want to start the render when there were no changes on a file for 5 seconds
|
||||
TimerTask task = scheduledUpdates.remove(regionPos);
|
||||
if (task != null) task.cancel();
|
||||
|
||||
try {
|
||||
String[] filenameParts = regionFileName.split("\\.");
|
||||
if (filenameParts.length < 3) return;
|
||||
task = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (MapUpdateService.this) {
|
||||
WorldRegionRenderTask task = new WorldRegionRenderTask(map, regionPos);
|
||||
scheduledUpdates.remove(regionPos);
|
||||
renderManager.scheduleRenderTask(task);
|
||||
|
||||
int rX = Integer.parseInt(filenameParts[1]);
|
||||
int rZ = Integer.parseInt(filenameParts[2]);
|
||||
Vector2i regionPos = new Vector2i(rX, rZ);
|
||||
|
||||
// we only want to start the render when there were no changes on a file for 5 seconds
|
||||
TimerTask task = scheduledUpdates.remove(regionPos);
|
||||
if (task != null) task.cancel();
|
||||
|
||||
task = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (RegionFileWatchService.this) {
|
||||
WorldRegionRenderTask task = new WorldRegionRenderTask(map, regionPos);
|
||||
scheduledUpdates.remove(regionPos);
|
||||
renderManager.scheduleRenderTask(task);
|
||||
|
||||
Logger.global.logDebug("Scheduled update for region-file: " + regionPos + " (Map: " + map.getId() + ")");
|
||||
}
|
||||
Logger.global.logDebug("Scheduled update for region-file: " + regionPos + " (Map: " + map.getId() + ")");
|
||||
}
|
||||
};
|
||||
scheduledUpdates.put(regionPos, task);
|
||||
delayTimer.schedule(task, 5000);
|
||||
} catch (NumberFormatException ignore) {}
|
||||
}
|
||||
};
|
||||
scheduledUpdates.put(regionPos, task);
|
||||
delayTimer.schedule(task, 5000);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
@ -151,7 +108,7 @@ public void close() {
|
||||
|
||||
try {
|
||||
this.watchService.close();
|
||||
} catch (IOException ex) {
|
||||
} catch (Exception ex) {
|
||||
Logger.global.logError("Exception while trying to close WatchService!", ex);
|
||||
}
|
||||
}
|
@ -94,7 +94,7 @@ public class Plugin implements ServerEventListener {
|
||||
|
||||
private Timer daemonTimer;
|
||||
|
||||
private Map<String, RegionFileWatchService> regionFileWatchServices;
|
||||
private Map<String, MapUpdateService> mapUpdateServices;
|
||||
|
||||
private PlayerSkinUpdater skinUpdater;
|
||||
|
||||
@ -316,8 +316,8 @@ public void run() {
|
||||
TimerTask fileWatcherRestartTask = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
regionFileWatchServices.values().forEach(RegionFileWatchService::close);
|
||||
regionFileWatchServices.clear();
|
||||
mapUpdateServices.values().forEach(MapUpdateService::close);
|
||||
mapUpdateServices.clear();
|
||||
initFileWatcherTasks();
|
||||
}
|
||||
};
|
||||
@ -351,7 +351,7 @@ public void run() {
|
||||
daemonTimer.scheduleAtFixedRate(metricsTask, TimeUnit.MINUTES.toMillis(1), TimeUnit.MINUTES.toMillis(30));
|
||||
|
||||
//watch map-changes
|
||||
this.regionFileWatchServices = new HashMap<>();
|
||||
this.mapUpdateServices = new HashMap<>();
|
||||
initFileWatcherTasks();
|
||||
|
||||
//register listener
|
||||
@ -408,11 +408,11 @@ public void unload(boolean keepWebserver) {
|
||||
daemonTimer = null;
|
||||
|
||||
//stop file-watchers
|
||||
if (regionFileWatchServices != null) {
|
||||
regionFileWatchServices.values().forEach(RegionFileWatchService::close);
|
||||
regionFileWatchServices.clear();
|
||||
if (mapUpdateServices != null) {
|
||||
mapUpdateServices.values().forEach(MapUpdateService::close);
|
||||
mapUpdateServices.clear();
|
||||
}
|
||||
regionFileWatchServices = null;
|
||||
mapUpdateServices = null;
|
||||
|
||||
// stop render-manager
|
||||
if (renderManager != null){
|
||||
@ -567,16 +567,20 @@ public synchronized void startWatchingMap(BmMap map) {
|
||||
stopWatchingMap(map);
|
||||
|
||||
try {
|
||||
RegionFileWatchService watcher = new RegionFileWatchService(renderManager, map);
|
||||
MapUpdateService watcher = new MapUpdateService(renderManager, map);
|
||||
watcher.start();
|
||||
regionFileWatchServices.put(map.getId(), watcher);
|
||||
mapUpdateServices.put(map.getId(), watcher);
|
||||
} catch (IOException ex) {
|
||||
Logger.global.logError("Failed to create file-watcher for map: " + map.getId() + " (This means the 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)");
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void stopWatchingMap(BmMap map) {
|
||||
RegionFileWatchService watcher = regionFileWatchServices.remove(map.getId());
|
||||
MapUpdateService watcher = mapUpdateServices.remove(map.getId());
|
||||
if (watcher != null) {
|
||||
watcher.close();
|
||||
}
|
||||
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.util;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* A watch service that watches for changes and events.
|
||||
* @param <T> The type of the events or changes this WatchService provides
|
||||
*/
|
||||
public interface WatchService<T> extends AutoCloseable {
|
||||
|
||||
@Nullable
|
||||
List<T> poll();
|
||||
|
||||
@Nullable List<T> poll(long timeout, TimeUnit unit) throws InterruptedException;
|
||||
|
||||
List<T> take() throws InterruptedException;
|
||||
|
||||
}
|
@ -27,7 +27,9 @@
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
import de.bluecolored.bluemap.core.util.Grid;
|
||||
import de.bluecolored.bluemap.core.util.WatchService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@ -72,6 +74,15 @@ public interface World {
|
||||
*/
|
||||
Collection<Vector2i> listRegions();
|
||||
|
||||
/**
|
||||
* Creates and returns a new {@link WatchService} which watches for any changes in this worlds regions.
|
||||
* @throws IOException if an IOException occurred while creating the watch-service
|
||||
* @throws UnsupportedOperationException if watching this world is not supported
|
||||
*/
|
||||
default WatchService<Vector2i> createRegionWatchService() throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all chunks from the specified region into the chunk cache (if there is a cache)
|
||||
*/
|
||||
@ -79,6 +90,9 @@ default void preloadRegionChunks(int x, int z) {
|
||||
preloadRegionChunks(x, z, pos -> true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the filtered chunks from the specified region into the chunk cache (if there is a cache)
|
||||
*/
|
||||
void preloadRegionChunks(int x, int z, Predicate<Vector2i> chunkFilter);
|
||||
|
||||
/**
|
||||
@ -91,9 +105,4 @@ default void preloadRegionChunks(int x, int z) {
|
||||
*/
|
||||
void invalidateChunkCache(int x, int z);
|
||||
|
||||
/**
|
||||
* Cleans up invalid cache-entries to free up memory
|
||||
*/
|
||||
void cleanUpChunkCache();
|
||||
|
||||
}
|
||||
|
@ -36,6 +36,7 @@
|
||||
import de.bluecolored.bluemap.core.util.Grid;
|
||||
import de.bluecolored.bluemap.core.util.Key;
|
||||
import de.bluecolored.bluemap.core.util.Vector2iCache;
|
||||
import de.bluecolored.bluemap.core.util.WatchService;
|
||||
import de.bluecolored.bluemap.core.world.*;
|
||||
import de.bluecolored.bluemap.core.world.mca.chunk.ChunkLoader;
|
||||
import de.bluecolored.bluemap.core.world.mca.data.DimensionTypeDeserializer;
|
||||
@ -165,21 +166,11 @@ public Collection<Vector2i> listRegions() {
|
||||
return stream
|
||||
.map(file -> {
|
||||
try {
|
||||
String fileName = file.getFileName().toString();
|
||||
|
||||
if (RegionType.forFileName(fileName) == null) return null;
|
||||
if (Files.size(file) <= 0) return null;
|
||||
|
||||
String[] filenameParts = fileName.split("\\.");
|
||||
int rX = Integer.parseInt(filenameParts[1]);
|
||||
int rZ = Integer.parseInt(filenameParts[2]);
|
||||
|
||||
return new Vector2i(rX, rZ);
|
||||
return RegionType.regionForFileName(file.getFileName().toString());
|
||||
} catch (IOException ex) {
|
||||
Logger.global.logError("Failed to read region-file: " + file, ex);
|
||||
return null;
|
||||
} catch (NumberFormatException ignore) {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
@ -190,6 +181,11 @@ public Collection<Vector2i> listRegions() {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public WatchService<Vector2i> createRegionWatchService() throws IOException {
|
||||
return new MCAWorldRegionWatchService(this.regionFolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preloadRegionChunks(int x, int z, Predicate<Vector2i> chunkFilter) {
|
||||
try {
|
||||
@ -223,11 +219,6 @@ public void invalidateChunkCache(int x, int z) {
|
||||
chunkCache.invalidate(VECTOR_2_I_CACHE.get(x, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanUpChunkCache() {
|
||||
chunkCache.cleanUp();
|
||||
}
|
||||
|
||||
private Region loadRegion(Vector2i regionPos) {
|
||||
return loadRegion(regionPos.getX(), regionPos.getY());
|
||||
}
|
||||
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.world.mca;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
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.Path;
|
||||
import java.nio.file.StandardWatchEventKinds;
|
||||
import java.nio.file.WatchKey;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class MCAWorldRegionWatchService implements WatchService<Vector2i> {
|
||||
|
||||
private final java.nio.file.WatchService watchService;
|
||||
|
||||
public MCAWorldRegionWatchService(Path regionFolder) throws IOException {
|
||||
this.watchService = regionFolder.getFileSystem().newWatchService();
|
||||
regionFolder.register(this.watchService,
|
||||
StandardWatchEventKinds.ENTRY_CREATE,
|
||||
StandardWatchEventKinds.ENTRY_MODIFY,
|
||||
StandardWatchEventKinds.ENTRY_DELETE
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<Vector2i> poll() {
|
||||
WatchKey key = watchService.poll();
|
||||
if (key == null) return null;
|
||||
return processWatchKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<Vector2i> poll(long timeout, TimeUnit unit) throws InterruptedException {
|
||||
WatchKey key = watchService.poll(timeout, unit);
|
||||
if (key == null) return null;
|
||||
return processWatchKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Vector2i> take() throws InterruptedException {
|
||||
WatchKey key = watchService.take();
|
||||
return processWatchKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
watchService.close();
|
||||
}
|
||||
|
||||
private List<Vector2i> processWatchKey(WatchKey key) {
|
||||
try {
|
||||
return key.pollEvents().stream()
|
||||
.map(event -> {
|
||||
if (event.context() instanceof Path path) {
|
||||
return RegionType.regionForFileName(path.getFileName().toString());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
} finally {
|
||||
key.reset();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -36,6 +36,7 @@
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/*
|
||||
* LinearFormat:
|
||||
@ -63,6 +64,7 @@
|
||||
public class LinearRegion implements Region {
|
||||
|
||||
public static final String FILE_SUFFIX = ".linear";
|
||||
public static final Pattern FILE_PATTERN = Pattern.compile("^r\\.(-?\\d+)\\.(-?\\d+)\\.linear$");
|
||||
|
||||
private static final long MAGIC = 0xc3ff13183cca9d9aL;
|
||||
|
||||
|
@ -40,11 +40,14 @@
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Getter
|
||||
public class MCARegion implements Region {
|
||||
|
||||
public static final String FILE_SUFFIX = ".mca";
|
||||
public static final Pattern FILE_PATTERN = Pattern.compile("^r\\.(-?\\d+)\\.(-?\\d+)\\.mca$");
|
||||
|
||||
public static final Compression[] CHUNK_COMPRESSION_MAP = new Compression[255];
|
||||
static {
|
||||
CHUNK_COMPRESSION_MAP[0] = Compression.NONE;
|
||||
|
@ -24,6 +24,7 @@
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.world.mca.region;
|
||||
|
||||
import com.flowpowered.math.vector.Vector2i;
|
||||
import de.bluecolored.bluemap.core.util.Key;
|
||||
import de.bluecolored.bluemap.core.util.Keyed;
|
||||
import de.bluecolored.bluemap.core.util.Registry;
|
||||
@ -31,16 +32,17 @@
|
||||
import de.bluecolored.bluemap.core.world.mca.MCAWorld;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public interface RegionType extends Keyed {
|
||||
|
||||
RegionType MCA = new Impl(Key.bluemap("mca"), MCARegion.FILE_SUFFIX, MCARegion::new, MCARegion::getRegionFileName);
|
||||
RegionType LINEAR = new Impl(Key.bluemap("linear"), LinearRegion.FILE_SUFFIX, LinearRegion::new, LinearRegion::getRegionFileName);
|
||||
RegionType MCA = new Impl(Key.bluemap("mca"), MCARegion::new, MCARegion::getRegionFileName, MCARegion.FILE_PATTERN);
|
||||
RegionType LINEAR = new Impl(Key.bluemap("linear"), LinearRegion::new, LinearRegion::getRegionFileName, LinearRegion.FILE_PATTERN);
|
||||
|
||||
RegionType DEFAULT = MCA;
|
||||
Registry<RegionType> REGISTRY = new Registry<>(
|
||||
@ -48,36 +50,55 @@ public interface RegionType extends Keyed {
|
||||
LINEAR
|
||||
);
|
||||
|
||||
String getFileSuffix();
|
||||
|
||||
/**
|
||||
* Creates a new {@link Region} from the given world and region-file
|
||||
*/
|
||||
Region createRegion(MCAWorld world, Path regionFile);
|
||||
|
||||
Path getRegionFile(Path regionFolder, int regionX, int regionZ);
|
||||
/**
|
||||
* Converts region coordinates into the region-file name.
|
||||
*/
|
||||
String getRegionFileName(int regionX, int regionZ);
|
||||
|
||||
/**
|
||||
* Converts the region-file name into region coordinates.
|
||||
* Returns null if the name does not match the expected format.
|
||||
*/
|
||||
@Nullable Vector2i getRegionFromFileName(String fileName);
|
||||
|
||||
static @Nullable RegionType forFileName(String fileName) {
|
||||
for (RegionType regionType : REGISTRY.values()) {
|
||||
if (fileName.endsWith(regionType.getFileSuffix()))
|
||||
if (regionType.getRegionFromFileName(fileName) != null)
|
||||
return regionType;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static @NotNull Region loadRegion(MCAWorld world, Path regionFolder, int regionX, int regionZ) {
|
||||
static @Nullable Vector2i regionForFileName(String fileName) {
|
||||
for (RegionType regionType : REGISTRY.values()) {
|
||||
Path regionFile = regionType.getRegionFile(regionFolder, regionX, regionZ);
|
||||
Vector2i pos = regionType.getRegionFromFileName(fileName);
|
||||
if (pos != null) return pos;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static Region loadRegion(MCAWorld world, Path regionFolder, int regionX, int regionZ) {
|
||||
for (RegionType regionType : REGISTRY.values()) {
|
||||
Path regionFile = regionFolder.resolve(regionType.getRegionFileName(regionX, regionZ));
|
||||
if (Files.exists(regionFile)) return regionType.createRegion(world, regionFile);
|
||||
}
|
||||
return DEFAULT.createRegion(world, DEFAULT.getRegionFile(regionFolder, regionX, regionZ));
|
||||
return DEFAULT.createRegion(world, regionFolder.resolve(DEFAULT.getRegionFileName(regionX, regionZ)));
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
class Impl implements RegionType {
|
||||
|
||||
@Getter private final Key key;
|
||||
@Getter private final String fileSuffix;
|
||||
private final RegionFactory regionFactory;
|
||||
private final RegionFileNameFunction regionFileNameFunction;
|
||||
private final Pattern regionFileNamePattern;
|
||||
|
||||
public Region createRegion(MCAWorld world, Path regionFile) {
|
||||
return this.regionFactory.create(world, regionFile);
|
||||
@ -87,8 +108,14 @@ public String getRegionFileName(int regionX, int regionZ) {
|
||||
return regionFileNameFunction.getRegionFileName(regionX, regionZ);
|
||||
}
|
||||
|
||||
public Path getRegionFile(Path regionFolder, int regionX, int regionZ) {
|
||||
return regionFolder.resolve(getRegionFileName(regionX, regionZ));
|
||||
@Override
|
||||
public @Nullable Vector2i getRegionFromFileName(String fileName) {
|
||||
Matcher matcher = regionFileNamePattern.matcher(fileName);
|
||||
if (!matcher.matches()) return null;
|
||||
return new Vector2i(
|
||||
Integer.parseInt(matcher.group(1)),
|
||||
Integer.parseInt(matcher.group(2))
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -31,7 +31,7 @@
|
||||
import de.bluecolored.bluemap.common.config.ConfigurationException;
|
||||
import de.bluecolored.bluemap.common.config.CoreConfig;
|
||||
import de.bluecolored.bluemap.common.config.WebserverConfig;
|
||||
import de.bluecolored.bluemap.common.plugin.RegionFileWatchService;
|
||||
import de.bluecolored.bluemap.common.plugin.MapUpdateService;
|
||||
import de.bluecolored.bluemap.common.rendermanager.MapUpdateTask;
|
||||
import de.bluecolored.bluemap.common.rendermanager.RenderManager;
|
||||
import de.bluecolored.bluemap.common.rendermanager.RenderTask;
|
||||
@ -88,16 +88,19 @@ public void renderMaps(BlueMapService blueMap, boolean watch, boolean forceRende
|
||||
Map<String, BmMap> maps = blueMap.getOrLoadMaps(mapFilter);
|
||||
|
||||
//watcher
|
||||
List<RegionFileWatchService> regionFileWatchServices = new ArrayList<>();
|
||||
List<MapUpdateService> mapUpdateServices = new ArrayList<>();
|
||||
if (watch) {
|
||||
for (BmMap map : maps.values()) {
|
||||
try {
|
||||
RegionFileWatchService watcher = new RegionFileWatchService(renderManager, map);
|
||||
MapUpdateService watcher = new MapUpdateService(renderManager, map);
|
||||
watcher.start();
|
||||
regionFileWatchServices.add(watcher);
|
||||
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);
|
||||
" (This 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)");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -150,8 +153,8 @@ public void run() {
|
||||
updateInfoTask.cancel();
|
||||
saveTask.cancel();
|
||||
|
||||
regionFileWatchServices.forEach(RegionFileWatchService::close);
|
||||
regionFileWatchServices.clear();
|
||||
mapUpdateServices.forEach(MapUpdateService::close);
|
||||
mapUpdateServices.clear();
|
||||
|
||||
renderManager.removeAllRenderTasks();
|
||||
try {
|
||||
|
Loading…
Reference in New Issue
Block a user