mirror of
https://github.com/Minestom/Minestom.git
synced 2024-06-28 23:44:58 +02:00
WIP palette
This commit is contained in:
parent
463e1f047f
commit
e65ab88a3b
|
@ -42,6 +42,11 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Could be a player, a monster, or an object.
|
||||||
|
* <p>
|
||||||
|
* To create your own entity you probably want to extends {@link ObjectEntity} or {@link EntityCreature} instead.
|
||||||
|
*/
|
||||||
public abstract class Entity implements Viewable, EventHandler, DataContainer {
|
public abstract class Entity implements Viewable, EventHandler, DataContainer {
|
||||||
|
|
||||||
private static final Map<Integer, Entity> entityById = new ConcurrentHashMap<>();
|
private static final Map<Integer, Entity> entityById = new ConcurrentHashMap<>();
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package net.minestom.server.instance;
|
package net.minestom.server.instance;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import net.minestom.server.MinecraftServer;
|
import net.minestom.server.MinecraftServer;
|
||||||
import net.minestom.server.Viewable;
|
import net.minestom.server.Viewable;
|
||||||
import net.minestom.server.data.Data;
|
import net.minestom.server.data.Data;
|
||||||
|
@ -32,7 +31,6 @@ import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
// TODO light data & API
|
// TODO light data & API
|
||||||
|
|
||||||
|
@ -69,11 +67,6 @@ public abstract class Chunk implements Viewable, DataContainer {
|
||||||
private final boolean shouldGenerate;
|
private final boolean shouldGenerate;
|
||||||
private boolean readOnly;
|
private boolean readOnly;
|
||||||
|
|
||||||
// Packet cache
|
|
||||||
private volatile boolean enableCachePacket;
|
|
||||||
protected volatile boolean packetUpdated;
|
|
||||||
private ByteBuf fullDataPacket;
|
|
||||||
|
|
||||||
protected volatile boolean loaded = true;
|
protected volatile boolean loaded = true;
|
||||||
protected final Set<Player> viewers = new CopyOnWriteArraySet<>();
|
protected final Set<Player> viewers = new CopyOnWriteArraySet<>();
|
||||||
|
|
||||||
|
@ -89,9 +82,6 @@ public abstract class Chunk implements Viewable, DataContainer {
|
||||||
this.chunkZ = chunkZ;
|
this.chunkZ = chunkZ;
|
||||||
this.shouldGenerate = shouldGenerate;
|
this.shouldGenerate = shouldGenerate;
|
||||||
|
|
||||||
// true by default
|
|
||||||
this.enableCachePacket = true;
|
|
||||||
|
|
||||||
if (biomes != null && biomes.length == BIOME_COUNT) {
|
if (biomes != null && biomes.length == BIOME_COUNT) {
|
||||||
this.biomes = biomes;
|
this.biomes = biomes;
|
||||||
} else {
|
} else {
|
||||||
|
@ -324,51 +314,6 @@ public abstract class Chunk implements Viewable, DataContainer {
|
||||||
this.readOnly = readOnly;
|
this.readOnly = readOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets if this chunk automatically cache the latest {@link ChunkDataPacket} version.
|
|
||||||
* <p>
|
|
||||||
* Retrieved with {@link #retrieveDataBuffer(Consumer)}.
|
|
||||||
*
|
|
||||||
* @return true if the chunk automatically cache the chunk packet
|
|
||||||
*/
|
|
||||||
public boolean enableCachePacket() {
|
|
||||||
return enableCachePacket;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables or disable the automatic {@link ChunkDataPacket} caching.
|
|
||||||
*
|
|
||||||
* @param enableCachePacket true to enable to chunk packet caching
|
|
||||||
*/
|
|
||||||
public synchronized void setEnableCachePacket(boolean enableCachePacket) {
|
|
||||||
this.enableCachePacket = enableCachePacket;
|
|
||||||
if (enableCachePacket && fullDataPacket != null) {
|
|
||||||
this.fullDataPacket.release();
|
|
||||||
this.fullDataPacket = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the cached data packet.
|
|
||||||
* <p>
|
|
||||||
* Use {@link #retrieveDataBuffer(Consumer)} to be sure to get the updated version.
|
|
||||||
*
|
|
||||||
* @return the last cached data packet, can be null or outdated
|
|
||||||
*/
|
|
||||||
public ByteBuf getFullDataPacket() {
|
|
||||||
return fullDataPacket;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the cached {@link ChunkDataPacket} of this chunk.
|
|
||||||
*
|
|
||||||
* @param fullDataPacket the new cached chunk packet
|
|
||||||
*/
|
|
||||||
public void setFullDataPacket(ByteBuf fullDataPacket) {
|
|
||||||
this.fullDataPacket = fullDataPacket;
|
|
||||||
this.packetUpdated = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes this chunk columnar space.
|
* Changes this chunk columnar space.
|
||||||
*
|
*
|
||||||
|
@ -378,27 +323,6 @@ public abstract class Chunk implements Viewable, DataContainer {
|
||||||
this.columnarSpace = columnarSpace;
|
this.columnarSpace = columnarSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves (and cache if needed) the updated data packet.
|
|
||||||
*
|
|
||||||
* @param consumer the consumer called once the packet is sure to be up-to-date
|
|
||||||
*/
|
|
||||||
public void retrieveDataBuffer(Consumer<ByteBuf> consumer) {
|
|
||||||
final ByteBuf data = getFullDataPacket();
|
|
||||||
if (data == null || !packetUpdated) {
|
|
||||||
// Packet has never been wrote or is outdated, write it
|
|
||||||
PacketWriterUtils.writeCallbackPacket(getFreshFullDataPacket(), packet -> {
|
|
||||||
if (enableCachePacket) {
|
|
||||||
setFullDataPacket(packet);
|
|
||||||
}
|
|
||||||
consumer.accept(packet);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Packet is up-to-date
|
|
||||||
consumer.accept(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a {@link ChunkDataPacket} which should contain the full chunk.
|
* Gets a {@link ChunkDataPacket} which should contain the full chunk.
|
||||||
*
|
*
|
||||||
|
@ -509,7 +433,7 @@ public abstract class Chunk implements Viewable, DataContainer {
|
||||||
final PlayerConnection playerConnection = player.getPlayerConnection();
|
final PlayerConnection playerConnection = player.getPlayerConnection();
|
||||||
|
|
||||||
// Retrieve & send the buffer to the connection
|
// Retrieve & send the buffer to the connection
|
||||||
retrieveDataBuffer(buf -> playerConnection.sendPacket(buf, true));
|
playerConnection.sendPacket(getFreshFullDataPacket());
|
||||||
|
|
||||||
// TODO do not hardcode
|
// TODO do not hardcode
|
||||||
if (MinecraftServer.isFixLighting()) {
|
if (MinecraftServer.isFixLighting()) {
|
||||||
|
@ -542,10 +466,8 @@ public abstract class Chunk implements Viewable, DataContainer {
|
||||||
* @param player the player to update the chunk to
|
* @param player the player to update the chunk to
|
||||||
*/
|
*/
|
||||||
public void sendChunkUpdate(@NotNull Player player) {
|
public void sendChunkUpdate(@NotNull Player player) {
|
||||||
retrieveDataBuffer(buf -> {
|
final PlayerConnection playerConnection = player.getPlayerConnection();
|
||||||
final PlayerConnection playerConnection = player.getPlayerConnection();
|
playerConnection.sendPacket(getFreshFullDataPacket());
|
||||||
playerConnection.sendPacket(buf, true);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -554,14 +476,10 @@ public abstract class Chunk implements Viewable, DataContainer {
|
||||||
public void sendChunkUpdate() {
|
public void sendChunkUpdate() {
|
||||||
final Set<Player> chunkViewers = getViewers();
|
final Set<Player> chunkViewers = getViewers();
|
||||||
if (!chunkViewers.isEmpty()) {
|
if (!chunkViewers.isEmpty()) {
|
||||||
retrieveDataBuffer(buf -> chunkViewers.forEach(player -> {
|
chunkViewers.forEach(player -> {
|
||||||
final PlayerConnection playerConnection = player.getPlayerConnection();
|
final PlayerConnection playerConnection = player.getPlayerConnection();
|
||||||
if (!PlayerUtils.isNettyClient(playerConnection))
|
playerConnection.sendPacket(getFreshFullDataPacket());
|
||||||
return;
|
});
|
||||||
|
|
||||||
playerConnection.sendPacket(buf, true);
|
|
||||||
}));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ import net.minestom.server.instance.block.CustomBlock;
|
||||||
import net.minestom.server.network.packet.server.play.ChunkDataPacket;
|
import net.minestom.server.network.packet.server.play.ChunkDataPacket;
|
||||||
import net.minestom.server.utils.ArrayUtils;
|
import net.minestom.server.utils.ArrayUtils;
|
||||||
import net.minestom.server.utils.BlockPosition;
|
import net.minestom.server.utils.BlockPosition;
|
||||||
import net.minestom.server.utils.MathUtils;
|
|
||||||
import net.minestom.server.utils.binary.BinaryReader;
|
import net.minestom.server.utils.binary.BinaryReader;
|
||||||
import net.minestom.server.utils.binary.BinaryWriter;
|
import net.minestom.server.utils.binary.BinaryWriter;
|
||||||
import net.minestom.server.utils.block.CustomBlockUtils;
|
import net.minestom.server.utils.block.CustomBlockUtils;
|
||||||
|
@ -36,6 +35,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
*/
|
*/
|
||||||
public class DynamicChunk extends Chunk {
|
public class DynamicChunk extends Chunk {
|
||||||
|
|
||||||
|
private static final int BITS_PER_ENTRY = 15;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the version which will be present in the serialized output.
|
* Represents the version which will be present in the serialized output.
|
||||||
* Used to define which deserializer to use.
|
* Used to define which deserializer to use.
|
||||||
|
@ -45,9 +46,11 @@ public class DynamicChunk extends Chunk {
|
||||||
// blocks id based on coordinate, see Chunk#getBlockIndex
|
// blocks id based on coordinate, see Chunk#getBlockIndex
|
||||||
// WARNING: those arrays are NOT thread-safe
|
// WARNING: those arrays are NOT thread-safe
|
||||||
// and modifying them can cause issue with block data, update, block entity and the cached chunk packet
|
// and modifying them can cause issue with block data, update, block entity and the cached chunk packet
|
||||||
protected final short[] blocksStateId = new short[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z];
|
//protected final short[] blocksStateId = new short[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z];
|
||||||
protected final short[] customBlocksId = new short[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z];
|
protected final short[] customBlocksId = new short[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z];
|
||||||
|
|
||||||
|
protected long[][] sectionBlocks = new long[CHUNK_SECTION_COUNT][0];
|
||||||
|
|
||||||
// Used to get all blocks with data (no null)
|
// Used to get all blocks with data (no null)
|
||||||
// Key is still chunk coordinates (see #getBlockIndex)
|
// Key is still chunk coordinates (see #getBlockIndex)
|
||||||
protected final Int2ObjectMap<Data> blocksData = new Int2ObjectOpenHashMap<>();
|
protected final Int2ObjectMap<Data> blocksData = new Int2ObjectOpenHashMap<>();
|
||||||
|
@ -80,12 +83,13 @@ public class DynamicChunk extends Chunk {
|
||||||
// True if the block is not complete air without any custom block capabilities
|
// True if the block is not complete air without any custom block capabilities
|
||||||
final boolean hasBlock = blockStateId != 0 || customBlockId != 0;
|
final boolean hasBlock = blockStateId != 0 || customBlockId != 0;
|
||||||
if (hasBlock) {
|
if (hasBlock) {
|
||||||
this.blocksStateId[index] = blockStateId;
|
setBlockAt(x, y, z, blockStateId);
|
||||||
this.customBlocksId[index] = customBlockId;
|
this.customBlocksId[index] = customBlockId;
|
||||||
} else {
|
} else {
|
||||||
// Block has been deleted, clear cache and return
|
// Block has been deleted, clear cache and return
|
||||||
|
|
||||||
this.blocksStateId[index] = 0; // Set to air
|
setBlockAt(x, y, z, (short) 0);
|
||||||
|
//this.blocksStateId[index] = 0; // Set to air
|
||||||
this.customBlocksId[index] = 0; // Remove custom block
|
this.customBlocksId[index] = 0; // Remove custom block
|
||||||
|
|
||||||
this.blocksData.remove(index);
|
this.blocksData.remove(index);
|
||||||
|
@ -94,8 +98,6 @@ public class DynamicChunk extends Chunk {
|
||||||
this.updatableBlocksLastUpdate.remove(index);
|
this.updatableBlocksLastUpdate.remove(index);
|
||||||
|
|
||||||
this.blockEntities.remove(index);
|
this.blockEntities.remove(index);
|
||||||
|
|
||||||
this.packetUpdated = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,8 +123,6 @@ public class DynamicChunk extends Chunk {
|
||||||
} else {
|
} else {
|
||||||
this.blockEntities.remove(index);
|
this.blockEntities.remove(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.packetUpdated = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -156,40 +156,26 @@ public class DynamicChunk extends Chunk {
|
||||||
@Override
|
@Override
|
||||||
public short getBlockStateId(int x, int y, int z) {
|
public short getBlockStateId(int x, int y, int z) {
|
||||||
final int index = getBlockIndex(x, y, z);
|
final int index = getBlockIndex(x, y, z);
|
||||||
if (!MathUtils.isBetween(index, 0, blocksStateId.length)) {
|
return getBlockAt(x, y, z);
|
||||||
return 0; // TODO: custom invalid block
|
|
||||||
}
|
|
||||||
return blocksStateId[index];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public short getCustomBlockId(int x, int y, int z) {
|
public short getCustomBlockId(int x, int y, int z) {
|
||||||
final int index = getBlockIndex(x, y, z);
|
final int index = getBlockIndex(x, y, z);
|
||||||
if (!MathUtils.isBetween(index, 0, blocksStateId.length)) {
|
|
||||||
return 0; // TODO: custom invalid block
|
|
||||||
}
|
|
||||||
return customBlocksId[index];
|
return customBlocksId[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void refreshBlockValue(int x, int y, int z, short blockStateId, short customBlockId) {
|
protected void refreshBlockValue(int x, int y, int z, short blockStateId, short customBlockId) {
|
||||||
final int blockIndex = getBlockIndex(x, y, z);
|
final int blockIndex = getBlockIndex(x, y, z);
|
||||||
if (!MathUtils.isBetween(blockIndex, 0, blocksStateId.length)) {
|
setBlockAt(x, y, z, blockStateId);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.blocksStateId[blockIndex] = blockStateId;
|
|
||||||
this.customBlocksId[blockIndex] = customBlockId;
|
this.customBlocksId[blockIndex] = customBlockId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void refreshBlockStateId(int x, int y, int z, short blockStateId) {
|
protected void refreshBlockStateId(int x, int y, int z, short blockStateId) {
|
||||||
final int blockIndex = getBlockIndex(x, y, z);
|
final int blockIndex = getBlockIndex(x, y, z);
|
||||||
if (!MathUtils.isBetween(blockIndex, 0, blocksStateId.length)) {
|
setBlockAt(x, y, z, blockStateId);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.blocksStateId[blockIndex] = blockStateId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -260,7 +246,7 @@ public class DynamicChunk extends Chunk {
|
||||||
for (byte z = 0; z < CHUNK_SIZE_Z; z++) {
|
for (byte z = 0; z < CHUNK_SIZE_Z; z++) {
|
||||||
final int index = getBlockIndex(x, y, z);
|
final int index = getBlockIndex(x, y, z);
|
||||||
|
|
||||||
final short blockStateId = blocksStateId[index];
|
final short blockStateId = getBlockAt(x, y, z);
|
||||||
final short customBlockId = customBlocksId[index];
|
final short customBlockId = customBlocksId[index];
|
||||||
|
|
||||||
// No block at the position
|
// No block at the position
|
||||||
|
@ -398,7 +384,8 @@ public class DynamicChunk extends Chunk {
|
||||||
fullDataPacket.biomes = biomes.clone();
|
fullDataPacket.biomes = biomes.clone();
|
||||||
fullDataPacket.chunkX = chunkX;
|
fullDataPacket.chunkX = chunkX;
|
||||||
fullDataPacket.chunkZ = chunkZ;
|
fullDataPacket.chunkZ = chunkZ;
|
||||||
fullDataPacket.blocksStateId = blocksStateId.clone();
|
fullDataPacket.bitsPerEntry = BITS_PER_ENTRY;
|
||||||
|
fullDataPacket.sectionBlocks = sectionBlocks.clone();
|
||||||
fullDataPacket.customBlocksId = customBlocksId.clone();
|
fullDataPacket.customBlocksId = customBlocksId.clone();
|
||||||
fullDataPacket.blockEntities = new HashSet<>(blockEntities);
|
fullDataPacket.blockEntities = new HashSet<>(blockEntities);
|
||||||
fullDataPacket.blocksData = new Int2ObjectOpenHashMap<>(blocksData);
|
fullDataPacket.blocksData = new Int2ObjectOpenHashMap<>(blocksData);
|
||||||
|
@ -409,7 +396,7 @@ public class DynamicChunk extends Chunk {
|
||||||
@Override
|
@Override
|
||||||
public Chunk copy(@NotNull Instance instance, int chunkX, int chunkZ) {
|
public Chunk copy(@NotNull Instance instance, int chunkX, int chunkZ) {
|
||||||
DynamicChunk dynamicChunk = new DynamicChunk(instance, biomes.clone(), chunkX, chunkZ);
|
DynamicChunk dynamicChunk = new DynamicChunk(instance, biomes.clone(), chunkX, chunkZ);
|
||||||
ArrayUtils.copyToDestination(blocksStateId, dynamicChunk.blocksStateId);
|
dynamicChunk.sectionBlocks = sectionBlocks.clone();
|
||||||
ArrayUtils.copyToDestination(customBlocksId, dynamicChunk.customBlocksId);
|
ArrayUtils.copyToDestination(customBlocksId, dynamicChunk.customBlocksId);
|
||||||
dynamicChunk.blocksData.putAll(blocksData);
|
dynamicChunk.blocksData.putAll(blocksData);
|
||||||
dynamicChunk.updatableBlocks.addAll(updatableBlocks);
|
dynamicChunk.updatableBlocks.addAll(updatableBlocks);
|
||||||
|
@ -418,4 +405,82 @@ public class DynamicChunk extends Chunk {
|
||||||
|
|
||||||
return dynamicChunk;
|
return dynamicChunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setBlockAt(int x, int y, int z, short blockId) {
|
||||||
|
x %= 16;
|
||||||
|
if (x < 0) {
|
||||||
|
x = CHUNK_SIZE_X + x;
|
||||||
|
}
|
||||||
|
z %= 16;
|
||||||
|
if (z < 0) {
|
||||||
|
z = CHUNK_SIZE_Z + z;
|
||||||
|
}
|
||||||
|
|
||||||
|
final char valuesPerLong = (char) (Long.SIZE / BITS_PER_ENTRY);
|
||||||
|
|
||||||
|
int sectionY = y % CHUNK_SECTION_SIZE;
|
||||||
|
int sectionIndex = (((sectionY * 16) + z) * 16) + x;
|
||||||
|
|
||||||
|
final int index = sectionIndex / valuesPerLong;
|
||||||
|
final int bitIndex = sectionIndex % valuesPerLong * BITS_PER_ENTRY;
|
||||||
|
|
||||||
|
final int section = y / CHUNK_SECTION_SIZE;
|
||||||
|
|
||||||
|
if (sectionBlocks[section].length == 0) {
|
||||||
|
sectionBlocks[section] = new long[getSize()];
|
||||||
|
}
|
||||||
|
|
||||||
|
long[] sectionBlock = sectionBlocks[section];
|
||||||
|
|
||||||
|
//System.out.println("test1 " + binary(sectionBlock[index]));
|
||||||
|
//System.out.println("test2 " + binary(((long) blockId << (bitIndex))));
|
||||||
|
sectionBlock[index] |= ((long) blockId << (bitIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String binary(long value) {
|
||||||
|
return "0b" + Long.toBinaryString(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private short getBlockAt(int x, int y, int z) {
|
||||||
|
x %= 16;
|
||||||
|
if (x < 0) {
|
||||||
|
x = CHUNK_SIZE_X + x;
|
||||||
|
}
|
||||||
|
z %= 16;
|
||||||
|
if (z < 0) {
|
||||||
|
z = CHUNK_SIZE_Z + z;
|
||||||
|
}
|
||||||
|
|
||||||
|
final char valuesPerLong = (char) (Long.SIZE / BITS_PER_ENTRY);
|
||||||
|
|
||||||
|
int sectionY = y % CHUNK_SECTION_SIZE;
|
||||||
|
int sectionIndex = (((sectionY * 16) + z) * 16) + x;
|
||||||
|
|
||||||
|
final int index = sectionIndex / valuesPerLong;
|
||||||
|
final int bitIndex = sectionIndex % valuesPerLong * BITS_PER_ENTRY;
|
||||||
|
|
||||||
|
final int section = y / CHUNK_SECTION_SIZE;
|
||||||
|
|
||||||
|
long[] blocks = sectionBlocks[section];
|
||||||
|
|
||||||
|
if (blocks.length == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
long mask = (1 << (bitIndex)) - 1;
|
||||||
|
|
||||||
|
/*System.out.println("data " + index + " " + bitIndex + " " + sectionIndex);
|
||||||
|
System.out.println("POS " + x + " " + y + " " + z);
|
||||||
|
System.out.println("mask " + binary(mask));
|
||||||
|
System.out.println("bin " + binary(blocks[index]));
|
||||||
|
System.out.println("result " + ((blocks[index] >> bitIndex) & mask));*/
|
||||||
|
return (short) (blocks[index] >> bitIndex & mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getSize() {
|
||||||
|
final int blockCount = 16 * 16 * 16; // A whole chunk section
|
||||||
|
final char valuesPerLong = (char) (Long.SIZE / BITS_PER_ENTRY);
|
||||||
|
final int arraySize = blockCount / valuesPerLong;
|
||||||
|
return arraySize;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
package net.minestom.server.instance;
|
package net.minestom.server.instance;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
import net.minestom.server.data.Data;
|
import net.minestom.server.data.Data;
|
||||||
import net.minestom.server.instance.block.BlockProvider;
|
import net.minestom.server.instance.block.BlockProvider;
|
||||||
|
@ -105,7 +104,7 @@ public class StaticChunk extends Chunk {
|
||||||
final int z = ChunkUtils.blockIndexToChunkPositionZ(i);
|
final int z = ChunkUtils.blockIndexToChunkPositionZ(i);
|
||||||
blocksStateId[i] = blockProvider.getBlockStateId(x, y, z);
|
blocksStateId[i] = blockProvider.getBlockStateId(x, y, z);
|
||||||
}
|
}
|
||||||
fullDataPacket.blocksStateId = blocksStateId;
|
//fullDataPacket.blocksStateId = blocksStateId;
|
||||||
fullDataPacket.customBlocksId = new short[0];
|
fullDataPacket.customBlocksId = new short[0];
|
||||||
fullDataPacket.blockEntities = new HashSet<>();
|
fullDataPacket.blockEntities = new HashSet<>();
|
||||||
fullDataPacket.blocksData = new Int2ObjectOpenHashMap<>();
|
fullDataPacket.blocksData = new Int2ObjectOpenHashMap<>();
|
||||||
|
@ -117,10 +116,10 @@ public class StaticChunk extends Chunk {
|
||||||
public Chunk copy(@NotNull Instance instance, int chunkX, int chunkZ) {
|
public Chunk copy(@NotNull Instance instance, int chunkX, int chunkZ) {
|
||||||
StaticChunk staticChunk = new StaticChunk(instance, biomes.clone(), chunkX, chunkZ, blockProvider);
|
StaticChunk staticChunk = new StaticChunk(instance, biomes.clone(), chunkX, chunkZ, blockProvider);
|
||||||
// Prevent re-writing the whole packet since it is static anyway
|
// Prevent re-writing the whole packet since it is static anyway
|
||||||
final ByteBuf packetBuffer = getFullDataPacket();
|
/*final ByteBuf packetBuffer = getFullDataPacket();
|
||||||
if (packetBuffer != null) {
|
if (packetBuffer != null) {
|
||||||
staticChunk.setFullDataPacket(packetBuffer);
|
staticChunk.setFullDataPacket(packetBuffer);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
return staticChunk;
|
return staticChunk;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import io.netty.buffer.Unpooled;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import net.minestom.server.MinecraftServer;
|
import net.minestom.server.MinecraftServer;
|
||||||
import net.minestom.server.data.Data;
|
import net.minestom.server.data.Data;
|
||||||
import net.minestom.server.instance.Chunk;
|
|
||||||
import net.minestom.server.instance.block.BlockManager;
|
import net.minestom.server.instance.block.BlockManager;
|
||||||
import net.minestom.server.instance.block.CustomBlock;
|
import net.minestom.server.instance.block.CustomBlock;
|
||||||
import net.minestom.server.network.packet.server.ServerPacket;
|
import net.minestom.server.network.packet.server.ServerPacket;
|
||||||
|
@ -28,18 +27,18 @@ public class ChunkDataPacket implements ServerPacket {
|
||||||
public Biome[] biomes;
|
public Biome[] biomes;
|
||||||
public int chunkX, chunkZ;
|
public int chunkX, chunkZ;
|
||||||
|
|
||||||
public short[] blocksStateId;
|
public int bitsPerEntry;
|
||||||
|
public long[][] sectionBlocks;
|
||||||
public short[] customBlocksId;
|
public short[] customBlocksId;
|
||||||
|
|
||||||
public Set<Integer> blockEntities;
|
public Set<Integer> blockEntities;
|
||||||
public Int2ObjectMap<Data> blocksData;
|
public Int2ObjectMap<Data> blocksData;
|
||||||
//public Chunk chunk;
|
|
||||||
|
|
||||||
public int[] sections;
|
public int[] sections;
|
||||||
|
|
||||||
private static final byte CHUNK_SECTION_COUNT = 16;
|
private static final byte CHUNK_SECTION_COUNT = 16;
|
||||||
private static final int BITS_PER_ENTRY = 15;
|
private static final int MAX_BITS_PER_ENTRY = 15;
|
||||||
private static final int MAX_BUFFER_SIZE = (Short.BYTES + Byte.BYTES + 5 * Byte.BYTES + (4096 * BITS_PER_ENTRY / Long.SIZE * Long.BYTES)) * CHUNK_SECTION_COUNT + 256 * Integer.BYTES;
|
private static final int MAX_BUFFER_SIZE = (Short.BYTES + Byte.BYTES + 5 * Byte.BYTES + (4096 * MAX_BITS_PER_ENTRY / Long.SIZE * Long.BYTES)) * CHUNK_SECTION_COUNT + 256 * Integer.BYTES;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(@NotNull BinaryWriter writer) {
|
public void write(@NotNull BinaryWriter writer) {
|
||||||
|
@ -51,10 +50,11 @@ public class ChunkDataPacket implements ServerPacket {
|
||||||
ByteBuf blocks = Unpooled.buffer(MAX_BUFFER_SIZE);
|
ByteBuf blocks = Unpooled.buffer(MAX_BUFFER_SIZE);
|
||||||
for (byte i = 0; i < CHUNK_SECTION_COUNT; i++) {
|
for (byte i = 0; i < CHUNK_SECTION_COUNT; i++) {
|
||||||
if (fullChunk || (sections.length == CHUNK_SECTION_COUNT && sections[i] != 0)) {
|
if (fullChunk || (sections.length == CHUNK_SECTION_COUNT && sections[i] != 0)) {
|
||||||
short[] section = getSection(i);
|
final long[] section = sectionBlocks[i];
|
||||||
if (section != null) { // section contains at least one block
|
if (section.length > 0) { // section contains at least one block
|
||||||
|
//if (true) {
|
||||||
mask |= 1 << i;
|
mask |= 1 << i;
|
||||||
Utils.writeBlocks(blocks, section, BITS_PER_ENTRY);
|
Utils.writeBlocks(blocks, section, bitsPerEntry);
|
||||||
} else {
|
} else {
|
||||||
mask |= 0;
|
mask |= 0;
|
||||||
}
|
}
|
||||||
|
@ -116,26 +116,6 @@ public class ChunkDataPacket implements ServerPacket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private short[] getSection(byte section) {
|
|
||||||
short[] blocks = new short[Chunk.CHUNK_SIZE_X * Chunk.CHUNK_SECTION_SIZE * Chunk.CHUNK_SIZE_Z];
|
|
||||||
boolean empty = true;
|
|
||||||
for (byte y = 0; y < Chunk.CHUNK_SECTION_SIZE; y++) {
|
|
||||||
for (byte x = 0; x < Chunk.CHUNK_SIZE_X; x++) {
|
|
||||||
for (byte z = 0; z < Chunk.CHUNK_SIZE_Z; z++) {
|
|
||||||
final int yPos = (y + Chunk.CHUNK_SECTION_SIZE * section);
|
|
||||||
final int index = ChunkUtils.getBlockIndex(x, yPos, z);
|
|
||||||
final short blockStateId = blocksStateId[index];
|
|
||||||
if (blockStateId != 0)
|
|
||||||
empty = false;
|
|
||||||
|
|
||||||
final int packetIndex = (((y * 16) + x) * 16) + z;
|
|
||||||
blocks[packetIndex] = blockStateId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return empty ? null : blocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getId() {
|
public int getId() {
|
||||||
return ServerPacketIdentifier.CHUNK_DATA;
|
return ServerPacketIdentifier.CHUNK_DATA;
|
||||||
|
|
|
@ -2,6 +2,7 @@ package net.minestom.server.utils;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import net.minestom.server.instance.Chunk;
|
import net.minestom.server.instance.Chunk;
|
||||||
|
import net.minestom.server.instance.block.Block;
|
||||||
import net.minestom.server.utils.binary.BinaryWriter;
|
import net.minestom.server.utils.binary.BinaryWriter;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
@ -115,31 +116,57 @@ public final class Utils {
|
||||||
70409299, 70409299, 0, 69273666, 69273666, 0, 68174084, 68174084, 0, Integer.MIN_VALUE,
|
70409299, 70409299, 0, 69273666, 69273666, 0, 68174084, 68174084, 0, Integer.MIN_VALUE,
|
||||||
0, 5};
|
0, 5};
|
||||||
|
|
||||||
public static void writeBlocks(ByteBuf buffer, short[] blocksId, int bitsPerEntry) {
|
public static void writeBlocks(ByteBuf buffer, long[] blocksId, int bitsPerEntry) {
|
||||||
short count = 0;
|
/*short count = 0;
|
||||||
for (short id : blocksId)
|
for (short id : blocksId)
|
||||||
if (id != 0)
|
if (id != 0)
|
||||||
count++;
|
count++;*/
|
||||||
|
|
||||||
buffer.writeShort(count);
|
|
||||||
|
//buffer.writeShort(count);
|
||||||
|
buffer.writeShort(200);
|
||||||
buffer.writeByte((byte) bitsPerEntry);
|
buffer.writeByte((byte) bitsPerEntry);
|
||||||
int[] blocksData = new int[Chunk.CHUNK_SIZE_X * Chunk.CHUNK_SECTION_SIZE * Chunk.CHUNK_SIZE_Z];
|
|
||||||
for (int y = 0; y < Chunk.CHUNK_SECTION_SIZE; y++) {
|
final long[] data = blocksId;//encodeBlocksTEST(bitsPerEntry);
|
||||||
for (int x = 0; x < Chunk.CHUNK_SIZE_X; x++) {
|
|
||||||
for (int z = 0; z < Chunk.CHUNK_SIZE_Z; z++) {
|
|
||||||
int sectionIndex = (((y * 16) + x) * 16) + z;
|
|
||||||
int index = y << 8 | z << 4 | x;
|
|
||||||
blocksData[index] = blocksId[sectionIndex];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final long[] data = encodeBlocks(blocksData, bitsPerEntry);
|
|
||||||
writeVarIntBuf(buffer, data.length);
|
writeVarIntBuf(buffer, data.length);
|
||||||
for (long datum : data) {
|
for (long datum : data) {
|
||||||
buffer.writeLong(datum);
|
buffer.writeLong(datum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized static long[] encodeBlocksTEST(int bitsPerEntry) {
|
||||||
|
//long test = (Block.TORCH.getBlockId() << (64 - 50 - bitsPerEntry + 1));
|
||||||
|
//System.out.println("BINARY: 0b" + Long.toBinaryString(test) + " " + (64 - 50 - bitsPerEntry + 1));
|
||||||
|
final int blockCount = 16 * 16 * 16; // A whole chunk section
|
||||||
|
final int longSize = Long.SIZE; // 64
|
||||||
|
final char valuesPerLong = (char) (longSize / bitsPerEntry);
|
||||||
|
final int arraySize = blockCount / valuesPerLong;
|
||||||
|
|
||||||
|
long[] data = new long[arraySize];
|
||||||
|
//data[0] = 0b000000000000001_000000000000001_000000000000001_000000000000001L;
|
||||||
|
//data[1] = 0b000000000000001_000000000000001_000000000000001_000000000000010L;
|
||||||
|
|
||||||
|
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 long blockId = x % 2 == 0 && z % 2 == 0 ? Block.AIR.getBlockId() : Block.LAVA.getBlockId();
|
||||||
|
int sectionIndex = (((y * 16) + z) * 16) + x;
|
||||||
|
|
||||||
|
final int index = sectionIndex / valuesPerLong;
|
||||||
|
final int bitIndex = sectionIndex % valuesPerLong * bitsPerEntry;
|
||||||
|
|
||||||
|
data[index] |= (blockId << bitIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String binary(long value) {
|
||||||
|
return "0b" + Long.toBinaryString(value);
|
||||||
|
}
|
||||||
|
|
||||||
public static long[] encodeBlocks(int[] blocks, int bitsPerEntry) {
|
public static long[] encodeBlocks(int[] blocks, int bitsPerEntry) {
|
||||||
final long maxEntryValue = (1L << bitsPerEntry) - 1;
|
final long maxEntryValue = (1L << bitsPerEntry) - 1;
|
||||||
final char valuesPerLong = (char) (64 / bitsPerEntry);
|
final char valuesPerLong = (char) (64 / bitsPerEntry);
|
||||||
|
@ -160,4 +187,5 @@ public final class Utils {
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user