@@ -28,12 +28,6 @@ import io.github.retrooper.packetevents.util.SpigotConversionUtil
2828import io.github.rothes.esu.bukkit.module.NetworkThrottleModule
2929import io.github.rothes.esu.bukkit.module.NetworkThrottleModule.config
3030import io.github.rothes.esu.bukkit.module.networkthrottle.chunkdatathrottle.ChunkDataThrottleHandler
31- import io.github.rothes.esu.bukkit.module.networkthrottle.chunkdatathrottle.ChunkDataThrottleHandler.Companion.INVISIBLE
32- import io.github.rothes.esu.bukkit.module.networkthrottle.chunkdatathrottle.ChunkDataThrottleHandler.Companion.X_MINUS
33- import io.github.rothes.esu.bukkit.module.networkthrottle.chunkdatathrottle.ChunkDataThrottleHandler.Companion.X_PLUS
34- import io.github.rothes.esu.bukkit.module.networkthrottle.chunkdatathrottle.ChunkDataThrottleHandler.Companion.Y_MINUS
35- import io.github.rothes.esu.bukkit.module.networkthrottle.chunkdatathrottle.ChunkDataThrottleHandler.Companion.Z_MINUS
36- import io.github.rothes.esu.bukkit.module.networkthrottle.chunkdatathrottle.ChunkDataThrottleHandler.Companion.Z_PLUS
3731import io.github.rothes.esu.bukkit.module.networkthrottle.chunkdatathrottle.ChunkSender
3832import io.github.rothes.esu.bukkit.module.networkthrottle.chunkdatathrottle.LevelHandler
3933import io.github.rothes.esu.bukkit.module.networkthrottle.chunkdatathrottle.PalettedContainerReader
@@ -61,12 +55,15 @@ import org.bukkit.World.Environment
6155import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer
6256import org.bukkit.entity.Player
6357import org.bukkit.event.EventHandler
58+ import org.bukkit.event.EventPriority
6459import org.bukkit.event.HandlerList
6560import org.bukkit.event.Listener
6661import org.bukkit.event.player.PlayerJoinEvent
62+ import org.bukkit.event.player.PlayerMoveEvent
6763import org.bukkit.event.player.PlayerQuitEvent
6864import java.nio.file.StandardOpenOption
6965import java.util.*
66+ import kotlin.experimental.and
7067import kotlin.experimental.or
7168import kotlin.io.path.fileSize
7269import kotlin.io.path.outputStream
@@ -81,13 +78,25 @@ class ChunkDataThrottleHandlerImpl: ChunkDataThrottleHandler,
8178 PacketListenerAbstract (PacketListenerPriority .HIGHEST ), Listener {
8279
8380 companion object {
81+ // The block B is in center. if Y_MINUS, block in B's bottom is occluding(i.e. blocking) .
82+ const val X_PLUS : Byte = 0b00_00001
83+ const val X_MINUS : Byte = 0b00_00010
84+ const val Z_PLUS : Byte = 0b00_00100
85+ const val Z_MINUS : Byte = 0b00_01000
86+ const val Y_MINUS : Byte = 0b00_10000
87+ const val INVISIBLE : Byte = 0b00_11111
88+
89+ const val X_LAVA : Byte = 0b01_00000
90+ const val Z_LAVA : Byte = 0b10_00000
8491
8592 const val SECTION_BLOCKS = 16 * 16 * 16
8693 const val BITS_ALL_TRUE = - 1L
8794
88- const val BV_VISIBLE : Byte = 0
89- const val BV_OCCLUDING : Byte = 1
90- const val BV_LAVA : Byte = 11
95+ const val BV_VISIBLE : Byte = 0b0
96+ const val BV_INVISIBLE : Byte = 0b1
97+ const val BV_LAVA_COVERED : Byte = 0b1011 // Why this value? flowing-lava id is 11, also the bit on BV_UPPER_OCCLUDING is 1
98+
99+ const val BV_UPPER_OCCLUDING : Byte = 0b10
91100
92101 private var LAVA_MIN = Int .MAX_VALUE
93102 private var LAVA_MAX = Int .MIN_VALUE
@@ -131,7 +140,7 @@ class ChunkDataThrottleHandlerImpl: ChunkDataThrottleHandler,
131140 }
132141 }.apply {
133142 for (i in LAVA_MIN .. LAVA_MAX ) {
134- this [i] = BV_LAVA
143+ this [i] = BV_LAVA_COVERED
135144 }
136145 }
137146 }
@@ -264,6 +273,20 @@ class ChunkDataThrottleHandlerImpl: ChunkDataThrottleHandler,
264273 }
265274 }
266275
276+ @EventHandler(priority = EventPriority .MONITOR , ignoreCancelled = true )
277+ fun onMove (e : PlayerMoveEvent ) {
278+ if (! config.chunkDataThrottle.detectLavaPool)
279+ return
280+ val player = e.player.nms
281+ val level = levelHandler.level(player)
282+ // Need to use chunk.getBlockState on Folia
283+ val chunk = level.getChunkIfLoaded(player.blockPosition()) ? : return
284+ val pos = listOf (player.blockPosition(), player.blockPosition().offset(0 , 1 , 0 ), player.blockPosition().offset(0 , - 1 , 0 ))
285+ if (pos.find { chunk.getBlockState(it).bukkitMaterial == Material .LAVA } != null ) {
286+ checkBlockUpdate(player.bukkitEntity, player.blockPosition())
287+ }
288+ }
289+
267290 override fun onPacketSend (event : PacketSendEvent ) {
268291 if (event.isCancelled) return
269292 try {
@@ -279,7 +302,7 @@ class ChunkDataThrottleHandlerImpl: ChunkDataThrottleHandler,
279302 val world = player.world
280303 val minHeight = world.minHeight
281304 for (block in wrapper.blocks) {
282- if (block.blockId.blocksView == BV_OCCLUDING ) {
305+ if (block.blockId.blocksView == BV_INVISIBLE ) {
283306 // Only check update if blocks get broken or transformed to non-blocksView
284307 continue
285308 }
@@ -289,7 +312,7 @@ class ChunkDataThrottleHandlerImpl: ChunkDataThrottleHandler,
289312
290313 PacketType .Play .Server .BLOCK_CHANGE -> {
291314 val wrapper = WrapperPlayServerBlockChange (event)
292- if (wrapper.blockId.blocksView == BV_OCCLUDING ) {
315+ if (wrapper.blockId.blocksView == BV_INVISIBLE ) {
293316 // Only check update if blocks get broken or transformed to non-blocksView
294317 return
295318 }
@@ -326,10 +349,10 @@ class ChunkDataThrottleHandlerImpl: ChunkDataThrottleHandler,
326349 val invisible = ByteArray (height shl 8 ) // 0: visible; 1: invisible; 2: visible, upper block blocks view
327350 if (! minimalHeightInvisibleCheck) for (i in 0 until 16 * 16 ) bvArr[i] = Y_MINUS
328351 // Handle neighbour chunks starts
329- handleNeighbourChunk(bvArr, level, column.x + 1 , column.z, 0x00 , 0x10 , + 0x0f , X_PLUS )
330- handleNeighbourChunk(bvArr, level, column.x - 1 , column.z, 0x0f , 0x10 , - 0x0f , X_MINUS )
331- handleNeighbourChunk(bvArr, level, column.x, column.z + 1 , 0x00 , 0x01 , + 0xf0 , Z_PLUS )
332- handleNeighbourChunk(bvArr, level, column.x, column.z - 1 , 0xf0 , 0x01 , - 0xf0 , Z_MINUS )
352+ handleNeighbourChunk(bvArr, level, column.x + 1 , column.z, 0x00 , 0x10 , + 0x0f , X_PLUS , X_LAVA )
353+ handleNeighbourChunk(bvArr, level, column.x - 1 , column.z, 0x0f , 0x10 , - 0x0f , X_MINUS , X_LAVA )
354+ handleNeighbourChunk(bvArr, level, column.x, column.z + 1 , 0x00 , 0x01 , + 0xf0 , Z_PLUS , Z_LAVA )
355+ handleNeighbourChunk(bvArr, level, column.x, column.z - 1 , 0xf0 , 0x01 , - 0xf0 , Z_MINUS , Z_LAVA )
333356 // Handle neighbour chunks ends
334357
335358 class SectionData (
@@ -378,18 +401,18 @@ class ChunkDataThrottleHandlerImpl: ChunkDataThrottleHandler,
378401
379402 for (i in 0 until SECTION_BLOCKS ) {
380403 when (blockingArr[data[i]]) {
381- BV_OCCLUDING -> {
404+ BV_INVISIBLE -> {
382405 addNearby(bvArr, id)
383406 // Make sure it's not out of bounds, while we are processing bedrock layer
384407 if (id >= 0x100 ) {
385408 // Check if previous block is complete invisible
386409 val previous = id - 0x100
387- invisible[previous] = if (bvArr[previous] == INVISIBLE ) 1 else 2
410+ invisible[previous] = if (bvArr[previous] and INVISIBLE == INVISIBLE ) BV_INVISIBLE else BV_UPPER_OCCLUDING
388411 }
389412 }
390- BV_LAVA -> {
413+ BV_LAVA_COVERED -> {
391414 if (id >= 0x100 ) {
392- invisible[id - 0x100 ] = BV_LAVA
415+ invisible[id - 0x100 ] = BV_LAVA_COVERED
393416 }
394417 }
395418 }
@@ -423,16 +446,47 @@ class ChunkDataThrottleHandlerImpl: ChunkDataThrottleHandler,
423446 if (z != 15 ) pending.add(id + 0x010 )
424447 }
425448 for (i in pending.iterator()) {
426- if (bvArr[i] == INVISIBLE ) {
427- invisible[i] = 1
449+ if (bvArr[i] and INVISIBLE == INVISIBLE ) {
450+ invisible[i] = BV_INVISIBLE
451+ }
452+ }
453+ }
454+ if (config.detectLavaPool) {
455+ val pending = IntArrayList ()
456+ id = - 1
457+ while (++ id < invisible.size) {
458+ if (invisible[id] != BV_LAVA_COVERED ) continue
459+
460+ val x = id and 0xf
461+ val z = id shr 4 and 0xf
462+ fun checkBlock (id : Int , b : Byte ) {
463+ if (invisible[id] and BV_UPPER_OCCLUDING == BV_UPPER_OCCLUDING ) {
464+ bvArr[id] = bvArr[id] or b
465+ pending.add(id)
466+ }
467+ }
468+ fun checkEdge (id : Int , check : Byte , set : Byte ) {
469+ if (bvArr[id] and check != 0 .toByte()) {
470+ bvArr[id] = bvArr[id] and set
471+ }
472+ }
473+ if (x != 0 ) checkBlock(id - 0x001 , X_PLUS ) else checkEdge(id - 0x001 , X_LAVA , X_MINUS )
474+ if (x != 15 ) checkBlock(id + 0x001 , X_MINUS ) else checkEdge(id - 0x001 , X_LAVA , X_PLUS )
475+ if (z != 0 ) checkBlock(id - 0x010 , Z_PLUS ) else checkEdge(id - 0x010 , Z_LAVA , Z_MINUS )
476+ if (z != 15 ) checkBlock(id + 0x010 , Z_MINUS ) else checkEdge(id - 0x010 , Z_LAVA , Z_PLUS )
477+ checkBlock(id + 0x100 , Y_MINUS )
478+ }
479+ for (i in pending.iterator()) {
480+ if (bvArr[i] and INVISIBLE == INVISIBLE ) {
481+ invisible[i] = BV_INVISIBLE
428482 }
429483 }
430484 }
431485 if (! config.netherRoofInvisibleCheck && world.environment == Environment .NETHER ) {
432486 // We could do the same thing to the top section,
433487 // but it never happens in vanilla generated chunks,
434488 // so, no.
435- checkSurfaceInvisible(bvArr, invisible, 0x1000 * (8 - 1 ), BV_OCCLUDING )
489+ checkSurfaceInvisible(bvArr, invisible, 0x1000 * (8 - 1 ), BV_INVISIBLE )
436490 }
437491
438492 val array = invisible.toLongArray()
@@ -559,7 +613,7 @@ class ChunkDataThrottleHandlerImpl: ChunkDataThrottleHandler,
559613// private val timesl = LongArrayList(10000000)
560614
561615 private fun handleNeighbourChunk (blocking : ByteArray , level : ServerLevel , chunkX : Int , chunkZ : Int ,
562- bid : Int , bidStep : Int , arrOffset : Int , arrValue : Byte ) {
616+ bid : Int , bidStep : Int , arrOffset : Int , arrValue : Byte , lavaValue : Byte ) {
563617 val chunk = level.getChunkIfLoaded(chunkX, chunkZ) ? : return
564618
565619 val indexLoop = 0x100 - bidStep * 16
@@ -568,10 +622,15 @@ class ChunkDataThrottleHandlerImpl: ChunkDataThrottleHandler,
568622 val states = section.container
569623 val palette = containerReader.getPalette(states)
570624 if (palette is SingleValuePalette <BlockState >) {
571- if (palette.valueFor(0 ).blocksView == BV_OCCLUDING ) {
625+ if (palette.valueFor(0 ).blocksView != BV_VISIBLE ) {
626+ val or = when (palette.valueFor(0 ).blocksView) {
627+ BV_INVISIBLE -> arrValue
628+ BV_LAVA_COVERED -> lavaValue
629+ else -> throw AssertionError ()
630+ }
572631 for (y in 0 until 16 ) {
573632 for (j in 0 until 16 ) {
574- (blockId + arrOffset).let { blocking[it] = blocking[it] or arrValue }
633+ (blockId + arrOffset).let { blocking[it] = blocking[it] or or }
575634 blockId + = bidStep
576635 }
577636 blockId + = indexLoop
@@ -597,8 +656,10 @@ class ChunkDataThrottleHandlerImpl: ChunkDataThrottleHandler,
597656
598657 for (y in 0 until 16 ) {
599658 for (j in 0 until 16 ) {
600- if (blockingArr[storage.get(blockId and 0xfff )] == BV_OCCLUDING )
601- (blockId + arrOffset).let { blocking[it] = blocking[it] or arrValue }
659+ when (blockingArr[storage.get(blockId and 0xfff )]) {
660+ BV_INVISIBLE -> (blockId + arrOffset).let { blocking[it] = blocking[it] or arrValue }
661+ BV_LAVA_COVERED -> (blockId + arrOffset).let { blocking[it] = blocking[it] or lavaValue }
662+ }
602663 blockId + = bidStep
603664 }
604665 blockId + = indexLoop
@@ -609,7 +670,7 @@ class ChunkDataThrottleHandlerImpl: ChunkDataThrottleHandler,
609670
610671 private fun checkSurfaceInvisible (bvArr : ByteArray , invisible : ByteArray , id : Int , setTo : Byte ) {
611672 for (i in id - 1 downTo id - 0x101 ) {
612- if (bvArr[i] == INVISIBLE ) {
673+ if (bvArr[i] and INVISIBLE == INVISIBLE ) {
613674 invisible[i] = setTo
614675 return
615676 }
@@ -642,6 +703,10 @@ class ChunkDataThrottleHandlerImpl: ChunkDataThrottleHandler,
642703 return arr
643704 }
644705
706+ private fun checkBlockUpdate (player : Player , blockPos : BlockPos , minHeight : Int = player.world.minHeight) {
707+ return checkBlockUpdate(player, blockPos.x, blockPos.y, blockPos.z, minHeight)
708+ }
709+
645710 private fun checkBlockUpdate (player : Player , blockLocation : Vector3i , minHeight : Int = player.world.minHeight) {
646711 return checkBlockUpdate(player, blockLocation.x, blockLocation.y, blockLocation.z, minHeight)
647712 }
0 commit comments