mirror of
https://github.com/Minestom/Minestom.git
synced 2024-11-17 08:05:25 +01:00
Chunk update
This commit is contained in:
parent
a7484f8644
commit
caa89dd2f8
@ -39,7 +39,10 @@ import net.minestom.server.scoreboard.Team;
|
||||
import net.minestom.server.sound.Sound;
|
||||
import net.minestom.server.sound.SoundCategory;
|
||||
import net.minestom.server.stat.PlayerStatistic;
|
||||
import net.minestom.server.utils.*;
|
||||
import net.minestom.server.utils.ArrayUtils;
|
||||
import net.minestom.server.utils.BlockPosition;
|
||||
import net.minestom.server.utils.MathUtils;
|
||||
import net.minestom.server.utils.Position;
|
||||
import net.minestom.server.utils.chunk.ChunkUtils;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import net.minestom.server.world.DimensionType;
|
||||
@ -540,7 +543,7 @@ public class Player extends LivingEntity implements CommandSender {
|
||||
viewableChunks.clear();
|
||||
|
||||
if (this.instance != null) {
|
||||
DimensionType instanceDimensionType = instance.getDimensionType();
|
||||
final DimensionType instanceDimensionType = instance.getDimensionType();
|
||||
if (dimensionType != instanceDimensionType)
|
||||
sendDimension(instanceDimensionType);
|
||||
}
|
||||
@ -557,7 +560,6 @@ public class Player extends LivingEntity implements CommandSender {
|
||||
if (chunk != null) {
|
||||
viewableChunks.add(chunk);
|
||||
chunk.addViewer(this);
|
||||
instance.sendChunk(this, chunk);
|
||||
if (chunk.getChunkX() == Math.floorDiv((int) getPosition().getX(), 16) && chunk.getChunkZ() == Math.floorDiv((int) getPosition().getZ(), 16))
|
||||
updateViewPosition(chunk);
|
||||
}
|
||||
@ -1196,7 +1198,7 @@ public class Player extends LivingEntity implements CommandSender {
|
||||
* It does remove and add the player from the chunks viewers list when removed or added
|
||||
* It also calls the events {@link PlayerChunkUnloadEvent} and {@link PlayerChunkLoadEvent}
|
||||
*
|
||||
* @param newChunk the current/new player chunk
|
||||
* @param newChunk the current/new player chunk
|
||||
*/
|
||||
protected void onChunkChange(Chunk newChunk) {
|
||||
final long[] lastVisibleChunks = new long[viewableChunks.size()];
|
||||
@ -1236,7 +1238,6 @@ public class Player extends LivingEntity implements CommandSender {
|
||||
}
|
||||
this.viewableChunks.add(chunk);
|
||||
chunk.addViewer(this);
|
||||
instance.sendChunk(this, chunk);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -15,18 +15,21 @@ import net.minestom.server.instance.block.CustomBlock;
|
||||
import net.minestom.server.instance.block.UpdateConsumer;
|
||||
import net.minestom.server.network.PacketWriterUtils;
|
||||
import net.minestom.server.network.packet.server.play.ChunkDataPacket;
|
||||
import net.minestom.server.network.packet.server.play.UpdateLightPacket;
|
||||
import net.minestom.server.network.player.PlayerConnection;
|
||||
import net.minestom.server.utils.BlockPosition;
|
||||
import net.minestom.server.utils.PacketUtils;
|
||||
import net.minestom.server.utils.chunk.ChunkUtils;
|
||||
import net.minestom.server.utils.player.PlayerUtils;
|
||||
import net.minestom.server.utils.time.CooldownUtils;
|
||||
import net.minestom.server.utils.time.UpdateOption;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import net.minestom.server.world.biomes.Biome;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
// TODO light data & API
|
||||
public abstract class Chunk implements Viewable {
|
||||
@ -56,7 +59,7 @@ public abstract class Chunk implements Viewable {
|
||||
// (block index)/(last update in ms)
|
||||
protected Int2LongMap updatableBlocksLastUpdate = new Int2LongOpenHashMap();
|
||||
|
||||
protected volatile boolean packetUpdated;
|
||||
protected AtomicBoolean packetUpdated = new AtomicBoolean(false);
|
||||
|
||||
// Block entities
|
||||
protected Set<Integer> blockEntities = new CopyOnWriteArraySet<>();
|
||||
@ -176,10 +179,22 @@ public abstract class Chunk implements Viewable {
|
||||
return chunkZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cached data packet
|
||||
* <p>
|
||||
* Use {@link #retrieveDataBuffer(Consumer)} to be sure to get the updated version
|
||||
*
|
||||
* @return the current cached data packet, can be null or outdated
|
||||
*/
|
||||
public ByteBuf getFullDataPacket() {
|
||||
return fullDataPacket;
|
||||
}
|
||||
|
||||
public void setFullDataPacket(ByteBuf fullDataPacket) {
|
||||
this.fullDataPacket = fullDataPacket;
|
||||
this.packetUpdated.set(true);
|
||||
}
|
||||
|
||||
protected boolean isBlockEntity(short blockStateId) {
|
||||
final Block block = Block.fromStateId(blockStateId);
|
||||
return block.hasBlockEntity();
|
||||
@ -189,17 +204,6 @@ public abstract class Chunk implements Viewable {
|
||||
return blockEntities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the columnar space linked to this chunk
|
||||
* <p>
|
||||
* Used internally by the pathfinder
|
||||
*
|
||||
* @return this chunk columnar space
|
||||
*/
|
||||
public PFColumnarSpace getColumnarSpace() {
|
||||
return columnarSpace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change this chunk columnar space
|
||||
*
|
||||
@ -209,19 +213,47 @@ public abstract class Chunk implements Viewable {
|
||||
this.columnarSpace = columnarSpace;
|
||||
}
|
||||
|
||||
public void setFullDataPacket(ByteBuf fullDataPacket) {
|
||||
this.fullDataPacket = fullDataPacket;
|
||||
this.packetUpdated = true;
|
||||
/**
|
||||
* Retrieve 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.get()) {
|
||||
PacketWriterUtils.writeCallbackPacket(getFreshFullDataPacket(), packet -> {
|
||||
setFullDataPacket(packet);
|
||||
consumer.accept(packet);
|
||||
});
|
||||
} else {
|
||||
consumer.accept(data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the chunk
|
||||
*
|
||||
* @return the serialized chunk
|
||||
* @throws IOException
|
||||
*/
|
||||
protected abstract byte[] getSerializedData() throws IOException;
|
||||
|
||||
/**
|
||||
* Get a {@link ChunkDataPacket} which should contain the full chunk
|
||||
*
|
||||
* @return a fresh full chunk data packet
|
||||
*/
|
||||
public ChunkDataPacket getFreshFullDataPacket() {
|
||||
ChunkDataPacket fullDataPacket = getFreshPacket();
|
||||
fullDataPacket.fullChunk = true;
|
||||
return fullDataPacket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@link ChunkDataPacket} which should contain the non-full chunk
|
||||
*
|
||||
* @return a fresh non-full chunk data packet
|
||||
*/
|
||||
public ChunkDataPacket getFreshPartialDataPacket() {
|
||||
ChunkDataPacket fullDataPacket = getFreshPacket();
|
||||
fullDataPacket.fullChunk = false;
|
||||
@ -233,20 +265,6 @@ public abstract class Chunk implements Viewable {
|
||||
*/
|
||||
protected abstract ChunkDataPacket getFreshPacket();
|
||||
|
||||
// Write the packet in the current thread
|
||||
public void refreshDataPacket() {
|
||||
final ByteBuf buffer = PacketUtils.writePacket(getFreshFullDataPacket());
|
||||
setFullDataPacket(buffer);
|
||||
}
|
||||
|
||||
// Write the packet in the writer thread pools
|
||||
public void refreshDataPacket(Runnable runnable) {
|
||||
PacketWriterUtils.writeCallbackPacket(getFreshFullDataPacket(), buf -> {
|
||||
setFullDataPacket(buf);
|
||||
runnable.run();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to verify if the chunk should still be kept in memory
|
||||
*
|
||||
@ -266,6 +284,9 @@ public abstract class Chunk implements Viewable {
|
||||
public boolean addViewer(Player player) {
|
||||
final boolean result = this.viewers.add(player);
|
||||
|
||||
// Send the chunk data & light packets to the player
|
||||
sendChunk(player);
|
||||
|
||||
PlayerChunkLoadEvent playerChunkLoadEvent = new PlayerChunkLoadEvent(player, chunkX, chunkZ);
|
||||
player.callEvent(PlayerChunkLoadEvent.class, playerChunkLoadEvent);
|
||||
return result;
|
||||
@ -286,6 +307,101 @@ public abstract class Chunk implements Viewable {
|
||||
return Collections.unmodifiableSet(viewers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the chunk data to {@code player}
|
||||
*
|
||||
* @param player the player
|
||||
*/
|
||||
protected void sendChunk(Player player) {
|
||||
if (!isLoaded())
|
||||
return;
|
||||
if (!PlayerUtils.isNettyClient(player))
|
||||
return;
|
||||
|
||||
final PlayerConnection playerConnection = player.getPlayerConnection();
|
||||
|
||||
retrieveDataBuffer(buf -> {
|
||||
playerConnection.sendPacket(buf, true);
|
||||
});
|
||||
|
||||
// TODO do not hardcode
|
||||
if (MinecraftServer.isFixLighting()) {
|
||||
UpdateLightPacket updateLightPacket = new UpdateLightPacket();
|
||||
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<>();
|
||||
List<byte[]> temp2 = new ArrayList<>();
|
||||
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;
|
||||
PacketWriterUtils.writeAndSend(player, updateLightPacket);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a full {@link ChunkDataPacket} to {@code player}
|
||||
*
|
||||
* @param player the player to update the chunk to
|
||||
*/
|
||||
public void sendChunkUpdate(Player player) {
|
||||
retrieveDataBuffer(buf -> {
|
||||
final PlayerConnection playerConnection = player.getPlayerConnection();
|
||||
playerConnection.sendPacket(buf, true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a full {@link ChunkDataPacket} to all chunk viewers
|
||||
*/
|
||||
public void sendChunkUpdate() {
|
||||
final Set<Player> chunkViewers = getViewers();
|
||||
if (!chunkViewers.isEmpty()) {
|
||||
retrieveDataBuffer(buf -> {
|
||||
chunkViewers.forEach(player -> {
|
||||
final PlayerConnection playerConnection = player.getPlayerConnection();
|
||||
if (!PlayerUtils.isNettyClient(playerConnection))
|
||||
return;
|
||||
|
||||
playerConnection.sendPacket(buf, true);
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a chunk section update packet to {@code player}
|
||||
*
|
||||
* @param section the section to update
|
||||
* @param player the player to send the packet to
|
||||
*/
|
||||
public void sendChunkSectionUpdate(int section, Player player) {
|
||||
if (!PlayerUtils.isNettyClient(player))
|
||||
return;
|
||||
|
||||
PacketWriterUtils.writeAndSend(player, getChunkSectionUpdatePacket(section));
|
||||
}
|
||||
|
||||
protected ChunkDataPacket getChunkSectionUpdatePacket(int section) {
|
||||
ChunkDataPacket chunkDataPacket = getFreshPartialDataPacket();
|
||||
chunkDataPacket.fullChunk = false;
|
||||
int[] sections = new int[16];
|
||||
sections[section] = 1;
|
||||
chunkDataPacket.sections = sections;
|
||||
return chunkDataPacket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the chunk as "unloaded"
|
||||
*/
|
||||
|
@ -19,188 +19,189 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
public class DynamicChunk extends Chunk {
|
||||
|
||||
public short[] blocksStateId = new short[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z];
|
||||
private short[] customBlocksId = new short[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z];
|
||||
public short[] blocksStateId = new short[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z];
|
||||
private short[] customBlocksId = new short[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z];
|
||||
|
||||
public DynamicChunk(Biome[] biomes, int chunkX, int chunkZ) {
|
||||
super(biomes, chunkX, chunkZ);
|
||||
}
|
||||
public DynamicChunk(Biome[] biomes, int chunkX, int chunkZ) {
|
||||
super(biomes, chunkX, chunkZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void UNSAFE_removeCustomBlock(int x, int y, int z) {
|
||||
final int index = getBlockIndex(x, y, z);
|
||||
this.customBlocksId[index] = 0; // Set to none
|
||||
this.blocksData.remove(index);
|
||||
@Override
|
||||
public void UNSAFE_removeCustomBlock(int x, int y, int z) {
|
||||
final int index = getBlockIndex(x, y, z);
|
||||
this.customBlocksId[index] = 0; // Set to none
|
||||
this.blocksData.remove(index);
|
||||
|
||||
this.updatableBlocks.remove(index);
|
||||
this.updatableBlocksLastUpdate.remove(index);
|
||||
this.updatableBlocks.remove(index);
|
||||
this.updatableBlocksLastUpdate.remove(index);
|
||||
|
||||
this.blockEntities.remove(index);
|
||||
}
|
||||
this.blockEntities.remove(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setBlock(int x, int y, int z, short blockStateId, short customId, Data data, UpdateConsumer updateConsumer) {
|
||||
final int index = getBlockIndex(x, y, z);
|
||||
if (blockStateId != 0
|
||||
|| (blockStateId == 0 && customId != 0 && updateConsumer != null)) { // Allow custom air block for update purpose, refused if no update consumer has been found
|
||||
this.blocksStateId[index] = blockStateId;
|
||||
this.customBlocksId[index] = customId;
|
||||
} else {
|
||||
// Block has been deleted, clear cache and return
|
||||
@Override
|
||||
protected void setBlock(int x, int y, int z, short blockStateId, short customId, Data data, UpdateConsumer updateConsumer) {
|
||||
final int index = getBlockIndex(x, y, z);
|
||||
if (blockStateId != 0
|
||||
|| (blockStateId == 0 && customId != 0 && updateConsumer != null)) { // Allow custom air block for update purpose, refused if no update consumer has been found
|
||||
this.blocksStateId[index] = blockStateId;
|
||||
this.customBlocksId[index] = customId;
|
||||
} else {
|
||||
// Block has been deleted, clear cache and return
|
||||
|
||||
this.blocksStateId[index] = 0; // Set to air
|
||||
this.blocksStateId[index] = 0; // Set to air
|
||||
|
||||
this.blocksData.remove(index);
|
||||
this.blocksData.remove(index);
|
||||
|
||||
this.updatableBlocks.remove(index);
|
||||
this.updatableBlocksLastUpdate.remove(index);
|
||||
this.updatableBlocks.remove(index);
|
||||
this.updatableBlocksLastUpdate.remove(index);
|
||||
|
||||
this.blockEntities.remove(index);
|
||||
this.blockEntities.remove(index);
|
||||
|
||||
this.packetUpdated = false;
|
||||
return;
|
||||
}
|
||||
this.packetUpdated.set(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the new data (or remove from the map if is null)
|
||||
if (data != null) {
|
||||
this.blocksData.put(index, data);
|
||||
} else {
|
||||
this.blocksData.remove(index);
|
||||
}
|
||||
// Set the new data (or remove from the map if is null)
|
||||
if (data != null) {
|
||||
this.blocksData.put(index, data);
|
||||
} else {
|
||||
this.blocksData.remove(index);
|
||||
}
|
||||
|
||||
// Set update consumer
|
||||
if (updateConsumer != null) {
|
||||
this.updatableBlocks.add(index);
|
||||
this.updatableBlocksLastUpdate.put(index, System.currentTimeMillis());
|
||||
} else {
|
||||
this.updatableBlocks.remove(index);
|
||||
this.updatableBlocksLastUpdate.remove(index);
|
||||
}
|
||||
// Set update consumer
|
||||
if (updateConsumer != null) {
|
||||
this.updatableBlocks.add(index);
|
||||
this.updatableBlocksLastUpdate.put(index, System.currentTimeMillis());
|
||||
} else {
|
||||
this.updatableBlocks.remove(index);
|
||||
this.updatableBlocksLastUpdate.remove(index);
|
||||
}
|
||||
|
||||
if (isBlockEntity(blockStateId)) {
|
||||
this.blockEntities.add(index);
|
||||
} else {
|
||||
this.blockEntities.remove(index);
|
||||
}
|
||||
// Set block entity
|
||||
if (isBlockEntity(blockStateId)) {
|
||||
this.blockEntities.add(index);
|
||||
} else {
|
||||
this.blockEntities.remove(index);
|
||||
}
|
||||
|
||||
this.packetUpdated = false;
|
||||
this.packetUpdated.set(false);
|
||||
|
||||
if (columnarSpace != null) {
|
||||
final ColumnarOcclusionFieldList columnarOcclusionFieldList = columnarSpace.occlusionFields();
|
||||
final PFBlockDescription blockDescription = new PFBlockDescription(Block.fromStateId(blockStateId));
|
||||
columnarOcclusionFieldList.onBlockChanged(x, y, z, blockDescription, 0);
|
||||
}
|
||||
}
|
||||
if (columnarSpace != null) {
|
||||
final ColumnarOcclusionFieldList columnarOcclusionFieldList = columnarSpace.occlusionFields();
|
||||
final PFBlockDescription blockDescription = new PFBlockDescription(Block.fromStateId(blockStateId));
|
||||
columnarOcclusionFieldList.onBlockChanged(x, y, z, blockDescription, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getBlockStateId(int x, int y, int z) {
|
||||
final int index = getBlockIndex(x, y, z);
|
||||
if (!MathUtils.isBetween(index, 0, blocksStateId.length)) {
|
||||
return 0; // TODO: custom invalid block
|
||||
}
|
||||
final short id = blocksStateId[index];
|
||||
return id;
|
||||
}
|
||||
@Override
|
||||
public short getBlockStateId(int x, int y, int z) {
|
||||
final int index = getBlockIndex(x, y, z);
|
||||
if (!MathUtils.isBetween(index, 0, blocksStateId.length)) {
|
||||
return 0; // TODO: custom invalid block
|
||||
}
|
||||
final short id = blocksStateId[index];
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getCustomBlockId(int x, int y, int z) {
|
||||
final int index = getBlockIndex(x, y, z);
|
||||
if (!MathUtils.isBetween(index, 0, blocksStateId.length)) {
|
||||
return 0; // TODO: custom invalid block
|
||||
}
|
||||
final short id = customBlocksId[index];
|
||||
return id;
|
||||
}
|
||||
@Override
|
||||
public short getCustomBlockId(int x, int y, int z) {
|
||||
final int index = getBlockIndex(x, y, z);
|
||||
if (!MathUtils.isBetween(index, 0, blocksStateId.length)) {
|
||||
return 0; // TODO: custom invalid block
|
||||
}
|
||||
final short id = customBlocksId[index];
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomBlock getCustomBlock(int x, int y, int z) {
|
||||
final int index = getBlockIndex(x, y, z);
|
||||
if (!MathUtils.isBetween(index, 0, blocksStateId.length)) {
|
||||
return null; // TODO: custom invalid block
|
||||
}
|
||||
final short id = customBlocksId[index];
|
||||
return id != 0 ? BLOCK_MANAGER.getCustomBlock(id) : null;
|
||||
}
|
||||
@Override
|
||||
public CustomBlock getCustomBlock(int x, int y, int z) {
|
||||
final int index = getBlockIndex(x, y, z);
|
||||
if (!MathUtils.isBetween(index, 0, blocksStateId.length)) {
|
||||
return null; // TODO: custom invalid block
|
||||
}
|
||||
final short id = customBlocksId[index];
|
||||
return id != 0 ? BLOCK_MANAGER.getCustomBlock(id) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void refreshBlockValue(int x, int y, int z, short blockStateId, short customId) {
|
||||
final int blockIndex = getBlockIndex(x, y, z);
|
||||
if (!MathUtils.isBetween(blockIndex, 0, blocksStateId.length)) {
|
||||
return;
|
||||
}
|
||||
@Override
|
||||
protected void refreshBlockValue(int x, int y, int z, short blockStateId, short customId) {
|
||||
final int blockIndex = getBlockIndex(x, y, z);
|
||||
if (!MathUtils.isBetween(blockIndex, 0, blocksStateId.length)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.blocksStateId[blockIndex] = blockStateId;
|
||||
this.customBlocksId[blockIndex] = customId;
|
||||
}
|
||||
this.blocksStateId[blockIndex] = blockStateId;
|
||||
this.customBlocksId[blockIndex] = customId;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void refreshBlockStateId(int x, int y, int z, short blockStateId) {
|
||||
final int blockIndex = getBlockIndex(x, y, z);
|
||||
if (!MathUtils.isBetween(blockIndex, 0, blocksStateId.length)) {
|
||||
return;
|
||||
}
|
||||
@Override
|
||||
protected void refreshBlockStateId(int x, int y, int z, short blockStateId) {
|
||||
final int blockIndex = getBlockIndex(x, y, z);
|
||||
if (!MathUtils.isBetween(blockIndex, 0, blocksStateId.length)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.blocksStateId[blockIndex] = blockStateId;
|
||||
}
|
||||
this.blocksStateId[blockIndex] = blockStateId;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] getSerializedData() throws IOException {
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(output);
|
||||
@Override
|
||||
protected byte[] getSerializedData() throws IOException {
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(output);
|
||||
|
||||
for (int i = 0; i < BIOME_COUNT; i++) {
|
||||
dos.writeByte(biomes[i].getId());
|
||||
}
|
||||
for (int i = 0; i < BIOME_COUNT; i++) {
|
||||
dos.writeByte(biomes[i].getId());
|
||||
}
|
||||
|
||||
for (byte x = 0; x < CHUNK_SIZE_X; x++) {
|
||||
for (short y = 0; y < CHUNK_SIZE_Y; y++) {
|
||||
for (byte z = 0; z < CHUNK_SIZE_Z; z++) {
|
||||
final int index = getBlockIndex(x, y, z);
|
||||
for (byte x = 0; x < CHUNK_SIZE_X; x++) {
|
||||
for (short y = 0; y < CHUNK_SIZE_Y; y++) {
|
||||
for (byte z = 0; z < CHUNK_SIZE_Z; z++) {
|
||||
final int index = getBlockIndex(x, y, z);
|
||||
|
||||
final short blockStateId = blocksStateId[index];
|
||||
final short customBlockId = customBlocksId[index];
|
||||
final short blockStateId = blocksStateId[index];
|
||||
final short customBlockId = customBlocksId[index];
|
||||
|
||||
if (blockStateId == 0 && customBlockId == 0)
|
||||
continue;
|
||||
if (blockStateId == 0 && customBlockId == 0)
|
||||
continue;
|
||||
|
||||
final Data data = blocksData.get(index);
|
||||
final Data data = blocksData.get(index);
|
||||
|
||||
// Chunk coordinates
|
||||
dos.writeInt(x);
|
||||
dos.writeInt(y);
|
||||
dos.writeInt(z);
|
||||
// Chunk coordinates
|
||||
dos.writeInt(x);
|
||||
dos.writeInt(y);
|
||||
dos.writeInt(z);
|
||||
|
||||
// Id
|
||||
dos.writeShort(blockStateId);
|
||||
dos.writeShort(customBlockId);
|
||||
// Id
|
||||
dos.writeShort(blockStateId);
|
||||
dos.writeShort(customBlockId);
|
||||
|
||||
// Data
|
||||
final boolean hasData = (data != null && (data instanceof SerializableData));
|
||||
dos.writeBoolean(hasData);
|
||||
if (hasData) {
|
||||
final byte[] d = ((SerializableData) data).getSerializedData();
|
||||
dos.writeInt(d.length);
|
||||
dos.write(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Data
|
||||
final boolean hasData = (data != null && (data instanceof SerializableData));
|
||||
dos.writeBoolean(hasData);
|
||||
if (hasData) {
|
||||
final byte[] d = ((SerializableData) data).getSerializedData();
|
||||
dos.writeInt(d.length);
|
||||
dos.write(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final byte[] result = output.toByteArray();
|
||||
return result;
|
||||
}
|
||||
final byte[] result = output.toByteArray();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ChunkDataPacket getFreshPacket() {
|
||||
ChunkDataPacket fullDataPacket = new ChunkDataPacket();
|
||||
fullDataPacket.biomes = biomes.clone();
|
||||
fullDataPacket.chunkX = chunkX;
|
||||
fullDataPacket.chunkZ = chunkZ;
|
||||
fullDataPacket.blocksStateId = blocksStateId.clone();
|
||||
fullDataPacket.customBlocksId = customBlocksId.clone();
|
||||
fullDataPacket.blockEntities = new CopyOnWriteArraySet<>(blockEntities);
|
||||
fullDataPacket.blocksData = new Int2ObjectOpenHashMap<>(blocksData);
|
||||
return fullDataPacket;
|
||||
}
|
||||
@Override
|
||||
protected ChunkDataPacket getFreshPacket() {
|
||||
ChunkDataPacket fullDataPacket = new ChunkDataPacket();
|
||||
fullDataPacket.biomes = biomes.clone();
|
||||
fullDataPacket.chunkX = chunkX;
|
||||
fullDataPacket.chunkZ = chunkZ;
|
||||
fullDataPacket.blocksStateId = blocksStateId.clone();
|
||||
fullDataPacket.customBlocksId = customBlocksId.clone();
|
||||
fullDataPacket.blockEntities = new CopyOnWriteArraySet<>(blockEntities);
|
||||
fullDataPacket.blocksData = new Int2ObjectOpenHashMap<>(blocksData);
|
||||
return fullDataPacket;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package net.minestom.server.instance;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.UpdateManager;
|
||||
import net.minestom.server.data.Data;
|
||||
@ -19,13 +18,11 @@ import net.minestom.server.instance.block.BlockManager;
|
||||
import net.minestom.server.instance.block.CustomBlock;
|
||||
import net.minestom.server.network.PacketWriterUtils;
|
||||
import net.minestom.server.network.packet.server.play.BlockActionPacket;
|
||||
import net.minestom.server.network.packet.server.play.ChunkDataPacket;
|
||||
import net.minestom.server.network.packet.server.play.TimeUpdatePacket;
|
||||
import net.minestom.server.storage.StorageFolder;
|
||||
import net.minestom.server.utils.BlockPosition;
|
||||
import net.minestom.server.utils.Position;
|
||||
import net.minestom.server.utils.chunk.ChunkUtils;
|
||||
import net.minestom.server.utils.player.PlayerUtils;
|
||||
import net.minestom.server.utils.time.CooldownUtils;
|
||||
import net.minestom.server.utils.time.TimeUnit;
|
||||
import net.minestom.server.utils.time.UpdateOption;
|
||||
@ -227,21 +224,6 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
|
||||
|
||||
protected abstract void createChunk(int chunkX, int chunkZ, Consumer<Chunk> callback);
|
||||
|
||||
/**
|
||||
* Send all chunks data to {@code player}
|
||||
*
|
||||
* @param player the player
|
||||
*/
|
||||
public abstract void sendChunks(Player player);
|
||||
|
||||
/**
|
||||
* Send a specific chunk data to {@code player}
|
||||
*
|
||||
* @param player the player
|
||||
* @param chunk the chunk
|
||||
*/
|
||||
public abstract void sendChunk(Player player, Chunk chunk);
|
||||
|
||||
/**
|
||||
* When set to true, chunks will load with players moving closer
|
||||
* Otherwise using {@link #loadChunk(int, int)} will be required to even spawn a player
|
||||
@ -264,50 +246,6 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
|
||||
*/
|
||||
public abstract boolean isInVoid(Position position);
|
||||
|
||||
//
|
||||
|
||||
/**
|
||||
* Send a full {@link ChunkDataPacket} to {@code player}
|
||||
*
|
||||
* @param player the player to update the chunk to
|
||||
* @param chunk the chunk to send
|
||||
*/
|
||||
public void sendChunkUpdate(Player player, Chunk chunk) {
|
||||
player.getPlayerConnection().sendPacket(chunk.getFullDataPacket(), true);
|
||||
}
|
||||
|
||||
protected void sendChunkUpdate(Collection<Player> players, Chunk chunk) {
|
||||
final ByteBuf chunkData = chunk.getFullDataPacket();
|
||||
players.forEach(player -> {
|
||||
if (!PlayerUtils.isNettyClient(player))
|
||||
return;
|
||||
|
||||
player.getPlayerConnection().sendPacket(chunkData, true);
|
||||
});
|
||||
}
|
||||
|
||||
protected void sendChunkSectionUpdate(Chunk chunk, int section, Collection<Player> players) {
|
||||
PacketWriterUtils.writeAndSend(players, getChunkSectionUpdatePacket(chunk, section));
|
||||
}
|
||||
|
||||
public void sendChunkSectionUpdate(Chunk chunk, int section, Player player) {
|
||||
if (!PlayerUtils.isNettyClient(player))
|
||||
return;
|
||||
|
||||
PacketWriterUtils.writeAndSend(player, getChunkSectionUpdatePacket(chunk, section));
|
||||
}
|
||||
|
||||
protected ChunkDataPacket getChunkSectionUpdatePacket(Chunk chunk, int section) {
|
||||
ChunkDataPacket chunkDataPacket = chunk.getFreshPartialDataPacket();
|
||||
chunkDataPacket.fullChunk = false;
|
||||
int[] sections = new int[16];
|
||||
sections[section] = 1;
|
||||
chunkDataPacket.sections = sections;
|
||||
return chunkDataPacket;
|
||||
}
|
||||
//
|
||||
|
||||
|
||||
/**
|
||||
* Get if the instance has been registered in {@link InstanceManager}
|
||||
*
|
||||
@ -783,7 +721,6 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
|
||||
|
||||
if (isPlayer) {
|
||||
final Player player = (Player) entity;
|
||||
sendChunks(player);
|
||||
getWorldBorder().init(player);
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
package net.minestom.server.instance;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.Setter;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.data.Data;
|
||||
@ -12,20 +11,18 @@ import net.minestom.server.event.player.PlayerBlockBreakEvent;
|
||||
import net.minestom.server.instance.batch.BlockBatch;
|
||||
import net.minestom.server.instance.batch.ChunkBatch;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.instance.block.BlockProvider;
|
||||
import net.minestom.server.instance.block.CustomBlock;
|
||||
import net.minestom.server.instance.block.rule.BlockPlacementRule;
|
||||
import net.minestom.server.network.PacketWriterUtils;
|
||||
import net.minestom.server.network.packet.server.play.BlockChangePacket;
|
||||
import net.minestom.server.network.packet.server.play.ParticlePacket;
|
||||
import net.minestom.server.network.packet.server.play.UnloadChunkPacket;
|
||||
import net.minestom.server.network.packet.server.play.UpdateLightPacket;
|
||||
import net.minestom.server.particle.Particle;
|
||||
import net.minestom.server.particle.ParticleCreator;
|
||||
import net.minestom.server.storage.StorageFolder;
|
||||
import net.minestom.server.utils.BlockPosition;
|
||||
import net.minestom.server.utils.Position;
|
||||
import net.minestom.server.utils.chunk.ChunkUtils;
|
||||
import net.minestom.server.utils.player.PlayerUtils;
|
||||
import net.minestom.server.utils.thread.MinestomThread;
|
||||
import net.minestom.server.utils.time.TimeUnit;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
@ -41,7 +38,6 @@ import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* InstanceContainer is an instance that contains chunks in contrary to SharedInstance.
|
||||
@ -66,7 +62,7 @@ public class InstanceContainer extends Instance {
|
||||
private boolean autoChunkLoad;
|
||||
|
||||
@Setter
|
||||
private BiFunction<Integer, Integer, Function<BlockPosition, Short>> chunkDecider;
|
||||
private BiFunction<Integer, Integer, BlockProvider> chunkDecider;
|
||||
|
||||
public InstanceContainer(UUID uniqueId, DimensionType dimensionType, StorageFolder storageFolder) {
|
||||
super(uniqueId, dimensionType);
|
||||
@ -256,7 +252,7 @@ public class InstanceContainer extends Instance {
|
||||
|
||||
// The player probably have a wrong version of this chunk section, send it
|
||||
if (blockStateId == 0) {
|
||||
sendChunkSectionUpdate(chunk, ChunkUtils.getSectionAt(y), player);
|
||||
chunk.sendChunkSectionUpdate(ChunkUtils.getSectionAt(y), player);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -286,7 +282,7 @@ public class InstanceContainer extends Instance {
|
||||
} else {
|
||||
// Cancelled so we need to refresh player chunk section
|
||||
final int section = ChunkUtils.getSectionAt(blockPosition.getY());
|
||||
sendChunkSectionUpdate(chunk, section, player);
|
||||
chunk.sendChunkSectionUpdate(section, player);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -439,11 +435,19 @@ public class InstanceContainer extends Instance {
|
||||
chunkGenerator.fillBiomes(biomes, chunkX, chunkZ);
|
||||
}
|
||||
|
||||
Function<BlockPosition, Short> chunkSuppler = chunkDecider != null ? chunkDecider.apply(chunkX, chunkZ) : null;
|
||||
final Chunk chunk = chunkSuppler == null ? new DynamicChunk(biomes, chunkX, chunkZ) : new StaticChunk(biomes, chunkX, chunkZ, chunkSuppler) ;
|
||||
final Chunk chunk;
|
||||
final BlockProvider blockProvider = chunkDecider != null ? chunkDecider.apply(chunkX, chunkZ) : null;
|
||||
if (blockProvider != null) {
|
||||
// Use static chunk
|
||||
chunk = new StaticChunk(biomes, chunkX, chunkZ, blockProvider);
|
||||
} else {
|
||||
// Use dynamic chunk
|
||||
chunk = new DynamicChunk(biomes, chunkX, chunkZ);
|
||||
}
|
||||
|
||||
cacheChunk(chunk);
|
||||
|
||||
if (chunkGenerator != null && chunkSuppler == null) {
|
||||
if (chunkGenerator != null && blockProvider == null) {
|
||||
final ChunkBatch chunkBatch = createChunkBatch(chunk);
|
||||
|
||||
chunkBatch.flushChunkGenerator(chunkGenerator, callback);
|
||||
@ -457,62 +461,6 @@ public class InstanceContainer extends Instance {
|
||||
callChunkLoadEvent(chunkX, chunkZ);
|
||||
}
|
||||
|
||||
public void sendChunkUpdate(Chunk chunk) {
|
||||
final Set<Player> chunkViewers = chunk.getViewers();
|
||||
if (!chunkViewers.isEmpty()) {
|
||||
sendChunkUpdate(chunkViewers, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendChunks(Player player) {
|
||||
for (Chunk chunk : getChunks()) {
|
||||
sendChunk(player, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendChunk(Player player, Chunk chunk) {
|
||||
if (!chunk.isLoaded())
|
||||
return;
|
||||
if (!PlayerUtils.isNettyClient(player))
|
||||
return;
|
||||
|
||||
final ByteBuf data = chunk.getFullDataPacket();
|
||||
if (data == null || !chunk.packetUpdated) {
|
||||
PacketWriterUtils.writeCallbackPacket(chunk.getFreshFullDataPacket(), packet -> {
|
||||
chunk.setFullDataPacket(packet);
|
||||
sendChunkUpdate(player, chunk);
|
||||
});
|
||||
} else {
|
||||
sendChunkUpdate(player, chunk);
|
||||
}
|
||||
|
||||
// TODO do not hardcode
|
||||
if (MinecraftServer.isFixLighting()) {
|
||||
UpdateLightPacket updateLightPacket = new UpdateLightPacket();
|
||||
updateLightPacket.chunkX = chunk.getChunkX();
|
||||
updateLightPacket.chunkZ = chunk.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<>();
|
||||
List<byte[]> temp2 = new ArrayList<>();
|
||||
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;
|
||||
PacketWriterUtils.writeAndSend(player, updateLightPacket);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableAutoChunkLoad(boolean enable) {
|
||||
this.autoChunkLoad = enable;
|
||||
|
@ -101,11 +101,6 @@ public class SharedInstance extends Instance {
|
||||
instanceContainer.setStorageFolder(storageFolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendChunkSectionUpdate(Chunk chunk, int section, Player player) {
|
||||
instanceContainer.sendChunkSectionUpdate(chunk, section, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void retrieveChunk(int chunkX, int chunkZ, Consumer<Chunk> callback) {
|
||||
instanceContainer.retrieveChunk(chunkX, chunkZ, callback);
|
||||
@ -116,16 +111,6 @@ public class SharedInstance extends Instance {
|
||||
instanceContainer.createChunk(chunkX, chunkZ, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendChunks(Player player) {
|
||||
instanceContainer.sendChunks(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendChunk(Player player, Chunk chunk) {
|
||||
instanceContainer.sendChunk(player, chunk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableAutoChunkLoad(boolean enable) {
|
||||
instanceContainer.enableAutoChunkLoad(enable);
|
||||
|
@ -2,82 +2,82 @@ package net.minestom.server.instance;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.instance.block.BlockProvider;
|
||||
import net.minestom.server.instance.block.CustomBlock;
|
||||
import net.minestom.server.instance.block.UpdateConsumer;
|
||||
import net.minestom.server.network.packet.server.play.ChunkDataPacket;
|
||||
import net.minestom.server.utils.BlockPosition;
|
||||
import net.minestom.server.utils.chunk.ChunkUtils;
|
||||
import net.minestom.server.world.biomes.Biome;
|
||||
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class StaticChunk extends Chunk {
|
||||
|
||||
final Function<BlockPosition, Short> blockProvider;
|
||||
protected final BlockProvider blockProvider;
|
||||
|
||||
public StaticChunk(Biome[] biomes, int chunkX, int chunkZ, Function<BlockPosition, Short> blockProvider) {
|
||||
super(biomes, chunkX, chunkZ);
|
||||
this.blockProvider = blockProvider;
|
||||
}
|
||||
public StaticChunk(Biome[] biomes, int chunkX, int chunkZ, BlockProvider blockProvider) {
|
||||
super(biomes, chunkX, chunkZ);
|
||||
this.blockProvider = blockProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void UNSAFE_removeCustomBlock(int x, int y, int z) {
|
||||
//noop
|
||||
}
|
||||
@Override
|
||||
public void UNSAFE_removeCustomBlock(int x, int y, int z) {
|
||||
//noop
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setBlock(int x, int y, int z, short blockStateId, short customId, Data data, UpdateConsumer updateConsumer) {
|
||||
//noop
|
||||
}
|
||||
@Override
|
||||
protected void setBlock(int x, int y, int z, short blockStateId, short customId, Data data, UpdateConsumer updateConsumer) {
|
||||
//noop
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getBlockStateId(int x, int y, int z) {
|
||||
return blockProvider.apply(new BlockPosition(x, y, z));
|
||||
}
|
||||
@Override
|
||||
public short getBlockStateId(int x, int y, int z) {
|
||||
return blockProvider.getBlockStateId(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getCustomBlockId(int x, int y, int z) {
|
||||
//noop
|
||||
return 0;
|
||||
}
|
||||
@Override
|
||||
public short getCustomBlockId(int x, int y, int z) {
|
||||
//noop
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomBlock getCustomBlock(int x, int y, int z) {
|
||||
//noop
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public CustomBlock getCustomBlock(int x, int y, int z) {
|
||||
//noop
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void refreshBlockValue(int x, int y, int z, short blockStateId, short customId) {
|
||||
//noop
|
||||
}
|
||||
@Override
|
||||
protected void refreshBlockValue(int x, int y, int z, short blockStateId, short customId) {
|
||||
//noop
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void refreshBlockStateId(int x, int y, int z, short blockStateId) {
|
||||
//noop
|
||||
}
|
||||
@Override
|
||||
protected void refreshBlockStateId(int x, int y, int z, short blockStateId) {
|
||||
//noop
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] getSerializedData(){
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
protected byte[] getSerializedData() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ChunkDataPacket getFreshPacket() {
|
||||
ChunkDataPacket fullDataPacket = new ChunkDataPacket();
|
||||
fullDataPacket.biomes = biomes.clone();
|
||||
fullDataPacket.chunkX = chunkX;
|
||||
fullDataPacket.chunkZ = chunkZ;
|
||||
short[] blocksStateId = new short[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z];
|
||||
for (int i = 0; i < blocksStateId.length; i++) {
|
||||
blocksStateId[i] = blockProvider.apply(ChunkUtils.getBlockPosition(i, 0, 0));
|
||||
}
|
||||
fullDataPacket.blocksStateId = blocksStateId;
|
||||
fullDataPacket.customBlocksId = new short[0];
|
||||
fullDataPacket.blockEntities = new CopyOnWriteArraySet<>(blockEntities);
|
||||
fullDataPacket.blocksData = new Int2ObjectOpenHashMap<>(blocksData);
|
||||
return fullDataPacket;
|
||||
}
|
||||
@Override
|
||||
protected ChunkDataPacket getFreshPacket() {
|
||||
ChunkDataPacket fullDataPacket = new ChunkDataPacket();
|
||||
fullDataPacket.biomes = biomes.clone();
|
||||
fullDataPacket.chunkX = chunkX;
|
||||
fullDataPacket.chunkZ = chunkZ;
|
||||
short[] blocksStateId = new short[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z];
|
||||
for (int i = 0; i < blocksStateId.length; i++) {
|
||||
final int[] pos = ChunkUtils.indexToPosition(i, 0, 0);
|
||||
blocksStateId[i] = blockProvider.getBlockStateId(pos[0], pos[1], pos[2]);
|
||||
}
|
||||
fullDataPacket.blocksStateId = blocksStateId;
|
||||
fullDataPacket.customBlocksId = new short[0];
|
||||
fullDataPacket.blockEntities = new CopyOnWriteArraySet<>(blockEntities);
|
||||
fullDataPacket.blocksData = new Int2ObjectOpenHashMap<>(blocksData);
|
||||
return fullDataPacket;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -74,9 +74,8 @@ public class BlockBatch implements InstanceBatch {
|
||||
data.apply(chunk);
|
||||
}
|
||||
|
||||
chunk.refreshDataPacket(() -> {
|
||||
instance.sendChunkUpdate(chunk);
|
||||
});
|
||||
// Refresh chunk for viewers
|
||||
chunk.sendChunkUpdate();
|
||||
|
||||
if (isLast) {
|
||||
if (callback != null)
|
||||
|
@ -102,9 +102,8 @@ public class ChunkBatch implements InstanceBatch {
|
||||
data.apply(chunk);
|
||||
}
|
||||
|
||||
chunk.refreshDataPacket(() -> {
|
||||
instance.sendChunkUpdate(chunk);
|
||||
});
|
||||
// Refresh chunk for viewers
|
||||
chunk.sendChunkUpdate();
|
||||
|
||||
if (callback != null)
|
||||
callback.accept(chunk);
|
||||
|
@ -0,0 +1,6 @@
|
||||
package net.minestom.server.instance.block;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface BlockProvider {
|
||||
short getBlockStateId(int x, int y, int z);
|
||||
}
|
@ -136,7 +136,7 @@ public class BlockPlacementListener {
|
||||
|
||||
// Refresh chunk section if needed
|
||||
if (refreshChunk) {
|
||||
instance.sendChunkSectionUpdate(chunk, ChunkUtils.getSectionAt(blockPosition.getY()), player);
|
||||
chunk.sendChunkSectionUpdate(ChunkUtils.getSectionAt(blockPosition.getY()), player);
|
||||
}
|
||||
|
||||
player.getInventory().refreshSlot(player.getHeldSlot());
|
||||
|
Loading…
Reference in New Issue
Block a user