a bunch more patches

This commit is contained in:
Jake 2021-11-23 21:44:21 -08:00 committed by MiniDigger | Martin
parent 2b29fe37a2
commit 0f23d73d1a
19 changed files with 96 additions and 95 deletions

View file

@ -1,33 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sat, 15 Jun 2019 10:28:25 -0700
Subject: [PATCH] Show blockstate location if we failed to read it
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
index aaddbaecc25af87c863fe51098eb322fd5702104..d2ff7e373476aaab0d4d08977c9d9f274fff67bf 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
@@ -18,6 +18,7 @@ public abstract class CraftBlockEntityState<T extends BlockEntity> extends Craft
this.tileEntity = tileEntity;
+ try { // Paper - show location on failure
// Paper start
this.snapshotDisabled = DISABLE_SNAPSHOT;
if (DISABLE_SNAPSHOT) {
@@ -30,6 +31,14 @@ public abstract class CraftBlockEntityState<T extends BlockEntity> extends Craft
this.load(this.snapshot);
}
// Paper end
+ // Paper start - show location on failure
+ } catch (Throwable thr) {
+ if (thr instanceof ThreadDeath) {
+ throw (ThreadDeath)thr;
+ }
+ throw new RuntimeException("Failed to read BlockState at: world: " + this.getWorld().getName() + " location: (" + this.getX() + ", " + this.getY() + ", " + this.getZ() + ")", thr);
+ }
+ // Paper end
}
public void refreshSnapshot() {

View file

@ -1,56 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sun, 24 Mar 2019 01:01:32 -0400
Subject: [PATCH] Only count Natural Spawned mobs towards natural spawn mob
limit
This resolves the super common complaint about mobs not spawning.
This was ultimately a flaw in the vanilla count algorithim that allows
spawners and other misc mobs to count against the mob limit, which are
not bounded, and can prevent the entire world from spawning new.
I believe Bukkits changes around persistence may of actually made it
worse than vanilla.
This should fully solve all of the issues around it so that only natural
influences natural spawns.
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
index 70fc301b25fb1e2271255b3d3b6facaf0cb87bad..6295242e1926ae3f03c304f3372dcca16a84bf3f 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -442,4 +442,15 @@ public class PaperWorldConfig {
private void preventMovingIntoUnloadedChunks() {
preventMovingIntoUnloadedChunks = getBoolean("prevent-moving-into-unloaded-chunks", false);
}
+
+ public boolean countAllMobsForSpawning = false;
+ private void countAllMobsForSpawning() {
+ countAllMobsForSpawning = getBoolean("count-all-mobs-for-spawning", false);
+ if (countAllMobsForSpawning) {
+ log("Counting all mobs for spawning. Mob farms may reduce natural spawns elsewhere in world.");
+ } else {
+ log("Using improved mob spawn limits (Only Natural Spawns impact spawn limits for more natural spawns)");
+ }
+ }
}
+
diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
index 7891ee828b030814034e6e1def7938a31fbe4fdd..461b64232c0f04e17e168f1e7f7857ee555200fc 100644
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
@@ -85,6 +85,13 @@ public final class NaturalSpawner {
MobCategory enumcreaturetype = entity.getType().getCategory();
if (enumcreaturetype != MobCategory.MISC) {
+ // Paper start - Only count natural spawns
+ if (!entity.level.paperConfig.countAllMobsForSpawning &&
+ !(entity.spawnReason == org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL ||
+ entity.spawnReason == org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CHUNK_GEN)) {
+ continue;
+ }
+ // Paper end
BlockPos blockposition = entity.blockPosition();
long j = ChunkPos.asLong(SectionPos.blockToSectionCoord(blockposition.getX()), SectionPos.blockToSectionCoord(blockposition.getZ()));

View file

@ -1,54 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Lucavon <lucavonlp@gmail.com>
Date: Tue, 23 Jul 2019 20:29:20 -0500
Subject: [PATCH] Configurable projectile relative velocity
This patch adds an option "disable relative projectile velocity", which, when
nabled, will cause projectiles to ignore the shooter's current velocity,
like they did in Minecraft 1.8 and prior.
If a player is falling, for example, their shooting range will be drastically
reduced, as a downwards velocity is applied to the projectile. This prevents
players from saving themselves from falling off floating islands, for example,
as a thrown ender pearl will not make it back to the island, while it would
have in 1.8.
While this could easily be done with plugins, too, there are multiple problems:
P1) If multiple plugins cancel the velocity by subtracting the shooter's velocity
from the projectile's velocity, the projectile's velocity would be different.
As there's no way to detect whether the projectile's velocity has already been
adjusted to ignore the player's velocity, plugins can't not do it if it's not
necessary.
P2) I've noticed some inconsistencies, e.g. weird velocity when shooting while
using an elytra. Checking for those inconsistencies is possible, but not as
efficient as just not applying the velocity in the first place.
P3) Solutions for 1) and especially 2) might not be future-proof, while this
server-internal fix makes this change future-proof.
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
index 6295242e1926ae3f03c304f3372dcca16a84bf3f..d9437b3ef3919bff5d2eebd8b5e016ddb7a0e793 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -452,5 +452,10 @@ public class PaperWorldConfig {
log("Using improved mob spawn limits (Only Natural Spawns impact spawn limits for more natural spawns)");
}
}
+
+ public boolean disableRelativeProjectileVelocity;
+ private void disableRelativeProjectileVelocity() {
+ disableRelativeProjectileVelocity = getBoolean("game-mechanics.disable-relative-projectile-velocity", false);
+ }
}
diff --git a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java
index 458a70f6e76eba707b890c78cbfd4f967eaf8a1b..6339203bda5e569d5df241dd589eb36e7233704b 100644
--- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java
+++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java
@@ -161,7 +161,7 @@ public abstract class Projectile extends Entity {
this.shoot((double) f5, (double) f6, (double) f7, modifierZ, modifierXYZ);
Vec3 vec3d = user.getDeltaMovement();
- this.setDeltaMovement(this.getDeltaMovement().add(vec3d.x, user.isOnGround() ? 0.0D : vec3d.y, vec3d.z));
+ if (!user.level.paperConfig.disableRelativeProjectileVelocity) this.setDeltaMovement(this.getDeltaMovement().add(vec3d.x, user.isOnGround() ? 0.0D : vec3d.y, vec3d.z)); // Paper - allow disabling relative velocity
}
// CraftBukkit start - call projectile hit event

View file

