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 61c170555c8854b102c640b0b6a615f9f732edbf..40c075c90993cdc7d0e6b908ed0f290a78286960 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 @@ -459,6 +459,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(ChunkHolder.FullChunkStatus.BORDER)) { + 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(ChunkHolder.FullChunkStatus.BORDER)) { + 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(ChunkHolder.FullChunkStatus.BORDER)) { + 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 ce231d8b230d4983b21c597357c0b22377e4bcca..91b0e10a60173b428612e2515fdcfd62110a7a3e 100644 --- a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java +++ b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java @@ -253,6 +253,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); } @@ -443,6 +467,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 5c3aa8335b47a8ac0956c6caed3569f9244f4bd6..562ce3115ff6814f52872eb0e36a95d255ea4187 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -286,6 +286,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 retrieval methods with search limits + 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 - add entity retrieval methods with search limits + protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, 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