Skip to content

Commit

Permalink
Smart sorting & Remove streams & Throttle hopper (#157)
Browse files Browse the repository at this point in the history
* =-=

* Reuse comparator

* Cleanup DAB patch
  • Loading branch information
HaHaWTH authored Nov 10, 2024
1 parent cca144f commit 848fb2d
Show file tree
Hide file tree
Showing 54 changed files with 250 additions and 42 deletions.
11 changes: 4 additions & 7 deletions patches/server/0008-Pufferfish-Dynamic-Activation-of-Brain.patch
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ index 0000000000000000000000000000000000000000..a2e60c43074df560eb01f150bf52b8d0
+ }
+}
diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java
index 0b2f2fbe462ed628ef3d640824d4162e79279089..e469b2782932275caa4bb760eefff82916c47d3a 100644
index 5242f861834737876f639195dc259666705411e4..e6ef67125f59195104b013de8f69669a8e059a1f 100644
--- a/src/main/java/org/spigotmc/ActivationRange.java
+++ b/src/main/java/org/spigotmc/ActivationRange.java
@@ -38,7 +38,6 @@ import co.aikar.timings.MinecraftTimings;
Expand All @@ -382,7 +382,7 @@ index 0b2f2fbe462ed628ef3d640824d4162e79279089..e469b2782932275caa4bb760eefff829
import org.galemc.gale.configuration.GaleWorldConfiguration;

public class ActivationRange
@@ -238,6 +237,26 @@ public class ActivationRange
@@ -238,6 +237,23 @@ public class ActivationRange
}
// Paper end - Configurable marker ticking
ActivationRange.activateEntity(entity);
Expand All @@ -394,10 +394,7 @@ index 0b2f2fbe462ed628ef3d640824d4162e79279089..e469b2782932275caa4bb760eefff829
+ entity.activatedPriorityReset = true;
+ entity.activatedPriority = org.dreeam.leaf.config.modules.opt.DynamicActivationofBrain.maximumActivationPrio;
+ }
+ net.minecraft.world.phys.Vec3 playerVec = player.position();
+ net.minecraft.world.phys.Vec3 entityVec = entity.position();
+ double diffX = playerVec.x - entityVec.x, diffY = playerVec.y - entityVec.y, diffZ = playerVec.z - entityVec.z;
+ int squaredDistance = (int) (diffX * diffX + diffY * diffY + diffZ * diffZ);
+ int squaredDistance = (int) player.distanceToSqr(entity);
+ entity.activatedPriority = squaredDistance > org.dreeam.leaf.config.modules.opt.DynamicActivationofBrain.startDistanceSquared ?
+ Math.max(1, Math.min(squaredDistance >> org.dreeam.leaf.config.modules.opt.DynamicActivationofBrain.activationDistanceMod, entity.activatedPriority)) :
+ 1;
Expand All @@ -409,7 +406,7 @@ index 0b2f2fbe462ed628ef3d640824d4162e79279089..e469b2782932275caa4bb760eefff829
}
// Paper end
}
@@ -254,12 +273,12 @@ public class ActivationRange
@@ -254,12 +270,12 @@ public class ActivationRange
if ( MinecraftServer.currentTick > entity.activatedTick )
{
if ( entity.defaultActivationState )
Expand Down
4 changes: 2 additions & 2 deletions patches/server/0011-Purpur-Server-Changes.patch
Original file line number Diff line number Diff line change
Expand Up @@ -24824,7 +24824,7 @@ index 0000000000000000000000000000000000000000..129acb8ad139decc6b1c023cb10bc32d
+ // Paper end - lifecycle events
+}
diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java
index 17fe0c5661671a5d7ba7627c41f8e3f6662878b7..5e00b8bfd99680b6a6d48d85e272f62bb61811eb 100644
index e6ef67125f59195104b013de8f69669a8e059a1f..275ccdf6cd322430ae29b606fdf6af510a62c480 100644
--- a/src/main/java/org/spigotmc/ActivationRange.java
+++ b/src/main/java/org/spigotmc/ActivationRange.java
@@ -215,6 +215,8 @@ public class ActivationRange
Expand All @@ -24836,7 +24836,7 @@ index 17fe0c5661671a5d7ba7627c41f8e3f6662878b7..5e00b8bfd99680b6a6d48d85e272f62b
// Paper start
int worldHeight = world.getHeight();
ActivationRange.maxBB = player.getBoundingBox().inflate( maxRange, worldHeight, maxRange );
@@ -413,6 +415,7 @@ public class ActivationRange
@@ -410,6 +412,7 @@ public class ActivationRange
*/
public static boolean checkIfActive(Entity entity)
{
Expand Down
4 changes: 2 additions & 2 deletions patches/server/0015-Remove-Timings.patch
Original file line number Diff line number Diff line change
Expand Up @@ -1838,7 +1838,7 @@ index 579c2e69d8f6ce8398eb1297d1d1ead98c9068a5..00000000000000000000000000000000
-
-}
diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java
index 5e00b8bfd99680b6a6d48d85e272f62bb61811eb..ebcecc78c475ba9436b894be971c3771ffc80caf 100644
index 275ccdf6cd322430ae29b606fdf6af510a62c480..4894b7b006e20a383e509b69b6642593478d78c1 100644
--- a/src/main/java/org/spigotmc/ActivationRange.java
+++ b/src/main/java/org/spigotmc/ActivationRange.java
@@ -34,7 +34,6 @@ import net.minecraft.world.entity.projectile.FireworkRocketEntity;
Expand All @@ -1857,7 +1857,7 @@ index 5e00b8bfd99680b6a6d48d85e272f62bb61811eb..ebcecc78c475ba9436b894be971c3771
final int miscActivationRange = world.spigotConfig.miscActivationRange;
final int raiderActivationRange = world.spigotConfig.raiderActivationRange;
final int animalActivationRange = world.spigotConfig.animalActivationRange;
@@ -262,7 +260,6 @@ public class ActivationRange
@@ -259,7 +257,6 @@ public class ActivationRange
}
// Paper end
}
Expand Down
62 changes: 62 additions & 0 deletions patches/server/0099-Remove-stream-in-PlayerSensor.patch
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);
}
}
31 changes: 31 additions & 0 deletions patches/server/0100-Remove-stream-in-GolemSensor.patch
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.

This file was deleted.

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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Original license: LGPL v3
Original project: https://github.com/CaffeineMC/lithium-fabric

diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index 241e2598973939b7d19768cd9ca7cd8f3818c053..7ce6ce7212b942a8d6b64c98fab8fe060d813c61 100644
index 241e2598973939b7d19768cd9ca7cd8f3818c053..ac3ef40aabf08081efd28dd30c0c856d1858c44f 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -2769,6 +2769,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
Expand Down
File renamed without changes.
51 changes: 51 additions & 0 deletions patches/server/0148-SparklyPaper-Throttle-hopper-when-full.patch
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.""");
+ }
+}

0 comments on commit 848fb2d

Please sign in to comment.