@@ -25,8 +25,10 @@ import io.github.rothes.esu.core.util.UnsafeUtils.usBooleanAccessor
2525import io.github.rothes.esu.core.util.extension.math.floorI
2626import io.github.rothes.esu.core.util.extension.math.frac
2727import io.github.rothes.esu.core.util.extension.math.square
28+ import it.unimi.dsi.fastutil.Hash
2829import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap
2930import it.unimi.dsi.fastutil.ints.IntArrayList
31+ import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap
3032import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap
3133import kotlinx.coroutines.*
3234import net.minecraft.server.level.ServerLevel
@@ -59,6 +61,7 @@ import kotlin.time.Duration.Companion.seconds
5961object RaytraceHandlerImpl: RaytraceHandler<RaytraceHandlerImpl.RaytraceConfig, EmptyConfiguration>() {
6062
6163 private const val COLLISION_EPSILON = 1E- 7
64+ private const val GRID_SIZE = 5
6265 private val INIT_SECTION : Array <LevelChunkSection > = arrayOf()
6366 private val ENTITY_TYPES : Int
6467
@@ -194,7 +197,7 @@ object RaytraceHandlerImpl: RaytraceHandler<RaytraceHandlerImpl.RaytraceConfig,
194197 }
195198 val entities = entityMap.int2ReferenceEntrySet().map {
196199 val squaredRange = (it.intKey + 8 ).square() // Add extra 8 blocks
197- SortedEntities (squaredRange, it.value)
200+ SlicedEntities (squaredRange, it.value)
198201 }
199202 /* Sort entities by tracking range */
200203
@@ -268,16 +271,19 @@ object RaytraceHandlerImpl: RaytraceHandler<RaytraceHandlerImpl.RaytraceConfig,
268271
269272 }
270273
271- fun tickPlayer (player : ServerPlayer , bukkit : Player , userCullData : UserCullData , level : ServerLevel , entities : List <SortedEntities >) {
274+ fun tickPlayer (player : ServerPlayer , bukkit : Player , userCullData : UserCullData , level : ServerLevel , collected : List <SlicedEntities >) {
272275 val viewDistanceSquared = (bukkit.viewDistance + 1 ).square() shl 8
273276
277+ val playerX = player.x
278+ val playerZ = player.z
279+
274280 val shouldCull = userCullData.shouldCull
275281 val predicatedPlayerPos = if (shouldCull && config.predicatePlayerPositon) {
276282 val velocity = playerVelocityGetter.getPlayerMoveVelocity(player)
277283 if (velocity.lengthSqr() >= 0.06 ) { // Threshold for sprinting
278- var x = player.x
284+ var x = playerX
279285 var y = player.eyeY
280- var z = player.z
286+ var z = playerZ
281287
282288 var vx = velocity.x
283289 var vy = velocity.y
@@ -298,26 +304,37 @@ object RaytraceHandlerImpl: RaytraceHandler<RaytraceHandlerImpl.RaytraceConfig,
298304
299305 var tickedEntities = 0
300306
301- for ((trackRange, entities) in entities) {
302- val maxRange = min(trackRange, viewDistanceSquared)
303- for (entity in entities) {
304- if (entity == = player) continue
305- val dist = (player.x - entity.x).square() + (player.z - entity.z).square()
306- if (dist > maxRange) continue
307-
308- tickedEntities++
307+ for (sorted in collected) {
308+ val range = min(sorted.trackRangeSquared, viewDistanceSquared)
309+ val sqrt = sqrt(range.toDouble())
310+ val minX = (playerX - sqrt).floorI() shr GRID_SIZE
311+ val maxX = (playerX + sqrt).floorI() shr GRID_SIZE
312+ val minZ = (playerZ - sqrt).floorI() shr GRID_SIZE
313+ val maxZ = (playerZ + sqrt).floorI() shr GRID_SIZE
314+ for (x in minX.. maxX) {
315+ for (z in minZ.. maxZ) {
316+ val index = x.toLong() and (z.toLong() shl Int .SIZE_BITS )
317+ val entities = sorted.slices.get(index) ? : continue
318+ for (entity in entities) {
319+ if (entity == = player) continue
320+ val dist = (playerX - entity.x).square() + (playerZ - entity.z).square()
321+ if (dist > range) continue
322+
323+ tickedEntities++
324+
325+ if (
326+ ! shouldCull
327+ || entity.isCurrentlyGlowing
328+ || config.visibleEntityTypes.contains(entity.type)
329+ || dist + (player.y - entity.y).square() <= forceVisibleDistanceSquared
330+ ) {
331+ userCullData.setCulled(entity.bukkitEntity, entity.id, false )
332+ continue
333+ }
309334
310- if (
311- ! shouldCull
312- || entity.isCurrentlyGlowing
313- || config.visibleEntityTypes.contains(entity.type)
314- || dist + (player.y - entity.y).square() <= forceVisibleDistanceSquared
315- ) {
316- userCullData.setCulled(entity.bukkitEntity, entity.id, false )
317- continue
335+ userCullData.setCulled(entity.bukkitEntity, entity.id, raytrace(player, predicatedPlayerPos, entity, level))
336+ }
318337 }
319-
320- userCullData.setCulled(entity.bukkitEntity, entity.id, raytrace(player, predicatedPlayerPos, entity, level))
321338 }
322339 }
323340 userCullData.shouldCull = tickedEntities >= config.cullThreshold
@@ -605,7 +622,27 @@ object RaytraceHandlerImpl: RaytraceHandler<RaytraceHandlerImpl.RaytraceConfig,
605622 }
606623 }
607624
608- data class SortedEntities (val trackRangeSquared : Int , val entities : List <Entity >)
625+ class SlicedEntities (val trackRangeSquared : Int , entities : List <Entity >) {
626+
627+ val slices = Long2ReferenceOpenHashMap <MutableList <Entity >>(16 , Hash .VERY_FAST_LOAD_FACTOR )
628+
629+ init {
630+ for (entity in entities) {
631+ val id = index(entity.x.floorI(), entity.z.floorI())
632+ val get = slices.get(id)
633+ if (get != null )
634+ get.add(entity)
635+ else
636+ slices.put(id, ArrayList <Entity >(16 ).also { it.add(entity) })
637+ }
638+ }
639+
640+ private fun index (x : Int , z : Int ): Long {
641+ val x = x shr GRID_SIZE
642+ val z = z shr GRID_SIZE
643+ return x.toLong() and (z.toLong() shl Int .SIZE_BITS )
644+ }
645+ }
609646
610647 data class RaytraceConfig (
611648 @Comment(" Asynchronous threads used to calculate visibility. More to update faster." )
0 commit comments