Skip to content

Commit 8b1c5ab

Browse files
committed
Make regions generic to support different types of chunks
1 parent 35fbcff commit 8b1c5ab

File tree

8 files changed

+108
-63
lines changed

8 files changed

+108
-63
lines changed

core/src/main/java/de/bluecolored/bluemap/core/world/ChunkConsumer.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,16 @@
2525
package de.bluecolored.bluemap.core.world;
2626

2727
@FunctionalInterface
28-
public interface ChunkConsumer {
28+
public interface ChunkConsumer<T> {
2929

3030
default boolean filter(int chunkX, int chunkZ, int lastModified) {
3131
return true;
3232
}
3333

34-
void accept(int chunkX, int chunkZ, Chunk chunk);
34+
void accept(int chunkX, int chunkZ, T chunk);
3535

3636
@FunctionalInterface
37-
interface ListOnly extends ChunkConsumer {
37+
interface ListOnly<T> extends ChunkConsumer<T> {
3838

3939
void accept(int chunkX, int chunkZ, int lastModified);
4040

@@ -45,7 +45,7 @@ default boolean filter(int chunkX, int chunkZ, int lastModified) {
4545
}
4646

4747
@Override
48-
default void accept(int chunkX, int chunkZ, Chunk chunk) {
48+
default void accept(int chunkX, int chunkZ, T chunk) {
4949
throw new IllegalStateException("Should never be called.");
5050
}
5151

core/src/main/java/de/bluecolored/bluemap/core/world/Region.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,23 @@
2626

2727
import java.io.IOException;
2828

29-
public interface Region {
29+
public interface Region<T> {
3030

3131
/**
3232
* Directly loads and returns the specified chunk.<br>
3333
* (implementations should consider overriding this method for a faster implementation)
3434
*/
35-
default Chunk loadChunk(int chunkX, int chunkZ) throws IOException {
36-
class SingleChunkConsumer implements ChunkConsumer {
37-
private Chunk foundChunk = Chunk.EMPTY_CHUNK;
35+
default T loadChunk(int chunkX, int chunkZ) throws IOException {
36+
class SingleChunkConsumer implements ChunkConsumer<T> {
37+
private T foundChunk = emptyChunk();
3838

3939
@Override
4040
public boolean filter(int x, int z, int lastModified) {
4141
return x == chunkX && z == chunkZ;
4242
}
4343

4444
@Override
45-
public void accept(int chunkX, int chunkZ, Chunk chunk) {
45+
public void accept(int chunkX, int chunkZ, T chunk) {
4646
this.foundChunk = chunk;
4747
}
4848
}
@@ -54,11 +54,13 @@ public void accept(int chunkX, int chunkZ, Chunk chunk) {
5454

5555
/**
5656
* Iterates over all chunks in this region and first calls {@link ChunkConsumer#filter(int, int, int)}.<br>
57-
* And if (any only if) that method returned <code>true</code>, the chunk will be loaded and {@link ChunkConsumer#accept(int, int, Chunk)}
57+
* And if (any only if) that method returned <code>true</code>, the chunk will be loaded and {@link ChunkConsumer#accept(int, int, T)}
5858
* will be called with the loaded chunk.
5959
* @param consumer the consumer choosing which chunks to load and accepting them
6060
* @throws IOException if an IOException occurred trying to read the region
6161
*/
62-
void iterateAllChunks(ChunkConsumer consumer) throws IOException;
62+
void iterateAllChunks(ChunkConsumer<T> consumer) throws IOException;
63+
64+
T emptyChunk();
6365

6466
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* This file is part of BlueMap, licensed under the MIT License (MIT).
3+
*
4+
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
5+
* Copyright (c) contributors
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
* THE SOFTWARE.
24+
*/
25+
package de.bluecolored.bluemap.core.world.mca;
26+
27+
import de.bluecolored.bluemap.core.storage.compression.Compression;
28+
29+
import java.io.IOException;
30+
31+
public interface ChunkLoader<T> {
32+
33+
T load(byte[] data, int offset, int length, Compression compression) throws IOException;
34+
35+
T emptyChunk();
36+
37+
}

core/src/main/java/de/bluecolored/bluemap/core/world/mca/MCAWorld.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
import de.bluecolored.bluemap.core.util.Vector2iCache;
3838
import de.bluecolored.bluemap.core.util.WatchService;
3939
import de.bluecolored.bluemap.core.world.*;
40-
import de.bluecolored.bluemap.core.world.mca.chunk.ChunkLoader;
40+
import de.bluecolored.bluemap.core.world.mca.chunk.MCAChunkLoader;
4141
import de.bluecolored.bluemap.core.world.mca.data.DimensionTypeDeserializer;
4242
import de.bluecolored.bluemap.core.world.mca.data.LevelData;
4343
import de.bluecolored.bluemap.core.world.mca.region.RegionType;
@@ -78,8 +78,8 @@ public class MCAWorld implements World {
7878
private final Path dimensionFolder;
7979
private final Path regionFolder;
8080

81-
private final ChunkLoader chunkLoader = new ChunkLoader(this);
82-
private final LoadingCache<Vector2i, Region> regionCache = Caffeine.newBuilder()
81+
private final MCAChunkLoader chunkLoader = new MCAChunkLoader(this);
82+
private final LoadingCache<Vector2i, Region<Chunk>> regionCache = Caffeine.newBuilder()
8383
.executor(BlueMap.THREAD_POOL)
8484
.softValues()
8585
.maximumSize(32)
@@ -153,11 +153,11 @@ private Chunk getChunk(Vector2i pos) {
153153
}
154154

155155
@Override
156-
public Region getRegion(int x, int z) {
156+
public Region<Chunk> getRegion(int x, int z) {
157157
return getRegion(VECTOR_2_I_CACHE.get(x, z));
158158
}
159159

160-
private Region getRegion(Vector2i pos) {
160+
private Region<Chunk> getRegion(Vector2i pos) {
161161
return regionCache.get(pos);
162162
}
163163

@@ -191,7 +191,7 @@ public WatchService<Vector2i> createRegionWatchService() throws IOException {
191191
@Override
192192
public void preloadRegionChunks(int x, int z, Predicate<Vector2i> chunkFilter) {
193193
try {
194-
getRegion(x, z).iterateAllChunks(new ChunkConsumer() {
194+
getRegion(x, z).iterateAllChunks(new ChunkConsumer<>() {
195195
@Override
196196
public boolean filter(int chunkX, int chunkZ, int lastModified) {
197197
Vector2i chunkPos = VECTOR_2_I_CACHE.get(chunkX, chunkZ);
@@ -221,12 +221,12 @@ public void invalidateChunkCache(int x, int z) {
221221
chunkCache.invalidate(VECTOR_2_I_CACHE.get(x, z));
222222
}
223223

224-
private Region loadRegion(Vector2i regionPos) {
224+
private Region<Chunk> loadRegion(Vector2i regionPos) {
225225
return loadRegion(regionPos.getX(), regionPos.getY());
226226
}
227227

228-
private Region loadRegion(int x, int z) {
229-
return RegionType.loadRegion(this, getRegionFolder(), x, z);
228+
private Region<Chunk> loadRegion(int x, int z) {
229+
return RegionType.loadRegion(chunkLoader, getRegionFolder(), x, z);
230230
}
231231

232232
private Chunk loadChunk(Vector2i chunkPos) {

core/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/ChunkLoader.java renamed to core/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/MCAChunkLoader.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
package de.bluecolored.bluemap.core.world.mca.chunk;
2626

2727
import de.bluecolored.bluemap.core.storage.compression.Compression;
28+
import de.bluecolored.bluemap.core.world.Chunk;
29+
import de.bluecolored.bluemap.core.world.mca.ChunkLoader;
2830
import de.bluecolored.bluemap.core.world.mca.MCAUtil;
2931
import de.bluecolored.bluemap.core.world.mca.MCAWorld;
3032
import lombok.Getter;
@@ -37,11 +39,11 @@
3739
import java.util.List;
3840
import java.util.function.BiFunction;
3941

40-
public class ChunkLoader {
42+
public class MCAChunkLoader implements ChunkLoader<Chunk> {
4143

4244
private final MCAWorld world;
4345

44-
public ChunkLoader(MCAWorld world) {
46+
public MCAChunkLoader(MCAWorld world) {
4547
this.world = world;
4648
}
4749

@@ -79,6 +81,11 @@ public MCAChunk load(byte[] data, int offset, int length, Compression compressio
7981
return chunk;
8082
}
8183

84+
@Override
85+
public Chunk emptyChunk() {
86+
return Chunk.EMPTY_CHUNK;
87+
}
88+
8289
private @Nullable ChunkVersionLoader<?> findBestLoaderForVersion(int version) {
8390
for (ChunkVersionLoader<?> loader : CHUNK_VERSION_LOADERS) {
8491
if (loader.mightSupport(version)) return loader;

core/src/main/java/de/bluecolored/bluemap/core/world/mca/region/LinearRegion.java

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@
2828
import de.bluecolored.bluemap.core.storage.compression.Compression;
2929
import de.bluecolored.bluemap.core.world.ChunkConsumer;
3030
import de.bluecolored.bluemap.core.world.Region;
31-
import de.bluecolored.bluemap.core.world.mca.MCAWorld;
32-
import de.bluecolored.bluemap.core.world.mca.chunk.MCAChunk;
31+
import de.bluecolored.bluemap.core.world.mca.ChunkLoader;
3332
import lombok.Getter;
3433

3534
import java.io.*;
@@ -61,14 +60,14 @@
6160
*/
6261

6362
@Getter
64-
public class LinearRegion implements Region {
63+
public class LinearRegion<T> implements Region<T> {
6564

6665
public static final String FILE_SUFFIX = ".linear";
6766
public static final Pattern FILE_PATTERN = Pattern.compile("^r\\.(-?\\d+)\\.(-?\\d+)\\.linear$");
6867

6968
private static final long MAGIC = 0xc3ff13183cca9d9aL;
7069

71-
private final MCAWorld world;
70+
private final ChunkLoader<T> chunkLoader;
7271
private final Path regionFile;
7372
private final Vector2i regionPos;
7473

@@ -82,8 +81,8 @@ public class LinearRegion implements Region {
8281
private long dataHash;
8382
private byte[] compressedData;
8483

85-
public LinearRegion(MCAWorld world, Path regionFile) throws IllegalArgumentException {
86-
this.world = world;
84+
public LinearRegion(ChunkLoader<T> chunkLoader, Path regionFile) throws IllegalArgumentException {
85+
this.chunkLoader = chunkLoader;
8786
this.regionFile = regionFile;
8887

8988
String[] filenameParts = regionFile.getFileName().toString().split("\\.");
@@ -93,12 +92,6 @@ public LinearRegion(MCAWorld world, Path regionFile) throws IllegalArgumentExcep
9392
this.regionPos = new Vector2i(rX, rZ);
9493
}
9594

96-
public LinearRegion(MCAWorld world, Vector2i regionPos) throws IllegalArgumentException {
97-
this.world = world;
98-
this.regionPos = regionPos;
99-
this.regionFile = world.getRegionFolder().resolve(getRegionFileName(regionPos.getX(), regionPos.getY()));
100-
}
101-
10295
private synchronized void init() throws IOException {
10396
if (initialized) return;
10497

@@ -141,7 +134,7 @@ private synchronized void init() throws IOException {
141134
}
142135

143136
@Override
144-
public void iterateAllChunks(ChunkConsumer consumer) throws IOException {
137+
public void iterateAllChunks(ChunkConsumer<T> consumer) throws IOException {
145138
if (!initialized) init();
146139

147140
int chunkStartX = regionPos.getX() * 32;
@@ -177,7 +170,7 @@ public void iterateAllChunks(ChunkConsumer consumer) throws IOException {
177170
chunkDataBuffer = new byte[length];
178171
dIn.readFully(chunkDataBuffer, 0, length);
179172

180-
MCAChunk chunk = world.getChunkLoader().load(chunkDataBuffer, 0, length, Compression.NONE);
173+
T chunk = chunkLoader.load(chunkDataBuffer, 0, length, Compression.NONE);
181174
consumer.accept(chunkX, chunkZ, chunk);
182175
} else {
183176
// skip before reading the next chunk, but only if there is a next chunk
@@ -193,6 +186,11 @@ public void iterateAllChunks(ChunkConsumer consumer) throws IOException {
193186
}
194187
}
195188

189+
@Override
190+
public T emptyChunk() {
191+
return chunkLoader.emptyChunk();
192+
}
193+
196194
public static String getRegionFileName(int regionX, int regionZ) {
197195
return "r." + regionX + "." + regionZ + FILE_SUFFIX;
198196
}

core/src/main/java/de/bluecolored/bluemap/core/world/mca/region/MCARegion.java

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
import de.bluecolored.bluemap.core.world.Chunk;
3030
import de.bluecolored.bluemap.core.world.ChunkConsumer;
3131
import de.bluecolored.bluemap.core.world.Region;
32-
import de.bluecolored.bluemap.core.world.mca.MCAWorld;
32+
import de.bluecolored.bluemap.core.world.mca.ChunkLoader;
33+
import de.bluecolored.bluemap.core.world.mca.chunk.MCAChunkLoader;
3334
import de.bluecolored.bluemap.core.world.mca.chunk.MCAChunk;
3435
import lombok.Getter;
3536

@@ -43,7 +44,7 @@
4344
import java.util.regex.Pattern;
4445

4546
@Getter
46-
public class MCARegion implements Region {
47+
public class MCARegion<T> implements Region<T> {
4748

4849
public static final String FILE_SUFFIX = ".mca";
4950
public static final Pattern FILE_PATTERN = Pattern.compile("^r\\.(-?\\d+)\\.(-?\\d+)\\.mca$");
@@ -57,12 +58,12 @@ public class MCARegion implements Region {
5758
CHUNK_COMPRESSION_MAP[4] = Compression.LZ4;
5859
}
5960

60-
private final MCAWorld world;
6161
private final Path regionFile;
62+
private final ChunkLoader<T> chunkLoader;
6263
private final Vector2i regionPos;
6364

64-
public MCARegion(MCAWorld world, Path regionFile) throws IllegalArgumentException {
65-
this.world = world;
65+
public MCARegion(ChunkLoader<T> chunkLoader, Path regionFile) throws IllegalArgumentException {
66+
this.chunkLoader = chunkLoader;
6667
this.regionFile = regionFile;
6768

6869
String[] filenameParts = regionFile.getFileName().toString().split("\\.");
@@ -72,18 +73,12 @@ public MCARegion(MCAWorld world, Path regionFile) throws IllegalArgumentExceptio
7273
this.regionPos = new Vector2i(rX, rZ);
7374
}
7475

75-
public MCARegion(MCAWorld world, Vector2i regionPos) throws IllegalArgumentException {
76-
this.world = world;
77-
this.regionPos = regionPos;
78-
this.regionFile = world.getRegionFolder().resolve(getRegionFileName(regionPos.getX(), regionPos.getY()));
79-
}
80-
8176
@Override
82-
public Chunk loadChunk(int chunkX, int chunkZ) throws IOException {
83-
if (Files.notExists(regionFile)) return Chunk.EMPTY_CHUNK;
77+
public T loadChunk(int chunkX, int chunkZ) throws IOException {
78+
if (Files.notExists(regionFile)) return chunkLoader.emptyChunk();
8479

8580
long fileLength = Files.size(regionFile);
86-
if (fileLength == 0) return Chunk.EMPTY_CHUNK;
81+
if (fileLength == 0) return chunkLoader.emptyChunk();
8782

8883
try (FileChannel channel = FileChannel.open(regionFile, StandardOpenOption.READ)) {
8984
int xzChunk = (chunkZ & 0b11111) << 5 | (chunkX & 0b11111);
@@ -98,7 +93,7 @@ public Chunk loadChunk(int chunkX, int chunkZ) throws IOException {
9893
offset *= 4096;
9994
int size = (header[3] & 0xFF) * 4096;
10095

101-
if (size == 0) return Chunk.EMPTY_CHUNK;
96+
if (size == 0) return chunkLoader.emptyChunk();
10297

10398
byte[] chunkDataBuffer = new byte[size];
10499

@@ -110,7 +105,7 @@ public Chunk loadChunk(int chunkX, int chunkZ) throws IOException {
110105
}
111106

112107
@Override
113-
public void iterateAllChunks(ChunkConsumer consumer) throws IOException {
108+
public void iterateAllChunks(ChunkConsumer<T> consumer) throws IOException {
114109
if (Files.notExists(regionFile)) return;
115110

116111
long fileLength = Files.size(regionFile);
@@ -157,21 +152,26 @@ public void iterateAllChunks(ChunkConsumer consumer) throws IOException {
157152
channel.position(offset);
158153
readFully(channel, chunkDataBuffer, 0, size);
159154

160-
MCAChunk chunk = loadChunk(chunkDataBuffer, size);
155+
T chunk = loadChunk(chunkDataBuffer, size);
161156
consumer.accept(chunkX, chunkZ, chunk);
162157
}
163158
}
164159
}
165160
}
166161
}
167162

168-
private MCAChunk loadChunk(byte[] data, int size) throws IOException {
163+
@Override
164+
public T emptyChunk() {
165+
return chunkLoader.emptyChunk();
166+
}
167+
168+
private T loadChunk(byte[] data, int size) throws IOException {
169169
int compressionTypeId = Byte.toUnsignedInt(data[4]);
170170
Compression compression = CHUNK_COMPRESSION_MAP[compressionTypeId];
171171
if (compression == null)
172172
throw new IOException("Unknown chunk compression-id: " + compressionTypeId);
173173

174-
return world.getChunkLoader().load(data, 5, size - 5, compression);
174+
return chunkLoader.load(data, 5, size - 5, compression);
175175
}
176176

177177
public static String getRegionFileName(int regionX, int regionZ) {

0 commit comments

Comments
 (0)