Proper naming for Section/Palette

This commit is contained in:
TheMode 2021-06-12 10:39:44 +02:00
parent f95b6121fe
commit 3b14fe12a3
7 changed files with 135 additions and 204 deletions

View File

@ -107,6 +107,10 @@ public abstract class Chunk implements BlockGetter, BlockSetter, Viewable, Ticka
@Override
public abstract void setBlock(int x, int y, int z, @NotNull Block block);
public abstract @NotNull Map<Integer, Section> getSections();
public abstract @Nullable Section getSection(int section);
/**
* Executes a chunk tick.
* <p>

View File

@ -1,10 +1,8 @@
package net.minestom.server.instance;
import com.extollit.gaming.ai.path.model.ColumnarOcclusionFieldList;
import it.unimi.dsi.fastutil.ints.Int2LongMap;
import it.unimi.dsi.fastutil.ints.Int2LongOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap;
import it.unimi.dsi.fastutil.objects.Object2ShortMap;
import it.unimi.dsi.fastutil.objects.Object2ShortOpenHashMap;
import net.minestom.server.MinecraftServer;
@ -14,7 +12,6 @@ import net.minestom.server.data.SerializableDataImpl;
import net.minestom.server.entity.pathfinding.PFBlockDescription;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.block.BlockHandler;
import net.minestom.server.instance.palette.PaletteStorage;
import net.minestom.server.network.packet.server.play.ChunkDataPacket;
import net.minestom.server.utils.binary.BinaryReader;
import net.minestom.server.utils.binary.BinaryWriter;
@ -28,6 +25,7 @@ import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.lang.ref.SoftReference;
import java.util.Map;
import java.util.Set;
/**
@ -43,31 +41,19 @@ public class DynamicChunk extends Chunk {
*/
private static final int DATA_FORMAT_VERSION = 1;
// WARNING: not thread-safe and should not be changed
protected PaletteStorage blockPalette;
protected final Int2ObjectRBTreeMap<Section> sectionMap = new Int2ObjectRBTreeMap<>();
// Key = ChunkUtils#getBlockIndex
protected final Int2ObjectOpenHashMap<BlockHandler> handlerMap = new Int2ObjectOpenHashMap<>();
protected final Int2ObjectOpenHashMap<NBTCompound> nbtMap = new Int2ObjectOpenHashMap<>();
// Contains CustomBlocks' block index which are updatable
protected final IntOpenHashSet updatableBlocks = new IntOpenHashSet();
// (block index)/(last update in ms)
protected final Int2LongMap updatableBlocksLastUpdate = new Int2LongOpenHashMap();
private long lastChangeTime;
private SoftReference<ChunkDataPacket> cachedPacket = new SoftReference<>(null);
private long cachedPacketTime;
public DynamicChunk(@NotNull Instance instance, @Nullable Biome[] biomes, int chunkX, int chunkZ,
@NotNull PaletteStorage blockPalette) {
super(instance, biomes, chunkX, chunkZ, true);
this.blockPalette = blockPalette;
}
public DynamicChunk(@NotNull Instance instance, @Nullable Biome[] biomes, int chunkX, int chunkZ) {
this(instance, biomes, chunkX, chunkZ, new PaletteStorage(8, 2));
super(instance, biomes, chunkX, chunkZ, true);
}
@Override
@ -85,9 +71,13 @@ public class DynamicChunk extends Chunk {
}
}
this.lastChangeTime = System.currentTimeMillis();
{
Section section = retrieveSection(y);
section.setBlockAt(x, y, z, blockStateId);
}
final int index = getBlockIndex(x, y, z);
// Block palette
setBlockAt(blockPalette, x, y, z, blockStateId);
// Handler
if (handler != null) {
this.handlerMap.put(index, handler);
@ -102,6 +92,16 @@ public class DynamicChunk extends Chunk {
}
}
@Override
public @NotNull Map<Integer, Section> getSections() {
return sectionMap;
}
@Override
public @Nullable Section getSection(int section) {
return sectionMap.get(section);
}
@Override
public void tick(long time) {
// TODO block update
@ -109,8 +109,9 @@ public class DynamicChunk extends Chunk {
@Override
public @NotNull Block getBlock(int x, int y, int z) {
final Section section = retrieveSection(y);
final int index = ChunkUtils.getBlockIndex(x, y, z);
final short blockStateId = getBlockAt(blockPalette, x, y, z);
final short blockStateId = section.getBlockAt(x, y, z);
BlockHandler handler = handlerMap.get(index);
NBTCompound nbt = nbtMap.get(index);
Block block = Block.fromStateId(blockStateId);
@ -180,7 +181,9 @@ public class DynamicChunk extends Chunk {
for (byte z = 0; z < CHUNK_SIZE_Z; z++) {
final int index = getBlockIndex(x, y, z);
final short blockStateId = getBlockAt(blockPalette, x, y, z);
final Section section = retrieveSection(y);
final short blockStateId = section.getBlockAt(x, y, z);
final short customBlockId = 0;//getBlockAt(customBlockPalette, x, y, z);
// No block at the position
@ -325,7 +328,7 @@ public class DynamicChunk extends Chunk {
packet.biomes = biomes;
packet.chunkX = chunkX;
packet.chunkZ = chunkZ;
packet.paletteStorage = blockPalette.clone();
packet.sections = sectionMap.clone(); // TODO deep clone
packet.handlerMap = handlerMap.clone();
packet.nbtMap = nbtMap.clone();
@ -338,10 +341,10 @@ public class DynamicChunk extends Chunk {
@Override
public Chunk copy(@NotNull Instance instance, int chunkX, int chunkZ) {
DynamicChunk dynamicChunk = new DynamicChunk(instance, biomes.clone(), chunkX, chunkZ);
dynamicChunk.blockPalette = blockPalette.clone();
for (var entry : sectionMap.int2ObjectEntrySet()) {
dynamicChunk.sectionMap.put(entry.getIntKey(), entry.getValue().clone());
}
dynamicChunk.handlerMap.putAll(handlerMap);
dynamicChunk.updatableBlocks.addAll(updatableBlocks);
dynamicChunk.updatableBlocksLastUpdate.putAll(updatableBlocksLastUpdate);
dynamicChunk.nbtMap.putAll(nbtMap);
return dynamicChunk;
@ -349,19 +352,13 @@ public class DynamicChunk extends Chunk {
@Override
public void reset() {
this.blockPalette.clear();
this.sectionMap.values().forEach(Section::clear);
this.handlerMap.clear();
this.nbtMap.clear();
this.updatableBlocks.clear();
this.updatableBlocksLastUpdate.clear();
}
private short getBlockAt(@NotNull PaletteStorage paletteStorage, int x, int y, int z) {
return paletteStorage.getBlockAt(x, y, z);
}
private void setBlockAt(@NotNull PaletteStorage paletteStorage, int x, int y, int z, short blockId) {
paletteStorage.setBlockAt(x, y, z, blockId);
this.lastChangeTime = System.currentTimeMillis();
private @NotNull Section retrieveSection(int y) {
final int sectionIndex = ChunkUtils.getSectionAt(y);
return sectionMap.computeIfAbsent(sectionIndex, key -> new Section());
}
}

View File

@ -0,0 +1,61 @@
package net.minestom.server.instance;
import net.minestom.server.instance.palette.Palette;
import net.minestom.server.utils.clone.PublicCloneable;
import org.jetbrains.annotations.NotNull;
public class Section implements PublicCloneable<Section> {
private final Palette palette;
private Section(Palette palette) {
this.palette = palette;
}
public Section() {
this(new Palette(8, 2));
}
public short getBlockAt(int x, int y, int z) {
x = toChunkCoordinate(x);
z = toChunkCoordinate(z);
return palette.getBlockAt(x, y, z);
}
public void setBlockAt(int x, int y, int z, short blockId) {
x = toChunkCoordinate(x);
z = toChunkCoordinate(z);
palette.setBlockAt(x, y, z, blockId);
}
public void clean() {
palette.clean();
}
public void clear() {
palette.clear();
}
public Palette getPalette() {
return palette;
}
@Override
public @NotNull Section clone() {
return new Section(palette.clone());
}
/**
* Converts a world coordinate to a chunk one.
*
* @param xz the world coordinate
* @return the chunk coordinate of {@code xz}
*/
private static int toChunkCoordinate(int xz) {
xz %= 16;
if (xz < 0) {
xz += Chunk.CHUNK_SECTION_SIZE;
}
return xz;
}
}

View File

@ -4,14 +4,13 @@ import it.unimi.dsi.fastutil.shorts.Short2ShortLinkedOpenHashMap;
import it.unimi.dsi.fastutil.shorts.Short2ShortOpenHashMap;
import net.minestom.server.MinecraftServer;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.block.Block;
import net.minestom.server.utils.MathUtils;
import net.minestom.server.utils.clone.PublicCloneable;
import org.jetbrains.annotations.NotNull;
import static net.minestom.server.instance.Chunk.CHUNK_SECTION_SIZE;
public class Section implements PublicCloneable<Section> {
public class Palette implements PublicCloneable<Palette> {
/**
* The maximum bits per entry value.
@ -55,7 +54,7 @@ public class Section implements PublicCloneable<Section> {
private short blockCount = 0;
protected Section(int bitsPerEntry, int bitsIncrement) {
public Palette(int bitsPerEntry, int bitsIncrement) {
this.bitsPerEntry = bitsPerEntry;
this.bitsIncrement = bitsIncrement;
@ -142,26 +141,26 @@ public class Section implements PublicCloneable<Section> {
public void resize(int newBitsPerEntry) {
newBitsPerEntry = fixBitsPerEntry(newBitsPerEntry);
Section section = new Section(newBitsPerEntry, bitsIncrement);
section.paletteBlockMap = paletteBlockMap;
section.blockPaletteMap = blockPaletteMap;
Palette palette = new Palette(newBitsPerEntry, bitsIncrement);
palette.paletteBlockMap = paletteBlockMap;
palette.blockPaletteMap = blockPaletteMap;
for (int y = 0; y < Chunk.CHUNK_SECTION_SIZE; y++) {
for (int x = 0; x < Chunk.CHUNK_SIZE_X; x++) {
for (int z = 0; z < Chunk.CHUNK_SIZE_Z; z++) {
final short blockId = getBlockAt(x, y, z);
section.setBlockAt(x, y, z, blockId);
palette.setBlockAt(x, y, z, blockId);
}
}
}
this.bitsPerEntry = section.bitsPerEntry;
this.bitsPerEntry = palette.bitsPerEntry;
this.valuesPerLong = section.valuesPerLong;
this.hasPalette = section.hasPalette;
this.valuesPerLong = palette.valuesPerLong;
this.hasPalette = palette.hasPalette;
this.blocks = section.blocks;
this.blockCount = section.blockCount;
this.blocks = palette.blocks;
this.blockCount = palette.blockCount;
}
/**
@ -311,14 +310,14 @@ public class Section implements PublicCloneable<Section> {
@NotNull
@Override
public Section clone() {
public Palette clone() {
try {
Section section = (Section) super.clone();
section.blocks = blocks.clone();
section.paletteBlockMap = paletteBlockMap.clone();
section.blockPaletteMap = blockPaletteMap.clone();
section.blockCount = blockCount;
return section;
Palette palette = (Palette) super.clone();
palette.blocks = blocks.clone();
palette.paletteBlockMap = paletteBlockMap.clone();
palette.blockPaletteMap = blockPaletteMap.clone();
palette.blockCount = blockCount;
return palette;
} catch (CloneNotSupportedException e) {
MinecraftServer.getExceptionManager().handleException(e);
throw new IllegalStateException("Weird thing happened");

View File

@ -1,130 +0,0 @@
package net.minestom.server.instance.palette;
import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap;
import net.minestom.server.MinecraftServer;
import net.minestom.server.instance.Chunk;
import net.minestom.server.utils.MathUtils;
import net.minestom.server.instance.block.Block;
import net.minestom.server.utils.chunk.ChunkUtils;
import net.minestom.server.utils.clone.PublicCloneable;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import static net.minestom.server.instance.Chunk.CHUNK_SECTION_SIZE;
/**
* Used to efficiently store blocks with an optional palette.
* <p>
* The format used is the one described in the {@link net.minestom.server.network.packet.server.play.ChunkDataPacket},
* the reason is that it allows us to write the packet much faster.
*/
public class PaletteStorage implements PublicCloneable<PaletteStorage> {
private Int2ObjectRBTreeMap<Section> sectionMap = new Int2ObjectRBTreeMap<>();
private final int defaultBitsPerEntry;
private final int defaultBitsIncrement;
/**
* Creates a new palette storage.
*
* @param defaultBitsPerEntry the number of bits used for one entry (block)
* @param defaultBitsIncrement the number of bits to add per-block once the palette array is filled
*/
public PaletteStorage(int defaultBitsPerEntry, int defaultBitsIncrement) {
Check.argCondition(defaultBitsPerEntry > Section.MAXIMUM_BITS_PER_ENTRY,
"The maximum bits per entry is 15");
this.defaultBitsPerEntry = defaultBitsPerEntry;
this.defaultBitsIncrement = defaultBitsIncrement;
}
public void setBlockAt(int x, int y, int z, short blockId) {
final int sectionIndex = ChunkUtils.getSectionAt(y);
x = toChunkCoordinate(x);
z = toChunkCoordinate(z);
Section section = getSection(sectionIndex);
if (section == null) {
section = new Section(defaultBitsPerEntry, defaultBitsIncrement);
setSection(sectionIndex, section);
}
section.setBlockAt(x, y, z, blockId);
}
public short getBlockAt(int x, int y, int z) {
final int sectionIndex = ChunkUtils.getSectionAt(y);
final Section section = getSection(sectionIndex);
if (section == null) {
return 0;
}
x = toChunkCoordinate(x);
z = toChunkCoordinate(z);
return section.getBlockAt(x, y, z);
}
public Int2ObjectRBTreeMap<Section> getSectionMap() {
return sectionMap;
}
public @Nullable Collection<Section> getSections() {
return sectionMap.values();
}
public @Nullable Section getSection(int section) {
return sectionMap.get(section);
}
/**
* Loops through all the sections and blocks to find unused array (empty chunk section)
* <p>
* Useful after clearing one or multiple sections of a chunk. Can be unnecessarily expensive if the chunk
* is composed of almost-empty sections since the loop will not stop until a non-air block is discovered.
*/
public synchronized void clean() {
getSections().forEach(Section::clean);
}
/**
* Clears all the data in the palette and data array.
*/
public void clear() {
getSections().forEach(Section::clear);
}
@NotNull
@Override
public PaletteStorage clone() {
try {
PaletteStorage paletteStorage = (PaletteStorage) super.clone();
// TODO deep clone
paletteStorage.sectionMap = sectionMap.clone();//CloneUtils.cloneArray(sections, Section[]::new);
return paletteStorage;
} catch (CloneNotSupportedException e) {
MinecraftServer.getExceptionManager().handleException(e);
throw new IllegalStateException("Weird thing happened");
}
}
private void setSection(int sectionIndex, Section section) {
this.sectionMap.put(sectionIndex, section);
}
/**
* Converts a world coordinate to a chunk one.
*
* @param xz the world coordinate
* @return the chunk coordinate of {@code xz}
*/
private static int toChunkCoordinate(int xz) {
xz %= 16;
if (xz < 0) {
xz += CHUNK_SECTION_SIZE;
}
return xz;
}
}

View File

@ -3,12 +3,10 @@ package net.minestom.server.network.packet.server.play;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.ints.Int2LongRBTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minestom.server.MinecraftServer;
import net.minestom.server.instance.Section;
import net.minestom.server.instance.block.BlockHandler;
import net.minestom.server.instance.palette.PaletteStorage;
import net.minestom.server.instance.palette.Section;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.BlockPosition;
@ -25,6 +23,8 @@ import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@ -36,9 +36,9 @@ public class ChunkDataPacket implements ServerPacket, CacheablePacket {
public Biome[] biomes;
public int chunkX, chunkZ;
public PaletteStorage paletteStorage = new PaletteStorage(8, 2);
public Int2ObjectMap<BlockHandler> handlerMap;
public Int2ObjectMap<NBTCompound> nbtMap;
public Map<Integer, Section> sections = new HashMap<>();
public Map<Integer, BlockHandler> handlerMap = new HashMap<>();
public Map<Integer, NBTCompound> nbtMap = new HashMap<>();
private static final byte CHUNK_SECTION_COUNT = 16;
private static final int MAX_BITS_PER_ENTRY = 16;
@ -71,8 +71,8 @@ public class ChunkDataPacket implements ServerPacket, CacheablePacket {
Int2LongRBTreeMap maskMap = new Int2LongRBTreeMap();
for (var entry : paletteStorage.getSectionMap().int2ObjectEntrySet()) {
final int index = entry.getIntKey();
for (var entry : sections.entrySet()) {
final int index = entry.getKey();
final Section section = entry.getValue();
final int lengthIndex = index % 64;
@ -82,7 +82,7 @@ public class ChunkDataPacket implements ServerPacket, CacheablePacket {
mask |= 1L << lengthIndex;
maskMap.put(maskIndex, mask);
Utils.writeSectionBlocks(blocks, section);
Utils.writePaletteBlocks(blocks, section.getPalette());
}
final int maskSize = maskMap.size();
@ -132,8 +132,8 @@ public class ChunkDataPacket implements ServerPacket, CacheablePacket {
} else {
writer.writeVarInt(handlerMap.size());
for (var entry : handlerMap.int2ObjectEntrySet()) {
final int index = entry.getIntKey();
for (var entry : handlerMap.entrySet()) {
final int index = entry.getKey();
final BlockHandler handler = entry.getValue();
final BlockPosition blockPosition = ChunkUtils.getBlockPosition(index, chunkX, chunkZ);
@ -175,7 +175,7 @@ public class ChunkDataPacket implements ServerPacket, CacheablePacket {
}
// Data
this.paletteStorage = new PaletteStorage(8, 1);
/*this.paletteStorage = new PaletteStorage(8, 1);
int blockArrayLength = reader.readVarInt();
if (maskCount > 0) {
final long mask = masks[0]; // TODO support for variable size
@ -208,7 +208,7 @@ public class ChunkDataPacket implements ServerPacket, CacheablePacket {
data[i] = reader.readLong();
}
}
}
}*/
// Block entities
final int blockEntityCount = reader.readVarInt();

View File

@ -2,7 +2,7 @@ package net.minestom.server.utils;
import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.shorts.Short2ShortLinkedOpenHashMap;
import net.minestom.server.instance.palette.Section;
import net.minestom.server.instance.palette.Palette;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
@ -117,10 +117,10 @@ public final class Utils {
return new UUID(uuidMost, uuidLeast);
}
public static void writeSectionBlocks(ByteBuf buffer, Section section) {
public static void writePaletteBlocks(ByteBuf buffer, Palette palette) {
final short blockCount = section.getBlockCount();
final int bitsPerEntry = section.getBitsPerEntry();
final short blockCount = palette.getBlockCount();
final int bitsPerEntry = palette.getBitsPerEntry();
buffer.writeShort(blockCount);
buffer.writeByte((byte) bitsPerEntry);
@ -128,14 +128,14 @@ public final class Utils {
// Palette
if (bitsPerEntry < 9) {
// Palette has to exist
final Short2ShortLinkedOpenHashMap paletteBlockMap = section.getPaletteBlockMap();
final Short2ShortLinkedOpenHashMap paletteBlockMap = palette.getPaletteBlockMap();
writeVarInt(buffer, paletteBlockMap.size());
for (short paletteValue : paletteBlockMap.values()) {
writeVarInt(buffer, paletteValue);
}
}
final long[] blocks = section.getBlocks();
final long[] blocks = palette.getBlocks();
writeVarInt(buffer, blocks.length);
for (long datum : blocks) {
buffer.writeLong(datum);