Skip to content

Commit 8e35aca

Browse files
committed
Updates to how blocks are sent
1 parent cfc0803 commit 8e35aca

6 files changed

Lines changed: 67 additions & 66 deletions

File tree

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ plugins {
44
}
55

66
group = 'codes.kooper'
7-
version = '1.0.0-alpha'
7+
version = '1.0.0-alpha-2'
88

99
repositories {
1010
mavenCentral()

src/main/java/codes/kooper/blockify/managers/BlockChangeManager.java

Lines changed: 58 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
1717
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerChunkData;
1818
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
19+
import io.papermc.paper.math.Position;
1920
import lombok.Getter;
2021
import org.bukkit.Bukkit;
2122
import org.bukkit.Chunk;
@@ -61,14 +62,7 @@ public void sendViews(Stage stage, Player player) {
6162
*/
6263
public void sendView(Player player, View view) {
6364
Audience audience = Audience.fromPlayers(new HashSet<>(Collections.singletonList(player)));
64-
ConcurrentHashMap<BlockifyChunk, ConcurrentHashMap<BlockifyPosition, BlockData>> blocks = new ConcurrentHashMap<>();
65-
for (Map.Entry<BlockifyChunk, ConcurrentHashMap<BlockifyPosition, BlockData>> entry : view.getBlocks().entrySet()) {
66-
if (!blocks.containsKey(entry.getKey())) {
67-
blocks.put(entry.getKey(), new ConcurrentHashMap<>());
68-
}
69-
blocks.get(entry.getKey()).putAll(entry.getValue());
70-
}
71-
sendBlockChanges(view.getStage(), audience, blocks);
65+
sendBlockChanges(view.getStage(), audience, view.getBlocks().keySet());
7266
}
7367

7468
/**
@@ -80,16 +74,7 @@ public void sendView(Player player, View view) {
8074
*/
8175
public void hideView(Player player, View view) {
8276
Audience audience = Audience.fromPlayers(new HashSet<>(Collections.singletonList(player)));
83-
ConcurrentHashMap<BlockifyChunk, ConcurrentHashMap<BlockifyPosition, BlockData>> blocks = new ConcurrentHashMap<>();
84-
for (Map.Entry<BlockifyChunk, ConcurrentHashMap<BlockifyPosition, BlockData>> entry : view.getBlocks().entrySet()) {
85-
if (!blocks.containsKey(entry.getKey())) {
86-
blocks.put(entry.getKey(), new ConcurrentHashMap<>());
87-
}
88-
for (Map.Entry<BlockifyPosition, BlockData> blockEntry : entry.getValue().entrySet()) {
89-
blocks.get(entry.getKey()).put(blockEntry.getKey(), view.getStage().getWorld().getBlockData(blockEntry.getKey().getX(), blockEntry.getKey().getY(), blockEntry.getKey().getZ()));
90-
}
91-
}
92-
sendBlockChanges(view.getStage(), audience, blocks);
77+
sendBlockChanges(view.getStage(), audience, view.getBlocks().keySet(), true);
9378
}
9479

9580
/**
@@ -111,14 +96,10 @@ public void hideViews(Stage stage, Player player) {
11196
* @param stage the stage
11297
* @param audience the audience
11398
* @param position the position
114-
* @param blockData the block data
11599
*/
116-
public void sendBlockChange(Stage stage, Audience audience, BlockifyPosition position, BlockData blockData) {
117-
ConcurrentHashMap<BlockifyChunk, ConcurrentHashMap<BlockifyPosition, BlockData>> blockChanges = new ConcurrentHashMap<>();
100+
public void sendBlockChange(Stage stage, Audience audience, BlockifyPosition position) {
118101
BlockifyChunk chunk = new BlockifyChunk(position.getX() >> 4, position.getZ() >> 4);
119-
blockChanges.put(chunk, new ConcurrentHashMap<>());
120-
blockChanges.get(chunk).put(position, blockData);
121-
sendBlockChanges(stage, audience, blockChanges);
102+
sendBlockChanges(stage, audience, Collections.singleton(chunk));
122103
}
123104

124105
/**
@@ -127,12 +108,23 @@ public void sendBlockChange(Stage stage, Audience audience, BlockifyPosition pos
127108
*
128109
* @param stage the stage
129110
* @param audience the audience
130-
* @param blockChanges the block changes
111+
* @param chunks the chunks to send
131112
*/
132-
public void sendBlockChanges(Stage stage, Audience audience, ConcurrentHashMap<BlockifyChunk, ConcurrentHashMap<BlockifyPosition, BlockData>> blockChanges) {
133-
if (blockChanges.isEmpty()) {
134-
return;
135-
}
113+
public void sendBlockChanges(Stage stage, Audience audience, Collection<BlockifyChunk> chunks) {
114+
sendBlockChanges(stage, audience, chunks, false);
115+
}
116+
117+
/**
118+
* Sends block changes to the audience.
119+
* Call Asynchronously
120+
*
121+
* @param stage the stage
122+
* @param audience the audience
123+
* @param chunks the chunks to send
124+
* @param unload whether to unload the chunks
125+
*/
126+
public void sendBlockChanges(Stage stage, Audience audience, Collection<BlockifyChunk> chunks, boolean unload) {
127+
ConcurrentHashMap<BlockifyChunk, ConcurrentHashMap<BlockifyPosition, BlockData>> blockChanges = getBlockChanges(stage, chunks);
136128
Bukkit.getScheduler().runTask(Blockify.getInstance(), () -> new OnBlockChangeSendEvent(stage, blockChanges).callEvent());
137129

138130
// If there is only one block change, send it to the player directly
@@ -152,6 +144,21 @@ public void sendBlockChanges(Stage stage, Audience audience, ConcurrentHashMap<B
152144
return;
153145
}
154146

147+
// Less than 3,000 blocks then use the player.sendBlockChanges method
148+
if (blockCount < 3000) {
149+
Map<Position, BlockData> multiBlockChange = new HashMap<>();
150+
for (BlockifyChunk chunk : chunks) {
151+
if (!stage.getWorld().isChunkLoaded(chunk.x(), chunk.z())) continue;
152+
for (Map.Entry<BlockifyPosition, BlockData> entry : blockChanges.get(chunk).entrySet()) {
153+
multiBlockChange.put(entry.getKey().toPosition(), entry.getValue());
154+
}
155+
}
156+
for (Player player : audience.getOnlinePlayers()) {
157+
player.sendMultiBlockChange(multiBlockChange);
158+
}
159+
return;
160+
}
161+
155162
// Send multiple block changes to the players
156163
for (Player onlinePlayer : audience.getOnlinePlayers()) {
157164
Location playerLocation = onlinePlayer.getLocation();
@@ -160,7 +167,7 @@ public void sendBlockChanges(Stage stage, Audience audience, ConcurrentHashMap<B
160167
// The chunk index is used to keep track of the current chunk being sent
161168
AtomicInteger chunkIndex = new AtomicInteger(0);
162169
// Create an array of chunks to send from the block changes map
163-
List<BlockifyChunk> chunksToSend = new ArrayList<>(List.of(blockChanges.keySet().toArray(new BlockifyChunk[0])));
170+
List<BlockifyChunk> chunksToSend = new ArrayList<>(chunks.stream().toList());
164171
chunksToSend.sort((chunk1, chunk2) -> {
165172
// Get distance from chunks to player
166173
int x = playerLocation.getBlockX() / 16;
@@ -206,10 +213,10 @@ public void sendBlockChanges(Stage stage, Audience audience, ConcurrentHashMap<B
206213
chunkIndex.getAndIncrement();
207214

208215
// Check if the chunk is loaded; if not, return
209-
if (!stage.getWorld().isChunkLoaded(chunk.x(), chunk.z())) return;
216+
if (!stage.getWorld().isChunkLoaded(chunk.x(), chunk.z())) continue;
210217

211218
// Send the chunk packet to the player
212-
Bukkit.getScheduler().runTaskAsynchronously(Blockify.getInstance(), () -> sendChunkPacket(onlinePlayer, chunk, blockChanges));
219+
Bukkit.getScheduler().runTaskAsynchronously(Blockify.getInstance(), () -> sendChunkPacket(stage, onlinePlayer, chunk, unload));
213220
}
214221
}, 0L, 1L));
215222
}
@@ -219,14 +226,18 @@ public void sendBlockChanges(Stage stage, Audience audience, ConcurrentHashMap<B
219226
* Sends a chunk packet to the player.
220227
* Call Asynchronously
221228
*
229+
* @param stage the stage
222230
* @param player the player
223231
* @param chunk the chunk
224-
* @param blockChanges the block changes
225232
*/
226-
public void sendChunkPacket(Player player, BlockifyChunk chunk, ConcurrentHashMap<BlockifyChunk, ConcurrentHashMap<BlockifyPosition, BlockData>> blockChanges) {
233+
public void sendChunkPacket(Stage stage, Player player, BlockifyChunk chunk, boolean unload) {
227234
User packetUser = PacketEvents.getAPI().getPlayerManager().getUser(player);
228-
ConcurrentHashMap<BlockifyPosition, BlockData> blockData = blockChanges.get(chunk);
229235
int ySections = packetUser.getTotalWorldHeight() >> 4;
236+
ConcurrentHashMap<BlockifyPosition, BlockData> blockData = null;
237+
if (!unload) {
238+
ConcurrentHashMap<BlockifyChunk, ConcurrentHashMap<BlockifyPosition, BlockData>> blockChanges = getBlockChanges(stage, Collections.singleton(chunk));
239+
blockData = blockChanges.get(chunk);
240+
}
230241
Map<BlockData, WrappedBlockState> blockDataToState = new HashMap<>();
231242
List<BaseChunk> chunks = new ArrayList<>();
232243
Chunk bukkitChunk = player.getWorld().getChunkAt(chunk.x(), chunk.z());
@@ -244,7 +255,7 @@ public void sendChunkPacket(Player player, BlockifyChunk chunk, ConcurrentHashMa
244255
int worldY = (i << 4) + y + minHeight;
245256
BlockifyPosition position = new BlockifyPosition(x + (chunk.x() << 4), worldY, z + (chunk.z() << 4));
246257

247-
if (blockData.containsKey(position)) {
258+
if (!unload && blockData != null && blockData.containsKey(position)) {
248259
BlockData data = blockData.get(position);
249260
WrappedBlockState state = blockDataToState.computeIfAbsent(data, SpigotConversionUtil::fromBukkitBlockData);
250261
baseChunk.set(x, y, z, state);
@@ -292,4 +303,15 @@ public void sendChunkPacket(Player player, BlockifyChunk chunk, ConcurrentHashMa
292303
WrapperPlayServerChunkData chunkData = new WrapperPlayServerChunkData(column, lightData);
293304
packetUser.sendPacketSilently(chunkData);
294305
}
306+
307+
private ConcurrentHashMap<BlockifyChunk, ConcurrentHashMap<BlockifyPosition, BlockData>> getBlockChanges(Stage stage, Collection<BlockifyChunk> chunks) {
308+
ConcurrentHashMap<BlockifyChunk, ConcurrentHashMap<BlockifyPosition, BlockData>> blockChanges = new ConcurrentHashMap<>();
309+
for (View view : stage.getViews()) {
310+
for (Map.Entry<BlockifyChunk, ConcurrentHashMap<BlockifyPosition, BlockData>> entry : view.getBlocks().entrySet()) {
311+
if (!chunks.contains(entry.getKey())) continue;
312+
blockChanges.put(entry.getKey(), entry.getValue());
313+
}
314+
}
315+
return blockChanges;
316+
}
295317
}

src/main/java/codes/kooper/blockify/models/Stage.java

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@
77
import lombok.Setter;
88
import org.bukkit.Location;
99
import org.bukkit.World;
10-
import org.bukkit.block.data.BlockData;
1110

1211
import java.util.HashSet;
1312
import java.util.Set;
14-
import java.util.concurrent.ConcurrentHashMap;
13+
import java.util.stream.Collectors;
1514

1615
@Getter
1716
@Setter
@@ -57,11 +56,7 @@ public boolean isLocationWithin(Location location) {
5756
* Send blocks to the audience. Should be called asynchronously.
5857
*/
5958
public void sendBlocksToAudience() {
60-
ConcurrentHashMap<BlockifyChunk, ConcurrentHashMap<BlockifyPosition, BlockData>> blocks = new ConcurrentHashMap<>();
61-
for (View view : views) {
62-
blocks.putAll(view.getBlocks());
63-
}
64-
Blockify.getInstance().getBlockChangeManager().sendBlockChanges(this, audience, blocks);
59+
Blockify.getInstance().getBlockChangeManager().sendBlockChanges(this, audience, getChunks());
6560
}
6661

6762
/**
@@ -71,20 +66,8 @@ public void sendBlocksToAudience() {
7166
* @param blocks Blocks to refresh to the audience.
7267
*/
7368
public void refreshBlocksToAudience(Set<BlockifyPosition> blocks) {
74-
ConcurrentHashMap<BlockifyChunk, ConcurrentHashMap<BlockifyPosition, BlockData>> blockChanges = new ConcurrentHashMap<>();
75-
for (View view : views) {
76-
for (BlockifyPosition position : blocks) {
77-
if (!view.hasBlock(position)) continue;
78-
if (blockChanges.containsKey(position.toBlockifyChunk())) {
79-
blockChanges.get(position.toBlockifyChunk()).put(position, view.getBlock(position));
80-
} else {
81-
ConcurrentHashMap<BlockifyPosition, BlockData> blockData = new ConcurrentHashMap<>();
82-
blockData.put(position, view.getBlock(position));
83-
blockChanges.put(position.toBlockifyChunk(), blockData);
84-
}
85-
}
86-
}
87-
Blockify.getInstance().getBlockChangeManager().sendBlockChanges(this, audience, blockChanges);
69+
Set<BlockifyChunk> chunks = blocks.stream().map(BlockifyPosition::toBlockifyChunk).collect(Collectors.toSet());
70+
Blockify.getInstance().getBlockChangeManager().sendBlockChanges(this, audience, chunks);
8871
}
8972

9073
/**

src/main/java/codes/kooper/blockify/protocol/BlockDigAdapter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ public void onPacketPlayReceive(PacketPlayReceiveEvent event) {
6363
blockifyBreakEvent.callEvent();
6464
// If block is not cancelled, break the block, otherwise, revert the block
6565
if (!blockifyBreakEvent.isCancelled()) {
66-
Blockify.getInstance().getBlockChangeManager().sendBlockChange(view.getStage(), view.getStage().getAudience(), position, Material.AIR.createBlockData());
6766
view.setBlock(position, Material.AIR.createBlockData());
67+
Blockify.getInstance().getBlockChangeManager().sendBlockChange(view.getStage(), view.getStage().getAudience(), position);
6868
} else {
6969
player.sendBlockChange(position.toLocation(player.getWorld()), blockData);
7070
}

src/main/java/codes/kooper/blockify/protocol/ChunkLoadAdapter.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import codes.kooper.blockify.Blockify;
44
import codes.kooper.blockify.models.Stage;
5-
import codes.kooper.blockify.models.View;
65
import codes.kooper.blockify.types.BlockifyChunk;
76
import com.github.retrooper.packetevents.event.SimplePacketListenerAbstract;
87
import com.github.retrooper.packetevents.event.simple.PacketPlaySendEvent;
@@ -37,17 +36,14 @@ public void onPacketPlaySend(PacketPlaySendEvent event) {
3736
// If the chunk is not in the world, return.
3837
if (!stage.getWorld().equals(player.getWorld())) return;
3938

40-
for (View view : stage.getViews()) {
41-
42-
// Check if the view has any blocks in the bound in the first place
43-
if (!view.hasChunk(chunkX, chunkZ)) return;
39+
if (stage.getChunks().contains(new BlockifyChunk(chunkX, chunkZ))) {
4440
BlockifyChunk blockifyChunk = new BlockifyChunk(chunkX, chunkZ);
4541

4642
// Cancel the packet to prevent the player from seeing the chunk
4743
event.setCancelled(true);
4844

4945
// Send the chunk packet to the player
50-
Bukkit.getServer().getScheduler().runTaskAsynchronously(Blockify.getInstance(), () -> Blockify.getInstance().getBlockChangeManager().sendChunkPacket(player, blockifyChunk, view.getBlocks()));
46+
Bukkit.getServer().getScheduler().runTaskAsynchronously(Blockify.getInstance(), () -> Blockify.getInstance().getBlockChangeManager().sendChunkPacket(stage, player, blockifyChunk, false));
5147
}
5248
}
5349
}

src/main/resources/plugin.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: Blockify
2-
version: '1.0.0-alpha'
2+
version: '1.0.0-alpha-2'
33
main: codes.kooper.blockify.Blockify
44
api-version: '1.20'
55
depend:

0 commit comments

Comments
 (0)