Skip to content

Commit

Permalink
Batch block updates
Browse files Browse the repository at this point in the history
  • Loading branch information
wode490390 committed Nov 25, 2024
1 parent ae386b6 commit 7395588
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 16 deletions.
6 changes: 2 additions & 4 deletions src/main/java/cn/nukkit/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -6237,11 +6237,9 @@ public boolean canBreathe() {
}

/**
* 1.18.0 后使用 UpdateSubChunkBlocksPacket 代替 UpdateBlockPacket 和 UpdateBlockSyncedPacket.
* WIP
* @since 1.18.0
*/
public void updateBlock(Block entry) {
//TODO: queue
public void updateSubChunkBlocks(int subChunkBlockX, int subChunkBlockY, int subChunkBlockZ, BlockChangeEntry[] layer0, BlockChangeEntry[] layer1) {
}

public void sendAdventureSettingsAndAbilities(Player player, AdventureSettings settings) {
Expand Down
59 changes: 47 additions & 12 deletions src/main/java/cn/nukkit/level/Level.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import cn.nukkit.network.Network;
import cn.nukkit.network.protocol.*;
import cn.nukkit.network.protocol.BatchPacket.Track;
import cn.nukkit.network.protocol.types.BlockChangeEntry;
import cn.nukkit.plugin.Plugin;
import cn.nukkit.scheduler.AsyncTask;
import cn.nukkit.scheduler.BlockUpdateScheduler;
Expand Down Expand Up @@ -270,7 +271,7 @@ public class Level implements ChunkManager, Metadatable {
private final long[] lastChunkPos = new long[CHUNK_CACHE_SIZE];
private final BaseFullChunk[] lastChunk = new BaseFullChunk[CHUNK_CACHE_SIZE];

private final Long2ObjectMap<IntSet> changedBlocks = new Long2ObjectOpenHashMap<>();
private final Long2ObjectMap<Int2ObjectMap<IntSet>> changedBlocks = new Long2ObjectOpenHashMap<>();
private final Lock changeBlockLock = new ReentrantLock();

private final BlockUpdateScheduler updateQueue;
Expand Down Expand Up @@ -1303,19 +1304,49 @@ private void sendChangedBlocks() {
return;
}

Iterator<Long2ObjectMap.Entry<IntSet>> iter = changedBlocks.long2ObjectEntrySet().iterator();
Iterator<Long2ObjectMap.Entry<Int2ObjectMap<IntSet>>> iter = changedBlocks.long2ObjectEntrySet().iterator();
while (iter.hasNext()) {
Long2ObjectMap.Entry<IntSet> entry = iter.next();
Long2ObjectMap.Entry<Int2ObjectMap<IntSet>> entry = iter.next();
long chunkIndex = entry.getLongKey();
IntSet localPositions = entry.getValue();
int chunkX = Level.getHashX(chunkIndex);
int chunkZ = Level.getHashZ(chunkIndex);
Collection<Player> players = getChunkPlayers(chunkX, chunkZ).values();

Vector3[] blocks = new Vector3[localPositions.size()];
int i = 0;
IntIterator iterator = localPositions.iterator();
while (iterator.hasNext()) {
blocks[i++] = getBlockXYZ(chunkIndex, iterator.nextInt());
if (players.isEmpty()) {
iter.remove();
continue;
}

int chunkBlockX = chunkX << 4;
int chunkBlockZ = chunkZ << 4;
Int2ObjectMap<IntSet> subChunks = entry.getValue();
for (Int2ObjectMap.Entry<IntSet> ent : subChunks.int2ObjectEntrySet()) {
int chunkBlockY = ent.getIntKey() << 4;
IntSet localPositions = ent.getValue();
int count = localPositions.size();
BlockChangeEntry[] layer0 = new BlockChangeEntry[count];
BlockChangeEntry[] layer1 = new BlockChangeEntry[count];
int index = 0;
for (int localPos : localPositions) {
int x = chunkBlockX | (localPos >> 8);
int y = chunkBlockY | (localPos & 0xf);
int z = chunkBlockZ | ((localPos >> 4) & 0xf);
layer0[index] = BlockChangeEntry.create(x, y, z, getFullBlock(0, x, y, z), UpdateBlockPacket.FLAG_ALL_PRIORITY);
layer1[index] = BlockChangeEntry.create(x, y, z, getFullBlock(1, x, y, z), UpdateBlockPacket.FLAG_ALL_PRIORITY);
index++;
}

UpdateSubChunkBlocksPacket packet = new UpdateSubChunkBlocksPacket();
packet.subChunkBlockX = chunkBlockX;
packet.subChunkBlockY = chunkBlockY;
packet.subChunkBlockZ = chunkBlockZ;
packet.layer0 = layer0;
packet.layer1 = layer1;
for (Player player : players) {
// player.updateSubChunkBlocks(chunkBlockX, chunkBlockY, chunkBlockZ, layer0, layer1);
player.dataPacket(packet);
}
}
sendBlocks(getChunkPlayers(Level.getHashX(chunkIndex), Level.getHashZ(chunkIndex)).values().toArray(new Player[0]), blocks, UpdateBlockPacket.FLAG_ALL_PRIORITY);

iter.remove();
}
Expand Down Expand Up @@ -2601,10 +2632,14 @@ private void addBlockChange(int x, int y, int z) {
}

private void addBlockChange(long index, int x, int y, int z) {
int chunkY = y >> 4;
int localIndex = ((x & 0xf) << 8) | ((z & 0xf) << 4) | (y & 0xf);

changeBlockLock.lock();
try {
changedBlocks.computeIfAbsent(index, key -> new IntOpenHashSet())
.add(Level.localBlockHash(x, y, z));
changedBlocks.computeIfAbsent(index, key -> new Int2ObjectOpenHashMap<>())
.computeIfAbsent(chunkY, key -> new IntOpenHashSet())
.add(localIndex);
} finally {
changeBlockLock.unlock();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package cn.nukkit.network.protocol;

import cn.nukkit.network.protocol.types.BlockChangeEntry;
import lombok.ToString;

@ToString
public class UpdateSubChunkBlocksPacket extends DataPacket {
public static final int NETWORK_ID = ProtocolInfo.UPDATE_SUB_CHUNK_BLOCKS_PACKET;

public int subChunkBlockX;
public int subChunkBlockY;
public int subChunkBlockZ;
public BlockChangeEntry[] layer0 = new BlockChangeEntry[0];
public BlockChangeEntry[] layer1 = new BlockChangeEntry[0];

@Override
public int pid() {
return NETWORK_ID;
}

@Override
public void decode() {
}

@Override
public void encode() {
this.reset();
this.putBlockVector3(this.subChunkBlockX, this.subChunkBlockY, this.subChunkBlockZ);

this.putUnsignedVarInt(this.layer0.length);
for (BlockChangeEntry entry : this.layer0) {
writeEntry(entry);
}

this.putUnsignedVarInt(this.layer1.length);
for (BlockChangeEntry entry : this.layer1) {
writeEntry(entry);
}
}

private void writeEntry(BlockChangeEntry entry) {
this.putBlockVector3(entry.x(), entry.y(), entry.z());
this.putUnsignedVarInt(entry.block());
this.putUnsignedVarInt(entry.flags());
this.putUnsignedVarLong(entry.syncedUpdateEntityUniqueId());
this.putUnsignedVarInt(entry.syncedUpdateType());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package cn.nukkit.network.protocol.types;

import cn.nukkit.network.protocol.UpdateBlockPacket;

public record BlockChangeEntry(
int x,
int y,
int z,
int block,
int flags,
// These two fields are useless 99.99% of the time; they are here to allow this packet to provide UpdateBlockSyncedPacket functionality.
long syncedUpdateEntityUniqueId,
int syncedUpdateType
) {
public static BlockChangeEntry create(int x, int y, int z, int block) {
return create(x, y, z, block, UpdateBlockPacket.FLAG_ALL_PRIORITY);
}

public static BlockChangeEntry create(int x, int y, int z, int block, int flags) {
return create(x, y, z, block, flags,-1, 0);
}

public static BlockChangeEntry create(int x, int y, int z, int block, int flags, long syncedUpdateEntityUniqueId, int syncedUpdateType) {
return new BlockChangeEntry(x, y, z, block, flags, syncedUpdateEntityUniqueId, syncedUpdateType);
}
}

0 comments on commit 7395588

Please sign in to comment.