Skip to content

Commit 6be5581

Browse files
cryptobenchclaude
andcommitted
Fix: Don't cache empty tiles to disk
Empty tiles (~270 bytes) are now detected and skipped from caching. This fixes the issue where unexplored chunks would cache empty tiles, and then continue serving stale empty tiles even after the chunk was explored. - Added EMPTY_TILE_THRESHOLD = 500 bytes - Skip caching for tiles below threshold in all cache paths - Ensures newly explored chunks regenerate fresh tiles Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 1692055 commit 6be5581

File tree

1 file changed

+7
-3
lines changed

1 file changed

+7
-3
lines changed

src/main/java/com/easywebmap/map/TileManager.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ public class TileManager {
2828
// Limit concurrent tile generations to prevent CPU spikes
2929
private static final int MAX_CONCURRENT_GENERATIONS = 4;
3030
private final Semaphore generationSemaphore = new Semaphore(MAX_CONCURRENT_GENERATIONS);
31+
// Empty tiles are ~270 bytes, real tiles are 10KB+
32+
private static final int EMPTY_TILE_THRESHOLD = 500;
3133

3234
public TileManager(EasyWebMap plugin) {
3335
this.plugin = plugin;
@@ -100,7 +102,7 @@ private CompletableFuture<byte[]> getCompositeTile(String worldName, int zoom, i
100102
this.pendingRequests.put(cacheKey, future);
101103
future.whenComplete((data, ex) -> {
102104
this.pendingRequests.remove(cacheKey);
103-
if (data != null && data.length > 0 && ex == null) {
105+
if (data != null && data.length > EMPTY_TILE_THRESHOLD && ex == null) {
104106
this.memoryCache.put(cacheKey, data);
105107
if (this.plugin.getConfig().isUseDiskCache()) {
106108
this.diskCache.putAsync(worldName, zoom, tileX, tileZ, data);
@@ -159,7 +161,8 @@ public CompletableFuture<byte[]> getBaseTile(String worldName, int tileX, int ti
159161
this.pendingRequests.put(cacheKey, future);
160162
future.whenComplete((data, ex) -> {
161163
this.pendingRequests.remove(cacheKey);
162-
if (data != null && data.length > 0 && ex == null) {
164+
// Don't cache empty tiles - they should regenerate when chunk gets explored
165+
if (data != null && data.length > EMPTY_TILE_THRESHOLD && ex == null) {
163166
this.memoryCache.put(cacheKey, data);
164167
if (this.plugin.getConfig().isUseDiskCache()) {
165168
this.diskCache.putAsync(worldName, 0, tileX, tileZ, data);
@@ -193,7 +196,8 @@ public CompletableFuture<PngEncoder.TileData> getBaseTileWithPixels(String world
193196
this.pendingPixelRequests.put(cacheKey, future);
194197
future.whenComplete((data, ex) -> {
195198
this.pendingPixelRequests.remove(cacheKey);
196-
if (data != null && !data.isEmpty() && ex == null) {
199+
// Don't cache empty tiles - they should regenerate when chunk gets explored
200+
if (data != null && !data.isEmpty() && data.pngBytes.length > EMPTY_TILE_THRESHOLD && ex == null) {
197201
// Cache pixels for compositing, evict if too many
198202
if (this.pixelCache.size() < MAX_PIXEL_CACHE) {
199203
this.pixelCache.put(cacheKey, data);

0 commit comments

Comments
 (0)