From 06baf83a902aea3c7cf612ba8f46401c4a3bc93d Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sat, 10 May 2025 22:53:54 +0200 Subject: [PATCH] Smart sort items in NearestItemSensor --- ...ntities-in-NearestLivingEntitySensor.patch | 13 +++-- ...mart-sort-items-in-NearestItemSensor.patch | 45 +++++++++++++++++ .../dreeam/leaf/util/fastBitRadixSort.java | 49 +++++++++---------- 3 files changed, 74 insertions(+), 33 deletions(-) create mode 100644 leaf-server/minecraft-patches/features/0173-Smart-sort-items-in-NearestItemSensor.patch diff --git a/leaf-server/minecraft-patches/features/0090-Smart-sort-entities-in-NearestLivingEntitySensor.patch b/leaf-server/minecraft-patches/features/0090-Smart-sort-entities-in-NearestLivingEntitySensor.patch index 9a7d0adc..1e87e011 100644 --- a/leaf-server/minecraft-patches/features/0090-Smart-sort-entities-in-NearestLivingEntitySensor.patch +++ b/leaf-server/minecraft-patches/features/0090-Smart-sort-entities-in-NearestLivingEntitySensor.patch @@ -12,10 +12,10 @@ In non-strict test, this can give ~60-110% improvement (524ms on Paper, 204ms on under 625 villagers situation. diff --git a/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java b/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java -index b0c5e41fefc7c9adf1a61bd5b52861736657d37e..3fc212c10d230f027292865a214a8164a84e284a 100644 +index b0c5e41fefc7c9adf1a61bd5b52861736657d37e..a887bb8ed2318d6ee39be350a1d0e7223ecf3ff5 100644 --- a/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java +++ b/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java -@@ -13,17 +13,30 @@ import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities; +@@ -13,17 +13,28 @@ import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities; import net.minecraft.world.phys.AABB; public class NearestLivingEntitySensor extends Sensor { @@ -34,13 +34,12 @@ index b0c5e41fefc7c9adf1a61bd5b52861736657d37e..3fc212c10d230f027292865a214a8164 AABB aabb = entity.getBoundingBox().inflate(attributeValue, attributeValue, attributeValue); - List entitiesOfClass = level.getEntitiesOfClass( - LivingEntity.class, aabb, matchableEntity -> matchableEntity != entity && matchableEntity.isAlive() -+ -+ List entities = level.getEntitiesOfClass( -+ LivingEntity.class, aabb, e -> e != entity && e.isAlive() && entity.distanceToSqr(e) <= rangeSqr - ); +- ); - entitiesOfClass.sort(Comparator.comparingDouble(entity::distanceToSqr)); + -+ LivingEntity[] sorted = this.sorter.sort(entities, entity); ++ List entities = level.getEntitiesOfClass(LivingEntity.class, aabb, e -> e != entity && e.isAlive() && entity.distanceToSqr(e) <= rangeSqr); ++ ++ LivingEntity[] sorted = this.sorter.sort(entities, entity, LivingEntity.class); + List sortedList = java.util.Arrays.asList(sorted); + Brain brain = entity.getBrain(); diff --git a/leaf-server/minecraft-patches/features/0173-Smart-sort-items-in-NearestItemSensor.patch b/leaf-server/minecraft-patches/features/0173-Smart-sort-items-in-NearestItemSensor.patch new file mode 100644 index 00000000..58bac1dd --- /dev/null +++ b/leaf-server/minecraft-patches/features/0173-Smart-sort-items-in-NearestItemSensor.patch @@ -0,0 +1,45 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Sat, 10 May 2025 22:04:42 +0200 +Subject: [PATCH] Smart sort items in NearestItemSensor + + +diff --git a/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java b/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java +index 09fd13e2d958da8326276c4dadf25bf488aff5ac..70036d073b644ff5f486491486c38db5df643107 100644 +--- a/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java ++++ b/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java +@@ -6,6 +6,7 @@ import java.util.List; + import java.util.Optional; + import java.util.Set; + import net.minecraft.server.level.ServerLevel; ++import net.minecraft.world.entity.Entity; + import net.minecraft.world.entity.Mob; + import net.minecraft.world.entity.ai.Brain; + import net.minecraft.world.entity.ai.memory.MemoryModuleType; +@@ -16,6 +17,8 @@ public class NearestItemSensor extends Sensor { + private static final long Y_RANGE = 16L; + public static final int MAX_DISTANCE_TO_WANTED_ITEM = 32; + ++ private final org.dreeam.leaf.util.fastBitRadixSort itemSorter; ++ public NearestItemSensor() {this.itemSorter = new org.dreeam.leaf.util.fastBitRadixSort();} + @Override + public Set> requires() { + return ImmutableSet.of(MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM); +@@ -24,12 +27,13 @@ public class NearestItemSensor extends Sensor { + @Override + protected void doTick(ServerLevel level, Mob entity) { + Brain brain = entity.getBrain(); +- List entitiesOfClass = level.getEntitiesOfClass(ItemEntity.class, entity.getBoundingBox().inflate(32.0, 16.0, 32.0), itemEntity -> itemEntity.closerThan(entity, MAX_DISTANCE_TO_WANTED_ITEM) && entity.wantsToPickUp(level, itemEntity.getItem())); // Paper - Perf: Move predicate into getEntities +- entitiesOfClass.sort(Comparator.comparingDouble(entity::distanceToSqr)); ++ List entitiesOfClass = level.getEntitiesOfClass(ItemEntity.class, entity.getBoundingBox().inflate(32.0, 16.0, 32.0), itemEntity -> itemEntity.closerThan(entity, MAX_DISTANCE_TO_WANTED_ITEM) && entity.wantsToPickUp(level, itemEntity.getItem())); ++ ItemEntity[] sortedItems = this.itemSorter.sort(entitiesOfClass, entity, ItemEntity.class); ++ + // Paper start - Perf: remove streams from hot code + ItemEntity nearest = null; +- for (final ItemEntity itemEntity : entitiesOfClass) { +- if (entity.hasLineOfSight(itemEntity)) { // Paper - Perf: Move predicate into getEntities ++ for (final ItemEntity itemEntity : sortedItems) { ++ if (entity.hasLineOfSight(itemEntity)) { + nearest = itemEntity; + break; + } diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/fastBitRadixSort.java b/leaf-server/src/main/java/org/dreeam/leaf/util/fastBitRadixSort.java index ee9fad2e..21b36d46 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/util/fastBitRadixSort.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/fastBitRadixSort.java @@ -1,57 +1,55 @@ package org.dreeam.leaf.util; import java.util.List; -import java.util.Arrays; // For Arrays.copyOf -import net.minecraft.world.entity.LivingEntity; +import java.util.Arrays; +import net.minecraft.world.entity.Entity; +import java.lang.reflect.Array; // Required for Array.newInstance public class fastBitRadixSort { private static final int SMALL_ARRAY_THRESHOLD = 2; - private LivingEntity[] entityBuffer = new LivingEntity[0]; + private Entity[] entityBuffer = new Entity[0]; private long[] bitsBuffer = new long[0]; - /** - * Sorts a list of LivingEntity objects based on their squared distance - * to a reference entity using a fast radix sort algorithm. - **/ - public LivingEntity[] sort( - List entities, - T_REF referenceEntity - ) { + @SuppressWarnings("unchecked") + public T[] sort(List entities, T_REF referenceEntity, Class entityClass) { int size = entities.size(); if (size <= 1) { - return entities.toArray(new LivingEntity[0]); + T[] resultArray = (T[]) Array.newInstance(entityClass, size); + return entities.toArray(resultArray); } if (this.entityBuffer.length < size) { - this.entityBuffer = new LivingEntity[size]; + this.entityBuffer = new Entity[size]; this.bitsBuffer = new long[size]; } for (int i = 0; i < size; i++) { - LivingEntity e = entities.get(i); - this.entityBuffer[i] = e; + this.entityBuffer[i] = entities.get(i); this.bitsBuffer[i] = Double.doubleToRawLongBits( - referenceEntity.distanceToSqr(e) + referenceEntity.distanceToSqr(entities.get(i)) ); } - // start from bit 62 (most significant for positive doubles, ignoring sign bit) fastRadixSort(this.entityBuffer, this.bitsBuffer, 0, size - 1, 62); - return Arrays.copyOf(this.entityBuffer, size); + + T[] resultArray = (T[]) Array.newInstance(entityClass, size); + for (int i = 0; i < size; i++) { + resultArray[i] = entityClass.cast(this.entityBuffer[i]); + } + return resultArray; } private void fastRadixSort( - LivingEntity[] ents, + Entity[] ents, long[] bits, int low, int high, int bit ) { if (bit < 0 || low >= high) { - return; // Base case: no bits left or subarray is trivial + return; } - // For small subarrays, insertion sort is generally faster if (high - low <= SMALL_ARRAY_THRESHOLD) { insertionSort(ents, bits, low, high); return; @@ -82,14 +80,14 @@ public class fastBitRadixSort { } private void insertionSort( - LivingEntity[] ents, + Entity[] ents, long[] bits, int low, int high ) { for (int i = low + 1; i <= high; i++) { int j = i; - LivingEntity currentEntity = ents[j]; + Entity currentEntity = ents[j]; long currentBits = bits[j]; while (j > low && bits[j - 1] > currentBits) { @@ -97,14 +95,13 @@ public class fastBitRadixSort { bits[j] = bits[j - 1]; j--; } - ents[j] = currentEntity; bits[j] = currentBits; } } - private void swap(LivingEntity[] ents, long[] bits, int a, int b) { - LivingEntity tempEntity = ents[a]; + private void swap(Entity[] ents, long[] bits, int a, int b) { + Entity tempEntity = ents[a]; ents[a] = ents[b]; ents[b] = tempEntity;