Skip to content

Commit

Permalink
feat: use new block update pk (#559)
Browse files Browse the repository at this point in the history
  • Loading branch information
smartcmd authored Jan 29, 2025
1 parent 09aa44f commit 14ede80
Show file tree
Hide file tree
Showing 10 changed files with 190 additions and 63 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,14 @@ Unless otherwise specified, any version comparison below is the comparison of se
will call this method in all dimensions now.
- (API) Added `ItemBaseComponent#getLockMode` and `ItemBaseComponent#setLockMode` methods to get and set the lock mode of an item.
- Implemented reeds (also called sugar cane) and cactus.
- Implemented `UpdateSubChunkBlocksPacket` related logic, which will make client load large range block updates much quicker (e.g.
using `/fill` command to fill a large area).

### Changed

- (API) Renamed `FullContainerTypeBuilder` to `Builder`.
- (API) Moved method `Chunk#isLoaded` to `UnsafeChunk#isLoaded`.
- (API) Made method `Dimension#createUpdateBlockPacket` private, consider using `Dimension#sendBlockUpdateTo` method instead.
- World will be skipped if failed to be load.
- Main thread will sleep a short time if gui is enabled when the server exits abnormally. This gives user time to see what goes wrong.
- Server won't crash if failed to load the descriptor of a plugin now. An error message will be print to the console instead.
Expand All @@ -56,6 +60,7 @@ Unless otherwise specified, any version comparison below is the comparison of se
### Removed

- Removed `Extension#afterServerStarted` method.
- Removed `UnsafeChunk#SECTION_SIZE`.

## [0.1.3](https://github.com/AllayMC/Allay/releases/tag/0.1.3) (API 0.4.0) - 2025-1-17

Expand Down
9 changes: 7 additions & 2 deletions api/src/main/java/org/allaymc/api/world/Dimension.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public interface Dimension {
*
* @return the created block update packet.
*/
static UpdateBlockPacket createUpdateBlockPacket(BlockState newBlockState, int x, int y, int z, int layer) {
private static UpdateBlockPacket createUpdateBlockPacket(BlockState newBlockState, int x, int y, int z, int layer) {
var updateBlockPacket = new UpdateBlockPacket();
updateBlockPacket.setBlockPosition(org.cloudburstmc.math.vector.Vector3i.from(x, y, z));
updateBlockPacket.setDefinition(newBlockState.toNetworkBlockDefinitionRuntime());
Expand Down Expand Up @@ -603,13 +603,18 @@ default <DATATYPE> void updateBlockProperty(BlockPropertyType<DATATYPE> property
if (oldBlockState == newBlockState) return;

chunk.setBlockState(xIndex, y, zIndex, newBlockState, layer);
chunk.sendChunkPacket(createUpdateBlockPacket(newBlockState, x, y, z, layer));
}

/**
* @see #getCollidingBlockStates(AABBfc, int, boolean)
*/
default BlockState[][][] getCollidingBlockStates(AABBfc aabb) {
return getCollidingBlockStates(aabb, 0);
}

/**
* @see #getCollidingBlockStates(AABBfc, int, boolean)
*/
default BlockState[][][] getCollidingBlockStates(AABBfc aabb, int layer) {
return getCollidingBlockStates(aabb, layer, false);
}
Expand Down
6 changes: 0 additions & 6 deletions api/src/main/java/org/allaymc/api/world/chunk/Chunk.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,6 @@
*/
@ThreadSafe
public interface Chunk extends UnsafeChunk {
/**
* Check if the chunk is loaded.
*
* @return {@code true} if the chunk is loaded, {@code false} otherwise
*/
boolean isLoaded();

/**
* Get the chunk loaders that load this chunk
Expand Down
20 changes: 18 additions & 2 deletions api/src/main/java/org/allaymc/api/world/chunk/UnsafeChunk.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
*/
@NotThreadSafe
public interface UnsafeChunk {
int SECTION_SIZE = 16 * 16 * 16;

/**
* Calculate the index of the pos in the chunk.
Expand All @@ -47,6 +46,13 @@ static int index(int x, int y, int z) {
return (x << 8) + (z << 4) + y;
}

/**
* Check if the chunk is loaded.
*
* @return {@code true} if the chunk is loaded, {@code false} otherwise
*/
boolean isLoaded();

/**
* Get the state of the chunk.
*
Expand Down Expand Up @@ -173,8 +179,18 @@ static int index(int x, int y, int z) {
* @param blockState the block state to set.
* @param layer the layer of the block.
*/
void setBlockState(@Range(from = 0, to = 15) int x, int y, @Range(from = 0, to = 15) int z, BlockState blockState, int layer);
void setBlockState(@Range(from = 0, to = 15) int x, int y, @Range(from = 0, to = 15) int z, BlockState blockState, int layer, boolean send);

/**
* @see #setBlockState(int, int, int, BlockState, int)
*/
default void setBlockState(@Range(from = 0, to = 15) int x, int y, @Range(from = 0, to = 15) int z, BlockState blockState, int layer) {
setBlockState(x, y, z, blockState, layer, true);
}

/**
* @see #setBlockState(int, int, int, BlockState, int)
*/
default void setBlockState(@Range(from = 0, to = 15) int x, int y, @Range(from = 0, to = 15) int z, BlockState blockState) {
setBlockState(x, y, z, blockState, 0);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import lombok.Getter;
import org.allaymc.api.block.type.BlockState;
import org.allaymc.api.block.type.BlockTypes;
import org.allaymc.api.world.Dimension;
import org.allaymc.api.world.chunk.ChunkSource;
import org.allaymc.api.world.chunk.UnsafeChunk;

Expand Down Expand Up @@ -39,10 +38,6 @@ private void setBlockStateInOtherChunk(int x, int y, int z, BlockState blockStat
if (chunk == null) return;

chunk.setBlockState(x & 15, y, z & 15, blockState, layer);
if (chunk.isLoaded()) {
// The chunk has been loaded into the world, so we need to send a block update packet
chunk.addChunkPacket(Dimension.createUpdateBlockPacket(blockState, x, y, z, layer));
}
}

public BlockState getBlockState(int x, int y, int z) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import io.netty.buffer.ByteBufOutputStream;
import it.unimi.dsi.fastutil.objects.ReferenceArrayList;
import lombok.extern.slf4j.Slf4j;
import org.allaymc.api.world.chunk.Chunk;
import org.allaymc.server.datastruct.bitarray.BitArray;
import org.allaymc.server.datastruct.bitarray.BitArrayVersion;
import org.cloudburstmc.nbt.NbtUtils;
Expand All @@ -22,6 +21,7 @@
@Slf4j
public final class Palette<V> {

private static final int SECTION_SIZE = 16 * 16 * 16;
private static final byte COPY_LAST_FLAG_HEADER = (byte) (0x7F << 1) | 1;
private static final BitArrayVersion INITIAL_VERSION = BitArrayVersion.V0;

Expand All @@ -37,7 +37,7 @@ public Palette(V first, BitArrayVersion version) {
}

public Palette(V first, List<V> palette, BitArrayVersion version) {
this.bitArray = version.createArray(Chunk.SECTION_SIZE);
this.bitArray = version.createArray(SECTION_SIZE);
this.palette = palette;
// Please note that the first entry shouldn't be changed
this.palette.add(first);
Expand Down Expand Up @@ -114,7 +114,7 @@ public void readFromStoragePersistent(ByteBuf byteBuf, PersistentDataDeserialize
this.palette.clear();

if (version == BitArrayVersion.V0) {
this.bitArray = version.createArray(Chunk.SECTION_SIZE, null);
this.bitArray = version.createArray(SECTION_SIZE, null);
this.palette.add(deserializer.deserialize(byteBuf));
return;
}
Expand Down Expand Up @@ -170,7 +170,7 @@ public void readFromStorageRuntime(ByteBuf byteBuf, RuntimeDataDeserializer<V> d
var paletteSize = 1;

if (version == BitArrayVersion.V0) {
this.bitArray = version.createArray(Chunk.SECTION_SIZE, null);
this.bitArray = version.createArray(SECTION_SIZE, null);
this.palette.add(deserializer.deserialize(byteBuf.readIntLE()));
return;
}
Expand Down Expand Up @@ -231,10 +231,10 @@ public void trim() {
var newPalette = new ReferenceArrayList<V>();
// Make sure the first entry won't be changed
newPalette.add(palette.getFirst());
var indexMapping = new int[Chunk.SECTION_SIZE];
var indexMapping = new int[SECTION_SIZE];
var paletteIndex = 1;

for (int index = 0; index < Chunk.SECTION_SIZE; index++) {
for (int index = 0; index < SECTION_SIZE; index++) {
var entry = get(index);
var newIndex = newPalette.indexOf(entry);
if (newIndex == -1) {
Expand All @@ -244,8 +244,8 @@ public void trim() {
indexMapping[index] = newIndex;
}

var newbitArray = BitArrayVersion.getMinimalVersion(paletteIndex).createArray(Chunk.SECTION_SIZE);
for (int index = 0; index < Chunk.SECTION_SIZE; index++) {
var newbitArray = BitArrayVersion.getMinimalVersion(paletteIndex).createArray(SECTION_SIZE);
for (int index = 0; index < SECTION_SIZE; index++) {
newbitArray.set(index, indexMapping[index]);
}

Expand All @@ -254,16 +254,16 @@ public void trim() {
}

private void readWords(ByteBuf byteBuf, BitArrayVersion version) {
var wordCount = version.getWordsForSize(Chunk.SECTION_SIZE);
var wordCount = version.getWordsForSize(SECTION_SIZE);
var words = new int[wordCount];
Arrays.setAll(words, i -> byteBuf.readIntLE());

this.bitArray = version.createArray(Chunk.SECTION_SIZE, words);
this.bitArray = version.createArray(SECTION_SIZE, words);
}

private void onResize(BitArrayVersion version) {
var newBitArray = version.createArray(Chunk.SECTION_SIZE);
for (int i = 0; i < Chunk.SECTION_SIZE; i++) {
var newBitArray = version.createArray(SECTION_SIZE);
for (int i = 0; i < SECTION_SIZE; i++) {
newBitArray.set(i, this.bitArray.get(i));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,20 @@ public void startTick() {
}

public void tick(long currentTick) {
// There may be new chunk packets during sleeping, let's send them first
this.chunkService.sendChunkPackets();

// Ticking
this.entityService.tick();
this.chunkService.tick(currentTick);
this.entityPhysicsService.tick();
this.blockUpdateService.tick();
if (!Server.SETTINGS.worldSettings().calculateLightAsync()) {
this.lightService.tick();
}

// Send the new chunk packets again after most of the work is done
this.chunkService.sendChunkPackets();
}

public void shutdown() {
Expand Down Expand Up @@ -122,14 +129,11 @@ public void setBlockState(int x, int y, int z, BlockState blockState, int layer,
blockState.getBehavior().onPlace(oldBlockStateWithPos, blockState, placementInfo);
oldBlockState.getBehavior().onReplace(oldBlockStateWithPos, blockState, placementInfo);
}
chunk.setBlockState(xIndex, y, zIndex, blockState, layer);
chunk.setBlockState(xIndex, y, zIndex, blockState, layer, send);

if (update) {
updateAround(x, y, z);
}
if (send) {
chunk.sendChunkPacket(Dimension.createUpdateBlockPacket(blockState, x, y, z, layer));
}

if (callBlockBehavior) {
chunk.getBlockState(xIndex, y, zIndex, layer == 0 ? 1 : 0).getBehavior().afterNeighborLayerReplace(oldBlockStateWithPos, blockState, placementInfo);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.Unpooled;
import io.netty.util.internal.PlatformDependent;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.allaymc.api.block.dto.BlockStateWithPos;
Expand Down Expand Up @@ -61,9 +60,6 @@ public class AllayChunk implements Chunk {
// The provided boolean value indicated whether the chunk is set successfully
@Setter
protected Consumer<Boolean> chunkSetCallback;
// Whether the chunk has been loaded into the world
@Getter
protected boolean loaded = false;
protected int autoSaveTimer = 0;
protected int updateLCG = ThreadLocalRandom.current().nextInt();

Expand Down Expand Up @@ -244,11 +240,11 @@ public BlockState getBlockState(int x, int y, int z, int layer) {
}

@Override
public void setBlockState(int x, int y, int z, BlockState blockState, int layer) {
public void setBlockState(int x, int y, int z, BlockState blockState, int layer, boolean send) {
checkXYZ(x, y, z);
var stamp = blockLock.writeLock();
try {
unsafeChunk.setBlockState(x, y, z, blockState, layer);
unsafeChunk.setBlockState(x, y, z, blockState, layer, send);
} finally {
blockLock.unlockWrite(stamp);
}
Expand Down Expand Up @@ -401,6 +397,11 @@ private void writeToNetwork0(ByteBuf byteBuf) {
}
}

@Override
public boolean isLoaded() {
return unsafeChunk.isLoaded();
}

@Override
public ChunkState getState() {
return unsafeChunk.getState();
Expand Down Expand Up @@ -505,7 +506,6 @@ public void afterSetChunk(Dimension dimension, boolean success) {
((AllayLightService) dimension.getLightService()).onBlockChange(x + (unsafeChunk.x << 4), y, z + (unsafeChunk.z << 4), blockState.getBlockStateData().lightEmission(), blockState.getBlockStateData().lightDampening());
});
((AllayLightService) dimension.getLightService()).onChunkLoad(this);
loaded = true;
}

@Override
Expand All @@ -526,7 +526,29 @@ public boolean hasScheduledUpdate(@Range(from = 0, to = 15) int x, int y, @Range

@Override
public void sendChunkPackets() {
if (chunkPacketQueue.isEmpty()) return;
if (chunkLoaders.isEmpty()) {
unsafeChunk.clearBlockChanges();
chunkPacketQueue.clear();
return;
}

// Send block updates
var pks = unsafeChunk.encodeAndClearBlockChanges();
// pks == null -> no block changes
if (pks != null) {
for (var pk : pks) {
if (pk == null) {
continue;
}

sendChunkPacket(pk);
}
}

// Send other chunk packets
if (chunkPacketQueue.isEmpty()) {
return;
}
ChunkPacketEntry entry;
while ((entry = chunkPacketQueue.poll()) != null) {
sendChunkPacket(entry.packet(), entry.chunkLoaderPredicate());
Expand Down
Loading

0 comments on commit 14ede80

Please sign in to comment.