mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2024-11-28 13:36:31 +01:00
Make regions generic to support different types of chunks
This commit is contained in:
parent
35fbcff370
commit
8b1c5ab3c2
@ -25,16 +25,16 @@
|
|||||||
package de.bluecolored.bluemap.core.world;
|
package de.bluecolored.bluemap.core.world;
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface ChunkConsumer {
|
public interface ChunkConsumer<T> {
|
||||||
|
|
||||||
default boolean filter(int chunkX, int chunkZ, int lastModified) {
|
default boolean filter(int chunkX, int chunkZ, int lastModified) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void accept(int chunkX, int chunkZ, Chunk chunk);
|
void accept(int chunkX, int chunkZ, T chunk);
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
interface ListOnly extends ChunkConsumer {
|
interface ListOnly<T> extends ChunkConsumer<T> {
|
||||||
|
|
||||||
void accept(int chunkX, int chunkZ, int lastModified);
|
void accept(int chunkX, int chunkZ, int lastModified);
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ default boolean filter(int chunkX, int chunkZ, int lastModified) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default void accept(int chunkX, int chunkZ, Chunk chunk) {
|
default void accept(int chunkX, int chunkZ, T chunk) {
|
||||||
throw new IllegalStateException("Should never be called.");
|
throw new IllegalStateException("Should never be called.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,15 +26,15 @@
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public interface Region {
|
public interface Region<T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Directly loads and returns the specified chunk.<br>
|
* Directly loads and returns the specified chunk.<br>
|
||||||
* (implementations should consider overriding this method for a faster implementation)
|
* (implementations should consider overriding this method for a faster implementation)
|
||||||
*/
|
*/
|
||||||
default Chunk loadChunk(int chunkX, int chunkZ) throws IOException {
|
default T loadChunk(int chunkX, int chunkZ) throws IOException {
|
||||||
class SingleChunkConsumer implements ChunkConsumer {
|
class SingleChunkConsumer implements ChunkConsumer<T> {
|
||||||
private Chunk foundChunk = Chunk.EMPTY_CHUNK;
|
private T foundChunk = emptyChunk();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean filter(int x, int z, int lastModified) {
|
public boolean filter(int x, int z, int lastModified) {
|
||||||
@ -42,7 +42,7 @@ public boolean filter(int x, int z, int lastModified) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void accept(int chunkX, int chunkZ, Chunk chunk) {
|
public void accept(int chunkX, int chunkZ, T chunk) {
|
||||||
this.foundChunk = chunk;
|
this.foundChunk = chunk;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -54,11 +54,13 @@ public void accept(int chunkX, int chunkZ, Chunk chunk) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterates over all chunks in this region and first calls {@link ChunkConsumer#filter(int, int, int)}.<br>
|
* Iterates over all chunks in this region and first calls {@link ChunkConsumer#filter(int, int, int)}.<br>
|
||||||
* And if (any only if) that method returned <code>true</code>, the chunk will be loaded and {@link ChunkConsumer#accept(int, int, Chunk)}
|
* And if (any only if) that method returned <code>true</code>, the chunk will be loaded and {@link ChunkConsumer#accept(int, int, T)}
|
||||||
* will be called with the loaded chunk.
|
* will be called with the loaded chunk.
|
||||||
* @param consumer the consumer choosing which chunks to load and accepting them
|
* @param consumer the consumer choosing which chunks to load and accepting them
|
||||||
* @throws IOException if an IOException occurred trying to read the region
|
* @throws IOException if an IOException occurred trying to read the region
|
||||||
*/
|
*/
|
||||||
void iterateAllChunks(ChunkConsumer consumer) throws IOException;
|
void iterateAllChunks(ChunkConsumer<T> consumer) throws IOException;
|
||||||
|
|
||||||
|
T emptyChunk();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* 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.world.mca;
|
||||||
|
|
||||||
|
import de.bluecolored.bluemap.core.storage.compression.Compression;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public interface ChunkLoader<T> {
|
||||||
|
|
||||||
|
T load(byte[] data, int offset, int length, Compression compression) throws IOException;
|
||||||
|
|
||||||
|
T emptyChunk();
|
||||||
|
|
||||||
|
}
|
@ -37,7 +37,7 @@
|
|||||||
import de.bluecolored.bluemap.core.util.Vector2iCache;
|
import de.bluecolored.bluemap.core.util.Vector2iCache;
|
||||||
import de.bluecolored.bluemap.core.util.WatchService;
|
import de.bluecolored.bluemap.core.util.WatchService;
|
||||||
import de.bluecolored.bluemap.core.world.*;
|
import de.bluecolored.bluemap.core.world.*;
|
||||||
import de.bluecolored.bluemap.core.world.mca.chunk.ChunkLoader;
|
import de.bluecolored.bluemap.core.world.mca.chunk.MCAChunkLoader;
|
||||||
import de.bluecolored.bluemap.core.world.mca.data.DimensionTypeDeserializer;
|
import de.bluecolored.bluemap.core.world.mca.data.DimensionTypeDeserializer;
|
||||||
import de.bluecolored.bluemap.core.world.mca.data.LevelData;
|
import de.bluecolored.bluemap.core.world.mca.data.LevelData;
|
||||||
import de.bluecolored.bluemap.core.world.mca.region.RegionType;
|
import de.bluecolored.bluemap.core.world.mca.region.RegionType;
|
||||||
@ -78,8 +78,8 @@ public class MCAWorld implements World {
|
|||||||
private final Path dimensionFolder;
|
private final Path dimensionFolder;
|
||||||
private final Path regionFolder;
|
private final Path regionFolder;
|
||||||
|
|
||||||
private final ChunkLoader chunkLoader = new ChunkLoader(this);
|
private final MCAChunkLoader chunkLoader = new MCAChunkLoader(this);
|
||||||
private final LoadingCache<Vector2i, Region> regionCache = Caffeine.newBuilder()
|
private final LoadingCache<Vector2i, Region<Chunk>> regionCache = Caffeine.newBuilder()
|
||||||
.executor(BlueMap.THREAD_POOL)
|
.executor(BlueMap.THREAD_POOL)
|
||||||
.softValues()
|
.softValues()
|
||||||
.maximumSize(32)
|
.maximumSize(32)
|
||||||
@ -153,11 +153,11 @@ private Chunk getChunk(Vector2i pos) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Region getRegion(int x, int z) {
|
public Region<Chunk> getRegion(int x, int z) {
|
||||||
return getRegion(VECTOR_2_I_CACHE.get(x, z));
|
return getRegion(VECTOR_2_I_CACHE.get(x, z));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Region getRegion(Vector2i pos) {
|
private Region<Chunk> getRegion(Vector2i pos) {
|
||||||
return regionCache.get(pos);
|
return regionCache.get(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,7 +191,7 @@ public WatchService<Vector2i> createRegionWatchService() throws IOException {
|
|||||||
@Override
|
@Override
|
||||||
public void preloadRegionChunks(int x, int z, Predicate<Vector2i> chunkFilter) {
|
public void preloadRegionChunks(int x, int z, Predicate<Vector2i> chunkFilter) {
|
||||||
try {
|
try {
|
||||||
getRegion(x, z).iterateAllChunks(new ChunkConsumer() {
|
getRegion(x, z).iterateAllChunks(new ChunkConsumer<>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean filter(int chunkX, int chunkZ, int lastModified) {
|
public boolean filter(int chunkX, int chunkZ, int lastModified) {
|
||||||
Vector2i chunkPos = VECTOR_2_I_CACHE.get(chunkX, chunkZ);
|
Vector2i chunkPos = VECTOR_2_I_CACHE.get(chunkX, chunkZ);
|
||||||
@ -221,12 +221,12 @@ public void invalidateChunkCache(int x, int z) {
|
|||||||
chunkCache.invalidate(VECTOR_2_I_CACHE.get(x, z));
|
chunkCache.invalidate(VECTOR_2_I_CACHE.get(x, z));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Region loadRegion(Vector2i regionPos) {
|
private Region<Chunk> loadRegion(Vector2i regionPos) {
|
||||||
return loadRegion(regionPos.getX(), regionPos.getY());
|
return loadRegion(regionPos.getX(), regionPos.getY());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Region loadRegion(int x, int z) {
|
private Region<Chunk> loadRegion(int x, int z) {
|
||||||
return RegionType.loadRegion(this, getRegionFolder(), x, z);
|
return RegionType.loadRegion(chunkLoader, getRegionFolder(), x, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Chunk loadChunk(Vector2i chunkPos) {
|
private Chunk loadChunk(Vector2i chunkPos) {
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
package de.bluecolored.bluemap.core.world.mca.chunk;
|
package de.bluecolored.bluemap.core.world.mca.chunk;
|
||||||
|
|
||||||
import de.bluecolored.bluemap.core.storage.compression.Compression;
|
import de.bluecolored.bluemap.core.storage.compression.Compression;
|
||||||
|
import de.bluecolored.bluemap.core.world.Chunk;
|
||||||
|
import de.bluecolored.bluemap.core.world.mca.ChunkLoader;
|
||||||
import de.bluecolored.bluemap.core.world.mca.MCAUtil;
|
import de.bluecolored.bluemap.core.world.mca.MCAUtil;
|
||||||
import de.bluecolored.bluemap.core.world.mca.MCAWorld;
|
import de.bluecolored.bluemap.core.world.mca.MCAWorld;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@ -37,11 +39,11 @@
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
public class ChunkLoader {
|
public class MCAChunkLoader implements ChunkLoader<Chunk> {
|
||||||
|
|
||||||
private final MCAWorld world;
|
private final MCAWorld world;
|
||||||
|
|
||||||
public ChunkLoader(MCAWorld world) {
|
public MCAChunkLoader(MCAWorld world) {
|
||||||
this.world = world;
|
this.world = world;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,6 +81,11 @@ public MCAChunk load(byte[] data, int offset, int length, Compression compressio
|
|||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Chunk emptyChunk() {
|
||||||
|
return Chunk.EMPTY_CHUNK;
|
||||||
|
}
|
||||||
|
|
||||||
private @Nullable ChunkVersionLoader<?> findBestLoaderForVersion(int version) {
|
private @Nullable ChunkVersionLoader<?> findBestLoaderForVersion(int version) {
|
||||||
for (ChunkVersionLoader<?> loader : CHUNK_VERSION_LOADERS) {
|
for (ChunkVersionLoader<?> loader : CHUNK_VERSION_LOADERS) {
|
||||||
if (loader.mightSupport(version)) return loader;
|
if (loader.mightSupport(version)) return loader;
|
@ -28,8 +28,7 @@
|
|||||||
import de.bluecolored.bluemap.core.storage.compression.Compression;
|
import de.bluecolored.bluemap.core.storage.compression.Compression;
|
||||||
import de.bluecolored.bluemap.core.world.ChunkConsumer;
|
import de.bluecolored.bluemap.core.world.ChunkConsumer;
|
||||||
import de.bluecolored.bluemap.core.world.Region;
|
import de.bluecolored.bluemap.core.world.Region;
|
||||||
import de.bluecolored.bluemap.core.world.mca.MCAWorld;
|
import de.bluecolored.bluemap.core.world.mca.ChunkLoader;
|
||||||
import de.bluecolored.bluemap.core.world.mca.chunk.MCAChunk;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
@ -61,14 +60,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public class LinearRegion implements Region {
|
public class LinearRegion<T> implements Region<T> {
|
||||||
|
|
||||||
public static final String FILE_SUFFIX = ".linear";
|
public static final String FILE_SUFFIX = ".linear";
|
||||||
public static final Pattern FILE_PATTERN = Pattern.compile("^r\\.(-?\\d+)\\.(-?\\d+)\\.linear$");
|
public static final Pattern FILE_PATTERN = Pattern.compile("^r\\.(-?\\d+)\\.(-?\\d+)\\.linear$");
|
||||||
|
|
||||||
private static final long MAGIC = 0xc3ff13183cca9d9aL;
|
private static final long MAGIC = 0xc3ff13183cca9d9aL;
|
||||||
|
|
||||||
private final MCAWorld world;
|
private final ChunkLoader<T> chunkLoader;
|
||||||
private final Path regionFile;
|
private final Path regionFile;
|
||||||
private final Vector2i regionPos;
|
private final Vector2i regionPos;
|
||||||
|
|
||||||
@ -82,8 +81,8 @@ public class LinearRegion implements Region {
|
|||||||
private long dataHash;
|
private long dataHash;
|
||||||
private byte[] compressedData;
|
private byte[] compressedData;
|
||||||
|
|
||||||
public LinearRegion(MCAWorld world, Path regionFile) throws IllegalArgumentException {
|
public LinearRegion(ChunkLoader<T> chunkLoader, Path regionFile) throws IllegalArgumentException {
|
||||||
this.world = world;
|
this.chunkLoader = chunkLoader;
|
||||||
this.regionFile = regionFile;
|
this.regionFile = regionFile;
|
||||||
|
|
||||||
String[] filenameParts = regionFile.getFileName().toString().split("\\.");
|
String[] filenameParts = regionFile.getFileName().toString().split("\\.");
|
||||||
@ -93,12 +92,6 @@ public LinearRegion(MCAWorld world, Path regionFile) throws IllegalArgumentExcep
|
|||||||
this.regionPos = new Vector2i(rX, rZ);
|
this.regionPos = new Vector2i(rX, rZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LinearRegion(MCAWorld world, Vector2i regionPos) throws IllegalArgumentException {
|
|
||||||
this.world = world;
|
|
||||||
this.regionPos = regionPos;
|
|
||||||
this.regionFile = world.getRegionFolder().resolve(getRegionFileName(regionPos.getX(), regionPos.getY()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void init() throws IOException {
|
private synchronized void init() throws IOException {
|
||||||
if (initialized) return;
|
if (initialized) return;
|
||||||
|
|
||||||
@ -141,7 +134,7 @@ private synchronized void init() throws IOException {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void iterateAllChunks(ChunkConsumer consumer) throws IOException {
|
public void iterateAllChunks(ChunkConsumer<T> consumer) throws IOException {
|
||||||
if (!initialized) init();
|
if (!initialized) init();
|
||||||
|
|
||||||
int chunkStartX = regionPos.getX() * 32;
|
int chunkStartX = regionPos.getX() * 32;
|
||||||
@ -177,7 +170,7 @@ public void iterateAllChunks(ChunkConsumer consumer) throws IOException {
|
|||||||
chunkDataBuffer = new byte[length];
|
chunkDataBuffer = new byte[length];
|
||||||
dIn.readFully(chunkDataBuffer, 0, length);
|
dIn.readFully(chunkDataBuffer, 0, length);
|
||||||
|
|
||||||
MCAChunk chunk = world.getChunkLoader().load(chunkDataBuffer, 0, length, Compression.NONE);
|
T chunk = chunkLoader.load(chunkDataBuffer, 0, length, Compression.NONE);
|
||||||
consumer.accept(chunkX, chunkZ, chunk);
|
consumer.accept(chunkX, chunkZ, chunk);
|
||||||
} else {
|
} else {
|
||||||
// skip before reading the next chunk, but only if there is a next chunk
|
// 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 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T emptyChunk() {
|
||||||
|
return chunkLoader.emptyChunk();
|
||||||
|
}
|
||||||
|
|
||||||
public static String getRegionFileName(int regionX, int regionZ) {
|
public static String getRegionFileName(int regionX, int regionZ) {
|
||||||
return "r." + regionX + "." + regionZ + FILE_SUFFIX;
|
return "r." + regionX + "." + regionZ + FILE_SUFFIX;
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,8 @@
|
|||||||
import de.bluecolored.bluemap.core.world.Chunk;
|
import de.bluecolored.bluemap.core.world.Chunk;
|
||||||
import de.bluecolored.bluemap.core.world.ChunkConsumer;
|
import de.bluecolored.bluemap.core.world.ChunkConsumer;
|
||||||
import de.bluecolored.bluemap.core.world.Region;
|
import de.bluecolored.bluemap.core.world.Region;
|
||||||
import de.bluecolored.bluemap.core.world.mca.MCAWorld;
|
import de.bluecolored.bluemap.core.world.mca.ChunkLoader;
|
||||||
|
import de.bluecolored.bluemap.core.world.mca.chunk.MCAChunkLoader;
|
||||||
import de.bluecolored.bluemap.core.world.mca.chunk.MCAChunk;
|
import de.bluecolored.bluemap.core.world.mca.chunk.MCAChunk;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
@ -43,7 +44,7 @@
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public class MCARegion implements Region {
|
public class MCARegion<T> implements Region<T> {
|
||||||
|
|
||||||
public static final String FILE_SUFFIX = ".mca";
|
public static final String FILE_SUFFIX = ".mca";
|
||||||
public static final Pattern FILE_PATTERN = Pattern.compile("^r\\.(-?\\d+)\\.(-?\\d+)\\.mca$");
|
public static final Pattern FILE_PATTERN = Pattern.compile("^r\\.(-?\\d+)\\.(-?\\d+)\\.mca$");
|
||||||
@ -57,12 +58,12 @@ public class MCARegion implements Region {
|
|||||||
CHUNK_COMPRESSION_MAP[4] = Compression.LZ4;
|
CHUNK_COMPRESSION_MAP[4] = Compression.LZ4;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MCAWorld world;
|
|
||||||
private final Path regionFile;
|
private final Path regionFile;
|
||||||
|
private final ChunkLoader<T> chunkLoader;
|
||||||
private final Vector2i regionPos;
|
private final Vector2i regionPos;
|
||||||
|
|
||||||
public MCARegion(MCAWorld world, Path regionFile) throws IllegalArgumentException {
|
public MCARegion(ChunkLoader<T> chunkLoader, Path regionFile) throws IllegalArgumentException {
|
||||||
this.world = world;
|
this.chunkLoader = chunkLoader;
|
||||||
this.regionFile = regionFile;
|
this.regionFile = regionFile;
|
||||||
|
|
||||||
String[] filenameParts = regionFile.getFileName().toString().split("\\.");
|
String[] filenameParts = regionFile.getFileName().toString().split("\\.");
|
||||||
@ -72,18 +73,12 @@ public MCARegion(MCAWorld world, Path regionFile) throws IllegalArgumentExceptio
|
|||||||
this.regionPos = new Vector2i(rX, rZ);
|
this.regionPos = new Vector2i(rX, rZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MCARegion(MCAWorld world, Vector2i regionPos) throws IllegalArgumentException {
|
|
||||||
this.world = world;
|
|
||||||
this.regionPos = regionPos;
|
|
||||||
this.regionFile = world.getRegionFolder().resolve(getRegionFileName(regionPos.getX(), regionPos.getY()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Chunk loadChunk(int chunkX, int chunkZ) throws IOException {
|
public T loadChunk(int chunkX, int chunkZ) throws IOException {
|
||||||
if (Files.notExists(regionFile)) return Chunk.EMPTY_CHUNK;
|
if (Files.notExists(regionFile)) return chunkLoader.emptyChunk();
|
||||||
|
|
||||||
long fileLength = Files.size(regionFile);
|
long fileLength = Files.size(regionFile);
|
||||||
if (fileLength == 0) return Chunk.EMPTY_CHUNK;
|
if (fileLength == 0) return chunkLoader.emptyChunk();
|
||||||
|
|
||||||
try (FileChannel channel = FileChannel.open(regionFile, StandardOpenOption.READ)) {
|
try (FileChannel channel = FileChannel.open(regionFile, StandardOpenOption.READ)) {
|
||||||
int xzChunk = (chunkZ & 0b11111) << 5 | (chunkX & 0b11111);
|
int xzChunk = (chunkZ & 0b11111) << 5 | (chunkX & 0b11111);
|
||||||
@ -98,7 +93,7 @@ public Chunk loadChunk(int chunkX, int chunkZ) throws IOException {
|
|||||||
offset *= 4096;
|
offset *= 4096;
|
||||||
int size = (header[3] & 0xFF) * 4096;
|
int size = (header[3] & 0xFF) * 4096;
|
||||||
|
|
||||||
if (size == 0) return Chunk.EMPTY_CHUNK;
|
if (size == 0) return chunkLoader.emptyChunk();
|
||||||
|
|
||||||
byte[] chunkDataBuffer = new byte[size];
|
byte[] chunkDataBuffer = new byte[size];
|
||||||
|
|
||||||
@ -110,7 +105,7 @@ public Chunk loadChunk(int chunkX, int chunkZ) throws IOException {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void iterateAllChunks(ChunkConsumer consumer) throws IOException {
|
public void iterateAllChunks(ChunkConsumer<T> consumer) throws IOException {
|
||||||
if (Files.notExists(regionFile)) return;
|
if (Files.notExists(regionFile)) return;
|
||||||
|
|
||||||
long fileLength = Files.size(regionFile);
|
long fileLength = Files.size(regionFile);
|
||||||
@ -157,7 +152,7 @@ public void iterateAllChunks(ChunkConsumer consumer) throws IOException {
|
|||||||
channel.position(offset);
|
channel.position(offset);
|
||||||
readFully(channel, chunkDataBuffer, 0, size);
|
readFully(channel, chunkDataBuffer, 0, size);
|
||||||
|
|
||||||
MCAChunk chunk = loadChunk(chunkDataBuffer, size);
|
T chunk = loadChunk(chunkDataBuffer, size);
|
||||||
consumer.accept(chunkX, chunkZ, chunk);
|
consumer.accept(chunkX, chunkZ, chunk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -165,13 +160,18 @@ public void iterateAllChunks(ChunkConsumer consumer) throws IOException {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private MCAChunk loadChunk(byte[] data, int size) throws IOException {
|
@Override
|
||||||
|
public T emptyChunk() {
|
||||||
|
return chunkLoader.emptyChunk();
|
||||||
|
}
|
||||||
|
|
||||||
|
private T loadChunk(byte[] data, int size) throws IOException {
|
||||||
int compressionTypeId = Byte.toUnsignedInt(data[4]);
|
int compressionTypeId = Byte.toUnsignedInt(data[4]);
|
||||||
Compression compression = CHUNK_COMPRESSION_MAP[compressionTypeId];
|
Compression compression = CHUNK_COMPRESSION_MAP[compressionTypeId];
|
||||||
if (compression == null)
|
if (compression == null)
|
||||||
throw new IOException("Unknown chunk compression-id: " + compressionTypeId);
|
throw new IOException("Unknown chunk compression-id: " + compressionTypeId);
|
||||||
|
|
||||||
return world.getChunkLoader().load(data, 5, size - 5, compression);
|
return chunkLoader.load(data, 5, size - 5, compression);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getRegionFileName(int regionX, int regionZ) {
|
public static String getRegionFileName(int regionX, int regionZ) {
|
||||||
|
@ -29,7 +29,8 @@
|
|||||||
import de.bluecolored.bluemap.core.util.Keyed;
|
import de.bluecolored.bluemap.core.util.Keyed;
|
||||||
import de.bluecolored.bluemap.core.util.Registry;
|
import de.bluecolored.bluemap.core.util.Registry;
|
||||||
import de.bluecolored.bluemap.core.world.Region;
|
import de.bluecolored.bluemap.core.world.Region;
|
||||||
import de.bluecolored.bluemap.core.world.mca.MCAWorld;
|
import de.bluecolored.bluemap.core.world.mca.ChunkLoader;
|
||||||
|
import de.bluecolored.bluemap.core.world.mca.chunk.MCAChunkLoader;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@ -53,7 +54,7 @@ public interface RegionType extends Keyed {
|
|||||||
/**
|
/**
|
||||||
* Creates a new {@link Region} from the given world and region-file
|
* Creates a new {@link Region} from the given world and region-file
|
||||||
*/
|
*/
|
||||||
Region createRegion(MCAWorld world, Path regionFile);
|
<T> Region<T> createRegion(ChunkLoader<T> chunkLoader, Path regionFile);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts region coordinates into the region-file name.
|
* Converts region coordinates into the region-file name.
|
||||||
@ -84,12 +85,12 @@ public interface RegionType extends Keyed {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Region loadRegion(MCAWorld world, Path regionFolder, int regionX, int regionZ) {
|
static <T> Region<T> loadRegion(ChunkLoader<T> chunkLoader, Path regionFolder, int regionX, int regionZ) {
|
||||||
for (RegionType regionType : REGISTRY.values()) {
|
for (RegionType regionType : REGISTRY.values()) {
|
||||||
Path regionFile = regionFolder.resolve(regionType.getRegionFileName(regionX, regionZ));
|
Path regionFile = regionFolder.resolve(regionType.getRegionFileName(regionX, regionZ));
|
||||||
if (Files.exists(regionFile)) return regionType.createRegion(world, regionFile);
|
if (Files.exists(regionFile)) return regionType.createRegion(chunkLoader, regionFile);
|
||||||
}
|
}
|
||||||
return DEFAULT.createRegion(world, regionFolder.resolve(DEFAULT.getRegionFileName(regionX, regionZ)));
|
return DEFAULT.createRegion(chunkLoader, regionFolder.resolve(DEFAULT.getRegionFileName(regionX, regionZ)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@ -100,8 +101,8 @@ class Impl implements RegionType {
|
|||||||
private final RegionFileNameFunction regionFileNameFunction;
|
private final RegionFileNameFunction regionFileNameFunction;
|
||||||
private final Pattern regionFileNamePattern;
|
private final Pattern regionFileNamePattern;
|
||||||
|
|
||||||
public Region createRegion(MCAWorld world, Path regionFile) {
|
public <T> Region<T> createRegion(ChunkLoader<T> chunkLoader, Path regionFile) {
|
||||||
return this.regionFactory.create(world, regionFile);
|
return this.regionFactory.create(chunkLoader, regionFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRegionFileName(int regionX, int regionZ) {
|
public String getRegionFileName(int regionX, int regionZ) {
|
||||||
@ -122,7 +123,7 @@ public String getRegionFileName(int regionX, int regionZ) {
|
|||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
interface RegionFactory {
|
interface RegionFactory {
|
||||||
Region create(MCAWorld world, Path regionFile);
|
<T> Region<T> create(ChunkLoader<T> chunkLoader, Path regionFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
|
Loading…
Reference in New Issue
Block a user