mirror of
https://github.com/Minestom/Minestom.git
synced 2025-03-02 11:21:15 +01:00
Added BatchOption
This commit is contained in:
parent
7bbb095156
commit
24d4e9b7e4
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user