mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2024-11-25 20:16:00 +01:00
Refactor Map-Storage to be more modular in preparation for SQL-storage
This commit is contained in:
parent
3608c050ac
commit
49e956ed66
@ -35,6 +35,8 @@
|
|||||||
import de.bluecolored.bluemap.core.mca.MCAWorld;
|
import de.bluecolored.bluemap.core.mca.MCAWorld;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
|
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
||||||
|
import de.bluecolored.bluemap.core.storage.FileStorage;
|
||||||
|
import de.bluecolored.bluemap.core.storage.Storage;
|
||||||
import de.bluecolored.bluemap.core.world.World;
|
import de.bluecolored.bluemap.core.world.World;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
|
|
||||||
@ -164,11 +166,16 @@ private synchronized void loadWorldsAndMaps() throws IOException, InterruptedExc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Storage storage = new FileStorage(
|
||||||
|
getRenderConfig().getWebRoot().toPath().resolve("data"),
|
||||||
|
mapConfig.getCompression()
|
||||||
|
);
|
||||||
|
|
||||||
BmMap map = new BmMap(
|
BmMap map = new BmMap(
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
world,
|
world,
|
||||||
getRenderConfig().getWebRoot().toPath().resolve("data").resolve(id),
|
storage,
|
||||||
getResourcePack(),
|
getResourcePack(),
|
||||||
mapConfig
|
mapConfig
|
||||||
);
|
);
|
||||||
|
@ -35,7 +35,6 @@
|
|||||||
import de.bluecolored.bluemap.common.rendermanager.MapUpdateTask;
|
import de.bluecolored.bluemap.common.rendermanager.MapUpdateTask;
|
||||||
import de.bluecolored.bluemap.common.rendermanager.RenderManager;
|
import de.bluecolored.bluemap.common.rendermanager.RenderManager;
|
||||||
import de.bluecolored.bluemap.common.rendermanager.WorldRegionRenderTask;
|
import de.bluecolored.bluemap.common.rendermanager.WorldRegionRenderTask;
|
||||||
import de.bluecolored.bluemap.core.map.BmMap;
|
|
||||||
import de.bluecolored.bluemap.core.world.Grid;
|
import de.bluecolored.bluemap.core.world.Grid;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -96,7 +95,7 @@ public boolean scheduleMapUpdateTask(BlueMapMap map, Collection<Vector2i> region
|
|||||||
@Override
|
@Override
|
||||||
public boolean scheduleMapPurgeTask(BlueMapMap map) throws IOException {
|
public boolean scheduleMapPurgeTask(BlueMapMap map) throws IOException {
|
||||||
BlueMapMapImpl cmap = castMap(map);
|
BlueMapMapImpl cmap = castMap(map);
|
||||||
return renderManager.scheduleRenderTask(new MapPurgeTask(cmap.getMapType()));
|
return renderManager.scheduleRenderTask(MapPurgeTask.create(cmap.getMapType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -64,7 +64,6 @@
|
|||||||
import de.bluecolored.bluemap.core.world.World;
|
import de.bluecolored.bluemap.core.world.World;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
@ -810,41 +809,33 @@ public int purgeCommand(CommandContext<S> context) {
|
|||||||
CommandSource source = commandSourceInterface.apply(context.getSource());
|
CommandSource source = commandSourceInterface.apply(context.getSource());
|
||||||
|
|
||||||
// parse map argument
|
// parse map argument
|
||||||
String mapId = context.getArgument("map", String.class);
|
String mapString = context.getArgument("map", String.class);
|
||||||
|
BmMap map = parseMap(mapString).orElse(null);
|
||||||
|
|
||||||
|
if (map == null) {
|
||||||
|
source.sendMessage(Text.of(TextColor.RED, "There is no ", helper.mapHelperHover(), " with this name: ", TextColor.WHITE, mapString));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
Path mapFolder = plugin.getRenderConfig().getWebRoot().toPath().resolve("data").resolve(mapId);
|
|
||||||
if (!Files.isDirectory(mapFolder)) {
|
|
||||||
source.sendMessage(Text.of(TextColor.RED, "There is no map-data to purge for the map-id '" + mapId + "'!"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<BmMap> optMap = parseMap(mapId);
|
|
||||||
|
|
||||||
// delete map
|
// delete map
|
||||||
MapPurgeTask purgeTask;
|
MapPurgeTask purgeTask = MapPurgeTask.create(map);
|
||||||
if (optMap.isPresent()){
|
|
||||||
purgeTask = new MapPurgeTask(optMap.get());
|
|
||||||
} else {
|
|
||||||
purgeTask = new MapPurgeTask(mapFolder);
|
|
||||||
}
|
|
||||||
|
|
||||||
plugin.getRenderManager().scheduleRenderTaskNext(purgeTask);
|
plugin.getRenderManager().scheduleRenderTaskNext(purgeTask);
|
||||||
source.sendMessage(Text.of(TextColor.GREEN, "Created new Task to purge map '" + mapId + "'"));
|
source.sendMessage(Text.of(TextColor.GREEN, "Created new Task to purge map '" + map.getId() + "'"));
|
||||||
|
|
||||||
// if map is loaded, reset it and start updating it after the purge
|
// reset the map and start updating it after the purge
|
||||||
if (optMap.isPresent()) {
|
RenderTask updateTask = new MapUpdateTask(map);
|
||||||
RenderTask updateTask = new MapUpdateTask(optMap.get());
|
plugin.getRenderManager().scheduleRenderTask(updateTask);
|
||||||
plugin.getRenderManager().scheduleRenderTask(updateTask);
|
source.sendMessage(Text.of(TextColor.GREEN, "Created new Update-Task for map '" + map.getId() + "'"));
|
||||||
source.sendMessage(Text.of(TextColor.GREEN, "Created new Update-Task for map '" + mapId + "'"));
|
source.sendMessage(Text.of(TextColor.GRAY, "If you don't this map to render again after the purge, use ",
|
||||||
source.sendMessage(Text.of(TextColor.GRAY, "If you don't want to render this map again, you need to remove it from your configuration first!"));
|
TextColor.DARK_GRAY, "/bluemap freeze " + map.getId(), TextColor.GRAY, " first!"));
|
||||||
}
|
|
||||||
|
|
||||||
source.sendMessage(Text.of(TextColor.GREEN, "Use ", TextColor.GRAY, "/bluemap", TextColor.GREEN, " to see the progress."));
|
source.sendMessage(Text.of(TextColor.GREEN, "Use ", TextColor.GRAY, "/bluemap", TextColor.GREEN, " to see the progress."));
|
||||||
} catch (IOException | IllegalArgumentException e) {
|
} catch (IOException | IllegalArgumentException e) {
|
||||||
source.sendMessage(Text.of(TextColor.RED, "There was an error trying to purge '" + mapId + "', see console for details."));
|
source.sendMessage(Text.of(TextColor.RED, "There was an error trying to purge '" + map.getId() + "', see console for details."));
|
||||||
Logger.global.logError("Failed to purge map '" + mapId + "'!", e);
|
Logger.global.logError("Failed to purge map '" + map.getId() + "'!", e);
|
||||||
}
|
}
|
||||||
}).start();
|
}).start();
|
||||||
|
|
||||||
|
@ -26,96 +26,179 @@
|
|||||||
|
|
||||||
import de.bluecolored.bluemap.core.debug.DebugDump;
|
import de.bluecolored.bluemap.core.debug.DebugDump;
|
||||||
import de.bluecolored.bluemap.core.map.BmMap;
|
import de.bluecolored.bluemap.core.map.BmMap;
|
||||||
|
import de.bluecolored.bluemap.core.storage.FileStorage;
|
||||||
|
import de.bluecolored.bluemap.core.storage.Storage;
|
||||||
import de.bluecolored.bluemap.core.util.FileUtils;
|
import de.bluecolored.bluemap.core.util.FileUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class MapPurgeTask implements RenderTask {
|
public abstract class MapPurgeTask implements RenderTask {
|
||||||
|
|
||||||
@DebugDump private final BmMap map;
|
public static MapPurgeTask create(BmMap map) throws IOException {
|
||||||
@DebugDump private final Path directory;
|
Storage storage = map.getStorage();
|
||||||
@DebugDump private final int subFilesCount;
|
if (storage instanceof FileStorage) {
|
||||||
private final LinkedList<Path> subFiles;
|
return new MapFilePurgeTask(map, (FileStorage) storage);
|
||||||
|
} else {
|
||||||
@DebugDump private volatile boolean hasMoreWork;
|
return new MapStoragePurgeTask(map);
|
||||||
@DebugDump private volatile boolean cancelled;
|
}
|
||||||
|
|
||||||
public MapPurgeTask(Path mapDirectory) throws IOException {
|
|
||||||
this(null, mapDirectory);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MapPurgeTask(BmMap map) throws IOException {
|
public static MapPurgeTask create(Path mapDirectory) throws IOException {
|
||||||
this(map, map.getFileRoot());
|
return new MapFilePurgeTask(mapDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MapPurgeTask(BmMap map, Path directory) throws IOException {
|
@DebugDump
|
||||||
this.map = map;
|
private static class MapFilePurgeTask extends MapPurgeTask {
|
||||||
this.directory = directory;
|
|
||||||
this.subFiles = Files.walk(directory, 3)
|
private final BmMap map;
|
||||||
.collect(Collectors.toCollection(LinkedList::new));
|
private final Path directory;
|
||||||
this.subFilesCount = subFiles.size();
|
private final int subFilesCount;
|
||||||
this.hasMoreWork = true;
|
private final LinkedList<Path> subFiles;
|
||||||
this.cancelled = false;
|
|
||||||
|
private volatile boolean hasMoreWork;
|
||||||
|
private volatile boolean cancelled;
|
||||||
|
|
||||||
|
public MapFilePurgeTask(Path mapDirectory) throws IOException {
|
||||||
|
this(null, mapDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapFilePurgeTask(BmMap map, FileStorage fileStorage) throws IOException {
|
||||||
|
this(map, fileStorage.getFilePath(map.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private MapFilePurgeTask(BmMap map, Path directory) throws IOException {
|
||||||
|
this.map = map;
|
||||||
|
this.directory = directory;
|
||||||
|
this.subFiles = Files.walk(directory, 3)
|
||||||
|
.collect(Collectors.toCollection(LinkedList::new));
|
||||||
|
this.subFilesCount = subFiles.size();
|
||||||
|
this.hasMoreWork = true;
|
||||||
|
this.cancelled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doWork() throws Exception {
|
||||||
|
synchronized (this) {
|
||||||
|
if (!this.hasMoreWork) return;
|
||||||
|
this.hasMoreWork = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// delete subFiles first to be able to track the progress and cancel
|
||||||
|
while (!subFiles.isEmpty()) {
|
||||||
|
Path subFile = subFiles.getLast();
|
||||||
|
FileUtils.delete(subFile.toFile());
|
||||||
|
subFiles.removeLast();
|
||||||
|
if (this.cancelled) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure everything is deleted
|
||||||
|
FileUtils.delete(directory.toFile());
|
||||||
|
} finally {
|
||||||
|
// reset map render state
|
||||||
|
if (this.map != null) {
|
||||||
|
this.map.getRenderState().reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasMoreWork() {
|
||||||
|
return this.hasMoreWork;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DebugDump
|
||||||
|
public double estimateProgress() {
|
||||||
|
return 1d - (subFiles.size() / (double) subFilesCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancel() {
|
||||||
|
this.cancelled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(RenderTask task) {
|
||||||
|
if (task == this) return true;
|
||||||
|
if (task instanceof MapFilePurgeTask) {
|
||||||
|
return ((MapFilePurgeTask) task).directory.toAbsolutePath().normalize()
|
||||||
|
.startsWith(this.directory.toAbsolutePath().normalize());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "Purge Map " + directory.getFileName();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@DebugDump
|
||||||
public void doWork() throws Exception {
|
private static class MapStoragePurgeTask extends MapPurgeTask {
|
||||||
synchronized (this) {
|
|
||||||
if (!this.hasMoreWork) return;
|
private final BmMap map;
|
||||||
|
|
||||||
|
private volatile boolean hasMoreWork;
|
||||||
|
|
||||||
|
public MapStoragePurgeTask(BmMap map) {
|
||||||
|
this.map = Objects.requireNonNull(map);
|
||||||
|
this.hasMoreWork = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doWork() throws Exception {
|
||||||
|
synchronized (this) {
|
||||||
|
if (!this.hasMoreWork) return;
|
||||||
|
this.hasMoreWork = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
map.getStorage().purgeMap(map.getId());
|
||||||
|
} finally {
|
||||||
|
// reset map render state
|
||||||
|
map.getRenderState().reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasMoreWork() {
|
||||||
|
return this.hasMoreWork;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DebugDump
|
||||||
|
public double estimateProgress() {
|
||||||
|
return 0d;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancel() {
|
||||||
this.hasMoreWork = false;
|
this.hasMoreWork = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
@Override
|
||||||
// delete subFiles first to be able to track the progress and cancel
|
public boolean contains(RenderTask task) {
|
||||||
while (!subFiles.isEmpty()) {
|
if (task == this) return true;
|
||||||
Path subFile = subFiles.getLast();
|
if (task instanceof MapStoragePurgeTask) {
|
||||||
FileUtils.delete(subFile.toFile());
|
return map.equals(((MapStoragePurgeTask) task).map);
|
||||||
subFiles.removeLast();
|
|
||||||
if (this.cancelled) return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure everything is deleted
|
return false;
|
||||||
FileUtils.delete(directory.toFile());
|
}
|
||||||
} finally {
|
|
||||||
// reset map render state
|
@Override
|
||||||
if (this.map != null) {
|
public String getDescription() {
|
||||||
this.map.getRenderState().reset();
|
return "Purge Map " + map.getId();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasMoreWork() {
|
|
||||||
return this.hasMoreWork;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public double estimateProgress() {
|
|
||||||
return 1d - (subFiles.size() / (double) subFilesCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void cancel() {
|
|
||||||
this.cancelled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean contains(RenderTask task) {
|
|
||||||
if (task == this) return true;
|
|
||||||
if (task instanceof MapPurgeTask) {
|
|
||||||
return ((MapPurgeTask) task).directory.toAbsolutePath().normalize().startsWith(this.directory.toAbsolutePath().normalize());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
}
|
||||||
public String getDescription() {
|
|
||||||
return "Purge Map " + directory.getFileName();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -28,6 +28,7 @@
|
|||||||
import com.flowpowered.math.vector.Vector3i;
|
import com.flowpowered.math.vector.Vector3i;
|
||||||
import de.bluecolored.bluemap.core.debug.DebugDump;
|
import de.bluecolored.bluemap.core.debug.DebugDump;
|
||||||
import de.bluecolored.bluemap.core.map.MapSettings;
|
import de.bluecolored.bluemap.core.map.MapSettings;
|
||||||
|
import de.bluecolored.bluemap.core.storage.Compression;
|
||||||
import de.bluecolored.bluemap.core.util.ConfigUtils;
|
import de.bluecolored.bluemap.core.util.ConfigUtils;
|
||||||
import org.spongepowered.configurate.ConfigurationNode;
|
import org.spongepowered.configurate.ConfigurationNode;
|
||||||
|
|
||||||
@ -53,7 +54,7 @@ public class MapConfig implements MapSettings {
|
|||||||
private Vector3i min, max;
|
private Vector3i min, max;
|
||||||
private boolean renderEdges;
|
private boolean renderEdges;
|
||||||
|
|
||||||
private boolean useGzip;
|
private Compression compression;
|
||||||
private boolean ignoreMissingLightData;
|
private boolean ignoreMissingLightData;
|
||||||
|
|
||||||
private int hiresTileSize;
|
private int hiresTileSize;
|
||||||
@ -106,7 +107,7 @@ public MapConfig(ConfigurationNode node) throws IOException {
|
|||||||
this.renderEdges = node.node("renderEdges").getBoolean(true);
|
this.renderEdges = node.node("renderEdges").getBoolean(true);
|
||||||
|
|
||||||
//useCompression
|
//useCompression
|
||||||
this.useGzip = node.node("useCompression").getBoolean(true);
|
this.compression = node.node("useCompression").getBoolean(true) ? Compression.GZIP : Compression.NONE;
|
||||||
|
|
||||||
//ignoreMissingLightData
|
//ignoreMissingLightData
|
||||||
this.ignoreMissingLightData = node.node("ignoreMissingLightData").getBoolean(false);
|
this.ignoreMissingLightData = node.node("ignoreMissingLightData").getBoolean(false);
|
||||||
@ -142,6 +143,10 @@ public int getSkyColor() {
|
|||||||
return skyColor;
|
return skyColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Compression getCompression() {
|
||||||
|
return compression;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getAmbientLight() {
|
public float getAmbientLight() {
|
||||||
return ambientLight;
|
return ambientLight;
|
||||||
@ -196,9 +201,4 @@ public boolean isRenderEdges() {
|
|||||||
return renderEdges;
|
return renderEdges;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean useGzipCompression() {
|
|
||||||
return useGzip;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -35,10 +35,10 @@
|
|||||||
@DebugDump
|
@DebugDump
|
||||||
public class RenderConfig {
|
public class RenderConfig {
|
||||||
|
|
||||||
private File webRoot = new File("web");
|
private File webRoot;
|
||||||
private boolean useCookies;
|
private boolean useCookies;
|
||||||
private boolean enableFreeFlight;
|
private boolean enableFreeFlight;
|
||||||
private List<MapConfig> mapConfigs = new ArrayList<>();
|
private List<MapConfig> mapConfigs;
|
||||||
|
|
||||||
public RenderConfig(ConfigurationNode node) throws IOException {
|
public RenderConfig(ConfigurationNode node) throws IOException {
|
||||||
|
|
||||||
|
@ -28,16 +28,19 @@
|
|||||||
import de.bluecolored.bluemap.core.debug.DebugDump;
|
import de.bluecolored.bluemap.core.debug.DebugDump;
|
||||||
import de.bluecolored.bluemap.core.logger.Logger;
|
import de.bluecolored.bluemap.core.logger.Logger;
|
||||||
import de.bluecolored.bluemap.core.map.hires.HiresModelManager;
|
import de.bluecolored.bluemap.core.map.hires.HiresModelManager;
|
||||||
import de.bluecolored.bluemap.core.map.lowres.LowresModelManager;
|
|
||||||
import de.bluecolored.bluemap.core.map.hires.HiresTileMeta;
|
import de.bluecolored.bluemap.core.map.hires.HiresTileMeta;
|
||||||
|
import de.bluecolored.bluemap.core.map.lowres.LowresModelManager;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
||||||
|
import de.bluecolored.bluemap.core.storage.*;
|
||||||
import de.bluecolored.bluemap.core.world.Grid;
|
import de.bluecolored.bluemap.core.world.Grid;
|
||||||
import de.bluecolored.bluemap.core.world.World;
|
import de.bluecolored.bluemap.core.world.World;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
@DebugDump
|
@DebugDump
|
||||||
@ -46,7 +49,7 @@ public class BmMap {
|
|||||||
private final String id;
|
private final String id;
|
||||||
private final String name;
|
private final String name;
|
||||||
private final World world;
|
private final World world;
|
||||||
private final Path fileRoot;
|
private final Storage storage;
|
||||||
|
|
||||||
private final MapRenderState renderState;
|
private final MapRenderState renderState;
|
||||||
|
|
||||||
@ -58,38 +61,37 @@ public class BmMap {
|
|||||||
private long renderTimeSumNanos;
|
private long renderTimeSumNanos;
|
||||||
private long tilesRendered;
|
private long tilesRendered;
|
||||||
|
|
||||||
public BmMap(String id, String name, World world, Path fileRoot, ResourcePack resourcePack, MapSettings settings) throws IOException {
|
public BmMap(String id, String name, World world, Storage storage, ResourcePack resourcePack, MapSettings settings) throws IOException {
|
||||||
this.id = Objects.requireNonNull(id);
|
this.id = Objects.requireNonNull(id);
|
||||||
this.name = Objects.requireNonNull(name);
|
this.name = Objects.requireNonNull(name);
|
||||||
this.world = Objects.requireNonNull(world);
|
this.world = Objects.requireNonNull(world);
|
||||||
this.fileRoot = Objects.requireNonNull(fileRoot);
|
this.storage = Objects.requireNonNull(storage);
|
||||||
|
|
||||||
Objects.requireNonNull(resourcePack);
|
Objects.requireNonNull(resourcePack);
|
||||||
Objects.requireNonNull(settings);
|
Objects.requireNonNull(settings);
|
||||||
|
|
||||||
this.renderState = new MapRenderState();
|
this.renderState = new MapRenderState();
|
||||||
|
|
||||||
File rstateFile = getRenderStateFile();
|
Optional<InputStream> rstateData = storage.readMeta(id, MetaType.RENDER_STATE);
|
||||||
if (rstateFile.exists()) {
|
if (rstateData.isPresent()) {
|
||||||
try {
|
try (InputStream in = rstateData.get()){
|
||||||
this.renderState.load(rstateFile);
|
this.renderState.load(in);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
Logger.global.logWarning("Failed to load render-state for map '" + getId() + "': " + ex);
|
Logger.global.logWarning("Failed to load render-state for map '" + getId() + "': " + ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.hiresModelManager = new HiresModelManager(
|
this.hiresModelManager = new HiresModelManager(
|
||||||
fileRoot.resolve("hires"),
|
storage.tileStorage(id, TileType.HIRES),
|
||||||
resourcePack,
|
resourcePack,
|
||||||
settings,
|
settings,
|
||||||
new Grid(settings.getHiresTileSize(), 2)
|
new Grid(settings.getHiresTileSize(), 2)
|
||||||
);
|
);
|
||||||
|
|
||||||
this.lowresModelManager = new LowresModelManager(
|
this.lowresModelManager = new LowresModelManager(
|
||||||
fileRoot.resolve("lowres"),
|
storage.tileStorage(id, TileType.LOWRES),
|
||||||
new Vector2i(settings.getLowresPointsPerLowresTile(), settings.getLowresPointsPerLowresTile()),
|
new Vector2i(settings.getLowresPointsPerLowresTile(), settings.getLowresPointsPerLowresTile()),
|
||||||
new Vector2i(settings.getLowresPointsPerHiresTile(), settings.getLowresPointsPerHiresTile()),
|
new Vector2i(settings.getLowresPointsPerHiresTile(), settings.getLowresPointsPerHiresTile())
|
||||||
settings.useGzipCompression()
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this.tileFilter = t -> true;
|
this.tileFilter = t -> true;
|
||||||
@ -116,17 +118,13 @@ public void renderTile(Vector2i tile) {
|
|||||||
public synchronized void save() {
|
public synchronized void save() {
|
||||||
lowresModelManager.save();
|
lowresModelManager.save();
|
||||||
|
|
||||||
try {
|
try (OutputStream out = storage.writeMeta(id, MetaType.RENDER_STATE)) {
|
||||||
this.renderState.save(getRenderStateFile());
|
this.renderState.save(out);
|
||||||
} catch (IOException ex){
|
} catch (IOException ex){
|
||||||
Logger.global.logError("Failed to save render-state for map: '" + this.id + "'!", ex);
|
Logger.global.logError("Failed to save render-state for map: '" + this.id + "'!", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public File getRenderStateFile() {
|
|
||||||
return fileRoot.resolve(".rstate").toFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@ -139,8 +137,8 @@ public World getWorld() {
|
|||||||
return world;
|
return world;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Path getFileRoot() {
|
public Storage getStorage() {
|
||||||
return fileRoot;
|
return storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MapRenderState getRenderState() {
|
public MapRenderState getRenderState() {
|
||||||
@ -189,7 +187,7 @@ public String toString() {
|
|||||||
"id='" + id + '\'' +
|
"id='" + id + '\'' +
|
||||||
", name='" + name + '\'' +
|
", name='" + name + '\'' +
|
||||||
", world=" + world +
|
", world=" + world +
|
||||||
", fileRoot=" + fileRoot +
|
", storage=" + storage +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
|
|
||||||
import com.flowpowered.math.vector.Vector2i;
|
import com.flowpowered.math.vector.Vector2i;
|
||||||
import de.bluecolored.bluemap.core.debug.DebugDump;
|
import de.bluecolored.bluemap.core.debug.DebugDump;
|
||||||
import de.bluecolored.bluemap.core.util.AtomicFileHelper;
|
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -57,12 +56,9 @@ public synchronized void reset() {
|
|||||||
regionRenderTimes.clear();
|
regionRenderTimes.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void save(File file) throws IOException {
|
public synchronized void save(OutputStream out) throws IOException {
|
||||||
OutputStream fOut = AtomicFileHelper.createFilepartOutputStream(file);
|
|
||||||
GZIPOutputStream gOut = new GZIPOutputStream(fOut);
|
|
||||||
|
|
||||||
try (
|
try (
|
||||||
DataOutputStream dOut = new DataOutputStream(gOut)
|
DataOutputStream dOut = new DataOutputStream(new GZIPOutputStream(out))
|
||||||
) {
|
) {
|
||||||
dOut.writeInt(regionRenderTimes.size());
|
dOut.writeInt(regionRenderTimes.size());
|
||||||
|
|
||||||
@ -79,13 +75,11 @@ public synchronized void save(File file) throws IOException {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void load(File file) throws IOException {
|
public synchronized void load(InputStream in) throws IOException {
|
||||||
regionRenderTimes.clear();
|
regionRenderTimes.clear();
|
||||||
|
|
||||||
try (
|
try (
|
||||||
FileInputStream fIn = new FileInputStream(file);
|
DataInputStream dIn = new DataInputStream(new GZIPInputStream(in))
|
||||||
GZIPInputStream gIn = new GZIPInputStream(fIn);
|
|
||||||
DataInputStream dIn = new DataInputStream(gIn)
|
|
||||||
) {
|
) {
|
||||||
int size = dIn.readInt();
|
int size = dIn.readInt();
|
||||||
|
|
||||||
@ -98,7 +92,7 @@ public synchronized void load(File file) throws IOException {
|
|||||||
|
|
||||||
regionRenderTimes.put(regionPos, renderTime);
|
regionRenderTimes.put(regionPos, renderTime);
|
||||||
}
|
}
|
||||||
}
|
} catch (EOFException ignore){} // ignoring a sudden end of stream, since it is save to only read as many as we can
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,34 +28,27 @@
|
|||||||
import com.flowpowered.math.vector.Vector3i;
|
import com.flowpowered.math.vector.Vector3i;
|
||||||
import de.bluecolored.bluemap.core.logger.Logger;
|
import de.bluecolored.bluemap.core.logger.Logger;
|
||||||
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
|
||||||
import de.bluecolored.bluemap.core.util.AtomicFileHelper;
|
import de.bluecolored.bluemap.core.storage.Storage;
|
||||||
import de.bluecolored.bluemap.core.util.FileUtils;
|
|
||||||
import de.bluecolored.bluemap.core.world.Grid;
|
import de.bluecolored.bluemap.core.world.Grid;
|
||||||
import de.bluecolored.bluemap.core.world.World;
|
import de.bluecolored.bluemap.core.world.World;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.zip.GZIPOutputStream;
|
|
||||||
|
|
||||||
public class HiresModelManager {
|
public class HiresModelManager {
|
||||||
|
|
||||||
private final Path fileRoot;
|
private final Storage.TileStorage storage;
|
||||||
private final HiresModelRenderer renderer;
|
private final HiresModelRenderer renderer;
|
||||||
private final Grid tileGrid;
|
private final Grid tileGrid;
|
||||||
private final boolean useGzip;
|
|
||||||
|
|
||||||
public HiresModelManager(Path fileRoot, ResourcePack resourcePack, RenderSettings renderSettings, Grid tileGrid) {
|
public HiresModelManager(Storage.TileStorage storage, ResourcePack resourcePack, RenderSettings renderSettings, Grid tileGrid) {
|
||||||
this(fileRoot, new HiresModelRenderer(resourcePack, renderSettings), tileGrid, renderSettings.useGzipCompression());
|
this(storage, new HiresModelRenderer(resourcePack, renderSettings), tileGrid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HiresModelManager(Path fileRoot, HiresModelRenderer renderer, Grid tileGrid, boolean useGzip) {
|
public HiresModelManager(Storage.TileStorage storage, HiresModelRenderer renderer, Grid tileGrid) {
|
||||||
this.fileRoot = fileRoot;
|
this.storage = storage;
|
||||||
this.renderer = renderer;
|
this.renderer = renderer;
|
||||||
|
|
||||||
this.tileGrid = tileGrid;
|
this.tileGrid = tileGrid;
|
||||||
|
|
||||||
this.useGzip = useGzip;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -79,25 +72,10 @@ public HiresTileMeta render(World world, Vector2i tile) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void save(final HiresTileModel model, Vector2i tile) {
|
private void save(final HiresTileModel model, Vector2i tile) {
|
||||||
File file = getFile(tile, useGzip);
|
try (OutputStream os = storage.write(tile)) {
|
||||||
|
|
||||||
OutputStream os = null;
|
|
||||||
try {
|
|
||||||
os = AtomicFileHelper.createFilepartOutputStream(file);
|
|
||||||
os = new BufferedOutputStream(os);
|
|
||||||
if (useGzip) os = new GZIPOutputStream(os);
|
|
||||||
|
|
||||||
model.writeBufferGeometryJson(os);
|
model.writeBufferGeometryJson(os);
|
||||||
} catch (IOException e){
|
} catch (IOException e){
|
||||||
Logger.global.logError("Failed to save hires model: " + file, e);
|
Logger.global.logError("Failed to save hires model: " + tile, e);
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
if (os != null) {
|
|
||||||
os.close();
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
Logger.global.logError("Failed to close file: " + file, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,11 +86,4 @@ public Grid getTileGrid() {
|
|||||||
return tileGrid;
|
return tileGrid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the file for a tile
|
|
||||||
*/
|
|
||||||
public File getFile(Vector2i tilePos, boolean gzip){
|
|
||||||
return FileUtils.coordsToFile(fileRoot, tilePos, "json" + (gzip ? ".gz" : ""));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -73,13 +73,6 @@ default boolean isRenderEdges() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* If gzip compression will be used to compress the generated files
|
|
||||||
*/
|
|
||||||
default boolean useGzipCompression() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
default boolean isInsideRenderBoundaries(int x, int z) {
|
default boolean isInsideRenderBoundaries(int x, int z) {
|
||||||
Vector3i min = getMin();
|
Vector3i min = getMin();
|
||||||
Vector3i max = getMax();
|
Vector3i max = getMax();
|
||||||
|
@ -26,16 +26,17 @@
|
|||||||
|
|
||||||
import com.flowpowered.math.vector.Vector2i;
|
import com.flowpowered.math.vector.Vector2i;
|
||||||
import com.flowpowered.math.vector.Vector3f;
|
import com.flowpowered.math.vector.Vector3f;
|
||||||
|
import de.bluecolored.bluemap.core.storage.Storage;
|
||||||
import de.bluecolored.bluemap.core.threejs.BufferGeometry;
|
import de.bluecolored.bluemap.core.threejs.BufferGeometry;
|
||||||
import de.bluecolored.bluemap.core.util.AtomicFileHelper;
|
|
||||||
import de.bluecolored.bluemap.core.util.MathUtils;
|
import de.bluecolored.bluemap.core.util.MathUtils;
|
||||||
import de.bluecolored.bluemap.core.util.ModelUtils;
|
import de.bluecolored.bluemap.core.util.ModelUtils;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.io.PrintWriter;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.zip.GZIPOutputStream;
|
|
||||||
|
|
||||||
public class LowresModel {
|
public class LowresModel {
|
||||||
|
|
||||||
@ -79,7 +80,7 @@ public void update(Vector2i point, float height, Vector3f color){
|
|||||||
* Saves this model to its file
|
* Saves this model to its file
|
||||||
* @param force if this is false, the model is only saved if it has any changes
|
* @param force if this is false, the model is only saved if it has any changes
|
||||||
*/
|
*/
|
||||||
public void save(File file, boolean force, boolean useGzip) throws IOException {
|
public void save(Storage.TileStorage storage, Vector2i tile, boolean force) throws IOException {
|
||||||
if (!force && !hasUnsavedChanges) return;
|
if (!force && !hasUnsavedChanges) return;
|
||||||
this.hasUnsavedChanges = false;
|
this.hasUnsavedChanges = false;
|
||||||
|
|
||||||
@ -91,11 +92,9 @@ public void save(File file, boolean force, boolean useGzip) throws IOException {
|
|||||||
}
|
}
|
||||||
|
|
||||||
synchronized (fileLock) {
|
synchronized (fileLock) {
|
||||||
OutputStream os = new BufferedOutputStream(AtomicFileHelper.createFilepartOutputStream(file));
|
|
||||||
if (useGzip) os = new GZIPOutputStream(os);
|
|
||||||
OutputStreamWriter osw = new OutputStreamWriter(os, StandardCharsets.UTF_8);
|
|
||||||
try (
|
try (
|
||||||
PrintWriter pw = new PrintWriter(osw)
|
PrintWriter pw = new PrintWriter(
|
||||||
|
new OutputStreamWriter(storage.write(tile), StandardCharsets.UTF_8))
|
||||||
){
|
){
|
||||||
pw.print(json);
|
pw.print(json);
|
||||||
}
|
}
|
||||||
|
@ -28,42 +28,36 @@
|
|||||||
import com.flowpowered.math.vector.Vector3f;
|
import com.flowpowered.math.vector.Vector3f;
|
||||||
import de.bluecolored.bluemap.core.logger.Logger;
|
import de.bluecolored.bluemap.core.logger.Logger;
|
||||||
import de.bluecolored.bluemap.core.map.hires.HiresTileMeta;
|
import de.bluecolored.bluemap.core.map.hires.HiresTileMeta;
|
||||||
|
import de.bluecolored.bluemap.core.storage.Storage;
|
||||||
import de.bluecolored.bluemap.core.threejs.BufferGeometry;
|
import de.bluecolored.bluemap.core.threejs.BufferGeometry;
|
||||||
import de.bluecolored.bluemap.core.util.FileUtils;
|
|
||||||
import de.bluecolored.bluemap.core.util.math.Color;
|
import de.bluecolored.bluemap.core.util.math.Color;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.zip.GZIPInputStream;
|
|
||||||
|
|
||||||
public class LowresModelManager {
|
public class LowresModelManager {
|
||||||
|
|
||||||
private final Path fileRoot;
|
private final Storage.TileStorage storage;
|
||||||
private final Vector2i pointsPerLowresTile;
|
private final Vector2i pointsPerLowresTile;
|
||||||
private final Vector2i pointsPerHiresTile;
|
private final Vector2i pointsPerHiresTile;
|
||||||
private final boolean useGzip;
|
|
||||||
|
|
||||||
private final Map<File, CachedModel> models;
|
private final Map<Vector2i, CachedModel> models;
|
||||||
|
|
||||||
public LowresModelManager(Path fileRoot, Vector2i pointsPerLowresTile, Vector2i pointsPerHiresTile, boolean useGzip) {
|
public LowresModelManager(Storage.TileStorage storage, Vector2i pointsPerLowresTile, Vector2i pointsPerHiresTile) {
|
||||||
this.fileRoot = fileRoot;
|
this.storage = storage;
|
||||||
|
|
||||||
this.pointsPerLowresTile = pointsPerLowresTile;
|
this.pointsPerLowresTile = pointsPerLowresTile;
|
||||||
this.pointsPerHiresTile = pointsPerHiresTile;
|
this.pointsPerHiresTile = pointsPerHiresTile;
|
||||||
|
|
||||||
models = new ConcurrentHashMap<>();
|
models = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
this.useGzip = useGzip;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -116,7 +110,7 @@ public void render(HiresTileMeta tileMeta) {
|
|||||||
* Saves all unsaved changes to the models to disk
|
* Saves all unsaved changes to the models to disk
|
||||||
*/
|
*/
|
||||||
public synchronized void save(){
|
public synchronized void save(){
|
||||||
for (Entry<File, CachedModel> entry : models.entrySet()){
|
for (Entry<Vector2i, CachedModel> entry : models.entrySet()){
|
||||||
saveModel(entry.getKey(), entry.getValue());
|
saveModel(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,47 +153,39 @@ public void update(int px, int pz, float height, Color color) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the file for a tile
|
|
||||||
*/
|
|
||||||
public File getFile(Vector2i tile, boolean useGzip){
|
|
||||||
return FileUtils.coordsToFile(fileRoot, tile, "json" + (useGzip ? ".gz" : ""));
|
|
||||||
}
|
|
||||||
|
|
||||||
private LowresModel getModel(Vector2i tile) {
|
private LowresModel getModel(Vector2i tile) {
|
||||||
|
|
||||||
File modelFile = getFile(tile, useGzip);
|
CachedModel model = models.get(tile);
|
||||||
CachedModel model = models.get(modelFile);
|
|
||||||
|
|
||||||
if (model == null){
|
if (model == null){
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
model = models.get(modelFile);
|
model = models.get(tile);
|
||||||
if (model == null){
|
if (model == null){
|
||||||
|
|
||||||
if (modelFile.exists()){
|
try {
|
||||||
try (FileInputStream fis = new FileInputStream(modelFile)) {
|
Optional<InputStream> optIs = storage.read(tile);
|
||||||
InputStream is = fis;
|
if (optIs.isPresent()){
|
||||||
if (useGzip) is = new GZIPInputStream(is);
|
try (InputStream is = optIs.get()) {
|
||||||
|
String json = IOUtils.toString(is, StandardCharsets.UTF_8);
|
||||||
|
|
||||||
String json = IOUtils.toString(is, StandardCharsets.UTF_8);
|
model = new CachedModel(BufferGeometry.fromJson(json));
|
||||||
|
|
||||||
model = new CachedModel(BufferGeometry.fromJson(json));
|
|
||||||
} catch (IllegalArgumentException | IOException ex){
|
|
||||||
Logger.global.logWarning("Failed to load lowres model '" + modelFile + "': " + ex);
|
|
||||||
|
|
||||||
try {
|
|
||||||
FileUtils.delete(modelFile);
|
|
||||||
} catch (IOException ex2) {
|
|
||||||
Logger.global.logError("Failed to delete lowres-file: " + modelFile, ex2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (IllegalArgumentException | IOException ex){
|
||||||
|
Logger.global.logWarning("Failed to load lowres model '" + tile + "': " + ex);
|
||||||
|
|
||||||
|
try {
|
||||||
|
storage.delete(tile);
|
||||||
|
} catch (IOException ex2) {
|
||||||
|
Logger.global.logError("Failed to delete lowres-file: " + tile, ex2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model == null){
|
if (model == null){
|
||||||
model = new CachedModel(pointsPerLowresTile);
|
model = new CachedModel(pointsPerLowresTile);
|
||||||
}
|
}
|
||||||
|
|
||||||
models.put(modelFile, model);
|
models.put(tile, model);
|
||||||
|
|
||||||
tidyUpModelCache();
|
tidyUpModelCache();
|
||||||
}
|
}
|
||||||
@ -217,12 +203,12 @@ private LowresModel getModel(Vector2i tile) {
|
|||||||
* This method gets automatically called if the cache grows, but if you want to ensure model will be saved after 2 minutes, you could e.g call this method every second.<br>
|
* This method gets automatically called if the cache grows, but if you want to ensure model will be saved after 2 minutes, you could e.g call this method every second.<br>
|
||||||
*/
|
*/
|
||||||
public synchronized void tidyUpModelCache() {
|
public synchronized void tidyUpModelCache() {
|
||||||
List<Entry<File, CachedModel>> entries = new ArrayList<>(models.size());
|
List<Entry<Vector2i, CachedModel>> entries = new ArrayList<>(models.size());
|
||||||
entries.addAll(models.entrySet());
|
entries.addAll(models.entrySet());
|
||||||
entries.sort((e1, e2) -> (int) Math.signum(e1.getValue().cacheTime - e2.getValue().cacheTime));
|
entries.sort((e1, e2) -> (int) Math.signum(e1.getValue().cacheTime - e2.getValue().cacheTime));
|
||||||
|
|
||||||
int size = entries.size();
|
int size = entries.size();
|
||||||
for (Entry<File, CachedModel> e : entries) {
|
for (Entry<Vector2i, CachedModel> e : entries) {
|
||||||
if (size > 10) {
|
if (size > 10) {
|
||||||
saveAndRemoveModel(e.getKey(), e.getValue());
|
saveAndRemoveModel(e.getKey(), e.getValue());
|
||||||
continue;
|
continue;
|
||||||
@ -234,22 +220,22 @@ public synchronized void tidyUpModelCache() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void saveAndRemoveModel(File modelFile, CachedModel model) {
|
private synchronized void saveAndRemoveModel(Vector2i tile, CachedModel model) {
|
||||||
models.remove(modelFile);
|
models.remove(tile);
|
||||||
try {
|
try {
|
||||||
model.save(modelFile, false, useGzip);
|
model.save(storage, tile,false);
|
||||||
//logger.logDebug("Saved and unloaded lowres tile: " + model.getTile());
|
//logger.logDebug("Saved and unloaded lowres tile: " + model.getTile());
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
Logger.global.logError("Failed to save and unload lowres-model: " + modelFile, ex);
|
Logger.global.logError("Failed to save and unload lowres-model: " + tile, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveModel(File modelFile, CachedModel model) {
|
private void saveModel(Vector2i tile, CachedModel model) {
|
||||||
try {
|
try {
|
||||||
model.save(modelFile, false, useGzip);
|
model.save(storage, tile, false);
|
||||||
//logger.logDebug("Saved lowres tile: " + model.getTile());
|
//logger.logDebug("Saved lowres tile: " + model.getTile());
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
Logger.global.logError("Failed to save lowres-model: " + modelFile, ex);
|
Logger.global.logError("Failed to save lowres-model: " + tile, ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
model.resetCacheTime();
|
model.resetCacheTime();
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* 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.storage;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
import java.util.zip.GZIPOutputStream;
|
||||||
|
|
||||||
|
public enum Compression {
|
||||||
|
|
||||||
|
NONE("") {
|
||||||
|
@Override
|
||||||
|
public OutputStream compress(OutputStream out) {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream decompress(InputStream in) {
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
GZIP(".gz"){
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OutputStream compress(OutputStream out) throws IOException {
|
||||||
|
return new GZIPOutputStream(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream decompress(InputStream in) throws IOException {
|
||||||
|
return new GZIPInputStream(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
private final String fileSuffix;
|
||||||
|
|
||||||
|
Compression(String fileSuffix) {
|
||||||
|
this.fileSuffix = fileSuffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFileSuffix() {
|
||||||
|
return fileSuffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract OutputStream compress(OutputStream out) throws IOException;
|
||||||
|
|
||||||
|
public abstract InputStream decompress(InputStream in) throws IOException;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
* 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.storage;
|
||||||
|
|
||||||
|
import com.flowpowered.math.vector.Vector2i;
|
||||||
|
import de.bluecolored.bluemap.core.debug.DebugDump;
|
||||||
|
import de.bluecolored.bluemap.core.util.AtomicFileHelper;
|
||||||
|
import de.bluecolored.bluemap.core.util.FileUtils;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@DebugDump
|
||||||
|
public class FileStorage extends Storage {
|
||||||
|
|
||||||
|
private static final EnumMap<MetaType, String> metaTypeFileNames = new EnumMap<>(MetaType.class);
|
||||||
|
static {
|
||||||
|
metaTypeFileNames.put(MetaType.TEXTURES, "../textures.json");
|
||||||
|
metaTypeFileNames.put(MetaType.SETTINGS, "../settings.json");
|
||||||
|
metaTypeFileNames.put(MetaType.MARKERS, "../markers.json");
|
||||||
|
metaTypeFileNames.put(MetaType.RENDER_STATE, ".rstate");
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Path root;
|
||||||
|
private final Compression compression;
|
||||||
|
|
||||||
|
public FileStorage(Path root, Compression compression) {
|
||||||
|
this.root = root;
|
||||||
|
this.compression = compression;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OutputStream writeMapTile(String mapId, TileType tileType, Vector2i tile) throws IOException {
|
||||||
|
Path file = getFilePath(mapId, tileType, tile);
|
||||||
|
|
||||||
|
OutputStream os = AtomicFileHelper.createFilepartOutputStream(file);
|
||||||
|
os = new BufferedOutputStream(os);
|
||||||
|
os = compression.compress(os);
|
||||||
|
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<InputStream> readMapTile(String mapId, TileType tileType, Vector2i tile) throws IOException {
|
||||||
|
Path file = getFilePath(mapId, tileType, tile);
|
||||||
|
|
||||||
|
if (!Files.exists(file)) return Optional.empty();
|
||||||
|
|
||||||
|
InputStream is = Files.newInputStream(file, StandardOpenOption.READ);
|
||||||
|
is = new BufferedInputStream(is);
|
||||||
|
|
||||||
|
return Optional.of(is);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteMapTile(String mapId, TileType tileType, Vector2i tile) throws IOException {
|
||||||
|
Path file = getFilePath(mapId, tileType, tile);
|
||||||
|
FileUtils.delete(file.toFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OutputStream writeMeta(String mapId, MetaType metaType) throws IOException {
|
||||||
|
Path file = getFilePath(mapId).resolve(getFilename(metaType));
|
||||||
|
|
||||||
|
OutputStream os = AtomicFileHelper.createFilepartOutputStream(file);
|
||||||
|
os = new BufferedOutputStream(os);
|
||||||
|
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<InputStream> readMeta(String mapId, MetaType metaType) throws IOException {
|
||||||
|
Path file = getFilePath(mapId).resolve(getFilename(metaType));
|
||||||
|
|
||||||
|
if (!Files.exists(file)) return Optional.empty();
|
||||||
|
|
||||||
|
InputStream is = Files.newInputStream(file, StandardOpenOption.READ);
|
||||||
|
is = new BufferedInputStream(is);
|
||||||
|
|
||||||
|
return Optional.of(is);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void purgeMap(String mapId) throws IOException {
|
||||||
|
FileUtils.delete(getFilePath(mapId).toFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Path getFilePath(String mapId, TileType tileType, Vector2i tile){
|
||||||
|
String path = "x" + tile.getX() + "z" + tile.getY();
|
||||||
|
char[] cs = path.toCharArray();
|
||||||
|
List<String> folders = new ArrayList<>();
|
||||||
|
StringBuilder folder = new StringBuilder();
|
||||||
|
for (char c : cs){
|
||||||
|
folder.append(c);
|
||||||
|
if (c >= '0' && c <= '9'){
|
||||||
|
folders.add(folder.toString());
|
||||||
|
folder.delete(0, folder.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String fileName = folders.remove(folders.size() - 1);
|
||||||
|
|
||||||
|
Path p = getFilePath(mapId).resolve(tileType.getTypeId());
|
||||||
|
for (String s : folders){
|
||||||
|
p = p.resolve(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.resolve(fileName + ".json" + compression.getFileSuffix());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Path getFilePath(String mapId) {
|
||||||
|
return root.resolve(mapId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getFilename(MetaType metaType) {
|
||||||
|
return metaTypeFileNames.getOrDefault(metaType, metaType.name().toLowerCase(Locale.ROOT));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package de.bluecolored.bluemap.core.storage;
|
||||||
|
|
||||||
|
public enum MetaType {
|
||||||
|
|
||||||
|
TEXTURES,
|
||||||
|
SETTINGS,
|
||||||
|
MARKERS,
|
||||||
|
RENDER_STATE
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* 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.storage;
|
||||||
|
|
||||||
|
import com.flowpowered.math.vector.Vector2i;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public abstract class Storage {
|
||||||
|
|
||||||
|
public abstract OutputStream writeMapTile(String mapId, TileType tileType, Vector2i tile) throws IOException;
|
||||||
|
|
||||||
|
public abstract Optional<InputStream> readMapTile(String mapId, TileType tileType, Vector2i tile) throws IOException;
|
||||||
|
|
||||||
|
public abstract void deleteMapTile(String mapId, TileType tileType, Vector2i tile) throws IOException;
|
||||||
|
|
||||||
|
public abstract OutputStream writeMeta(String mapId, MetaType metaType) throws IOException;
|
||||||
|
|
||||||
|
public abstract Optional<InputStream> readMeta(String mapId, MetaType metaType) throws IOException;
|
||||||
|
|
||||||
|
public abstract void purgeMap(String mapId) throws IOException;
|
||||||
|
|
||||||
|
public TileStorage tileStorage(final String mapId, final TileType tileType) {
|
||||||
|
return new TileStorage(mapId, tileType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TileStorage {
|
||||||
|
|
||||||
|
private final String mapId;
|
||||||
|
private final TileType tileType;
|
||||||
|
|
||||||
|
private TileStorage(String mapId, TileType tileType) {
|
||||||
|
this.mapId = mapId;
|
||||||
|
this.tileType = tileType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OutputStream write(Vector2i tile) throws IOException {
|
||||||
|
return Storage.this.writeMapTile(mapId, tileType, tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<InputStream> read(Vector2i tile) throws IOException {
|
||||||
|
return Storage.this.readMapTile(mapId, tileType, tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete(Vector2i tile) throws IOException {
|
||||||
|
deleteMapTile(mapId, tileType, tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* 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.storage;
|
||||||
|
|
||||||
|
public enum TileType {
|
||||||
|
|
||||||
|
HIRES ("hires"),
|
||||||
|
LOWRES ("lowres");
|
||||||
|
|
||||||
|
private final String typeId;
|
||||||
|
|
||||||
|
TileType(String typeId) {
|
||||||
|
this.typeId = typeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTypeId() {
|
||||||
|
return typeId;
|
||||||
|
}
|
||||||
|
}
|
@ -31,8 +31,6 @@
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class FileUtils {
|
public class FileUtils {
|
||||||
|
@ -47,6 +47,7 @@
|
|||||||
import org.spongepowered.api.config.ConfigDir;
|
import org.spongepowered.api.config.ConfigDir;
|
||||||
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
|
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
|
||||||
import org.spongepowered.api.event.Listener;
|
import org.spongepowered.api.event.Listener;
|
||||||
|
import org.spongepowered.api.event.block.TickBlockEvent;
|
||||||
import org.spongepowered.api.event.lifecycle.RefreshGameEvent;
|
import org.spongepowered.api.event.lifecycle.RefreshGameEvent;
|
||||||
import org.spongepowered.api.event.lifecycle.RegisterCommandEvent;
|
import org.spongepowered.api.event.lifecycle.RegisterCommandEvent;
|
||||||
import org.spongepowered.api.event.lifecycle.StartedEngineEvent;
|
import org.spongepowered.api.event.lifecycle.StartedEngineEvent;
|
||||||
|
Loading…
Reference in New Issue
Block a user