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 8fcaa00e461c7f4413bf655ddd8165a2b908f900..404b99def4562942e036089085a667979b1cac81 100644 --- a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java +++ b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java @@ -259,6 +259,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); } @@ -449,6 +473,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 507d676c58daf2297eb729543a7f86c896a984d6..d041ad1e52c78c553da4d92745d2b07fc3eab522 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -221,6 +221,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 - cannon entity merging public final me.samsuik.sakura.explosion.density.BlockDensityCache densityCache = new me.samsuik.sakura.explosion.density.BlockDensityCache(); // Sakura - explosion density cache + // Sakura start - add entity retrival methods with search limits + public final 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 final 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 final 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 final 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 final 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 final 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 - add entity retrival methods with search limits + 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 - sakura configuration files // Paper - create paper world config; 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 - create paper world config