forked from mirror/BlueMap
Make lowres tile saving even more robust
This commit is contained in:
parent
62174c60c5
commit
f42ebdbc79
@ -52,8 +52,8 @@ public void doWork() throws Exception {
|
|||||||
}
|
}
|
||||||
if (this.cancelled) return;
|
if (this.cancelled) return;
|
||||||
|
|
||||||
// save lowres-tile-manager to clear/flush any buffered data
|
// discard any pending lowres changes
|
||||||
this.map.getLowresTileManager().save();
|
this.map.getLowresTileManager().discard();
|
||||||
|
|
||||||
// purge the map
|
// purge the map
|
||||||
map.getStorage().delete(progress -> {
|
map.getStorage().delete(progress -> {
|
||||||
|
@ -27,8 +27,6 @@
|
|||||||
import com.flowpowered.math.vector.Vector2i;
|
import com.flowpowered.math.vector.Vector2i;
|
||||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||||
import com.github.benmanes.caffeine.cache.RemovalCause;
|
|
||||||
import com.github.benmanes.caffeine.cache.Scheduler;
|
|
||||||
import de.bluecolored.bluemap.core.BlueMap;
|
import de.bluecolored.bluemap.core.BlueMap;
|
||||||
import de.bluecolored.bluemap.core.logger.Logger;
|
import de.bluecolored.bluemap.core.logger.Logger;
|
||||||
import de.bluecolored.bluemap.core.storage.GridStorage;
|
import de.bluecolored.bluemap.core.storage.GridStorage;
|
||||||
@ -40,10 +38,15 @@
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class LowresLayer {
|
public class LowresLayer {
|
||||||
|
|
||||||
|
private static final int MAX_PENDING = 200;
|
||||||
|
private static final int DISCARD_THRESHOLD = MAX_PENDING / 2;
|
||||||
|
|
||||||
private static final Vector2iCache VECTOR_2_I_CACHE = new Vector2iCache();
|
private static final Vector2iCache VECTOR_2_I_CACHE = new Vector2iCache();
|
||||||
|
|
||||||
private final GridStorage storage;
|
private final GridStorage storage;
|
||||||
@ -52,9 +55,12 @@ public class LowresLayer {
|
|||||||
private final int lodFactor;
|
private final int lodFactor;
|
||||||
|
|
||||||
private final int lod;
|
private final int lod;
|
||||||
|
private final LoadingCache<Vector2i, LowresTile> tileWeakInstanceCache;
|
||||||
private final LoadingCache<Vector2i, LowresTile> tileCache;
|
private final LoadingCache<Vector2i, LowresTile> tileCache;
|
||||||
@Nullable private final LowresLayer nextLayer;
|
@Nullable private final LowresLayer nextLayer;
|
||||||
|
|
||||||
|
private final Map<Vector2i, LowresTile> pendingChanges;
|
||||||
|
|
||||||
public LowresLayer(
|
public LowresLayer(
|
||||||
GridStorage storage, Grid tileGrid, int lodFactor,
|
GridStorage storage, Grid tileGrid, int lodFactor,
|
||||||
int lod, @Nullable LowresLayer nextLayer
|
int lod, @Nullable LowresLayer nextLayer
|
||||||
@ -69,23 +75,33 @@ public LowresLayer(
|
|||||||
|
|
||||||
// this extra cache makes sure that a tile instance is reused as long as it is still referenced somewhere ..
|
// this extra cache makes sure that a tile instance is reused as long as it is still referenced somewhere ..
|
||||||
// so always only one instance of the same lowres-tile exists
|
// so always only one instance of the same lowres-tile exists
|
||||||
LoadingCache<Vector2i, LowresTile> tileWeakInstanceCache = Caffeine.newBuilder()
|
this.tileWeakInstanceCache = Caffeine.newBuilder()
|
||||||
.executor(BlueMap.THREAD_POOL)
|
.executor(BlueMap.THREAD_POOL)
|
||||||
.weakValues()
|
.weakValues()
|
||||||
.build(this::createTile);
|
.build(this::createTile);
|
||||||
|
|
||||||
this.tileCache = Caffeine.newBuilder()
|
this.tileCache = Caffeine.newBuilder()
|
||||||
.executor(BlueMap.THREAD_POOL)
|
.executor(BlueMap.THREAD_POOL)
|
||||||
.scheduler(Scheduler.systemScheduler())
|
.softValues()
|
||||||
.expireAfterAccess(10, TimeUnit.SECONDS)
|
.maximumSize(1000)
|
||||||
.expireAfterWrite(5, TimeUnit.MINUTES)
|
.expireAfterAccess(1, TimeUnit.MINUTES)
|
||||||
.removalListener((Vector2i key, LowresTile value, RemovalCause cause) -> saveTile(key, value))
|
|
||||||
.build(tileWeakInstanceCache::get);
|
.build(tileWeakInstanceCache::get);
|
||||||
|
|
||||||
|
this.pendingChanges = new ConcurrentHashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void save() {
|
public void save() {
|
||||||
|
pendingChanges.entrySet().removeIf(entry -> saveTile(entry.getKey(), entry.getValue()));
|
||||||
|
if (pendingChanges.size() >= DISCARD_THRESHOLD) {
|
||||||
|
Logger.global.logDebug("Discarding changes of " + pendingChanges.size() + " lowres-tiles that failed to save!");
|
||||||
|
pendingChanges.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void discard() {
|
||||||
|
pendingChanges.clear();
|
||||||
tileCache.invalidateAll();
|
tileCache.invalidateAll();
|
||||||
tileCache.cleanUp();
|
tileWeakInstanceCache.invalidateAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
private LowresTile createTile(Vector2i tilePos) {
|
private LowresTile createTile(Vector2i tilePos) {
|
||||||
@ -99,13 +115,12 @@ private LowresTile createTile(Vector2i tilePos) {
|
|||||||
return new LowresTile(tileGrid.getGridSize());
|
return new LowresTile(tileGrid.getGridSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveTile(Vector2i tilePos, @Nullable LowresTile tile) {
|
private boolean saveTile(Vector2i tilePos, LowresTile tile) {
|
||||||
if (tile == null) return;
|
|
||||||
|
|
||||||
// check if storage is closed
|
// check if storage is closed
|
||||||
if (storage.isClosed()){
|
if (storage.isClosed()){
|
||||||
Logger.global.logDebug("Tried to save tile " + tilePos + " (lod: " + lod + ") but storage is already closed.");
|
Logger.global.logDebug("Tried to save tile " + tilePos + " (lod: " + lod + ") but storage is already closed.");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// save the tile
|
// save the tile
|
||||||
@ -113,11 +128,12 @@ private void saveTile(Vector2i tilePos, @Nullable LowresTile tile) {
|
|||||||
tile.save(out);
|
tile.save(out);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Logger.global.logError("Failed to save tile " + tilePos + " (lod: " + lod + ")", e);
|
Logger.global.logError("Failed to save tile " + tilePos + " (lod: " + lod + ")", e);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// write to next LOD (prepare for the most confusing grid-math you will ever see)
|
if (this.nextLayer == null) return true;
|
||||||
if (this.nextLayer == null) return;
|
|
||||||
|
|
||||||
|
// write to next LOD (prepare for the most confusing grid-math you will ever see)
|
||||||
Color averageColor = new Color();
|
Color averageColor = new Color();
|
||||||
int averageHeight, averageBlockLight;
|
int averageHeight, averageBlockLight;
|
||||||
int count;
|
int count;
|
||||||
@ -160,29 +176,37 @@ private void saveTile(Vector2i tilePos, @Nullable LowresTile tile) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private LowresTile getTile(int x, int z) {
|
private LowresTile accessTile(int x, int z) {
|
||||||
return tileCache.get(VECTOR_2_I_CACHE.get(x, z));
|
Vector2i tilePos = VECTOR_2_I_CACHE.get(x, z);
|
||||||
|
LowresTile tile = tileCache.get(tilePos);
|
||||||
|
|
||||||
|
if (pendingChanges.size() >= MAX_PENDING) save();
|
||||||
|
pendingChanges.put(tilePos, tile);
|
||||||
|
|
||||||
|
return tile;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set(int cellX, int cellZ, int pixelX, int pixelZ, Color color, int height, int blockLight) {
|
void set(int cellX, int cellZ, int pixelX, int pixelZ, Color color, int height, int blockLight) {
|
||||||
getTile(cellX, cellZ)
|
accessTile(cellX, cellZ)
|
||||||
.set(pixelX, pixelZ, color, height, blockLight);
|
.set(pixelX, pixelZ, color, height, blockLight);
|
||||||
|
|
||||||
// for seamless edges
|
// for seamless edges
|
||||||
if (pixelX == 0) {
|
if (pixelX == 0) {
|
||||||
getTile(cellX - 1, cellZ)
|
accessTile(cellX - 1, cellZ)
|
||||||
.set(tileGrid.getGridSize().getX(), pixelZ, color, height, blockLight);
|
.set(tileGrid.getGridSize().getX(), pixelZ, color, height, blockLight);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pixelZ == 0) {
|
if (pixelZ == 0) {
|
||||||
getTile(cellX, cellZ - 1)
|
accessTile(cellX, cellZ - 1)
|
||||||
.set(pixelX, tileGrid.getGridSize().getY(), color, height, blockLight);
|
.set(pixelX, tileGrid.getGridSize().getY(), color, height, blockLight);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pixelX == 0 && pixelZ == 0) {
|
if (pixelX == 0 && pixelZ == 0) {
|
||||||
getTile(cellX - 1, cellZ - 1)
|
accessTile(cellX - 1, cellZ - 1)
|
||||||
.set(tileGrid.getGridSize().getX(), tileGrid.getGridSize().getY(), color, height, blockLight);
|
.set(tileGrid.getGridSize().getX(), tileGrid.getGridSize().getY(), color, height, blockLight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,12 @@ public synchronized void save() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized void discard() {
|
||||||
|
for (LowresLayer layer : this.layers) {
|
||||||
|
layer.discard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Grid getTileGrid() {
|
public Grid getTileGrid() {
|
||||||
return tileGrid;
|
return tileGrid;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user