Skip to content

Commit

Permalink
feat: support for world-access driven heightmaps
Browse files Browse the repository at this point in the history
  • Loading branch information
mworzala committed Mar 26, 2024
1 parent bcc7d3e commit 3a76ff8
Show file tree
Hide file tree
Showing 9 changed files with 79 additions and 21 deletions.
5 changes: 1 addition & 4 deletions src/main/java/net/hollowcube/polar/AnvilPolar.java
Original file line number Diff line number Diff line change
Expand Up @@ -235,12 +235,9 @@ public class AnvilPolar {
if (blockEntity != null) blockEntities.add(blockEntity);
}

var heightmaps = new byte[PolarChunk.HEIGHTMAP_BYTE_SIZE][PolarChunk.HEIGHTMAPS.length];
var heightmaps = new int[PolarChunk.MAX_HEIGHTMAPS][];
chunkData.getCompound("Heightmaps");
//todo: heightmaps
// MOTION_BLOCKING MOTION_BLOCKING_NO_LEAVES
// OCEAN_FLOOR OCEAN_FLOOR_WG
// WORLD_SURFACE WORLD_SURFACE_WG

var userData = new byte[0];

Expand Down
7 changes: 7 additions & 0 deletions src/main/java/net/hollowcube/polar/PaletteUtil.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
package net.hollowcube.polar;

import net.minestom.server.utils.validate.Check;

