-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Smart sorting & Remove streams & Throttle hopper (#157)
* =-= * Reuse comparator * Cleanup DAB patch
- Loading branch information
Showing
54 changed files
with
250 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 | ||
From: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com> | ||
Date: Tue, 9 Nov 2077 00:00:00 +0800 | ||
Subject: [PATCH] Remove stream in PlayerSensor | ||
|
||
Stream operations in PlayerSensor take too much time | ||
while ticking Villager farms, so just replace it with for loop =-= | ||
Before: 164ms | ||
After: 18ms | ||
|
||
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java | ||
index 2e887e426dcd79e2dda401f127d0e01ca537e80e..6ef968a5224830959d6f36193810debe7f14e21e 100644 | ||
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java | ||
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/PlayerSensor.java | ||
@@ -21,17 +21,39 @@ public class PlayerSensor extends Sensor<LivingEntity> { | ||
|
||
@Override | ||
protected void doTick(ServerLevel world, LivingEntity entity) { | ||
- List<Player> list = world.players() | ||
- .stream() | ||
- .filter(EntitySelector.NO_SPECTATORS) | ||
- .filter(player -> entity.closerThan(player, 16.0)) | ||
- .sorted(Comparator.comparingDouble(entity::distanceToSqr)) | ||
- .collect(Collectors.toList()); | ||
+ // Leaf start - Remove stream in PlayerSensor | ||
+ List<Player> list = new java.util.ArrayList<>(); | ||
+ for (Player player : world.players()) { | ||
+ if (!EntitySelector.NO_SPECTATORS.test(player)) { | ||
+ continue; | ||
+ } | ||
+ if (!entity.closerThan(player, 16.0)) { | ||
+ continue; | ||
+ } | ||
+ list.add(player); | ||
+ } | ||
+ list.sort(Comparator.comparingDouble(entity::distanceToSqr)); | ||
+ // Leaf end - Remove stream in PlayerSensor | ||
Brain<?> brain = entity.getBrain(); | ||
brain.setMemory(MemoryModuleType.NEAREST_PLAYERS, list); | ||
- List<Player> list2 = list.stream().filter(player -> isEntityTargetable(entity, player)).collect(Collectors.toList()); | ||
+ // Leaf start - Remove stream in PlayerSensor | ||
+ List<Player> list2 = new java.util.ArrayList<>(); | ||
+ for (Player player : list) { | ||
+ if (isEntityTargetable(entity, player)) { | ||
+ list2.add(player); | ||
+ } | ||
+ } | ||
+ // Leaf end - Remove stream in PlayerSensor | ||
brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_PLAYER, list2.isEmpty() ? null : list2.get(0)); | ||
- Optional<Player> optional = list2.stream().filter(player -> isEntityAttackable(entity, player)).findFirst(); | ||
+ // Leaf start - Remove stream in PlayerSensor | ||
+ Optional<Player> optional = Optional.empty(); | ||
+ for (Player player : list2) { | ||
+ if (isEntityAttackable(entity, player)) { | ||
+ optional = Optional.of(player); | ||
+ break; | ||
+ } | ||
+ } | ||
+ // Leaf end - Remove stream in PlayerSensor | ||
brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER, optional); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 | ||
From: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com> | ||
Date: Tue, 9 Nov 2077 00:00:00 +0800 | ||
Subject: [PATCH] Remove stream in GolemSensor | ||
|
||
Stream operations in GolemSensor is really expensive and takes | ||
up 80% time per method call. | ||
Before: 192ms | ||
After: 17ms | ||
|
||
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/GolemSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/GolemSensor.java | ||
index 94d730e2d8159184a95da4b23266bf2e330be707..f78a342270104ca1082ea535d1152541d7297d8c 100644 | ||
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/GolemSensor.java | ||
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/GolemSensor.java | ||
@@ -34,7 +34,15 @@ public class GolemSensor extends Sensor<LivingEntity> { | ||
public static void checkForNearbyGolem(LivingEntity entity) { | ||
Optional<List<LivingEntity>> optional = entity.getBrain().getMemory(MemoryModuleType.NEAREST_LIVING_ENTITIES); | ||
if (!optional.isEmpty()) { | ||
- boolean bl = optional.get().stream().anyMatch(livingEntity -> livingEntity.getType().equals(EntityType.IRON_GOLEM)); | ||
+ // Leaf start - Remove stream in GolemSensor | ||
+ boolean bl = false; | ||
+ for (LivingEntity livingEntity : optional.get()) { | ||
+ if (livingEntity.getType().equals(EntityType.IRON_GOLEM)) { | ||
+ bl = true; | ||
+ break; | ||
+ } | ||
+ } | ||
+ // Leaf end - Remove stream in GolemSensor | ||
if (bl) { | ||
golemDetected(entity); | ||
} |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
30 changes: 0 additions & 30 deletions
30
patches/server/0128-Use-QuickSort-in-NearestLivingEntitySensor.patch
This file was deleted.
Oops, something went wrong.
File renamed without changes.
97 changes: 97 additions & 0 deletions
97
patches/server/0130-Smart-sort-entities-in-NearestLivingEntitySensor.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 | ||
From: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com> | ||
Date: Tue, 9 Nov 2077 00:00:00 +0800 | ||
Subject: [PATCH] Smart sort entities in NearestLivingEntitySensor | ||
|
||
This patch optimizes sorting algorithm by dynamically sorting based | ||
on entity count, if entity count doesn't reach the Bucket Sort threshold, | ||
Quick Sort of Fastutil will be used. | ||
When entity count reached the threshold, Bucket Sort will be used. | ||
This offers a 10~15% performance improvement in average. | ||
In best situation, this can give an up to 50% improvement. | ||
|
||
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java | ||
index 5a059e1ec232d82e8e891ae78fea962bec2f878e..7cb18a2191a4b520ee81230106045d18faa384ee 100644 | ||
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java | ||
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestLivingEntitySensor.java | ||
@@ -12,16 +12,77 @@ import net.minecraft.world.entity.ai.memory.NearestVisibleLivingEntities; | ||
import net.minecraft.world.phys.AABB; | ||
|
||
public class NearestLivingEntitySensor<T extends LivingEntity> extends Sensor<T> { | ||
+ // Leaf start - Smart sort entities in NearestLivingEntitySensor | ||
+ private static final int NUM_BUCKETS = org.apache.commons.lang3.math.NumberUtils.toInt((System.getProperty("Leaf.nearestEntitySensorBucketCount", "10")), 10); | ||
+ private static final int BUCKET_SORT_THRESHOLD = (int) Math.floor(NUM_BUCKETS * org.apache.commons.lang3.math.NumberUtils.toDouble(System.getProperty("Leaf.nearestEntitySensorBucketSortThresholdRatio", "2.0"), 2.0D)); | ||
+ // Leaf end - Smart sort entities in NearestLivingEntitySensor | ||
@Override | ||
protected void doTick(ServerLevel world, T entity) { | ||
AABB aABB = entity.getBoundingBox().inflate((double)this.radiusXZ(), (double)this.radiusY(), (double)this.radiusXZ()); | ||
List<LivingEntity> list = world.getEntitiesOfClass(LivingEntity.class, aABB, e -> e != entity && e.isAlive()); | ||
- list.sort(Comparator.comparingDouble(entity::distanceToSqr)); | ||
+ // Leaf start - Smart sort entities in NearestLivingEntitySensor | ||
+ LivingEntity[] sortedEntities = smartSortEntities(list.toArray(new LivingEntity[]{}), entity); | ||
+ List<LivingEntity> sortedList = java.util.Arrays.asList(sortedEntities); | ||
+ // Leaf end - Smart sort entities in NearestLivingEntitySensor | ||
Brain<?> brain = entity.getBrain(); | ||
- brain.setMemory(MemoryModuleType.NEAREST_LIVING_ENTITIES, list); | ||
- brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, new NearestVisibleLivingEntities(entity, list)); | ||
+ // Leaf start - Smart sort entities in NearestLivingEntitySensor | ||
+ brain.setMemory(MemoryModuleType.NEAREST_LIVING_ENTITIES, sortedList); | ||
+ brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, new NearestVisibleLivingEntities(entity, sortedList)); | ||
+ // Leaf end - Smart sort entities in NearestLivingEntitySensor | ||
} | ||
|
||
+ // Leaf start - Smart sort entities in NearestLivingEntitySensor | ||
+ private LivingEntity[] smartSortEntities(LivingEntity[] entities, T referenceEntity) { | ||
+ if (entities.length <= 1) { | ||
+ return entities; | ||
+ } | ||
+ final Comparator<LivingEntity> comparator = Comparator.comparingDouble(referenceEntity::distanceToSqr); | ||
+ | ||
+ if (entities.length < BUCKET_SORT_THRESHOLD) { | ||
+ it.unimi.dsi.fastutil.objects.ObjectArrays.quickSort(entities, comparator); | ||
+ return entities; | ||
+ } | ||
+ | ||
+ // Create buckets | ||
+ @SuppressWarnings("unchecked") | ||
+ List<LivingEntity>[] buckets = new List[NUM_BUCKETS]; | ||
+ for (int i = 0; i < NUM_BUCKETS; i++) { | ||
+ buckets[i] = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(); | ||
+ } | ||
+ | ||
+ // Find max distance to normalize bucket distribution - Leaf | ||
+ double maxDistSq = 0.0; | ||
+ for (LivingEntity e : entities) { | ||
+ maxDistSq = Math.max(maxDistSq, referenceEntity.distanceToSqr(e)); | ||
+ } | ||
+ | ||
+ // Handle edge case where all entities are at the same position - Leaf | ||
+ if (maxDistSq == 0.0) { | ||
+ return entities; | ||
+ } | ||
+ | ||
+ // Distribute entities into buckets | ||
+ for (LivingEntity e : entities) { | ||
+ double distSq = referenceEntity.distanceToSqr(e); | ||
+ int bucketIndex = (int) ((distSq / maxDistSq) * (NUM_BUCKETS - 1)); | ||
+ buckets[bucketIndex].add(e); | ||
+ } | ||
+ | ||
+ // Sort each bucket and combine results | ||
+ int currentIndex = 0; | ||
+ for (List<LivingEntity> bucket : buckets) { | ||
+ if (!bucket.isEmpty()) { | ||
+ bucket.sort(comparator); | ||
+ for (LivingEntity e : bucket) { | ||
+ entities[currentIndex++] = e; | ||
+ } | ||
+ } | ||
+ } | ||
+ | ||
+ return entities; | ||
+ } | ||
+ // Leaf end - Smart sort entities in NearestLivingEntitySensor | ||
+ | ||
protected int radiusXZ() { | ||
return 16; | ||
} |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
51 changes: 51 additions & 0 deletions
51
patches/server/0148-SparklyPaper-Throttle-hopper-when-full.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 | ||
From: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com> | ||
Date: Tue, 9 Nov 2077 00:00:00 +0800 | ||
Subject: [PATCH] SparklyPaper Throttle hopper when full | ||
|
||
Original project: https://github.com/SparklyPower/SparklyPaper | ||
|
||
diff --git a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java | ||
index 99fc84e10a10f3d4eededd6ce7be7700e3c3a8e4..58c7c1db907d6c7a7ff99308260dadf487d79864 100644 | ||
--- a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java | ||
+++ b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java | ||
@@ -443,6 +443,11 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen | ||
Direction enumdirection = blockEntity.facing.getOpposite(); | ||
|
||
if (HopperBlockEntity.isFullContainer(iinventory, enumdirection)) { | ||
+ // Leaf start - Throttle hopper when full | ||
+ if (org.dreeam.leaf.config.modules.opt.ThrottleHopperWhenFull.enabled && org.dreeam.leaf.config.modules.opt.ThrottleHopperWhenFull.skipTicks > 0) { | ||
+ blockEntity.setCooldown(org.dreeam.leaf.config.modules.opt.ThrottleHopperWhenFull.skipTicks); | ||
+ } | ||
+ // Leaf end - Throttle hopper when full | ||
return false; | ||
} else { | ||
// Paper start - Perf: Optimize Hoppers | ||
diff --git a/src/main/java/org/dreeam/leaf/config/modules/opt/ThrottleHopperWhenFull.java b/src/main/java/org/dreeam/leaf/config/modules/opt/ThrottleHopperWhenFull.java | ||
new file mode 100644 | ||
index 0000000000000000000000000000000000000000..97257fb1db8e54a35b9badb8cf0a2be2e0646e51 | ||
--- /dev/null | ||
+++ b/src/main/java/org/dreeam/leaf/config/modules/opt/ThrottleHopperWhenFull.java | ||
@@ -0,0 +1,22 @@ | ||
+package org.dreeam.leaf.config.modules.opt; | ||
+ | ||
+import org.dreeam.leaf.config.ConfigModules; | ||
+import org.dreeam.leaf.config.EnumConfigCategory; | ||
+ | ||
+public class ThrottleHopperWhenFull extends ConfigModules { | ||
+ | ||
+ public String getBasePath() { | ||
+ return EnumConfigCategory.PERF.getBaseKeyName() + ".throttle-hopper-when-full"; | ||
+ } | ||
+ | ||
+ public static boolean enabled = false; | ||
+ public static int skipTicks = 0; | ||
+ | ||
+ @Override | ||
+ public void onLoaded() { | ||
+ enabled = config.getBoolean(getBasePath() + ".enabled", enabled, """ | ||
+ Throttles the hopper if target container is full."""); | ||
+ skipTicks = config.getInt(getBasePath() + ".skip-ticks", skipTicks, """ | ||
+ How many ticks to throttle when the Hopper is throttled."""); | ||
+ } | ||
+} |