Skip to content

Commit f42ebdb

Browse files
committed
Make lowres tile saving even more robust
1 parent 62174c6 commit f42ebdb

File tree

3 files changed

+51
-21
lines changed

3 files changed

+51
-21
lines changed

BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/rendermanager/MapPurgeTask.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ public void doWork() throws Exception {
5252
}
5353
if (this.cancelled) return;
5454

55-
// save lowres-tile-manager to clear/flush any buffered data
56-
this.map.getLowresTileManager().save();
55+
// discard any pending lowres changes
56+
this.map.getLowresTileManager().discard();
5757

5858
// purge the map
5959
map.getStorage().delete(progress -> {

BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/lowres/LowresLayer.java

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@
2727
import com.flowpowered.math.vector.Vector2i;
2828
import com.github.benmanes.caffeine.cache.Caffeine;
2929
import com.github.benmanes.caffeine.cache.LoadingCache;
30-
import com.github.benmanes.caffeine.cache.RemovalCause;
31-
import com.github.benmanes.caffeine.cache.Scheduler;
3230
import de.bluecolored.bluemap.core.BlueMap;
3331
import de.bluecolored.bluemap.core.logger.Logger;
3432
import de.bluecolored.bluemap.core.storage.GridStorage;
@@ -40,10 +38,15 @@
4038
import java.io.IOException;
4139
import java.io.InputStream;
4240
import java.io.OutputStream;
41+
import java.util.Map;
42+
import java.util.concurrent.ConcurrentHashMap;
4343
import java.util.concurrent.TimeUnit;
4444

4545
public class LowresLayer {
4646

47+
private static final int MAX_PENDING = 200;
48+
private static final int DISCARD_THRESHOLD = MAX_PENDING / 2;
49+
4750
private static final Vector2iCache VECTOR_2_I_CACHE = new Vector2iCache();
4851

4952
private final GridStorage storage;
@@ -52,9 +55,12 @@ public class LowresLayer {
5255
private final int lodFactor;
5356

5457
private final int lod;
58+
private final LoadingCache<Vector2i, LowresTile> tileWeakInstanceCache;
5559
private final LoadingCache<Vector2i, LowresTile> tileCache;
5660
@Nullable private final LowresLayer nextLayer;
5761

62+
private final Map<Vector2i, LowresTile> pendingChanges;
63+
5864
public LowresLayer(
5965
GridStorage storage, Grid tileGrid, int lodFactor,
6066
int lod, @Nullable LowresLayer nextLayer
@@ -69,23 +75,33 @@ public LowresLayer(
6975

7076
// this extra cache makes sure that a tile instance is reused as long as it is still referenced somewhere ..
7177
// so always only one instance of the same lowres-tile exists
72-
LoadingCache<Vector2i, LowresTile> tileWeakInstanceCache = Caffeine.newBuilder()
78+
this.tileWeakInstanceCache = Caffeine.newBuilder()
7379
.executor(BlueMap.THREAD_POOL)
7480
.weakValues()
7581
.build(this::createTile);
7682

7783
this.tileCache = Caffeine.newBuilder()
7884
.executor(BlueMap.THREAD_POOL)
79-
.scheduler(Scheduler.systemScheduler())
80-
.expireAfterAccess(10, TimeUnit.SECONDS)
81-
.expireAfterWrite(5, TimeUnit.MINUTES)
82-
.removalListener((Vector2i key, LowresTile value, RemovalCause cause) -> saveTile(key, value))
85+
.softValues()
86+
.maximumSize(1000)
87+
.expireAfterAccess(1, TimeUnit.MINUTES)
8388
.build(tileWeakInstanceCache::get);
89+
90+
this.pendingChanges = new ConcurrentHashMap<>();
8491
}
8592

8693
public void save() {
94+
pendingChanges.entrySet().removeIf(entry -> saveTile(entry.getKey(), entry.getValue()));
95+
if (pendingChanges.size() >= DISCARD_THRESHOLD) {
96+
Logger.global.logDebug("Discarding changes of " + pendingChanges.size() + " lowres-tiles that failed to save!");
97+
pendingChanges.clear();
98+
}
99+
}
100+
101+
public void discard() {
102+
pendingChanges.clear();
87103
tileCache.invalidateAll();
88-
tileCache.cleanUp();
104+
tileWeakInstanceCache.invalidateAll();
89105
}
90106

91107
private LowresTile createTile(Vector2i tilePos) {
@@ -99,25 +115,25 @@ private LowresTile createTile(Vector2i tilePos) {
99115
return new LowresTile(tileGrid.getGridSize());
100116
}
101117

102-
private void saveTile(Vector2i tilePos, @Nullable LowresTile tile) {
103-
if (tile == null) return;
118+
private boolean saveTile(Vector2i tilePos, LowresTile tile) {
104119

105120
// check if storage is closed
106121
if (storage.isClosed()){
107122
Logger.global.logDebug("Tried to save tile " + tilePos + " (lod: " + lod + ") but storage is already closed.");
108-
return;
123+
return false;
109124
}
110125

111126
// save the tile
112127
try (OutputStream out = storage.write(tilePos.getX(), tilePos.getY())) {
113128
tile.save(out);
114129
} catch (IOException e) {
115130
Logger.global.logError("Failed to save tile " + tilePos + " (lod: " + lod + ")", e);
131+
return false;
116132
}
117133

118-
// write to next LOD (prepare for the most confusing grid-math you will ever see)
119-
if (this.nextLayer == null) return;
134+
if (this.nextLayer == null) return true;
120135

136+
// write to next LOD (prepare for the most confusing grid-math you will ever see)
121137
Color averageColor = new Color();
122138
int averageHeight, averageBlockLight;
123139
int count;
@@ -160,29 +176,37 @@ private void saveTile(Vector2i tilePos, @Nullable LowresTile tile) {
160176
);
161177
}
162178
}
179+
180+
return true;
163181
}
164182

165-
private LowresTile getTile(int x, int z) {
166-
return tileCache.get(VECTOR_2_I_CACHE.get(x, z));
183+
private LowresTile accessTile(int x, int z) {
184+
Vector2i tilePos = VECTOR_2_I_CACHE.get(x, z);
185+
LowresTile tile = tileCache.get(tilePos);
186+
187+
if (pendingChanges.size() >= MAX_PENDING) save();
188+
pendingChanges.put(tilePos, tile);
189+
190+
return tile;
167191
}
168192

169193
void set(int cellX, int cellZ, int pixelX, int pixelZ, Color color, int height, int blockLight) {
170-
getTile(cellX, cellZ)
194+
accessTile(cellX, cellZ)
171195
.set(pixelX, pixelZ, color, height, blockLight);
172196

173197
// for seamless edges
174198
if (pixelX == 0) {
175-
getTile(cellX - 1, cellZ)
199+
accessTile(cellX - 1, cellZ)
176200
.set(tileGrid.getGridSize().getX(), pixelZ, color, height, blockLight);
177201
}
178202

179203
if (pixelZ == 0) {
180-
getTile(cellX, cellZ - 1)
204+
accessTile(cellX, cellZ - 1)
181205
.set(pixelX, tileGrid.getGridSize().getY(), color, height, blockLight);
182206
}
183207

184208
if (pixelX == 0 && pixelZ == 0) {
185-
getTile(cellX - 1, cellZ - 1)
209+
accessTile(cellX - 1, cellZ - 1)
186210
.set(tileGrid.getGridSize().getX(), tileGrid.getGridSize().getY(), color, height, blockLight);
187211
}
188212
}

BlueMapCore/src/main/java/de/bluecolored/bluemap/core/map/lowres/LowresTileManager.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ public synchronized void save() {
5454
}
5555
}
5656

57+
public synchronized void discard() {
58+
for (LowresLayer layer : this.layers) {
59+
layer.discard();
60+
}
61+
}
62+
5763
public Grid getTileGrid() {
5864
return tileGrid;
5965
}

0 commit comments

Comments
 (0)