From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Samsuik Date: Thu, 23 Sep 2021 15:19:31 +0100 Subject: [PATCH] Limited Get Entities diff --git a/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java b/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java index 15ee41452992714108efe53b708b5a4e1da7c1ff..b62a1ae53a891802db17046271b9981ab3af1df0 100644 --- a/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java +++ b/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java @@ -522,6 +522,128 @@ public final class EntityLookup implements LevelEntityGetter { return slices; } + // Sakura start - limited get entities + public void getLimitedEntities(final Entity except, final AABB box, final List into, + final Predicate predicate, final int limit, final int search) { + final int minChunkX = (Mth.floor(box.minX) - 2) >> 4; + final int minChunkZ = (Mth.floor(box.minZ) - 2) >> 4; + final int maxChunkX = (Mth.floor(box.maxX) + 2) >> 4; + final int maxChunkZ = (Mth.floor(box.maxZ) + 2) >> 4; + + final int minRegionX = minChunkX >> REGION_SHIFT; + final int minRegionZ = minChunkZ >> REGION_SHIFT; + final int maxRegionX = maxChunkX >> REGION_SHIFT; + final int maxRegionZ = maxChunkZ >> REGION_SHIFT; + + for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { + final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; + final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; + + for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { + final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); + + if (region == null) { + continue; + } + + final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; + final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; + + for (int currZ = minZ; currZ <= maxZ; ++currZ) { + for (int currX = minX; currX <= maxX; ++currX) { + final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); + if (chunk == null || !chunk.status.isOrAfter(FullChunkStatus.FULL)) { + continue; + } + + chunk.getLimitedEntities(except, box, into, predicate, limit, search); + } + } + } + } + } + + public void getLimitedEntities(final EntityType type, final AABB box, final List into, + final Predicate predicate, final int limit, final int search) { + final int minChunkX = (Mth.floor(box.minX) - 2) >> 4; + final int minChunkZ = (Mth.floor(box.minZ) - 2) >> 4; + final int maxChunkX = (Mth.floor(box.maxX) + 2) >> 4; + final int maxChunkZ = (Mth.floor(box.maxZ) + 2) >> 4; + + final int minRegionX = minChunkX >> REGION_SHIFT; + final int minRegionZ = minChunkZ >> REGION_SHIFT; + final int maxRegionX = maxChunkX >> REGION_SHIFT; + final int maxRegionZ = maxChunkZ >> REGION_SHIFT; + + for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { + final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; + final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; + + for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { + final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); + + if (region == null) { + continue; + } + + final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; + final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; + + for (int currZ = minZ; currZ <= maxZ; ++currZ) { + for (int currX = minX; currX <= maxX; ++currX) { + final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); + if (chunk == null || !chunk.status.isOrAfter(FullChunkStatus.FULL)) { + continue; + } + + chunk.getLimitedEntities(type, box, (List)into, (Predicate)predicate, limit, search); + } + } + } + } + } + + public void getLimitedEntities(final Class clazz, final Entity except, final AABB box, final List into, + final Predicate predicate, final int limit, final int search) { + final int minChunkX = (Mth.floor(box.minX) - 2) >> 4; + final int minChunkZ = (Mth.floor(box.minZ) - 2) >> 4; + final int maxChunkX = (Mth.floor(box.maxX) + 2) >> 4; + final int maxChunkZ = (Mth.floor(box.maxZ) + 2) >> 4; + + final int minRegionX = minChunkX >> REGION_SHIFT; + final int minRegionZ = minChunkZ >> REGION_SHIFT; + final int maxRegionX = maxChunkX >> REGION_SHIFT; + final int maxRegionZ = maxChunkZ >> REGION_SHIFT; + + for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) { + final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0; + final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK; + + for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) { + final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ); + + if (region == null) { + continue; + } + + final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0; + final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK; + + for (int currZ = minZ; currZ <= maxZ; ++currZ) { + for (int currX = minX; currX <= maxX; ++currX) { + final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT)); + if (chunk == null || !chunk.status.isOrAfter(FullChunkStatus.FULL)) { + continue; + } + + chunk.getLimitedEntities(clazz, except, box, into, predicate, limit, search); + } + } + } + } + } + // Sakura end + public void getEntitiesWithoutDragonParts(final Entity except, final AABB box, final List into, final Predicate predicate) { final int minChunkX = (Mth.floor(box.minX) - 2) >> 4; final int minChunkZ = (Mth.floor(box.minZ) - 2) >> 4; diff --git a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java index d917a19c838ed3d74322abd85e1f737e852b5d7b..1ba10713c85d6f19f075cc267602a04c7e048252 100644 --- a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java +++ b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java @@ -258,6 +258,30 @@ public final class ChunkEntitySlices { } // Sakura end + // Sakura start - limited get entities + public void getLimitedEntities(final Entity except, final AABB box, final List into, final Predicate predicate, final int limit, final int search) { + this.allEntities.getLimitedEntities(except, box, into, predicate, limit, search); + } + + public void getLimitedEntities(final EntityType type, final AABB box, final List into, + final Predicate predicate, final int limit, final int search) { + this.allEntities.getLimitedEntities(type, box, (List)into, (Predicate)predicate, limit, search); + } + + public void getLimitedEntities(final Class clazz, final Entity except, final AABB box, final List into, + final Predicate predicate, final int limit, final int search) { + EntityCollectionBySection collection = this.entitiesByClass.get(clazz); + if (collection != null) { + collection.getLimitedEntities(except, clazz, box, (List)into, (Predicate)predicate, limit, search); + } else { + synchronized (this) { + this.entitiesByClass.putIfAbsent(clazz, collection = this.initClass(clazz)); + } + collection.getLimitedEntities(except, clazz, box, (List)into, (Predicate)predicate, limit, search); + } + } + // Sakura end + public void getHardCollidingEntities(final Entity except, final AABB box, final List into, final Predicate predicate) { this.hardCollidingEntities.getEntities(except, box, into, predicate); } @@ -448,6 +472,155 @@ public final class ChunkEntitySlices { } // Sakura end + // Sakura start - limited get entities + public void getLimitedEntities(final Entity except, final Class clazz, final AABB box, final List into, + final Predicate predicate, final int limit, int search) { + if (this.count == 0) { + return; + } + + final int minSection = this.manager.minSection; + final int maxSection = this.manager.maxSection; + + final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); + final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); + + // TODO use the bitset + + final BasicEntityList[] entitiesBySection = this.entitiesBySection; + + for (int section = min; section <= max; ++section) { + final BasicEntityList list = entitiesBySection[section - minSection]; + + if (list == null) { + continue; + } + + final Entity[] storage = list.storage; + + int len = Math.min(storage.length, list.size()); + int index = manager.world.random.nextInt(len); // starting position + + for (int i = 0; i < len; ++i) { + index = (index + 1) % len; // Update index + final Entity entity = storage[index]; + + if (into.size() >= limit || search-- <= 0) { + return; + } + + if (entity == null || entity == except || !clazz.isInstance(entity) || !entity.getBoundingBox().intersects(box)) { + continue; + } + + if (predicate != null && !predicate.test(entity)) { + continue; + } + + into.add(entity); + } + } + } + + public void getLimitedEntities(final EntityType type, final AABB box, final List into, + final Predicate predicate, final int limit, int search) { + if (this.count == 0) { + return; + } + + final int minSection = this.manager.minSection; + final int maxSection = this.manager.maxSection; + + final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); + final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); + + // TODO use the bitset + + final BasicEntityList[] entitiesBySection = this.entitiesBySection; + + for (int section = min; section <= max; ++section) { + final BasicEntityList list = entitiesBySection[section - minSection]; + + if (list == null) { + continue; + } + + final Entity[] storage = list.storage; + + int len = Math.min(storage.length, list.size()); + int index = manager.world.random.nextInt(len); // starting position + + for (int i = 0; i < len; ++i) { + index = (index + 1) % len; // Update index + final Entity entity = storage[index]; + + if (into.size() >= limit || search-- <= 0) { + return; + } + + if (entity == null || (type != null && entity.getType() != type) || !entity.getBoundingBox().intersects(box)) { + continue; + } + + if (predicate != null && !predicate.test((T)entity)) { + continue; + } + + into.add((T)entity); + } + } + } + + public void getLimitedEntities(final Entity except, final AABB box, final List into, + final Predicate predicate, final int limit, int search) { + if (this.count == 0) { + return; + } + + final int minSection = this.manager.minSection; + final int maxSection = this.manager.maxSection; + + final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection); + final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection); + + // TODO use the bitset + + final BasicEntityList[] entitiesBySection = this.entitiesBySection; + + for (int section = min; section <= max; ++section) { + final BasicEntityList list = entitiesBySection[section - minSection]; + + if (list == null) { + continue; + } + + final Entity[] storage = list.storage; + + int len = Math.min(storage.length, list.size()); + int index = manager.world.random.nextInt(len); // starting position + + for (int i = 0; i < len; ++i) { + index = (index + 1) % len; // Update index + final Entity entity = storage[index]; + + if (into.size() >= limit || search-- <= 0) { + return; + } + + if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) { + continue; + } + + if (predicate != null && !predicate.test(entity)) { + continue; + } + + into.add(entity); + } + } + } + // Sakura end + public void getEntities(final Entity except, final AABB box, final List into, final Predicate predicate) { if (this.count == 0) { return; diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java index 3e1271d07883f05fff21d0ec6088fe42a579a706..6fca6c3d8200251e24f3df886f17f35179b1f760 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -237,6 +237,39 @@ public abstract class Level implements LevelAccessor, AutoCloseable { public final me.samsuik.sakura.entity.merge.MergeHistory mergeHistory = new me.samsuik.sakura.entity.merge.MergeHistory(); // Sakura public final me.samsuik.sakura.explosion.DensityCache densityCache = new me.samsuik.sakura.explosion.DensityCache(); + // Sakura start - limited get entities + public void getLimitedEntities(Entity except, AABB box, Predicate predicate, List into, int limit, int search) { + ((ServerLevel)this).getEntityLookup().getLimitedEntities(except, box, into, predicate, limit, search); + } + + public List getLimitedEntities(net.minecraft.world.entity.EntityType filter, AABB box, Predicate predicate, int limit, int search) { + List ret = new java.util.ArrayList<>(); + ((ServerLevel)this).getEntityLookup().getLimitedEntities(filter, box, ret, predicate, limit, search); + return ret; + } + + public void getLimitedEntitiesByClass(Class clazz, Entity except, final AABB box, List into, + Predicate predicate, int limit, int search) { + ((ServerLevel)this).getEntityLookup().getLimitedEntities((Class)clazz, except, box, (List)into, (Predicate)predicate, limit, search); + } + + public List getLimitedEntitiesOfClass(Class entityClass, AABB box, Predicate predicate, int limit, int search) { + List ret = new java.util.ArrayList<>(); + ((ServerLevel)this).getEntityLookup().getLimitedEntities(entityClass, null, box, ret, predicate, limit, search); + return ret; + } + + public List getLimitedEntities(@Nullable Entity except, AABB box, Predicate predicate, int limit, int search) { + List list = Lists.newArrayList(); + getLimitedEntities(except, box, predicate, list, limit, search); + return list; + } + + public List getLimitedEntities(@Nullable Entity except, AABB box, int limit, int search) { + return this.getLimitedEntities(except, box, net.minecraft.world.entity.EntitySelector.NO_SPECTATORS, limit, search); + } + // Sakura end + protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, RegistryAccess iregistrycustom, Holder holder, Supplier supplier, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function paperWorldConfigCreator, Supplier sakuraWorldConfigCreator, java.util.concurrent.Executor executor) { // Sakura // Paper - Async-Anti-Xray - Pass executor this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper