Added BatchOption

This commit is contained in:
Felix Cravic 2020-11-25 09:47:04 +01:00
parent 7bbb095156
commit 24d4e9b7e4
6 changed files with 171 additions and 59 deletions

View File

@ -8,6 +8,7 @@ import net.minestom.server.entity.Player;
import net.minestom.server.entity.pathfinding.PFColumnarSpace;
import net.minestom.server.event.player.PlayerChunkLoadEvent;
import net.minestom.server.event.player.PlayerChunkUnloadEvent;
import net.minestom.server.instance.batch.BatchOption;
import net.minestom.server.instance.batch.BlockBatch;
import net.minestom.server.instance.batch.ChunkBatch;
import net.minestom.server.instance.block.Block;
@ -240,15 +241,22 @@ public abstract class Chunk implements Viewable, DataContainer {
/**
* Creates a copy of this chunk, including blocks state id, custom block id, biomes, update data.
* <p>
* The instance and chunk position (X/Z) can be modified using the given arguments.
* The chunk position (X/Z) can be modified using the given arguments.
*
* @param chunkX the new chunk X
* @param chunkZ the new chunk Z
* @param chunkX the chunk X of the copy
* @param chunkZ the chunk Z of the copy
* @return a copy of this chunk with a potentially new instance and position
*/
@NotNull
public abstract Chunk copy(int chunkX, int chunkZ);
/**
* Resets the chunk, this means clearing all the data making it empty.
* <p>
* Used for {@link BatchOption#isFullChunk()}.
*/
public abstract void reset();
/**
* Gets the {@link CustomBlock} at a position.
*
@ -386,6 +394,37 @@ public abstract class Chunk implements Viewable, DataContainer {
return fullDataPacket;
}
/**
* Gets the light packet of this chunk.
*
* @return the light packet
*/
@NotNull
public UpdateLightPacket getLightPacket() {
// TODO do not hardcode light
UpdateLightPacket updateLightPacket = new UpdateLightPacket(getIdentifier(), getLastChangeTime());
updateLightPacket.chunkX = getChunkX();
updateLightPacket.chunkZ = getChunkZ();
updateLightPacket.skyLightMask = 0x3FFF0;
updateLightPacket.blockLightMask = 0x3F;
updateLightPacket.emptySkyLightMask = 0x0F;
updateLightPacket.emptyBlockLightMask = 0x3FFC0;
byte[] bytes = new byte[2048];
Arrays.fill(bytes, (byte) 0xFF);
List<byte[]> temp = new ArrayList<>(14);
List<byte[]> temp2 = new ArrayList<>(6);
for (int i = 0; i < 14; ++i) {
temp.add(bytes);
}
for (int i = 0; i < 6; ++i) {
temp2.add(bytes);
}
updateLightPacket.skyLight = temp;
updateLightPacket.blockLight = temp2;
return updateLightPacket;
}
/**
* Used to verify if the chunk should still be kept in memory.
*
@ -469,7 +508,7 @@ public abstract class Chunk implements Viewable, DataContainer {
*
* @param player the player
*/
protected synchronized void sendChunk(@NotNull Player player) {
public synchronized void sendChunk(@NotNull Player player) {
// Only send loaded chunk
if (!isLoaded())
return;
@ -479,30 +518,16 @@ public abstract class Chunk implements Viewable, DataContainer {
// Retrieve & send the buffer to the connection
playerConnection.sendPacket(getFreshFullDataPacket());
// TODO do not hardcode light
{
UpdateLightPacket updateLightPacket = new UpdateLightPacket(getIdentifier(), getLastChangeTime());
updateLightPacket.chunkX = getChunkX();
updateLightPacket.chunkZ = getChunkZ();
updateLightPacket.skyLightMask = 0x3FFF0;
updateLightPacket.blockLightMask = 0x3F;
updateLightPacket.emptySkyLightMask = 0x0F;
updateLightPacket.emptyBlockLightMask = 0x3FFC0;
byte[] bytes = new byte[2048];
Arrays.fill(bytes, (byte) 0xFF);
List<byte[]> temp = new ArrayList<>(14);
List<byte[]> temp2 = new ArrayList<>(6);
for (int i = 0; i < 14; ++i) {
temp.add(bytes);
}
for (int i = 0; i < 6; ++i) {
temp2.add(bytes);
}
updateLightPacket.skyLight = temp;
updateLightPacket.blockLight = temp2;
playerConnection.sendPacket(getLightPacket());
}
playerConnection.sendPacket(updateLightPacket);
public synchronized void sendChunk() {
if (!isLoaded()) {
return;
}
sendPacketToViewers(getFreshFullDataPacket());
sendPacketToViewers(getLightPacket());
}
/**

View File

@ -31,7 +31,7 @@ import java.util.Set;
/**
* Represents a {@link Chunk} which store each individual block in memory.
* <p>
* WARNING: not thread-safe
* WARNING: not thread-safe.
*/
public class DynamicChunk extends Chunk {
@ -408,6 +408,17 @@ public class DynamicChunk extends Chunk {
return dynamicChunk;
}
@Override
public void reset() {
this.blockPalette.clear();
this.customBlockPalette.clear();
this.blocksData.clear();
this.updatableBlocks.clear();
this.updatableBlocksLastUpdate.clear();
this.blockEntities.clear();
}
private short getBlockAt(@NotNull PaletteStorage paletteStorage, int x, int y, int z) {
return paletteStorage.getBlockAt(x, y, z);
}

View File

@ -0,0 +1,33 @@
package net.minestom.server.instance.batch;
import org.jetbrains.annotations.NotNull;
public class BatchOption {
private boolean fullChunk = false;
public BatchOption() {
}
/**
* Gets if the batch is responsible for composing the whole chunk.
* <p>
* Having it to true means that the batch will clear the chunk data before placing the blocks.
*
* @return true if the batch is responsible for all the chunk
*/
public boolean isFullChunk() {
return fullChunk;
}
/**
* @param fullChunk true to make this batch composes the whole chunk
* @return 'this' for chaining
* @see #isFullChunk()
*/
@NotNull
public BatchOption setFullChunk(boolean fullChunk) {
this.fullChunk = fullChunk;
return this;
}
}

View File

@ -6,6 +6,8 @@ import net.minestom.server.instance.Instance;
import net.minestom.server.instance.InstanceContainer;
import net.minestom.server.instance.block.CustomBlock;
import net.minestom.server.utils.block.CustomBlockUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
@ -23,33 +25,39 @@ import java.util.concurrent.atomic.AtomicInteger;
public class BlockBatch implements InstanceBatch {
private final InstanceContainer instance;
private final BatchOption batchOption;
private final Map<Chunk, List<BlockData>> data = new HashMap<>();
public BlockBatch(InstanceContainer instance) {
public BlockBatch(@NotNull InstanceContainer instance, @NotNull BatchOption batchOption) {
this.instance = instance;
this.batchOption = batchOption;
}
public BlockBatch(@NotNull InstanceContainer instance) {
this(instance, new BatchOption());
}
@Override
public synchronized void setBlockStateId(int x, int y, int z, short blockStateId, Data data) {
public synchronized void setBlockStateId(int x, int y, int z, short blockStateId, @Nullable Data data) {
final Chunk chunk = this.instance.getChunkAt(x, z);
addBlockData(chunk, x, y, z, blockStateId, (short) 0, data);
}
@Override
public void setCustomBlock(int x, int y, int z, short customBlockId, Data data) {
public void setCustomBlock(int x, int y, int z, short customBlockId, @Nullable Data data) {
final Chunk chunk = this.instance.getChunkAt(x, z);
final CustomBlock customBlock = BLOCK_MANAGER.getCustomBlock(customBlockId);
addBlockData(chunk, x, y, z, customBlock.getDefaultBlockStateId(), customBlockId, data);
}
@Override
public synchronized void setSeparateBlocks(int x, int y, int z, short blockStateId, short customBlockId, Data data) {
public synchronized void setSeparateBlocks(int x, int y, int z, short blockStateId, short customBlockId, @Nullable Data data) {
final Chunk chunk = this.instance.getChunkAt(x, z);
addBlockData(chunk, x, y, z, blockStateId, customBlockId, data);
}
private void addBlockData(Chunk chunk, int x, int y, int z, short blockStateId, short customBlockId, Data data) {
private void addBlockData(@NotNull Chunk chunk, int x, int y, int z, short blockStateId, short customBlockId, @Nullable Data data) {
List<BlockData> blocksData = this.data.get(chunk);
if (blocksData == null)
blocksData = new ArrayList<>();
@ -67,7 +75,7 @@ public class BlockBatch implements InstanceBatch {
this.data.put(chunk, blocksData);
}
public void flush(Runnable callback) {
public void flush(@Nullable Runnable callback) {
synchronized (data) {
AtomicInteger counter = new AtomicInteger();
for (Map.Entry<Chunk, List<BlockData>> entry : data.entrySet()) {
@ -78,12 +86,20 @@ public class BlockBatch implements InstanceBatch {
if (!chunk.isLoaded())
return;
if (batchOption.isFullChunk()) {
chunk.reset();
}
for (BlockData data : dataList) {
data.apply(chunk);
}
// Refresh chunk for viewers
chunk.sendChunkUpdate();
if (batchOption.isFullChunk()) {
chunk.sendChunk();
} else {
chunk.sendChunkUpdate();
}
final boolean isLast = counter.incrementAndGet() == data.size();

View File

@ -30,6 +30,7 @@ public class ChunkBatch implements InstanceBatch {
private final InstanceContainer instance;
private final Chunk chunk;
private final BatchOption batchOption;
private final boolean generationBatch;
@ -42,9 +43,11 @@ public class ChunkBatch implements InstanceBatch {
private Int2ObjectMap<Data> blockDataMap;
public ChunkBatch(@NotNull InstanceContainer instance, @NotNull Chunk chunk,
@NotNull BatchOption batchOption,
boolean generationBatch) {
this.instance = instance;
this.chunk = chunk;
this.batchOption = batchOption;
this.generationBatch = generationBatch;
if (!generationBatch) {
@ -53,6 +56,11 @@ public class ChunkBatch implements InstanceBatch {
}
}
public ChunkBatch(@NotNull InstanceContainer instance, @NotNull Chunk chunk,
boolean generationBatch) {
this(instance, chunk, new BatchOption(), generationBatch);
}
@Override
public void setBlockStateId(int x, int y, int z, short blockStateId, @Nullable Data data) {
addBlockData((byte) x, y, (byte) z, blockStateId, (short) 0, data);
@ -120,6 +128,10 @@ public class ChunkBatch implements InstanceBatch {
final List<ChunkPopulator> populators = chunkGenerator.getPopulators();
final boolean hasPopulator = populators != null && !populators.isEmpty();
if (batchOption.isFullChunk()) {
this.chunk.reset();
}
chunkGenerator.generateChunkData(this, chunk.getChunkX(), chunk.getChunkZ());
if (hasPopulator) {
@ -128,16 +140,7 @@ public class ChunkBatch implements InstanceBatch {
}
}
// Refresh chunk for viewers
this.chunk.sendChunkUpdate();
this.instance.refreshLastBlockChangeTime();
// Safe callback
instance.scheduleNextTick(inst -> {
OptionalCallback.execute(callback, chunk);
});
updateChunk(callback, true);
}
});
}
@ -197,18 +200,7 @@ public class ChunkBatch implements InstanceBatch {
}
}
// Refresh chunk for viewers
chunk.sendChunkUpdate();
this.instance.refreshLastBlockChangeTime();
if (callback != null) {
if (safeCallback) {
this.instance.scheduleNextTick(inst -> callback.accept(chunk));
} else {
callback.accept(chunk);
}
}
updateChunk(callback, safeCallback);
}
}
@ -234,8 +226,26 @@ public class ChunkBatch implements InstanceBatch {
ChunkUtils.blockIndexToChunkPositionY(index),
ChunkUtils.blockIndexToChunkPositionZ(index),
blockId, customBlockId, data, CustomBlockUtils.hasUpdate(customBlockId));
}
private void updateChunk(@Nullable ChunkCallback callback, boolean safeCallback) {
// Refresh chunk for viewers
if (batchOption.isFullChunk()) {
chunk.sendChunk();
} else {
chunk.sendChunkUpdate();
}
this.instance.refreshLastBlockChangeTime();
if (callback != null) {
if (safeCallback) {
this.instance.scheduleNextTick(inst -> callback.accept(chunk));
} else {
callback.accept(chunk);
}
}
}
}

View File

@ -45,12 +45,12 @@ public class PaletteStorage {
private int valuesPerLong;
private boolean hasPalette;
private long[][] sectionBlocks = new long[CHUNK_SECTION_COUNT][0];
private long[][] sectionBlocks;
// chunk section - palette index = block id
private Short2ShortLinkedOpenHashMap[] paletteBlockMaps = new Short2ShortLinkedOpenHashMap[CHUNK_SECTION_COUNT];
private Short2ShortLinkedOpenHashMap[] paletteBlockMaps;
// chunk section - block id = palette index
private Short2ShortOpenHashMap[] blockPaletteMaps = new Short2ShortOpenHashMap[CHUNK_SECTION_COUNT];
private Short2ShortOpenHashMap[] blockPaletteMaps;
/**
* Creates a new palette storage.
@ -72,6 +72,15 @@ public class PaletteStorage {
this.valuesPerLong = Long.SIZE / bitsPerEntry;
this.hasPalette = bitsPerEntry <= PALETTE_MAXIMUM_BITS;
init();
}
private void init() {
this.sectionBlocks = new long[CHUNK_SECTION_COUNT][0];
this.paletteBlockMaps = new Short2ShortLinkedOpenHashMap[CHUNK_SECTION_COUNT];
this.blockPaletteMaps = new Short2ShortOpenHashMap[CHUNK_SECTION_COUNT];
}
public void setBlockAt(int x, int y, int z, short blockId) {
@ -139,6 +148,14 @@ public class PaletteStorage {
}
}
/**
* Clears all the data in the palette and data array.
*/
public void clear() {
init();
}
@NotNull
public PaletteStorage copy() {
PaletteStorage paletteStorage = new PaletteStorage(bitsPerEntry, bitsIncrement);
paletteStorage.sectionBlocks = sectionBlocks.clone();