mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-02 04:56:50 +01:00
8c5b837e05
Firstly, the old methods all routed to the CompletableFuture method. However, the CF method could not guarantee that if the caller was off-main that the future would be "completed" on-main. Since the callback methods used the CF one, this meant that the callback methods did not guarantee that the callbacks were to be called on the main thread. Now, all methods route to getChunkAtAsync(x, z, gen, urgent, cb) so that the methods with the callback are guaranteed to invoke the callback on the main thread. The CF behavior remains unchanged; it may still appear to complete on main if invoked off-main. Secondly, remove the scheduleOnMain invocation in the async chunk completion. This unnecessarily delays the callback by 1 tick. Thirdly, add getChunksAtAsync(minX, minZ, maxX, maxZ, ...) which will load chunks within an area. This method is provided as a helper as keeping all chunks loaded within an area can be complicated to implement for plugins (due to the lacking ticket API), and is already implemented internally anyways. Fourthly, remove the ticket addition that occured with getChunkAt and getChunkAtAsync. The ticket addition may delay the unloading of the chunk unnecessarily. It also fixes a very rare timing bug where the future/callback would be completed after the chunk unloads.
217 lines
12 KiB
Diff
217 lines
12 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: JRoy <joshroy126@gmail.com>
|
|
Date: Wed, 1 Jul 2020 18:01:49 -0400
|
|
Subject: [PATCH] Remove streams from hot code
|
|
|
|
Feature patch
|
|
|
|
Co-authored-by: Bjarne Koll <lynxplay101@gmail.com>
|
|
Co-authored-by: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java b/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java
|
|
index 6a270c9adb044a6e0b1c8e09b9106d51989fd761..d4581127366736c54f74e4ef7479236b18fb487d 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java
|
|
@@ -57,7 +57,7 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E>
|
|
if (this.hasRequiredMemories(entity)) {
|
|
this.status = Behavior.Status.RUNNING;
|
|
this.orderPolicy.apply(this.behaviors);
|
|
- this.runningPolicy.apply(this.behaviors.stream(), world, entity, time);
|
|
+ this.runningPolicy.apply(this.behaviors, world, entity, time); // Paper - Perf: Remove streams from hot code
|
|
return true;
|
|
} else {
|
|
return false;
|
|
@@ -66,7 +66,13 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E>
|
|
|
|
@Override
|
|
public final void tickOrStop(ServerLevel world, E entity, long time) {
|
|
- this.behaviors.stream().filter(task -> task.getStatus() == Behavior.Status.RUNNING).forEach(task -> task.tickOrStop(world, entity, time));
|
|
+ // Paper start - Perf: Remove streams from hot code
|
|
+ for (final BehaviorControl<? super E> task : this.behaviors) {
|
|
+ if (task.getStatus() == Behavior.Status.RUNNING) {
|
|
+ task.tickOrStop(world, entity, time);
|
|
+ }
|
|
+ }
|
|
+ // Paper end - Perf: Remove streams from hot code
|
|
if (this.behaviors.stream().noneMatch(task -> task.getStatus() == Behavior.Status.RUNNING)) {
|
|
this.doStop(world, entity, time);
|
|
}
|
|
@@ -75,8 +81,16 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E>
|
|
@Override
|
|
public final void doStop(ServerLevel world, E entity, long time) {
|
|
this.status = Behavior.Status.STOPPED;
|
|
- this.behaviors.stream().filter(task -> task.getStatus() == Behavior.Status.RUNNING).forEach(task -> task.doStop(world, entity, time));
|
|
- this.exitErasedMemories.forEach(entity.getBrain()::eraseMemory);
|
|
+ // Paper start - Perf: Remove streams from hot code
|
|
+ for (final BehaviorControl<? super E> task : this.behaviors) {
|
|
+ if (task.getStatus() == Behavior.Status.RUNNING) {
|
|
+ task.doStop(world, entity, time);
|
|
+ }
|
|
+ }
|
|
+ for (final MemoryModuleType<?> exitErasedMemory : this.exitErasedMemories) {
|
|
+ entity.getBrain().eraseMemory(exitErasedMemory);
|
|
+ }
|
|
+ // Paper end - Perf: Remove streams from hot code
|
|
}
|
|
|
|
@Override
|
|
@@ -111,18 +125,30 @@ public class GateBehavior<E extends LivingEntity> implements BehaviorControl<E>
|
|
|
|
public static enum RunningPolicy {
|
|
RUN_ONE {
|
|
+ // Paper start - Perf: Remove streams from hot code
|
|
@Override
|
|
- public <E extends LivingEntity> void apply(Stream<BehaviorControl<? super E>> tasks, ServerLevel world, E entity, long time) {
|
|
- tasks.filter(task -> task.getStatus() == Behavior.Status.STOPPED).filter(task -> task.tryStart(world, entity, time)).findFirst();
|
|
+ public <E extends LivingEntity> void apply(ShufflingList<BehaviorControl<? super E>> tasks, ServerLevel world, E entity, long time) {
|
|
+ for (final BehaviorControl<? super E> task : tasks) {
|
|
+ if (task.getStatus() == Behavior.Status.STOPPED && task.tryStart(world, entity, time)) {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ // Paper end - Perf: Remove streams from hot code
|
|
}
|
|
},
|
|
TRY_ALL {
|
|
+ // Paper start - Perf: Remove streams from hot code
|
|
@Override
|
|
- public <E extends LivingEntity> void apply(Stream<BehaviorControl<? super E>> tasks, ServerLevel world, E entity, long time) {
|
|
- tasks.filter(task -> task.getStatus() == Behavior.Status.STOPPED).forEach(task -> task.tryStart(world, entity, time));
|
|
+ public <E extends LivingEntity> void apply(ShufflingList<BehaviorControl<? super E>> tasks, ServerLevel world, E entity, long time) {
|
|
+ for (final BehaviorControl<? super E> task : tasks) {
|
|
+ if (task.getStatus() == Behavior.Status.STOPPED) {
|
|
+ task.tryStart(world, entity, time);
|
|
+ }
|
|
+ }
|
|
+ // Paper end - Perf: Remove streams from hot code
|
|
}
|
|
};
|
|
|
|
- public abstract <E extends LivingEntity> void apply(Stream<BehaviorControl<? super E>> tasks, ServerLevel world, E entity, long time);
|
|
+ public abstract <E extends LivingEntity> void apply(ShufflingList<BehaviorControl<? super E>> tasks, ServerLevel world, E entity, long time); // Paper - Perf: Remove streams from hot code
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java b/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java
|
|
index aa32804bc9affe9a615d3ffaa513f6f09aab3f32..c7f012674361a323c1efeca4660cd3f46d308ee1 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java
|
|
@@ -59,8 +59,22 @@ public class GossipContainer {
|
|
return this.gossips.entrySet().stream().flatMap(entry -> entry.getValue().unpack(entry.getKey()));
|
|
}
|
|
|
|
+ // Paper start - Perf: Remove streams from hot code
|
|
+ private List<GossipContainer.GossipEntry> decompress() {
|
|
+ List<GossipContainer.GossipEntry> list = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>();
|
|
+ for (Map.Entry<UUID, GossipContainer.EntityGossips> entry : this.gossips.entrySet()) {
|
|
+ for (GossipContainer.GossipEntry cur : entry.getValue().decompress(entry.getKey())) {
|
|
+ if (cur.weightedValue() != 0) {
|
|
+ list.add(cur);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return list;
|
|
+ }
|
|
+ // Paper end - Perf: Remove streams from hot code
|
|
+
|
|
private Collection<GossipContainer.GossipEntry> selectGossipsForTransfer(RandomSource random, int count) {
|
|
- List<GossipContainer.GossipEntry> list = this.unpack().toList();
|
|
+ List<GossipContainer.GossipEntry> list = this.decompress(); // Paper - Perf: Remove streams from hot code
|
|
if (list.isEmpty()) {
|
|
return Collections.emptyList();
|
|
} else {
|
|
@@ -145,7 +159,7 @@ public class GossipContainer {
|
|
|
|
public <T> T store(DynamicOps<T> ops) {
|
|
return GossipContainer.GossipEntry.LIST_CODEC
|
|
- .encodeStart(ops, this.unpack().toList())
|
|
+ .encodeStart(ops, this.decompress()) // Paper - Perf: Remove streams from hot code
|
|
.resultOrPartial(error -> LOGGER.warn("Failed to serialize gossips: {}", error))
|
|
.orElseGet(ops::emptyList);
|
|
}
|
|
@@ -172,12 +186,23 @@ public class GossipContainer {
|
|
final Object2IntMap<GossipType> entries = new Object2IntOpenHashMap<>();
|
|
|
|
public int weightedValue(Predicate<GossipType> gossipTypeFilter) {
|
|
- return this.entries
|
|
- .object2IntEntrySet()
|
|
- .stream()
|
|
- .filter(entry -> gossipTypeFilter.test(entry.getKey()))
|
|
- .mapToInt(entry -> entry.getIntValue() * entry.getKey().weight)
|
|
- .sum();
|
|
+ // Paper start - Perf: Remove streams from hot code
|
|
+ int weight = 0;
|
|
+ for (Object2IntMap.Entry<GossipType> entry : entries.object2IntEntrySet()) {
|
|
+ if (gossipTypeFilter.test(entry.getKey())) {
|
|
+ weight += entry.getIntValue() * entry.getKey().weight;
|
|
+ }
|
|
+ }
|
|
+ return weight;
|
|
+ }
|
|
+
|
|
+ public List<GossipContainer.GossipEntry> decompress(UUID uuid) {
|
|
+ List<GossipContainer.GossipEntry> list = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>();
|
|
+ for (Object2IntMap.Entry<GossipType> entry : entries.object2IntEntrySet()) {
|
|
+ list.add(new GossipContainer.GossipEntry(uuid, entry.getKey(), entry.getIntValue()));
|
|
+ }
|
|
+ return list;
|
|
+ // Paper end - Perf: Remove streams from hot code
|
|
}
|
|
|
|
public Stream<GossipContainer.GossipEntry> unpack(UUID target) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java
|
|
index ba6fd4df02d3097d3ea4273092e27592893de445..de768cf877b899f640b5e13ea9ea9c6410a2e79e 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestItemSensor.java
|
|
@@ -24,13 +24,17 @@ public class NearestItemSensor extends Sensor<Mob> {
|
|
@Override
|
|
protected void doTick(ServerLevel world, Mob entity) {
|
|
Brain<?> brain = entity.getBrain();
|
|
- List<ItemEntity> list = world.getEntitiesOfClass(ItemEntity.class, entity.getBoundingBox().inflate(32.0, 16.0, 32.0), itemEntity -> true);
|
|
+ List<ItemEntity> list = world.getEntitiesOfClass(ItemEntity.class, entity.getBoundingBox().inflate(32.0, 16.0, 32.0), itemEntity -> itemEntity.closerThan(entity, MAX_DISTANCE_TO_WANTED_ITEM) && entity.wantsToPickUp(world, itemEntity.getItem())); // Paper - Perf: Move predicate into getEntities
|
|
list.sort(Comparator.comparingDouble(entity::distanceToSqr));
|
|
- Optional<ItemEntity> optional = list.stream()
|
|
- .filter(itemEntity -> entity.wantsToPickUp(world, itemEntity.getItem()))
|
|
- .filter(itemEntityx -> itemEntityx.closerThan(entity, 32.0))
|
|
- .filter(entity::hasLineOfSight)
|
|
- .findFirst();
|
|
- brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM, optional);
|
|
+ // Paper start - Perf: remove streams from hot code
|
|
+ ItemEntity nearest = null;
|
|
+ for (ItemEntity entityItem : list) {
|
|
+ if (entity.hasLineOfSight(entityItem)) { // Paper - Perf: Move predicate into getEntities
|
|
+ nearest = entityItem;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ brain.setMemory(MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM, Optional.ofNullable(nearest));
|
|
+ // Paper end - Perf: remove streams from hot code
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/levelgen/Beardifier.java b/src/main/java/net/minecraft/world/level/levelgen/Beardifier.java
|
|
index 04552e2cf3de2dc41dfe7e7a2e88b200eaaf280b..ca93a97256350789ca56f910862c9d717ca7670b 100644
|
|
--- a/src/main/java/net/minecraft/world/level/levelgen/Beardifier.java
|
|
+++ b/src/main/java/net/minecraft/world/level/levelgen/Beardifier.java
|
|
@@ -35,9 +35,10 @@ public class Beardifier implements DensityFunctions.BeardifierOrMarker {
|
|
int j = pos.getMinBlockZ();
|
|
ObjectList<Beardifier.Rigid> objectList = new ObjectArrayList<>(10);
|
|
ObjectList<JigsawJunction> objectList2 = new ObjectArrayList<>(32);
|
|
- world.startsForStructure(pos, structure -> structure.terrainAdaptation() != TerrainAdjustment.NONE)
|
|
- .forEach(
|
|
- start -> {
|
|
+ // Paper start - Perf: Remove streams from hot code
|
|
+ for (net.minecraft.world.level.levelgen.structure.StructureStart start : world.startsForStructure(pos, (structure) -> {
|
|
+ return structure.terrainAdaptation() != TerrainAdjustment.NONE;
|
|
+ })) { // Paper end - Perf: Remove streams from hot code
|
|
TerrainAdjustment terrainAdjustment = start.getStructure().terrainAdaptation();
|
|
|
|
for (StructurePiece structurePiece : start.getPieces()) {
|
|
@@ -65,8 +66,7 @@ public class Beardifier implements DensityFunctions.BeardifierOrMarker {
|
|
}
|
|
}
|
|
}
|
|
- }
|
|
- );
|
|
+ } // Paper - Perf: Remove streams from hot code
|
|
return new Beardifier(objectList.iterator(), objectList2.iterator());
|
|
}
|
|
|