final class PaletteUtil {
private PaletteUtil() {}

public static int bitsToRepresent(int n) {
Check.argCondition(n < 1, "n must be greater than 0");
return Integer.SIZE - Integer.numberOfLeadingZeros(n);
}

public static long[] pack(int[] ints, int bitsPerEntry) {
int intsPerLong = (int) Math.floor(64d / bitsPerEntry);
long[] longs = new long[(int) Math.ceil(ints.length / (double) intsPerLong)];
Expand Down
8 changes: 5 additions & 3 deletions src/main/java/net/hollowcube/polar/PolarChunk.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package net.hollowcube.polar;


import net.minestom.server.instance.Chunk;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;

Expand All @@ -14,7 +15,7 @@ public record PolarChunk(
int z,
PolarSection[] sections,
List<BlockEntity> blockEntities,
byte[][] heightmaps,
int[][] heightmaps,
byte[] userData
) {

Expand All @@ -34,9 +35,10 @@ public record PolarChunk(
HEIGHTMAP_WORLD_SURFACE,
HEIGHTMAP_WORLD_SURFACE_WG,
};
static final int HEIGHTMAP_BYTE_SIZE = 32;
static final int HEIGHTMAP_SIZE = Chunk.CHUNK_SIZE_X * Chunk.CHUNK_SIZE_Z;
static final int MAX_HEIGHTMAPS = 32;

public byte @Nullable [] heightmap(int type) {
public int @Nullable [] heightmap(int type) {
return heightmaps[type];
}

Expand Down
10 changes: 5 additions & 5 deletions src/main/java/net/hollowcube/polar/PolarLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ public void loadInstance(@NotNull Instance instance) {
loadBlockEntity(blockEntity, chunk);
}

worldAccess.loadHeightmaps(chunk, chunkData.heightmaps());

var userData = chunkData.userData();
if (userData.length > 0) {
worldAccess.loadChunkData(chunk, new NetworkBuffer(ByteBuffer.wrap(userData)));
Expand Down Expand Up @@ -286,7 +288,7 @@ private void updateChunkData(@NotNull Short2ObjectMap<String> blockCache, @NotNu
var sections = new PolarSection[dimension.getHeight() / Chunk.CHUNK_SECTION_SIZE];
assert sections.length == chunk.getSections().size(): "World height mismatch";

var heightmaps = new byte[32][PolarChunk.HEIGHTMAPS.length];
var heightmaps = new int[PolarChunk.MAX_HEIGHTMAPS][];

var userData = new byte[0];

Expand Down Expand Up @@ -365,11 +367,9 @@ private void updateChunkData(@NotNull Short2ObjectMap<String> blockCache, @NotNu
);
}

//todo heightmaps

if (worldAccess != null)
userData = NetworkBuffer.makeArray(b -> worldAccess.saveChunkData(chunk, b));
worldAccess.saveHeightmaps(chunk, heightmaps);

userData = NetworkBuffer.makeArray(b -> worldAccess.saveChunkData(chunk, b));
}

worldDataLock.writeLock().lock();
Expand Down
12 changes: 8 additions & 4 deletions src/main/java/net/hollowcube/polar/PolarReader.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package net.hollowcube.polar;

import com.github.luben.zstd.Zstd;
import net.minestom.server.instance.Chunk;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.chunk.ChunkUtils;
Expand Down Expand Up @@ -66,13 +67,16 @@ private PolarReader() {}

var blockEntities = buffer.readCollection(b -> readBlockEntity(version, b), MAX_BLOCK_ENTITIES);

var heightmaps = new byte[PolarChunk.HEIGHTMAP_BYTE_SIZE][PolarChunk.HEIGHTMAPS.length];
var heightmaps = new int[PolarChunk.MAX_HEIGHTMAPS][];
int heightmapMask = buffer.read(INT);
for (int i = 0; i < PolarChunk.HEIGHTMAPS.length; i++) {
if ((heightmapMask & PolarChunk.HEIGHTMAPS[i]) == 0)
for (int i = 0; i < PolarChunk.MAX_HEIGHTMAPS; i++) {
if ((heightmapMask & (1 << i)) == 0)
continue;

heightmaps[i] = buffer.readBytes(32);
var packed = buffer.read(LONG_ARRAY);
var bitsPerEntry = packed.length * 64 / PolarChunk.HEIGHTMAP_SIZE;
heightmaps[i] = new int[PolarChunk.HEIGHTMAP_SIZE];
PaletteUtil.unpack(heightmaps[i], packed, bitsPerEntry);
}

// Objects
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/net/hollowcube/polar/PolarWorldAccess.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.world.biomes.Biome;
import net.minestom.server.world.biomes.VanillaBiome;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

Expand Down Expand Up @@ -61,6 +62,12 @@ default void loadChunkData(@NotNull Chunk chunk, @Nullable NetworkBuffer userDat
*/
default void saveChunkData(@NotNull Chunk chunk, @NotNull NetworkBuffer userData) {}

@ApiStatus.Experimental
default void loadHeightmaps(@NotNull Chunk chunk, int[][] heightmaps) {}

@ApiStatus.Experimental
default void saveHeightmaps(@NotNull Chunk chunk, int[][] heightmaps) {}

/**
* Called when a chunk is being loaded by a {@link PolarLoader} to convert biome ids back to instances.
* <br/><br/>
Expand Down
22 changes: 18 additions & 4 deletions src/main/java/net/hollowcube/polar/PolarWriter.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package net.hollowcube.polar;

import com.github.luben.zstd.Zstd;
import net.minestom.server.instance.Chunk;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.utils.chunk.ChunkUtils;
import org.jetbrains.annotations.NotNull;
Expand All @@ -19,7 +20,7 @@ public static byte[] write(@NotNull PolarWorld world) {
content.write(BYTE, world.minSection());
content.write(BYTE, world.maxSection());
content.write(BYTE_ARRAY, world.userData());
content.writeCollection(world.chunks(), PolarWriter::writeChunk);
content.writeCollection(world.chunks(), (b , c) -> writeChunk(b, c, world.maxSection() - world.minSection() + 1));

// Create final buffer
return NetworkBuffer.makeArray(buffer -> {
Expand All @@ -39,7 +40,7 @@ public static byte[] write(@NotNull PolarWorld world) {
});
}

private static void writeChunk(@NotNull NetworkBuffer buffer, @NotNull PolarChunk chunk) {
private static void writeChunk(@NotNull NetworkBuffer buffer, @NotNull PolarChunk chunk, int sectionCount) {
buffer.write(VAR_INT, chunk.x());
buffer.write(VAR_INT, chunk.z());

Expand All @@ -48,8 +49,21 @@ private static void writeChunk(@NotNull NetworkBuffer buffer, @NotNull PolarChun
}
buffer.writeCollection(chunk.blockEntities(), PolarWriter::writeBlockEntity);

//todo heightmaps
buffer.write(INT, PolarChunk.HEIGHTMAP_NONE);
{
int heightmapBits = 0;
for (int i = 0; i < PolarChunk.MAX_HEIGHTMAPS; i++) {
if (chunk.heightmap(i) != null)
heightmapBits |= 1 << i;
}
buffer.write(INT, heightmapBits);

int bitsPerEntry = PaletteUtil.bitsToRepresent(sectionCount * Chunk.CHUNK_SECTION_SIZE);
for (int i = 0; i < PolarChunk.MAX_HEIGHTMAPS; i++) {
var heightmap = chunk.heightmap(i);
if (heightmap == null) continue;
buffer.write(LONG_ARRAY, PaletteUtil.pack(heightmap, bitsPerEntry));
}
}

buffer.write(BYTE_ARRAY, chunk.userData());
}
Expand Down
25 changes: 25 additions & 0 deletions src/test/java/net/hollowcube/polar/TestPolarReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

Expand All @@ -26,4 +29,26 @@ void testNewerVersionFail() {
assertEquals("Unsupported Polar version. Up to " + PolarWorld.LATEST_VERSION + " is supported, found 20560.", e.getMessage());
}

@Test
void testHeightmapReadWrite() {
var world = new PolarWorld();
var emptySections = new PolarSection[24];
Arrays.fill(emptySections, new PolarSection());
var heightmaps = new int[PolarChunk.MAX_HEIGHTMAPS][];
heightmaps[0] = new int[PolarChunk.HEIGHTMAP_SIZE];
for (int i = 0; i < PolarChunk.HEIGHTMAP_SIZE; i++) {
heightmaps[0][i] = i;
}
world.updateChunkAt(0, 0, new PolarChunk(0, 0, emptySections, List.of(), heightmaps, new byte[0]));

var raw = PolarWriter.write(world);
var newWorld = PolarReader.read(raw);
var newChunk = newWorld.chunkAt(0, 0);
var newHeightmap = newChunk.heightmap(0);
for (int i = 0; i < PolarChunk.HEIGHTMAP_SIZE; i++) {
assertEquals(i, newHeightmap[i]);
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ void testWriteRead() {

var emptySections = new PolarSection[24];
Arrays.fill(emptySections, new PolarSection());
world.updateChunkAt(0, 0, new PolarChunk(0, 0, emptySections, List.of(), new byte[0][0], new byte[0]));

var heightmaps = new int[PolarChunk.MAX_HEIGHTMAPS][];
world.updateChunkAt(0, 0, new PolarChunk(0, 0, emptySections, List.of(), heightmaps, new byte[0]));

var wa = new UpdateTimeWorldAccess();
var loader = new PolarLoader(world).setWorldAccess(wa);
Expand Down

0 comments on commit 3a76ff8

Please sign in to comment.