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 c62793c50e95e2e9588e20a59a1d8fb70ddf7760..87b7254406166f4b98bd2e2ca1e57b53d789867c 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -291,6 +291,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 + // 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