a bunch more patches

This commit is contained in:
Jake Potrebic
2021-11-23 21:44:21 -08:00
parent f629237348
commit cc3b3eba50
19 changed files with 35 additions and 294 deletions

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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/levelgen/PatrolSpawner.java
+++ b/src/main/java/net/minecraft/world/level/levelgen/PatrolSpawner.java
@@ -0,0 +0,0 @@ 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,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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java
+++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java
@@ -0,0 +0,0 @@ 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,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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -0,0 +0,0 @@ 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());
@@ -0,0 +0,0 @@ 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,18 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Nassim Jahnke <nassim@njahnke.dev>
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -0,0 +0,0 @@ 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,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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -0,0 +0,0 @@
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
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
entity.discard();
needsRemoval = true;
}
+ checkDupeUUID(worldserver, entity); // Paper
return !needsRemoval;
}));
// CraftBukkit end
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
+++ b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
@@ -0,0 +0,0 @@ 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,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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -0,0 +0,0 @@ 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,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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -0,0 +0,0 @@ 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,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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/inventory/HorseInventoryMenu.java
+++ b/src/main/java/net/minecraft/world/inventory/HorseInventoryMenu.java
@@ -0,0 +0,0 @@ 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,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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -0,0 +0,0 @@ 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,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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
+++ b/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
@@ -0,0 +0,0 @@ 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);
}
}
@@ -0,0 +0,0 @@ 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,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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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,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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
@@ -0,0 +0,0 @@ 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,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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java
+++ b/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/item/ItemStack.java
+++ b/src/main/java/net/minecraft/world/item/ItemStack.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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();
@@ -0,0 +0,0 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
}
- return false;
+ return false;*/ // Paper - end commenting out replaced block for Hopper Optimizations
}
}
}
@@ -0,0 +0,0 @@ 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();
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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)) {
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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);
}

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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -0,0 +0,0 @@ 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);
}
}
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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,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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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,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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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,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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/decoration/HangingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/decoration/HangingEntity.java
@@ -0,0 +0,0 @@ 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;