a bunch more patches
This commit is contained in:
parent
2b29fe37a2
commit
0f23d73d1a
19 changed files with 96 additions and 95 deletions
|
@ -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() {
|
|
@ -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()));
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
|
|
@ -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");
|
|
@ -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) {
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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) {
|
|
@ -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;
|
||||
}
|
|
@ -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());
|
||||
|
|
@ -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)) {
|
|
@ -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()) {
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue