Skip to content

Commit 407250b

Browse files
committed
[EntityCulling] Reflect to showEntity not same thread, save memory
1 parent 9ec6c46 commit 407250b

File tree

6 files changed

+111
-18
lines changed

6 files changed

+111
-18
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package io.github.rothes.esu.bukkit.module.networkthrottle.entityculling
2+
3+
import org.bukkit.entity.Entity
4+
import org.bukkit.entity.Player
5+
6+
interface PlayerEntityVisibilityHandler {
7+
8+
fun forceShowEntity(player: Player, bukkitEntity: Entity)
9+
10+
}

bukkit/src/main/kotlin/io/github/rothes/esu/bukkit/module/networkthrottle/entityculling/UserCullData.kt

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import io.github.rothes.esu.bukkit.bootstrap
44
import io.github.rothes.esu.bukkit.module.networkthrottle.entityculling.CullDataManager.raytraceHandler
55
import io.github.rothes.esu.bukkit.plugin
66
import io.github.rothes.esu.bukkit.util.scheduler.Scheduler
7+
import io.github.rothes.esu.bukkit.util.version.Versioned
78
import io.github.rothes.esu.bukkit.util.version.adapter.PlayerAdapter.Companion.connected
89
import io.github.rothes.esu.bukkit.util.version.adapter.TickThreadAdapter.Companion.checkTickThread
910
import it.unimi.dsi.fastutil.Hash
@@ -17,6 +18,10 @@ class UserCullData(
1718
var player: Player,
1819
) {
1920

21+
companion object {
22+
val playerEntityVisibilityHandler by Versioned(PlayerEntityVisibilityHandler::class.java)
23+
}
24+
2025
private val hiddenEntities = Int2ReferenceOpenHashMap<Entity>(64, Hash.VERY_FAST_LOAD_FACTOR)
2126
private val pendingChanges = mutableListOf<CulledChange>()
2227
private var tickedTime = 0
@@ -29,12 +34,12 @@ class UserCullData(
2934
if (culled) {
3035
synchronized(hiddenEntities) {
3136
if (hiddenEntities.put(entityId, entity) == null && pend)
32-
pendCulledChange(entity, entityId, true)
37+
pendCulledChange(entity, true)
3338
}
3439
} else {
3540
synchronized(hiddenEntities) {
3641
if (hiddenEntities.remove(entityId) != null && pend)
37-
pendCulledChange(entity, entityId, false)
42+
pendCulledChange(entity, false)
3843
}
3944
}
4045
}
@@ -67,12 +72,10 @@ class UserCullData(
6772

6873
private fun reset() {
6974
synchronized(hiddenEntities) {
70-
val iterator = hiddenEntities.int2ReferenceEntrySet().iterator()
71-
for (entry in iterator) {
72-
val id = entry.intKey
73-
val entity = entry.value
74-
iterator.remove()
75-
pendCulledChange(entity, id, false)
75+
val values = hiddenEntities.values
76+
hiddenEntities.clear()
77+
for (entity in values) {
78+
pendCulledChange(entity, false)
7679
}
7780
}
7881
}
@@ -84,9 +87,12 @@ class UserCullData(
8487
val iterator = hiddenEntities.int2ReferenceEntrySet().iterator()
8588
for (entry in iterator) {
8689
val entity = entry.value
87-
if (!raytraceHandler.isValid(entity)) {
88-
iterator.remove()
90+
var flag = !raytraceHandler.isValid(entity)
91+
if (entity.world != player.world) {
92+
playerEntityVisibilityHandler.forceShowEntity(player, entity)
93+
flag = true
8994
}
95+
if (flag) iterator.remove()
9096
}
9197
}
9298
} catch (e: Throwable) {
@@ -108,9 +114,11 @@ class UserCullData(
108114
for (change in list) {
109115
if (!raytraceHandler.isValid(change.entity)) continue
110116
if (!change.entity.checkTickThread()) {
111-
// Not on tick thread, we can only roll state back
112-
if (!change.culled)
113-
setCulled(change.entity, change.entityId, true, pend = false)
117+
// Not on tick thread, we can only reflect to make changes,
118+
// We don't need to update TrackedEntity cuz not same thread.
119+
if (!change.culled) {
120+
playerEntityVisibilityHandler.forceShowEntity(player, change.entity)
121+
}
114122
continue
115123
}
116124
if (change.culled)
@@ -124,10 +132,10 @@ class UserCullData(
124132
}
125133
}
126134

127-
private fun pendCulledChange(entity: Entity, entityId: Int, culled: Boolean) {
128-
pendingChanges.add(CulledChange(entity, entityId, culled))
135+
private fun pendCulledChange(entity: Entity, culled: Boolean) {
136+
pendingChanges.add(CulledChange(entity, culled))
129137
}
130138

131-
private data class CulledChange(val entity: Entity, val entityId: Int, val culled: Boolean)
139+
private data class CulledChange(val entity: Entity, val culled: Boolean)
132140

133141
}

bukkit/version/v1_17_1/src/main/kotlin/io/github/rothes/esu/bukkit/util/version/adapter/nms/v1/EntityHandleGetterImpl.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import io.github.rothes.esu.bukkit.util.version.adapter.nms.EntityHandleGetter
44
import net.minecraft.world.entity.Entity
55
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftEntity
66

7-
class EntityHandleGetterImpl: EntityHandleGetter {
7+
object EntityHandleGetterImpl: EntityHandleGetter {
88

99
override fun getHandle(entity: org.bukkit.entity.Entity): Entity {
1010
return (entity as CraftEntity).handle
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package io.github.rothes.esu.bukkit.module.networkthrottle.entityculling.v1_18
2+
3+
import io.github.rothes.esu.bukkit.bootstrap
4+
import io.github.rothes.esu.bukkit.module.networkthrottle.entityculling.PlayerEntityVisibilityHandler
5+
import io.github.rothes.esu.bukkit.util.version.Versioned
6+
import io.github.rothes.esu.bukkit.util.version.adapter.nms.EntityHandleGetter
7+
import io.github.rothes.esu.core.util.ReflectionUtils.handle
8+
import io.github.rothes.esu.core.util.UnsafeUtils.usNullableObjAccessor
9+
import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer
10+
import org.bukkit.entity.Entity
11+
import org.bukkit.entity.Player
12+
import org.bukkit.plugin.Plugin
13+
import java.lang.ref.WeakReference
14+
import java.util.*
15+
16+
object PlayerEntityVisibilityHandlerImpl : PlayerEntityVisibilityHandler {
17+
18+
private val hiddenEntities = CraftPlayer::class.java.getDeclaredField("hiddenEntities").usNullableObjAccessor
19+
private val pluginWeakReferences = CraftPlayer::class.java.getDeclaredMethod("getPluginWeakReference", Plugin::class.java).handle
20+
private val entityHandleGetter by Versioned(EntityHandleGetter::class.java)
21+
22+
override fun forceShowEntity(player: Player, bukkitEntity: Entity) {
23+
@Suppress("UNCHECKED_CAST")
24+
val map = hiddenEntities[player] as MutableMap<UUID, MutableSet<WeakReference<Plugin>>>
25+
val uuid = entityHandleGetter.getHandle(bukkitEntity).uuid
26+
val set = map[uuid] ?: return
27+
val pluginReference = pluginWeakReferences.invokeExact(bootstrap as Plugin) as WeakReference<*>
28+
if (!set.remove(pluginReference)) return
29+
if (set.isEmpty()) map.remove(uuid)
30+
}
31+
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package io.github.rothes.esu.bukkit.module.networkthrottle.entityculling.v1_20
2+
3+
import io.github.rothes.esu.bukkit.bootstrap
4+
import io.github.rothes.esu.bukkit.module.networkthrottle.entityculling.PlayerEntityVisibilityHandler
5+
import io.github.rothes.esu.bukkit.util.version.Versioned
6+
import io.github.rothes.esu.bukkit.util.version.adapter.nms.EntityHandleGetter
7+
import io.github.rothes.esu.core.util.ReflectionUtils.handle
8+
import io.github.rothes.esu.core.util.UnsafeUtils.usNullableObjAccessor
9+
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer
10+
import org.bukkit.entity.Entity
11+
import org.bukkit.entity.Player
12+
import org.bukkit.plugin.Plugin
13+
import java.lang.ref.WeakReference
14+
import java.util.*
15+
16+
object PlayerEntityVisibilityHandlerImpl : PlayerEntityVisibilityHandler {
17+
18+
private val hiddenEntities = CraftPlayer::class.java.getDeclaredField("invertedVisibilityEntities").usNullableObjAccessor
19+
private val pluginWeakReferences = CraftPlayer::class.java.getDeclaredMethod("getPluginWeakReference", Plugin::class.java).handle
20+
private val entityHandleGetter by Versioned(EntityHandleGetter::class.java)
21+
22+
override fun forceShowEntity(player: Player, bukkitEntity: Entity) {
23+
@Suppress("UNCHECKED_CAST")
24+
val map = hiddenEntities[player] as MutableMap<UUID, MutableSet<WeakReference<Plugin>>>
25+
val entity = entityHandleGetter.getHandle(bukkitEntity)
26+
val uuid = entity.uuid
27+
val set = map[uuid]
28+
if (entity.visibleByDefault) {
29+
set ?: return
30+
val pluginReference = pluginWeakReferences.invokeExact(bootstrap as Plugin) as WeakReference<*>
31+
if (!set.remove(pluginReference)) return
32+
if (set.isEmpty()) map.remove(uuid)
33+
} else {
34+
@Suppress("UNCHECKED_CAST")
35+
val pluginReference = pluginWeakReferences.invokeExact(bootstrap as Plugin) as WeakReference<Plugin>
36+
if (set != null)
37+
set.add(pluginReference)
38+
else
39+
map[uuid] = hashSetOf(pluginReference)
40+
}
41+
}
42+
43+
}

bukkit/version/v1_20_2/src/main/kotlin/io/github/rothes/esu/bukkit/util/version/adapter/nms/v1_20_2__paper/EntityHandleGetterImpl.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import io.github.rothes.esu.bukkit.util.version.adapter.nms.EntityHandleGetter
44
import net.minecraft.world.entity.Entity
55
import org.bukkit.craftbukkit.v1_20_R2.entity.CraftEntity
66

7-
class EntityHandleGetterImpl: EntityHandleGetter {
7+
object EntityHandleGetterImpl: EntityHandleGetter {
88

99
override fun getHandle(entity: org.bukkit.entity.Entity): Entity {
1010
return (entity as CraftEntity).handleRaw

0 commit comments

Comments
 (0)