Skip to content

Commit 94e4a53

Browse files
committed
[EntityCulling] opt: Grid partition entities
TODO: Huge CPU cache requirement, even slower than not to slice
1 parent 7c43210 commit 94e4a53

File tree

1 file changed

+60
-23
lines changed
  • bukkit/version/v1_18/src/main/kotlin/io/github/rothes/esu/bukkit/module/networkthrottle/entityculling/v1_18

1 file changed

+60
-23
lines changed

bukkit/version/v1_18/src/main/kotlin/io/github/rothes/esu/bukkit/module/networkthrottle/entityculling/v1_18/RaytraceHandlerImpl.kt

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ import io.github.rothes.esu.core.util.UnsafeUtils.usBooleanAccessor
2525
import io.github.rothes.esu.core.util.extension.math.floorI
2626
import io.github.rothes.esu.core.util.extension.math.frac
2727
import io.github.rothes.esu.core.util.extension.math.square
28+
import it.unimi.dsi.fastutil.Hash
2829
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap
2930
import it.unimi.dsi.fastutil.ints.IntArrayList
31+
import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap
3032
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap
3133
import kotlinx.coroutines.*
3234
import net.minecraft.server.level.ServerLevel
@@ -59,6 +61,7 @@ import kotlin.time.Duration.Companion.seconds
5961
object 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

Comments
 (0)