@@ -73,6 +73,8 @@ public void onEnable() {
7373 startProcessorTask ();
7474 }
7575
76+ public record ChunkSectionCoord (int cx , int cy , int cz ) {}
77+
7678 @ Override
7779 public void onDisable () {
7880 if (processorTask != null ) processorTask .cancel ();
@@ -196,6 +198,7 @@ public void run() {
196198 if (world .isChunkLoaded (vec .getBlockX (), vec .getBlockZ ())) {
197199 processAndSendChunk (player , world .getChunkAt (vec .getBlockX (), vec .getBlockZ ()));
198200 } else {
201+ world .loadChunk (vec .getBlockX (), vec .getBlockZ (), true );
199202 queue .add (vec );
200203 }
201204 }
@@ -350,57 +353,125 @@ private byte[] createSectionPayload(ChunkSnapshot snapshot, int cx, int cz, int
350353
351354
352355 @ EventHandler (priority = EventPriority .MONITOR , ignoreCancelled = true )
353- public void onBlockBreak (BlockBreakEvent event ) { if (event .getBlock ().getY () < 0 ) sendBlockUpdateToNearby (event .getBlock ().getLocation (), Material .AIR .createBlockData ()); }
356+ public void onBlockBreak (BlockBreakEvent event ) { if (event .getBlock ().getY () < 0 ) handleBlockChange (event .getBlock ().getLocation (), event . getBlock (). getBlockData (), Material .AIR .createBlockData ()); }
354357 @ EventHandler (priority = EventPriority .MONITOR , ignoreCancelled = true )
355- public void onBlockPlace (BlockPlaceEvent event ) { if (event .getBlock ().getY () < 0 ) sendBlockUpdateToNearby (event .getBlock ().getLocation (), event .getBlock ().getBlockData ()); }
358+ public void onBlockPlace (BlockPlaceEvent event ) { if (event .getBlock ().getY () < 0 ) handleBlockChange (event .getBlock ().getLocation (), event . getBlockReplacedState (). getBlockData (), event .getBlock ().getBlockData ()); }
356359 @ EventHandler (priority = EventPriority .MONITOR , ignoreCancelled = true )
357- public void onBlockPhysics (BlockPhysicsEvent event ) { if (event .getBlock ().getY () < 0 ) sendBlockUpdateToNearby (event .getBlock ().getLocation (), event .getBlock ().getBlockData ()); }
358-
359- private void sendBlockUpdateToNearby (Location loc , BlockData data ) {
360- try {
361- byte [] payload = createBlockUpdatePayload (loc , data );
362- if (payload == null ) return ;
363- for (Player p : loc .getWorld ().getPlayers ()) {
364- if (p .getLocation ().distanceSquared (loc ) < 4096 ) p .sendPluginMessage (this , CHANNEL , payload );
365- }
366- } catch (IOException e ) { getLogger ().severe ("Failed to send block update: " + e .getMessage ()); }
360+ public void onBlockPhysics (BlockPhysicsEvent event ) { Block block = event .getBlock (); if (block .getY () < 0 ) sendSingleBlockUpdate (block .getLocation (), block .getBlockData ());sendLightingUpdate (block .getLocation ()); }
361+
362+ private void sendSingleBlockUpdate (Location loc , BlockData data ) {
363+ try (ByteArrayOutputStream bout = new ByteArrayOutputStream (64 );
364+ DataOutputStream out = new DataOutputStream (bout )) {
365+
366+ out .writeUTF ("block_update" );
367+ out .writeInt (loc .getBlockX ());
368+ out .writeInt (loc .getBlockY ());
369+ out .writeInt (loc .getBlockZ ());
370+
371+ int [] legacyData = viablockids .toLegacy (data );
372+ out .writeShort ((short ) ((legacyData [1 ] << 12 ) | (legacyData [0 ] & 0xFFF )));
373+
374+ byte [] payload = bout .toByteArray ();
375+
376+ new BukkitRunnable () {
377+ @ Override
378+ public void run () {
379+ for (Player p : loc .getWorld ().getPlayers ()) {
380+ if (p .getLocation ().distanceSquared (loc ) < 4096 ) {
381+ p .sendPluginMessage (TuffX .this , CHANNEL , payload );
382+ }
383+ }
384+ }
385+ }.runTaskAsynchronously (this );
386+
387+ } catch (IOException e ) {
388+ getLogger ().severe ("Failed to create single block update payload: " + e .getMessage ());
389+ }
367390 }
368-
369- private byte [] createBlockUpdatePayload (Location loc , BlockData data ) throws IOException {
370- Map <Location , Byte > lightUpdates = new HashMap <>();
371- int radius = 16 ;
372- World world = loc .getWorld ();
373391
374- for (int x = loc .getBlockX () - radius ; x <= loc .getBlockX () + radius ; x ++) {
375- for (int y = loc .getBlockY () - radius ; y <= loc .getBlockY () + radius ; y ++) {
376- for (int z = loc .getBlockZ () - radius ; z <= loc .getBlockZ () + radius ; z ++) {
377- if (y >= 0 || y < -64 ) continue ;
392+ private void handleBlockChange (Location loc , BlockData oldData , BlockData newData ) {
393+ sendSingleBlockUpdate (loc , newData );
394+
395+ boolean oldEmits = oldData .getLightEmission () > 0 ;
396+ boolean newEmits = newData .getLightEmission () > 0 ;
397+ boolean oldOccludes = oldData .getMaterial ().isOccluding ();
398+ boolean newOccludes = newData .getMaterial ().isOccluding ();
378399
379- Block block = world .getBlockAt (x , y , z );
380- int blockLight = block .getLightFromBlocks ();
381- int skyLight = block .getLightFromSky ();
382- byte packedLight = (byte ) ((skyLight << 4 ) | blockLight );
400+ if (oldEmits != newEmits || oldOccludes != newOccludes ) {
401+ sendLightingUpdate (loc );
402+ }
403+ }
404+
405+ private void sendLightingUpdate (Location loc ) {
406+ Set <ChunkSectionCoord > sectionsToUpdate = new HashSet <>();
407+ World world = loc .getWorld ();
408+
409+ for (int dx = -1 ; dx <= 1 ; dx ++) {
410+ for (int dy = -1 ; dy <= 1 ; dy ++) {
411+ for (int dz = -1 ; dz <= 1 ; dz ++) {
412+ Location neighbor = loc .clone ().add (dx , dy , dz );
413+ if (neighbor .getY () < -64 || neighbor .getY () >= 0 ) continue ;
383414
384- lightUpdates .put (new Location (world , x , y , z ), packedLight );
415+ sectionsToUpdate .add (new ChunkSectionCoord (
416+ neighbor .getBlockX () >> 4 ,
417+ neighbor .getBlockY () >> 4 ,
418+ neighbor .getBlockZ () >> 4
419+ ));
385420 }
386421 }
387422 }
388423
389- try (ByteArrayOutputStream bout = new ByteArrayOutputStream (); DataOutputStream out = new DataOutputStream (bout )) {
390- out .writeUTF ("block_update" );
391- out .writeInt (loc .getBlockX ()); out .writeInt (loc .getBlockY ()); out .writeInt (loc .getBlockZ ());
392- int [] legacyData = viablockids .toLegacy (data );
393- out .writeShort ((short ) ((legacyData [1 ] << 12 ) | (legacyData [0 ] & 0xFFF )));
424+ for (ChunkSectionCoord sectionCoord : sectionsToUpdate ) {
425+ ChunkSnapshot snapshot = world .getChunkAt (sectionCoord .cx , sectionCoord .cz ).getChunkSnapshot (true , false , false );
394426
395- out .writeInt (lightUpdates .size ());
396- for (Map .Entry <Location , Byte > entry : lightUpdates .entrySet ()) {
397- Location pos = entry .getKey ();
398- out .writeInt (pos .getBlockX ());
399- out .writeInt (pos .getBlockY ());
400- out .writeInt (pos .getBlockZ ());
401- out .writeByte (entry .getValue ());
402- }
427+ new BukkitRunnable () {
428+ @ Override
429+ public void run () {
430+ try {
431+ byte [] payload = createLightingPayload (snapshot , sectionCoord );
432+ new BukkitRunnable () {
433+ @ Override
434+ public void run () {
435+ for (Player p : world .getPlayers ()) {
436+ if (p .getLocation ().distanceSquared (loc ) < 4096 ) {
437+ p .sendPluginMessage (TuffX .this , CHANNEL , payload );
438+ }
439+ }
440+ }
441+ }.runTask (TuffX .this );
442+ } catch (IOException e ) {
443+ getLogger ().severe ("Failed to create lighting payload: " + e .getMessage ());
444+ }
445+ }
446+ }.runTaskAsynchronously (this );
447+ }
448+ }
449+
450+ private byte [] createLightingPayload (ChunkSnapshot snapshot , ChunkSectionCoord section ) throws IOException {
451+ try (ByteArrayOutputStream bout = new ByteArrayOutputStream (4120 );
452+ DataOutputStream out = new DataOutputStream (bout )) {
453+
454+ out .writeUTF ("lighting_update" );
455+ out .writeInt (section .cx );
456+ out .writeInt (section .cz );
457+ out .writeInt (section .cy );
403458
459+ byte [] lightData = new byte [4096 ];
460+ int baseY = section .cy * 16 ;
461+ int i = 0 ;
462+
463+ for (int y = 0 ; y < 16 ; y ++) {
464+ for (int z = 0 ; z < 16 ; z ++) {
465+ for (int x = 0 ; x < 16 ; x ++) {
466+ int worldY = baseY + y ;
467+ int blockLight = snapshot .getBlockEmittedLight (x , worldY , z );
468+ int skyLight = snapshot .getBlockSkyLight (x , worldY , z );
469+ lightData [i ++] = (byte ) ((skyLight << 4 ) | blockLight );
470+ }
471+ }
472+ }
473+
474+ out .write (lightData );
404475 return bout .toByteArray ();
405476 }
406477 }
0 commit comments