@ -1,19 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: kickash32 <kickash32@gmail.com>
Date: Tue, 30 Jul 2019 03:17:16 +0500
Subject: [PATCH] offset item frame ticking
diff --git a/src/main/java/net/minecraft/world/entity/decoration/HangingEntity.java b/src/main/java/net/minecraft/world/entity/decoration/HangingEntity.java
index acea1c03afb5b3e522edb072fd1c3f9b5c3edccc..ca9decf85dd1af0baf0d34a48aa67cbb9f4eb586 100644
--- a/src/main/java/net/minecraft/world/entity/decoration/HangingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/decoration/HangingEntity.java
@@ -35,7 +35,7 @@ public abstract class HangingEntity extends Entity {
protected static final Predicate<Entity> HANGING_ENTITY = (entity) -> {
return entity instanceof HangingEntity;
};
- private int checkInterval;
+ private int checkInterval; { this.checkInterval = this.getId() % this.level.spigotConfig.hangingTickFrequency; } // Paper
public BlockPos pos;
protected Direction direction;

View file

@ -1,42 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Paul Sauve <paul@burngames.net>
Date: Sun, 14 Jul 2019 21:05:03 -0500
Subject: [PATCH] Do less work if we have a custom Bukkit generator
If the Bukkit generator already has a spawn, use it immediately instead
of spending time generating one that we won't use
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index cae3c20eba546dcf42d035e9a5998302df350d4b..7ef0c9c8edf1202d0e20a505b18f9d36bdc20139 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -691,12 +691,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
worldProperties.setSpawn(BlockPos.ZERO.above(80), 0.0F);
} else {
ChunkGenerator chunkgenerator = world.getChunkSource().getGenerator();
- BiomeSource worldchunkmanager = chunkgenerator.getBiomeSource();
- Random random = new Random(world.getSeed());
- BlockPos blockposition = worldchunkmanager.findBiomeHorizontal(0, world.getSeaLevel(), 0, 256, (biomebase) -> {
- return biomebase.getMobSettings().playerSpawnFriendly();
- }, random);
- ChunkPos chunkcoordintpair = blockposition == null ? new ChunkPos(0, 0) : new ChunkPos(blockposition);
+ // Paper start - moved down
// CraftBukkit start
if (world.generator != null) {
Random rand = new Random(world.getSeed());
@@ -712,6 +707,15 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
}
// CraftBukkit end
+ // Paper start - if the generator created a spawn for us, then there is no need for us to also create a spawn -
+ // only do it if the generator did not
+ BiomeSource worldchunkmanager = chunkgenerator.getBiomeSource();
+ Random random = new Random(world.getSeed());
+ BlockPos blockposition = worldchunkmanager.findBiomeHorizontal(0, world.getSeaLevel(), 0, 256, (biomebase) -> {
+ return biomebase.getMobSettings().playerSpawnFriendly();
+ }, random);
+ ChunkPos chunkcoordintpair = blockposition == null ? new ChunkPos(0, 0) : new ChunkPos(blockposition);
+ // Paper end
if (blockposition == null) {
MinecraftServer.LOGGER.warn("Unable to find spawn biome");

View file

@ -1,25 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Tue, 13 Aug 2019 06:35:17 -0700
Subject: [PATCH] Fix MC-158900
The problem was we were checking isExpired() on the entry, but if it
was expired at that point, then it would be null.
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index 7dd874c6aa1268665d4a64dfc41013c304991273..993f31b9106b5e20c62bfd405daa9fe55046840f 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -607,8 +607,10 @@ public abstract class PlayerList {
Player player = entity.getBukkitEntity();
PlayerLoginEvent event = new PlayerLoginEvent(player, hostname, ((java.net.InetSocketAddress) socketaddress).getAddress(), ((java.net.InetSocketAddress) loginlistener.connection.getRawAddress()).getAddress());
- if (this.getBans().isBanned(gameprofile) && !this.getBans().get(gameprofile).hasExpired()) {
- UserBanListEntry gameprofilebanentry = (UserBanListEntry) this.bans.get(gameprofile);
+ // Paper start - Fix MC-158900
+ UserBanListEntry gameprofilebanentry;
+ if (getBans().isBanned(gameprofile) && (gameprofilebanentry = getBans().get(gameprofile)) != null) {
+ // Paper end
chatmessage = new TranslatableComponent("multiplayer.disconnect.banned.reason", new Object[]{gameprofilebanentry.getReason()});
if (gameprofilebanentry.getExpires() != null) {

View file

@ -1,52 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: kickash32 <kickash32@gmail.com>
Date: Mon, 19 Aug 2019 19:42:35 +0500
Subject: [PATCH] Prevent consuming the wrong itemstack
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index 472b8db386dfb580734999900562d358e5ea146a..55ac5e8cad4c7eee3d8b165698200e9afcd44594 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -3554,15 +3554,18 @@ public abstract class LivingEntity extends Entity {
this.entityData.set(LivingEntity.DATA_LIVING_ENTITY_FLAGS, (byte) j);
}
- public void startUsingItem(InteractionHand hand) {
- ItemStack itemstack = this.getItemInHand(hand);
+ // Paper start -- OBFHELPER and forwarder to method with forceUpdate parameter
+ public void startUsingItem(InteractionHand hand) { this.updateActiveItem(hand, false); }
+ public void updateActiveItem(InteractionHand enumhand, boolean forceUpdate) {
+ // Paper end
+ ItemStack itemstack = this.getItemInHand(enumhand);
- if (!itemstack.isEmpty() && !this.isUsingItem()) {
+ if (!itemstack.isEmpty() && !this.isUsingItem() || forceUpdate) { // Paper use override flag
this.useItem = itemstack;
this.useItemRemaining = itemstack.getUseDuration();
if (!this.level.isClientSide) {
this.setLivingEntityFlag(1, true);
- this.setLivingEntityFlag(2, hand == InteractionHand.OFF_HAND);
+ this.setLivingEntityFlag(2, enumhand == InteractionHand.OFF_HAND);
}
}
@@ -3635,6 +3638,7 @@ public abstract class LivingEntity extends Entity {
this.releaseUsingItem();
} else {
if (!this.useItem.isEmpty() && this.isUsingItem()) {
+ this.updateActiveItem(this.getUsedItemHand(), true); // Paper
this.triggerItemUseEffects(this.useItem, 16);
// CraftBukkit start - fire PlayerItemConsumeEvent
ItemStack itemstack;
@@ -3669,8 +3673,8 @@ public abstract class LivingEntity extends Entity {
}
this.stopUsingItem();
- // Paper start - if the replacement is anything but the default, update the client inventory
- if (this instanceof ServerPlayer && !com.google.common.base.Objects.equal(defaultReplacement, itemstack)) {
+ // Paper start
+ if (this instanceof ServerPlayer) {
((ServerPlayer) this).getBukkitEntity().updateInventory();
}
// Paper end

View file

@ -1,155 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Byteflux <byte@byteflux.net>
Date: Wed, 2 Mar 2016 02:17:54 -0600
Subject: [PATCH] Generator Settings
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
index d9437b3ef3919bff5d2eebd8b5e016ddb7a0e793..d3da5175ce1075511229ea52f1237898bcae9a11 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -457,5 +457,10 @@ public class PaperWorldConfig {
private void disableRelativeProjectileVelocity() {
disableRelativeProjectileVelocity = getBoolean("game-mechanics.disable-relative-projectile-velocity", false);
}
+
+ public boolean generateFlatBedrock;
+ private void generatorSettings() {
+ generateFlatBedrock = getBoolean("generator-settings.flat-bedrock", false);
+ }
}
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index fcd25c4476e2afcf4e676dca7a8abad9cc112bef..41253d8adf85cf318fcb1cee36ac1763f440fca6 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -726,7 +726,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
this.markPositionReplaceable(pos);
- return Either.left(new ProtoChunk(pos, UpgradeData.EMPTY, this.level));
+ return Either.left(new ProtoChunk(pos, UpgradeData.EMPTY, this.level, this.level)); // Paper - add level
// Paper start - Async chunk io
};
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> ret = new CompletableFuture<>();
diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
index 974ab04b08bbd3c27a394b37c1af112be5f28f43..149ac5ec368b53a9a5e9208bd49a3c9453625d9c 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
@@ -29,6 +29,17 @@ public interface ChunkAccess extends BlockGetter, FeatureAccess {
return GameEventDispatcher.NOOP;
}
+ // Paper start
+ default boolean generateFlatBedrock() {
+ if (this.getLevel() != null) {
+ return this.getLevel().paperConfig.generateFlatBedrock;
+ }
+ return false;
+ }
+
+ net.minecraft.world.level.Level getLevel();
+ // Paper end
+
BlockState getType(final int x, final int y, final int z); // Paper
@Nullable
BlockState setBlockState(BlockPos pos, BlockState state, boolean moved);
diff --git a/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java b/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java
index 452b513e8b89d865a396066adaf4feb1140e1c62..8245c5834ec69beb8e3b95fb3900601009a9273f 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java
@@ -25,7 +25,7 @@ public class ImposterProtoChunk extends ProtoChunk {
private final LevelChunk wrapped;
public ImposterProtoChunk(LevelChunk wrapped) {
- super(wrapped.getPos(), UpgradeData.EMPTY, wrapped);
+ super(wrapped.getPos(), UpgradeData.EMPTY, wrapped, wrapped.level); // Paper - add level
this.wrapped = wrapped;
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java b/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java
index 873fea54aecca411b6dee1ed3566f93c4fb9670f..7dc3d806a680150c6a2fffa1436fd63bbdc31eb3 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java
@@ -63,16 +63,45 @@ public class ProtoChunk implements ChunkAccess {
private long inhabitedTime;
private final Map<GenerationStep.Carving, BitSet> carvingMasks = new Object2ObjectArrayMap<>();
private volatile boolean isLightCorrect;
+ // Paper start - Add level
+ final net.minecraft.world.level.Level level;
+ @Override
+ public net.minecraft.world.level.Level getLevel() {
+ return this.level;
+ }
+ // Paper end
+ private static boolean PRINTED_OUTDATED_CTOR_MSG = false; // Paper - Add level
+ @Deprecated // Paper start - add level
public ProtoChunk(ChunkPos pos, UpgradeData upgradeData, LevelHeightAccessor world) {
+ // Paper start
+ this(pos, upgradeData, world, null);
+ if (!PRINTED_OUTDATED_CTOR_MSG) {
+ new IllegalArgumentException("Must use ProtoChunk constructor with the ServerLevel parameter").printStackTrace();
+ PRINTED_OUTDATED_CTOR_MSG = true;
+ }
+ }
+ public ProtoChunk(ChunkPos pos, UpgradeData upgradeData, LevelHeightAccessor world, net.minecraft.server.level.ServerLevel level) {
+ // Paper end
this(pos, upgradeData, (LevelChunkSection[])null, new ProtoTickList<>((block) -> {
return block == null || block.defaultBlockState().isAir();
}, pos, world), new ProtoTickList<>((fluid) -> {
return fluid == null || fluid == Fluids.EMPTY;
- }, pos, world), world);
+ }, pos, world), world, level); // Paper - add level
}
+ @Deprecated // Paper start - add level
public ProtoChunk(ChunkPos pos, UpgradeData upgradeData, @Nullable LevelChunkSection[] levelChunkSections, ProtoTickList<Block> blockTickScheduler, ProtoTickList<Fluid> fluidTickScheduler, LevelHeightAccessor world) {
+ // Paper start
+ this(pos, upgradeData, levelChunkSections, blockTickScheduler, fluidTickScheduler, world, null);
+ if (!PRINTED_OUTDATED_CTOR_MSG) {
+ new IllegalArgumentException("Must use ProtoChunk constructor with the ServerLevel parameter").printStackTrace();
+ PRINTED_OUTDATED_CTOR_MSG = true;
+ }
+ }
+ public ProtoChunk(ChunkPos pos, UpgradeData upgradeData, @Nullable LevelChunkSection[] levelChunkSections, ProtoTickList<Block> blockTickScheduler, ProtoTickList<Fluid> fluidTickScheduler, LevelHeightAccessor world, net.minecraft.server.level.ServerLevel level) {
+ this.level = level;
+ // Paper end
this.chunkPos = pos;
this.upgradeData = upgradeData;
this.blockTicks = blockTickScheduler;
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
index 1eaedda19b05e1ec429fa505c72c9e2743eb32b7..83fa00de1a7cb690c763cec9c8d4b3fcd44e7c74 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
@@ -208,7 +208,7 @@ public class ChunkSerializer {
// CraftBukkit end
});
} else {
- ProtoChunk protochunk = new ProtoChunk(pos, chunkconverter, achunksection, protochunkticklist, protochunkticklist1, world);
+ ProtoChunk protochunk = new ProtoChunk(pos, chunkconverter, achunksection, protochunkticklist, protochunkticklist1, world, world); // Paper - add level
protochunk.setBiomes(biomestorage);
object = protochunk;
diff --git a/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java b/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
index 32363a57a4b4f6912f03732ce6a0bb005449f525..5cc63122b8e2c955b2d756000c1677d51e8d8629 100644
--- a/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
+++ b/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
@@ -323,7 +323,7 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator {
if (flag1) {
for (l1 = 0; l1 < 5; ++l1) {
- if (l1 <= random.nextInt(5)) {
+ if (l1 <= (chunk.generateFlatBedrock() ? 0 : random.nextInt(5))) { // Paper - Configurable flat bedrock roof
chunk.setBlockState(blockposition_mutableblockposition.set(blockposition.getX(), i1 - l1, blockposition.getZ()), Blocks.BEDROCK.defaultBlockState(), false);
}
}
@@ -331,7 +331,7 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator {
if (flag2) {
for (l1 = 4; l1 >= 0; --l1) {
- if (l1 <= random.nextInt(5)) {
+ if (l1 <= (chunk.generateFlatBedrock() ? 0 : random.nextInt(5))) { // Paper - Configurable flat bedrock floor{
chunk.setBlockState(blockposition_mutableblockposition.set(blockposition.getX(), l + l1, blockposition.getZ()), Blocks.BEDROCK.defaultBlockState(), false);
}
}

View file

@ -1,23 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Tue, 24 Sep 2019 16:03:00 -0700
Subject: [PATCH] Fix MC-161754
Fixes https://github.com/PaperMC/Paper/issues/2580
We can use an entity valid check since this method is invoked for
each inventory iteraction (thanks to CB) and on player tick (vanilla).
diff --git a/src/main/java/net/minecraft/world/inventory/HorseInventoryMenu.java b/src/main/java/net/minecraft/world/inventory/HorseInventoryMenu.java
index f3e17f00f09feb4df4274b09bc7fa3c3022a5fbb..ffc596d8db77aaeed43f79b895bf4a1c7baeeab2 100644
--- a/src/main/java/net/minecraft/world/inventory/HorseInventoryMenu.java
+++ b/src/main/java/net/minecraft/world/inventory/HorseInventoryMenu.java
@@ -95,7 +95,7 @@ public class HorseInventoryMenu extends AbstractContainerMenu {
@Override
public boolean stillValid(Player player) {
- return !this.horse.hasInventoryChanged(this.horseContainer) && this.horseContainer.stillValid(player) && this.horse.isAlive() && this.horse.distanceTo((Entity) player) < 8.0F;
+ return !this.horse.hasInventoryChanged(this.horseContainer) && this.horseContainer.stillValid(player) && (this.horse.isAlive() && this.horse.valid) && this.horse.distanceTo((Entity) player) < 8.0F; // Paper - Fix MC-161754
}
private boolean hasChest(AbstractHorse horse) {

View file

@ -1,18 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Nassim Jahnke <jahnke.nassim@gmail.com>
Date: Sat, 11 Sep 2021 11:56:51 +0200
Subject: [PATCH] Dont send unnecessary sign update
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index ea19697d8e60a993979d61a4d0f89110fd2cc574..84dc3599cdccc29efdf15b987640f3243007639a 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -2843,6 +2843,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
if (!tileentitysign.isEditable() || !this.player.getUUID().equals(tileentitysign.getPlayerWhoMayEdit())) {
ServerGamePacketListenerImpl.LOGGER.warn("Player {} just tried to change non-editable sign", this.player.getName().getString());
+ if (this.player.distanceToSqr(blockposition.getX(), blockposition.getY(), blockposition.getZ()) < 32 * 32) // Paper
this.send(tileentity.getUpdatePacket()); // CraftBukkit
return;
}

View file

@ -1,32 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <blake.galbreath@gmail.com>
Date: Wed, 9 Oct 2019 21:51:43 -0500
Subject: [PATCH] Fix stuck in sneak when changing worlds (MC-10657)
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index a055419b381a1e244d9d88208f0fcf2e5ba6b379..3e3582742792858c2b8328676faed68ddb6da674 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -1097,6 +1097,8 @@ public class ServerPlayer extends Player {
this.lastSentHealth = -1.0F;
this.lastSentFood = -1;
+ setShiftKeyDown(false); // Paper - fix MC-10657
+
// CraftBukkit start
PlayerChangedWorldEvent changeEvent = new PlayerChangedWorldEvent(this.getBukkitEntity(), worldserver1.getWorld());
this.level.getCraftServer().getPluginManager().callEvent(changeEvent);
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index f01a416cbb0a7d786e5033d2a3b25cb7048c232b..a2eb7689eafe20db59357ab3fad0e59cdef3481a 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -829,6 +829,8 @@ public abstract class PlayerList {
entityplayer.connection.send(new ClientboundUpdateMobEffectPacket(entityplayer.getId(), mobEffect));
}
+ entityplayer.setShiftKeyDown(false); // Paper - fix MC-10657
+
// Fire advancement trigger
entityplayer.triggerDimensionChangeTriggers(((CraftWorld) fromWorld).getHandle());

View file

@ -1,33 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <blake.galbreath@gmail.com>
Date: Wed, 9 Oct 2019 21:46:15 -0500
Subject: [PATCH] Add option to disable pillager patrols
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
index d3da5175ce1075511229ea52f1237898bcae9a11..a9ab8cbb739a72222dc7775f52ef2cfdc49fd29f 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -462,5 +462,10 @@ public class PaperWorldConfig {
private void generatorSettings() {
generateFlatBedrock = getBoolean("generator-settings.flat-bedrock", false);
}
+
+ public boolean disablePillagerPatrols = false;
+ private void pillagerSettings() {
+ disablePillagerPatrols = getBoolean("game-mechanics.disable-pillager-patrols", disablePillagerPatrols);
+ }
}
diff --git a/src/main/java/net/minecraft/world/level/levelgen/PatrolSpawner.java b/src/main/java/net/minecraft/world/level/levelgen/PatrolSpawner.java
index e0a376617f6b6232591942da0bc9d7b1ee58c2e7..744b58d59a5f34ed3bd6f2d4a0f876acfa6a7135 100644
--- a/src/main/java/net/minecraft/world/level/levelgen/PatrolSpawner.java
+++ b/src/main/java/net/minecraft/world/level/levelgen/PatrolSpawner.java
@@ -26,6 +26,7 @@ public class PatrolSpawner implements CustomSpawner {
@Override
public int tick(ServerLevel world, boolean spawnMonsters, boolean spawnAnimals) {
+ if (world.paperConfig.disablePillagerPatrols) return 0; // Paper
if (!spawnMonsters) {
return 0;
} else if (!world.getGameRules().getBoolean(GameRules.RULE_DO_PATROL_SPAWNING)) {

View file

@ -1,23 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Lukasz Derlatka <toranktto@gmail.com>
Date: Mon, 11 Nov 2019 16:08:13 +0100
Subject: [PATCH] Fix AssertionError when player hand set to empty type
Fixes an AssertionError when setting the player's item in hand to null or a new ItemStack of Air in PlayerInteractEvent
Fixes GH-2718
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 84dc3599cdccc29efdf15b987640f3243007639a..98a0034f4e19a244c22619236cd8ab76b05df1bb 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -1720,6 +1720,10 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
this.player.getBukkitEntity().updateInventory(); // SPIGOT-2524
return;
}
+ // Paper start
+ itemstack = this.player.getItemInHand(enumhand);
+ if (itemstack.isEmpty()) return;
+ // Paper end
InteractionResult enuminteractionresult = this.player.gameMode.useItem(this.player, worldserver, itemstack, enumhand);
if (enuminteractionresult.shouldSwing()) {

View file

@ -1,20 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Callahan <mr.callahhh@gmail.com>
Date: Mon, 13 Jan 2020 23:47:28 -0600
Subject: [PATCH] Prevent sync chunk loads when villagers try to find beds
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/SleepInBed.java b/src/main/java/net/minecraft/world/entity/ai/behavior/SleepInBed.java
index 455774a211c679367c6e7845a11159ad84ca07e2..673b6e60731d440cc02b1e86bfba50e6ebeb0da9 100644
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/SleepInBed.java
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/SleepInBed.java
@@ -41,7 +41,8 @@ public class SleepInBed extends Behavior<LivingEntity> {
}
}
- BlockState blockState = world.getBlockState(globalPos.pos());
+ BlockState blockState = world.getTypeIfLoaded(globalPos.pos()); // Paper
+ if (blockState == null) { return false; } // Paper
return globalPos.pos().closerThan(entity.position(), 2.0D) && blockState.is(BlockTags.BEDS) && !blockState.getValue(BedBlock.OCCUPIED);
}
}

View file

@ -1,65 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <Blake.Galbreath@GMail.com>
Date: Wed, 18 Dec 2019 22:21:35 -0600
Subject: [PATCH] MC-145656 Fix Follow Range Initial Target
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
index a9ab8cbb739a72222dc7775f52ef2cfdc49fd29f..72fc65fde0be760ef6a98d26ee7adf45c8a0242e 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -467,5 +467,10 @@ public class PaperWorldConfig {
private void pillagerSettings() {
disablePillagerPatrols = getBoolean("game-mechanics.disable-pillager-patrols", disablePillagerPatrols);
}
+
+ public boolean entitiesTargetWithFollowRange = false;
+ private void entitiesTargetWithFollowRange() {
+ entitiesTargetWithFollowRange = getBoolean("entities-target-with-follow-range", entitiesTargetWithFollowRange);
+ }
}
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java
index 6cbd2fc4a7041f957966e5b09616e70aae63c0d4..b5bbcb9fa6de4c919e4d4fabbab483054d81574e 100644
--- a/src/main/java/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java
@@ -32,6 +32,7 @@ public class NearestAttackableTargetGoal<T extends LivingEntity> extends TargetG
this.randomInterval = reciprocalChance;
this.setFlags(EnumSet.of(Goal.Flag.TARGET));
this.targetConditions = TargetingConditions.forCombat().range(this.getFollowDistance()).selector(targetPredicate);
+ if (mob.level.paperConfig.entitiesTargetWithFollowRange) this.targetConditions.useFollowRange(); // Paper
}
@Override
diff --git a/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java b/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java
index e45434b844c98c322e1432c2382c1ccb8c3e57a7..3ee691d4caccbc1b3e0f52decb41d436ac0d08ec 100644
--- a/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java
+++ b/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java
@@ -75,7 +75,7 @@ public class TargetingConditions {
if (this.range > 0.0D) {
double d = this.testInvisible ? targetEntity.getVisibilityPercent(baseEntity) : 1.0D;
- double e = Math.max(this.range * d, 2.0D);
+ double e = Math.max((this.useFollowRange ? this.getFollowRange(baseEntity) : this.range) * d, 2.0D); // Paper
double f = baseEntity.distanceToSqr(targetEntity.getX(), targetEntity.getY(), targetEntity.getZ());
if (f > e * e) {
return false;
@@ -90,4 +90,18 @@ public class TargetingConditions {
return true;
}
}
+
+ // Paper start
+ private boolean useFollowRange = false;
+
+ public TargetingConditions useFollowRange() {
+ this.useFollowRange = true;
+ return this;
+ }
+
+ private double getFollowRange(LivingEntity entityliving) {
+ net.minecraft.world.entity.ai.attributes.AttributeInstance attributeinstance = entityliving.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.FOLLOW_RANGE);
+ return attributeinstance == null ? 16.0D : attributeinstance.getValue();
+ }
+ // Paper end
}

View file

@ -1,193 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sat, 21 Jul 2018 14:27:34 -0400
Subject: [PATCH] Duplicate UUID Resolve Option
Due to a bug in https://github.com/PaperMC/Paper/commit/2e29af3df05ec0a383f48be549d1c03200756d24
which was added all the way back in March of 2016, it was unknown (potentially not at the time)
that an entity might actually change the seed of the random object.
At some point, EntitySquid did start setting the seed. Due to this shared random, this caused
every entity to use a Random object with a predictable seed.
This has caused entities to potentially generate with the same UUID....
Over the years, servers have had entities disappear, but no sign of trouble
because CraftBukkit removed the log lines indicating that something was wrong.
We have fixed the root issue causing duplicate UUID's, however we now have chunk
files full of entities that have the same UUID as another entity!
When these chunks load, the 2nd entity will not be added to the world correctly.
If that chunk loads in a different order in the future, then it will reverse and the
missing one is now the one added to the world and not the other. This results in very
inconsistent entity behavior.
This change allows you to recover any duplicate entity by generating a new UUID for it.
This also lets you delete them instead if you don't want to risk having new entities added to
the world that you previously did not see.
But for those who are ok with leaving this inconsistent behavior, you may use WARN or NOTHING options.
It is recommended you regenerate the entities, as these were legit entities, and deserve your love.
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
index 72fc65fde0be760ef6a98d26ee7adf45c8a0242e..8108cbc492dc14e5dd5a183105e89eb0cfb378fe 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -443,6 +443,45 @@ public class PaperWorldConfig {
preventMovingIntoUnloadedChunks = getBoolean("prevent-moving-into-unloaded-chunks", false);
}
+ public enum DuplicateUUIDMode {
+ SAFE_REGEN, DELETE, NOTHING, WARN
+ }
+ public DuplicateUUIDMode duplicateUUIDMode = DuplicateUUIDMode.SAFE_REGEN;
+ public int duplicateUUIDDeleteRange = 32;
+ private void repairDuplicateUUID() {
+ String desiredMode = getString("duplicate-uuid-resolver", "saferegen").toLowerCase().trim();
+ duplicateUUIDDeleteRange = getInt("duplicate-uuid-saferegen-delete-range", duplicateUUIDDeleteRange);
+ switch (desiredMode.toLowerCase()) {
+ case "regen":
+ case "regenerate":
+ case "saferegen":
+ case "saferegenerate":
+ duplicateUUIDMode = DuplicateUUIDMode.SAFE_REGEN;
+ log("Duplicate UUID Resolve: Regenerate New UUID if distant (Delete likely duplicates within " + duplicateUUIDDeleteRange + " blocks)");
+ break;
+ case "remove":
+ case "delete":
+ duplicateUUIDMode = DuplicateUUIDMode.DELETE;
+ log("Duplicate UUID Resolve: Delete Entity");
+ break;
+ case "silent":
+ case "nothing":
+ duplicateUUIDMode = DuplicateUUIDMode.NOTHING;
+ logError("Duplicate UUID Resolve: Do Nothing (no logs) - Warning, may lose indication of bad things happening");
+ break;
+ case "log":
+ case "warn":
+ duplicateUUIDMode = DuplicateUUIDMode.WARN;
+ log("Duplicate UUID Resolve: Warn (do nothing but log it happened, may be spammy)");
+ break;
+ default:
+ duplicateUUIDMode = DuplicateUUIDMode.WARN;
+ logError("Warning: Invalid duplicate-uuid-resolver config " + desiredMode + " - must be one of: regen, delete, nothing, warn");
+ log("Duplicate UUID Resolve: Warn (do nothing but log it happened, may be spammy)");
+ break;
+ }
+ }
+
public boolean countAllMobsForSpawning = false;
private void countAllMobsForSpawning() {
countAllMobsForSpawning = getBoolean("count-all-mobs-for-spawning", false);
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 41253d8adf85cf318fcb1cee36ac1763f440fca6..ba1514301a8b20fcc594ae5555b491e88a98e6dd 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -1,6 +1,7 @@
package net.minecraft.server.level;
import co.aikar.timings.Timing; // Paper
+import com.destroystokyo.paper.PaperWorldConfig; // Paper
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.ComparisonChain; // Paper
@@ -24,13 +25,17 @@ import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.BitSet;
+import java.util.HashMap; // Paper
+import java.util.Collection;
import java.util.Iterator;
import java.util.List;
+import java.util.Map; // Paper
import java.util.Objects;
import java.util.Optional;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.CancellationException;
+import java.util.UUID; // Paper
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CompletionStage;
@@ -836,6 +841,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
entity.discard();
needsRemoval = true;
}
+ checkDupeUUID(worldserver, entity); // Paper
return !needsRemoval;
}));
// CraftBukkit end
@@ -885,6 +891,43 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
});
}
+ // Paper start
+ private static void checkDupeUUID(ServerLevel level, Entity entity) {
+ PaperWorldConfig.DuplicateUUIDMode mode = level.paperConfig.duplicateUUIDMode;
+ if (mode != PaperWorldConfig.DuplicateUUIDMode.WARN
+ && mode != PaperWorldConfig.DuplicateUUIDMode.DELETE
+ && mode != PaperWorldConfig.DuplicateUUIDMode.SAFE_REGEN) {
+ return;
+ }
+ Entity other = level.getEntity(entity.getUUID());
+
+ if (mode == PaperWorldConfig.DuplicateUUIDMode.SAFE_REGEN && other != null && !other.isRemoved()
+ && Objects.equals(other.getEncodeId(), entity.getEncodeId())
+ && entity.getBukkitEntity().getLocation().distance(other.getBukkitEntity().getLocation()) < level.paperConfig.duplicateUUIDDeleteRange
+ ) {
+ if (Level.DEBUG_ENTITIES) LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", deleted entity " + entity + " because it was near the duplicate and likely an actual duplicate. See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
+ entity.discard();
+ return;
+ }
+ if (other != null && !other.isRemoved()) {
+ switch (mode) {
+ case SAFE_REGEN: {
+ entity.setUUID(UUID.randomUUID());
+ if (Level.DEBUG_ENTITIES) LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", regenerated UUID for " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
+ break;
+ }
+ case DELETE: {
+ if (Level.DEBUG_ENTITIES) LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", deleted entity " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
+ entity.discard();
+ break;
+ }
+ default:
+ if (Level.DEBUG_ENTITIES) LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", doing nothing to " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
+ break;
+ }
+ }
+ }
+ // Paper end
public CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> prepareTickingChunk(ChunkHolder holder) {
ChunkPos chunkcoordintpair = holder.getPos();
CompletableFuture<Either<List<ChunkAccess>, ChunkHolder.ChunkLoadingFailure>> completablefuture = this.getChunkRangeFuture(chunkcoordintpair, 1, (i) -> {
diff --git a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
index 23640964305b7f2ffd54ed3044712308f03c3d42..f698723f8f7feecc749df10a316118184391f31a 100644
--- a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
+++ b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
@@ -78,7 +78,22 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
private boolean addEntityUuid(T entity) {
if (!this.knownUuids.add(entity.getUUID())) {
+ // Paper start
+ T conflict = this.visibleEntityStorage.getEntity(entity.getUUID());
+ if (conflict != null && ((Entity) conflict).isRemoved()) {
+ stopTracking(conflict); // remove the existing entity
+ return true;
+ }
+ // Paper end
PersistentEntitySectionManager.LOGGER.warn("UUID of added entity already exists: {}", entity);
+ // Paper start
+ if (net.minecraft.world.level.Level.DEBUG_ENTITIES && ((Entity) entity).level.paperConfig.duplicateUUIDMode != com.destroystokyo.paper.PaperWorldConfig.DuplicateUUIDMode.NOTHING) {
+ if (((Entity) entity).addedToWorldStack != null) {
+ ((Entity) entity).addedToWorldStack.printStackTrace();
+ }
+ net.minecraft.server.level.ServerLevel.getAddToWorldStackTrace((net.minecraft.world.entity.Entity) entity).printStackTrace();
+ }
+ // Paper end
return false;
} else {
return true;

View file

@ -1,503 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Wed, 27 Apr 2016 22:09:52 -0400
Subject: [PATCH] Optimize Hoppers
* Removes unnecessary extra calls to .update() that are very expensive
* Lots of itemstack cloning removed. Only clone if the item is actually moved
* Return true when a plugin cancels inventory move item event instead of false, as false causes pulls to cycle through all items.
However, pushes do not exhibit the same behavior, so this is not something plugins could of been relying on.
* Add option (Default on) to cooldown hoppers when they fail to move an item due to full inventory
* Skip subsequent InventoryMoveItemEvents if a plugin does not use the item after first event fire for an iteration
* Don't check for Entities with Inventories if the block above us is also occluding (not just Inventoried)
* Remove Streams from Item Suck In and restore restore 1.12 AABB checks which is simpler and no voxel allocations (was doing TWO Item Suck ins)
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
index 8108cbc492dc14e5dd5a183105e89eb0cfb378fe..e8746656ac12d3498e78cb603c14d4c31abbc023 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -511,5 +511,17 @@ public class PaperWorldConfig {
private void entitiesTargetWithFollowRange() {
entitiesTargetWithFollowRange = getBoolean("entities-target-with-follow-range", entitiesTargetWithFollowRange);
}
+
+ public boolean cooldownHopperWhenFull = true;
+ public boolean disableHopperMoveEvents = false;
+ public boolean hoppersIgnoreOccludingBlocks = true;
+ private void hopperOptimizations() {
+ cooldownHopperWhenFull = getBoolean("hopper.cooldown-when-full", cooldownHopperWhenFull);
+ log("Cooldown Hoppers when Full: " + (cooldownHopperWhenFull ? "enabled" : "disabled"));
+ disableHopperMoveEvents = getBoolean("hopper.disable-move-event", disableHopperMoveEvents);
+ log("Hopper Move Item Events: " + (disableHopperMoveEvents ? "disabled" : "enabled"));
+ hoppersIgnoreOccludingBlocks = getBoolean("hopper.ignore-occluding-blocks", hoppersIgnoreOccludingBlocks);
+ log("Hopper Ignore Occluding Blocks: " + (hoppersIgnoreOccludingBlocks ? "enabled" : "disabled"));
+ }
}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index fa9d70300ab1ecb83b8a21e5a707dd7ffd7ad74b..bf08c45525cd898ad12d70bec5de3762063af2e0 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1443,6 +1443,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
while (iterator.hasNext()) {
ServerLevel worldserver = (ServerLevel) iterator.next();
worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper
+ net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = worldserver.paperConfig.disableHopperMoveEvents || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper
this.profiler.push(() -> {
return worldserver + " " + worldserver.dimension().location();
diff --git a/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java b/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java
index 449d2e7b18608ca36282f1a29e69457fc525307e..c738cb0433ea4a86d82372bf66e29c01f991d2c6 100644
--- a/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java
+++ b/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java
@@ -68,6 +68,13 @@ public class MinecartHopper extends AbstractMinecartContainer implements Hopper
this.enabled = enabled;
}
+ // Paper start - add back getLevel
+ @Override
+ public net.minecraft.world.level.Level getLevel() {
+ return this.level;
+ }
+ // Paper end
+
@Override
public double getLevelX() {
return this.getX();
diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java
index c5683f76f6f11246dc5bbaa13dfc253e5e286590..5ee807be448c6bfb98ac2a16bda829926b9cf994 100644
--- a/src/main/java/net/minecraft/world/item/ItemStack.java
+++ b/src/main/java/net/minecraft/world/item/ItemStack.java
@@ -603,11 +603,12 @@ public final class ItemStack {
return this.getItem().interactLivingEntity(this, user, entity, hand);
}
- public ItemStack copy() {
- if (this.isEmpty()) {
+ public ItemStack copy() { return cloneItemStack(false); } // Paper
+ public ItemStack cloneItemStack(boolean origItem) { // Paper
+ if (!origItem && this.isEmpty()) { // Paper
return ItemStack.EMPTY;
} else {
- ItemStack itemstack = new ItemStack(this.getItem(), this.count);
+ ItemStack itemstack = new ItemStack(origItem ? this.item : this.getItem(), this.count); // Paper
itemstack.setPopTime(this.getPopTime());
if (this.tag != null) {
diff --git a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
index 1d1764766d2b4a0b7bf4078ce428bb1474f709df..38c4dac82793930777eaf0189c5534234a9162ed 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
@@ -63,6 +63,7 @@ public abstract class BlockEntity implements net.minecraft.server.KeyedObject {
getMinecraftKey(); // Try to load if it doesn't exists.
return tileEntityKeyString;
}
+ static boolean IGNORE_TILE_UPDATES = false;
// Paper end
@Nullable
@@ -145,6 +146,7 @@ public abstract class BlockEntity implements net.minecraft.server.KeyedObject {
public void setChanged() {
if (this.level != null) {
+ if (IGNORE_TILE_UPDATES) return; // Paper
BlockEntity.setChanged(this.level, this.worldPosition, this.blockState);
}
diff --git a/src/main/java/net/minecraft/world/level/block/entity/Hopper.java b/src/main/java/net/minecraft/world/level/block/entity/Hopper.java
index a05acf709735b40ca86f978508c63a86065fd405..71dd26ca6626631b94d53818cd06b93f61485369 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/Hopper.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/Hopper.java
@@ -14,6 +14,10 @@ public interface Hopper extends Container {
return SUCK;
}
+ net.minecraft.world.level.Level getLevel(); // Paper
+
+ default net.minecraft.core.BlockPos getBlockPosition() { return new net.minecraft.core.BlockPos(getLevelX(), getLevelY(), getLevelZ()); } // Paper
+
double getLevelX();
double getLevelY();
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 3e2832087e8c2c0e2c45f18b0da49299611e76ad..89b9982e65acfcaa84e10d37e619f18b77f4ca92 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
@@ -3,7 +3,6 @@ package net.minecraft.world.level.block.entity;
import java.util.Iterator;
import java.util.List;
import java.util.function.BooleanSupplier;
-import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javax.annotation.Nullable;
import net.minecraft.core.BlockPos;
@@ -33,7 +32,6 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.shapes.BooleanOp;
import net.minecraft.world.phys.shapes.Shapes;
-import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.entity.HumanEntity;
@@ -192,6 +190,159 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
return false;
}
+ // Paper start - Optimize Hoppers
+ private static boolean skipPullModeEventFire = false;
+ private static boolean skipPushModeEventFire = false;
+ public static boolean skipHopperEvents = false;
+
+ private static boolean hopperPush(Level level, BlockPos pos, Container destination, Direction enumdirection, HopperBlockEntity hopper) {
+ skipPushModeEventFire = skipHopperEvents;
+ boolean foundItem = false;
+ for (int i = 0; i < hopper.getContainerSize(); ++i) {
+ ItemStack item = hopper.getItem(i);
+ if (!item.isEmpty()) {
+ foundItem = true;
+ ItemStack origItemStack = item;
+ ItemStack itemstack = origItemStack;
+
+ final int origCount = origItemStack.getCount();
+ final int moved = Math.min(level.spigotConfig.hopperAmount, origCount);
+ origItemStack.setCount(moved);
+
+ // We only need to fire the event once to give protection plugins a chance to cancel this event
+ // Because nothing uses getItem, every event call should end up the same result.
+ if (!skipPushModeEventFire) {
+ itemstack = callPushMoveEvent(destination, itemstack, hopper);
+ if (itemstack == null) { // cancelled
+ origItemStack.setCount(origCount);
+ return false;
+ }
+ }
+ final ItemStack itemstack2 = addItem(hopper, destination, itemstack, enumdirection);
+ final int remaining = itemstack2.getCount();
+ if (remaining != moved) {
+ origItemStack = origItemStack.cloneItemStack(true);
+ origItemStack.setCount(origCount);
+ if (!origItemStack.isEmpty()) {
+ origItemStack.setCount(origCount - moved + remaining);
+ }
+ hopper.setItem(i, origItemStack);
+ destination.setChanged();
+ return true;
+ }
+ origItemStack.setCount(origCount);
+ }
+ }
+ if (foundItem && level.paperConfig.cooldownHopperWhenFull) { // Inventory was full - cooldown
+ hopper.setCooldown(level.spigotConfig.hopperTransfer);
+ }
+ return false;
+ }
+
+ private static boolean hopperPull(Hopper ihopper, Container iinventory, ItemStack origItemStack, int i) {
+ ItemStack itemstack = origItemStack;
+ final int origCount = origItemStack.getCount();
+ final Level world = ihopper.getLevel();
+ final int moved = Math.min(world.spigotConfig.hopperAmount, origCount);
+ itemstack.setCount(moved);
+
+ if (!skipPullModeEventFire) {
+ itemstack = callPullMoveEvent(ihopper, iinventory, itemstack);
+ if (itemstack == null) { // cancelled
+ origItemStack.setCount(origCount);
+ // Drastically improve performance by returning true.
+ // No plugin could of relied on the behavior of false as the other call
+ // site for IMIE did not exhibit the same behavior
+ return true;
+ }
+ }
+
+ final ItemStack itemstack2 = addItem(iinventory, ihopper, itemstack, null);
+ final int remaining = itemstack2.getCount();
+ if (remaining != moved) {
+ origItemStack = origItemStack.cloneItemStack(true);
+ origItemStack.setCount(origCount);
+ if (!origItemStack.isEmpty()) {
+ origItemStack.setCount(origCount - moved + remaining);
+ }
+ IGNORE_TILE_UPDATES = true;
+ iinventory.setItem(i, origItemStack);
+ IGNORE_TILE_UPDATES = false;
+ iinventory.setChanged();
+ return true;
+ }
+ origItemStack.setCount(origCount);
+
+ if (world.paperConfig.cooldownHopperWhenFull) {
+ cooldownHopper(ihopper);
+ }
+
+ return false;
+ }
+
+ private static ItemStack callPushMoveEvent(Container iinventory, ItemStack itemstack, HopperBlockEntity hopper) {
+ Inventory destinationInventory = getInventory(iinventory);
+ InventoryMoveItemEvent event = new InventoryMoveItemEvent(hopper.getOwner(false).getInventory(),
+ CraftItemStack.asCraftMirror(itemstack), destinationInventory, true);
+ boolean result = event.callEvent();
+ if (!event.calledGetItem && !event.calledSetItem) {
+ skipPushModeEventFire = true;
+ }
+ if (!result) {
+ cooldownHopper(hopper);
+ return null;
+ }
+
+ if (event.calledSetItem) {
+ return CraftItemStack.asNMSCopy(event.getItem());
+ } else {
+ return itemstack;
+ }
+ }
+
+ private static ItemStack callPullMoveEvent(Hopper hopper, Container iinventory, ItemStack itemstack) {
+ Inventory sourceInventory = getInventory(iinventory);
+ Inventory destination = getInventory(hopper);
+
+ InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory,
+ // Mirror is safe as we no plugins ever use this item
+ CraftItemStack.asCraftMirror(itemstack), destination, false);
+ boolean result = event.callEvent();
+ if (!event.calledGetItem && !event.calledSetItem) {
+ skipPullModeEventFire = true;
+ }
+ if (!result) {
+ cooldownHopper(hopper);
+ return null;
+ }
+
+ if (event.calledSetItem) {
+ return CraftItemStack.asNMSCopy(event.getItem());
+ } else {
+ return itemstack;
+ }
+ }
+
+ private static Inventory getInventory(Container iinventory) {
+ Inventory sourceInventory;// Have to special case large chests as they work oddly
+ if (iinventory instanceof CompoundContainer) {
+ sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((CompoundContainer) iinventory);
+ } else if (iinventory instanceof BlockEntity) {
+ sourceInventory = ((BlockEntity) iinventory).getOwner(false).getInventory();
+ } else {
+ sourceInventory = iinventory.getOwner().getInventory();
+ }
+ return sourceInventory;
+ }
+
+ private static void cooldownHopper(Hopper hopper) {
+ if (hopper instanceof HopperBlockEntity) {
+ ((HopperBlockEntity) hopper).setCooldown(hopper.getLevel().spigotConfig.hopperTransfer);
+ } else if (hopper instanceof MinecartHopper) {
+ ((MinecartHopper) hopper).setCooldown(hopper.getLevel().spigotConfig.hopperTransfer / 2);
+ }
+ }
+ // Paper end
private static boolean a(Level world, BlockPos blockposition, BlockState iblockdata, Container iinventory, HopperBlockEntity hopper) { // CraftBukkit
Container iinventory1 = HopperBlockEntity.getAttachedContainer(world, blockposition, iblockdata);
@@ -204,6 +355,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
if (HopperBlockEntity.isFullContainer(iinventory1, enumdirection)) {
return false;
} else {
+ return hopperPush(world, blockposition, iinventory1, enumdirection, hopper); /* // Paper - disable rest
for (int i = 0; i < iinventory.getContainerSize(); ++i) {
if (!iinventory.getItem(i).isEmpty()) {
ItemStack itemstack = iinventory.getItem(i).copy();
@@ -241,7 +393,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
}
- return false;
+ return false;*/ // Paper - end commenting out replaced block for Hopper Optimizations
}
}
}
@@ -251,27 +403,68 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
private static boolean isFullContainer(Container inventory, Direction direction) {
- return HopperBlockEntity.getSlots(inventory, direction).allMatch((i) -> {
- ItemStack itemstack = inventory.getItem(i);
-
- return itemstack.getCount() >= itemstack.getMaxStackSize();
- });
+ return allMatch(inventory, direction, STACK_SIZE_TEST); // Paper - no streams
}
private static boolean isEmptyContainer(Container inv, Direction facing) {
- return HopperBlockEntity.getSlots(inv, facing).allMatch((i) -> {
- return inv.getItem(i).isEmpty();
- });
+ // Paper start
+ return allMatch(inv, facing, IS_EMPTY_TEST);
+ }
+ private static boolean allMatch(Container iinventory, Direction enumdirection, java.util.function.BiPredicate<ItemStack, Integer> test) {
+ if (iinventory instanceof WorldlyContainer) {
+ for (int i : ((WorldlyContainer) iinventory).getSlotsForFace(enumdirection)) {
+ if (!test.test(iinventory.getItem(i), i)) {
+ return false;
+ }
+ }
+ } else {
+ int size = iinventory.getContainerSize();
+ for (int i = 0; i < size; i++) {
+ if (!test.test(iinventory.getItem(i), i)) {
+ return false;
+ }
+ }
+ }
+ return true;
}
+ private static boolean anyMatch(Container iinventory, Direction enumdirection, java.util.function.BiPredicate<ItemStack, Integer> test) {
+ if (iinventory instanceof WorldlyContainer) {
+ for (int i : ((WorldlyContainer) iinventory).getSlotsForFace(enumdirection)) {
+ if (test.test(iinventory.getItem(i), i)) {
+ return true;
+ }
+ }
+ } else {
+ int size = iinventory.getContainerSize();
+ for (int i = 0; i < size; i++) {
+ if (test.test(iinventory.getItem(i), i)) {
+ return true;
+ }
+ }
+ }
+ return true;
+ }
+ private static final java.util.function.BiPredicate<ItemStack, Integer> STACK_SIZE_TEST = (itemstack, i) -> itemstack.getCount() >= itemstack.getMaxStackSize();
+ private static final java.util.function.BiPredicate<ItemStack, Integer> IS_EMPTY_TEST = (itemstack, i) -> itemstack.isEmpty();
+ // Paper end
+
public static boolean suckInItems(Level world, Hopper hopper) {
Container iinventory = HopperBlockEntity.getSourceContainer(world, hopper);
if (iinventory != null) {
Direction enumdirection = Direction.DOWN;
- return HopperBlockEntity.isEmptyContainer(iinventory, enumdirection) ? false : HopperBlockEntity.getSlots(iinventory, enumdirection).anyMatch((i) -> {
- return HopperBlockEntity.a(hopper, iinventory, i, enumdirection, world); // Spigot
+ // Paper start - optimize hoppers and remove streams
+ skipPullModeEventFire = skipHopperEvents;
+ return !HopperBlockEntity.isEmptyContainer(iinventory, enumdirection) && anyMatch(iinventory, enumdirection, (item, i) -> {
+ // Logic copied from below to avoid extra getItem calls
+ if (!item.isEmpty() && canTakeItemFromContainer(iinventory, item, i, enumdirection)) {
+ return hopperPull(hopper, iinventory, item, i);
+ } else {
+ return false;
+ }
+ // Paper end
});
} else {
Iterator iterator = HopperBlockEntity.getItemsAtAndAbove(world, hopper).iterator();
@@ -290,10 +483,12 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
}
+ // Paper - method unused as logic is inlined above
private static boolean a(Hopper ihopper, Container iinventory, int i, Direction enumdirection, Level world) { // Spigot
ItemStack itemstack = iinventory.getItem(i);
- if (!itemstack.isEmpty() && HopperBlockEntity.canTakeItemFromContainer(iinventory, itemstack, i, enumdirection)) {
+ if (!itemstack.isEmpty() && HopperBlockEntity.canTakeItemFromContainer(iinventory, itemstack, i, enumdirection)) { // If this logic changes, update above. this is left inused incase reflective plugins
+ return hopperPull(ihopper, iinventory, itemstack, i); /* // Paper - disable rest
ItemStack itemstack1 = itemstack.copy();
// ItemStack itemstack2 = addItem(iinventory, ihopper, iinventory.splitStack(i, 1), (EnumDirection) null);
// CraftBukkit start - Call event on collection of items from inventories into the hopper
@@ -330,7 +525,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
itemstack1.shrink(origCount - itemstack2.getCount()); // Spigot
- iinventory.setItem(i, itemstack1);
+ iinventory.setItem(i, itemstack1);*/ // Paper - end commenting out replaced block for Hopper Optimizations
}
return false;
@@ -339,7 +534,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
public static boolean addItem(Container inventory, ItemEntity itemEntity) {
boolean flag = false;
// CraftBukkit start
- InventoryPickupItemEvent event = new InventoryPickupItemEvent(inventory.getOwner().getInventory(), (org.bukkit.entity.Item) itemEntity.getBukkitEntity());
+ InventoryPickupItemEvent event = new InventoryPickupItemEvent(getInventory(inventory), (org.bukkit.entity.Item) itemEntity.getBukkitEntity()); // Paper - use getInventory() to avoid snapshot creation
itemEntity.level.getCraftServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return false;
@@ -398,7 +593,9 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
stack = stack.split(to.getMaxStackSize());
}
// Spigot end
+ IGNORE_TILE_UPDATES = true; // Paper
to.setItem(slot, stack);
+ IGNORE_TILE_UPDATES = false; // Paper
stack = ItemStack.EMPTY;
flag = true;
} else if (HopperBlockEntity.canMergeItems(itemstack1, stack)) {
@@ -449,18 +646,23 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
public static List<ItemEntity> getItemsAtAndAbove(Level world, Hopper hopper) {
- return (List) hopper.getSuckShape().toAabbs().stream().flatMap((axisalignedbb) -> {
- return world.getEntitiesOfClass(ItemEntity.class, axisalignedbb.move(hopper.getLevelX() - 0.5D, hopper.getLevelY() - 0.5D, hopper.getLevelZ() - 0.5D), EntitySelector.ENTITY_STILL_ALIVE).stream();
- }).collect(Collectors.toList());
+ // Paper start - Optimize item suck in. remove streams, restore 1.12 checks. Seriously checking the bowl?!
+ double d0 = hopper.getLevelX();
+ double d1 = hopper.getLevelY();
+ double d2 = hopper.getLevelZ();
+ AABB bb = new AABB(d0 - 0.5D, d1, d2 - 0.5D, d0 + 0.5D, d1 + 1.5D, d2 + 0.5D);
+ return world.getEntitiesOfClass(ItemEntity.class, bb, Entity::isAlive);
+ // Paper end
}
@Nullable
public static Container getContainerAt(Level world, BlockPos pos) {
- return HopperBlockEntity.getContainerAt(world, (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D);
+ return HopperBlockEntity.getContainerAt(world, (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, true); // Paper
}
+ public static Container getContainerAt(Level world, double x, double y, double z) { return getContainerAt(world, x, y, z, false); } // Paper - overload to default false
@Nullable
- private static Container getContainerAt(Level world, double x, double y, double z) {
+ private static Container getContainerAt(Level world, double x, double y, double z, boolean optimizeEntities) {
Object object = null;
BlockPos blockposition = new BlockPos(x, y, z);
if ( !world.hasChunkAt( blockposition ) ) return null; // Spigot
@@ -480,7 +682,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
}
- if (object == null) {
+ if (object == null && (!optimizeEntities || !world.paperConfig.hoppersIgnoreOccludingBlocks || !org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(block).isOccluding())) { // Paper
List<Entity> list = world.getEntities((Entity) null, new AABB(x - 0.5D, y - 0.5D, z - 0.5D, x + 0.5D, y + 0.5D, z + 0.5D), EntitySelector.CONTAINER_ENTITY_SELECTOR);
if (!list.isEmpty()) {
diff --git a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
index f23fff80d07ac7d06715efe67cb49ebbe704967b..ed3518fe7c841d9e1a9c97626acaa3d765a6d76f 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
@@ -95,12 +95,19 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc
@Override
public boolean isEmpty() {
this.unpackLootTable((Player)null);
- return this.getItems().stream().allMatch(ItemStack::isEmpty);
+ // Paper start
+ for (ItemStack itemStack : this.getItems()) {
+ if (!itemStack.isEmpty()) {
+ return false;
+ }
+ }
+ // Paper end
+ return true;
}
@Override
public ItemStack getItem(int slot) {
- this.unpackLootTable((Player)null);
+ if (slot == 0) this.unpackLootTable((Player) null); // Paper
return this.getItems().get(slot);
}