1616import com .github .retrooper .packetevents .protocol .world .states .WrappedBlockState ;
1717import com .github .retrooper .packetevents .wrapper .play .server .WrapperPlayServerChunkData ;
1818import io .github .retrooper .packetevents .util .SpigotConversionUtil ;
19+ import io .papermc .paper .math .Position ;
1920import lombok .Getter ;
2021import org .bukkit .Bukkit ;
2122import 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}
0 commit comments