diff options
author | Nassim Jahnke <[email protected]> | 2024-12-03 19:54:10 +0100 |
---|---|---|
committer | Nassim Jahnke <[email protected]> | 2024-12-03 20:00:08 +0100 |
commit | bd55e322d8cfb8f4d4fed04b38243bc4940fab50 (patch) | |
tree | c417bfb8a8727f6bc27e59586fb507b48bff51a2 /patches/server | |
parent | c60e47fa58c171befcfb4ca48f38a201d559f90b (diff) | |
download | Paper-bd55e322d8cfb8f4d4fed04b38243bc4940fab50.tar.gz Paper-bd55e322d8cfb8f4d4fed04b38243bc4940fab50.zip |
More more more work
Diffstat (limited to 'patches/server')
261 files changed, 20300 insertions, 21 deletions
diff --git a/patches/server/0263-Don-t-allow-digging-into-unloaded-chunks.patch b/patches/server/0263-Don-t-allow-digging-into-unloaded-chunks.patch index 6f9db2ddeb..7603da369f 100644 --- a/patches/server/0263-Don-t-allow-digging-into-unloaded-chunks.patch +++ b/patches/server/0263-Don-t-allow-digging-into-unloaded-chunks.patch @@ -59,19 +59,19 @@ index 4c8189a2a7edea824545a24dccb376b8eceac001..4623c8acd125dff4919c4e2045b84831 this.level.destroyBlockProgress(this.player.getId(), pos, -1); diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index a87ea0f22b44c2fb67fd51bc8c9b0067aacac7de..1be04d0c910ebd58f2eededcdf81c94279d521c7 100644 +index a87ea0f22b44c2fb67fd51bc8c9b0067aacac7de..6ea2a25789fe7f90dae7e3de7966c88f39e929a8 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -1668,6 +1668,12 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl case START_DESTROY_BLOCK: case ABORT_DESTROY_BLOCK: case STOP_DESTROY_BLOCK: -+ // Paper start - Don't allow digging into unloaded chunks -+ if (this.player.level().getChunkIfLoadedImmediately(blockposition.getX() >> 4, blockposition.getZ() >> 4) == null) { -+ this.player.connection.ackBlockChangesUpTo(packet.getSequence()); -+ return; -+ } -+ // Paper end - Don't allow digging into unloaded chunks ++ // Paper start - Don't allow digging into unloaded chunks ++ if (this.player.level().getChunkIfLoadedImmediately(blockposition.getX() >> 4, blockposition.getZ() >> 4) == null) { ++ this.player.connection.ackBlockChangesUpTo(packet.getSequence()); ++ return; ++ } ++ // Paper end - Don't allow digging into unloaded chunks this.player.gameMode.handleBlockBreakAction(blockposition, packetplayinblockdig_enumplayerdigtype, packet.getDirection(), this.player.level().getMaxY(), packet.getSequence()); this.player.connection.ackBlockChangesUpTo(packet.getSequence()); return; diff --git a/patches/server/0267-Book-size-limits.patch b/patches/server/0267-Book-size-limits.patch index e43d682a1b..2c4374aa7a 100644 --- a/patches/server/0267-Book-size-limits.patch +++ b/patches/server/0267-Book-size-limits.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Book size limits Puts some limits on the size of books. diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 1be04d0c910ebd58f2eededcdf81c94279d521c7..ca0aa2acbc1316201d7a9c69e4aad8ffe4d61d83 100644 +index 6ea2a25789fe7f90dae7e3de7966c88f39e929a8..fb01e24ea6fd1d1f4beabb862c70332f92a80d78 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -1117,6 +1117,44 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl diff --git a/patches/server/0274-Brigadier-Mojang-API.patch b/patches/server/0274-Brigadier-Mojang-API.patch index dcabb54ddb..9dc98d9edd 100644 --- a/patches/server/0274-Brigadier-Mojang-API.patch +++ b/patches/server/0274-Brigadier-Mojang-API.patch @@ -119,7 +119,7 @@ index 6212d94503023f7bb5ca21785cbb69babe4421c3..642d5c6849debc5a266605b0df30d552 if (commandnode2.canUse(source)) { diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index ca0aa2acbc1316201d7a9c69e4aad8ffe4d61d83..c32dd209e751f8c0e8e73c91a9d4b2c21c59b883 100644 +index fb01e24ea6fd1d1f4beabb862c70332f92a80d78..ba10d147705d7d775999d94053cf47b5f481b905 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -778,19 +778,34 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl diff --git a/patches/server/0275-Limit-Client-Sign-length-more.patch b/patches/server/0275-Limit-Client-Sign-length-more.patch index 8e4daa3d06..808ca0937b 100644 --- a/patches/server/0275-Limit-Client-Sign-length-more.patch +++ b/patches/server/0275-Limit-Client-Sign-length-more.patch @@ -22,7 +22,7 @@ it only impacts data sent from the client. Set -DPaper.maxSignLength=XX to change limit or -1 to disable diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index c32dd209e751f8c0e8e73c91a9d4b2c21c59b883..969fee86d2422cbadfbce18d8bbaf72ff30b5c3d 100644 +index ba10d147705d7d775999d94053cf47b5f481b905..9f984834f3da2582f21172f2d52783ad3ff5c46d 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -310,6 +310,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl diff --git a/patches/server/0347-Prevent-teleporting-dead-entities.patch b/patches/server/0347-Prevent-teleporting-dead-entities.patch index 7582f1a3d6..7f2a2917af 100644 --- a/patches/server/0347-Prevent-teleporting-dead-entities.patch +++ b/patches/server/0347-Prevent-teleporting-dead-entities.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Prevent teleporting dead entities diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 969fee86d2422cbadfbce18d8bbaf72ff30b5c3d..d754eda8e8111886e610bc68e8980b54b43eadb1 100644 +index 9f984834f3da2582f21172f2d52783ad3ff5c46d..e9e0a3d0958b309ca17322b11a6612be59ca41a6 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -1625,6 +1625,13 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl diff --git a/patches/server/0362-Prevent-position-desync-causing-tp-exploit.patch b/patches/server/0362-Prevent-position-desync-causing-tp-exploit.patch index a3f9d5b3c7..4421a00963 100644 --- a/patches/server/0362-Prevent-position-desync-causing-tp-exploit.patch +++ b/patches/server/0362-Prevent-position-desync-causing-tp-exploit.patch @@ -13,7 +13,7 @@ behaviour, we need to move all of this dangerous logic outside of the move call and into an appropriate place in the tick method. diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index d754eda8e8111886e610bc68e8980b54b43eadb1..7d7fff91beafd5cf67296efaa06308f1363585a0 100644 +index e9e0a3d0958b309ca17322b11a6612be59ca41a6..b7e3c15055681b916482503f6b2370b362863c9c 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -1413,6 +1413,11 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl diff --git a/patches/server/0364-Add-PlayerRecipeBookClickEvent.patch b/patches/server/0364-Add-PlayerRecipeBookClickEvent.patch index abaa3fa71e..1bda25313e 100644 --- a/patches/server/0364-Add-PlayerRecipeBookClickEvent.patch +++ b/patches/server/0364-Add-PlayerRecipeBookClickEvent.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add PlayerRecipeBookClickEvent diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 7d7fff91beafd5cf67296efaa06308f1363585a0..a7d1b4d4668b90445e1cba786be74fbd069d3330 100644 +index b7e3c15055681b916482503f6b2370b362863c9c..71785d4b2204030dc718c10494840013d5ea72fa 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -201,6 +201,7 @@ import net.minecraft.world.phys.Vec3; diff --git a/patches/server/0366-Add-permission-for-command-blocks.patch b/patches/server/0366-Add-permission-for-command-blocks.patch index 604ac9cb23..88dc3a8c71 100644 --- a/patches/server/0366-Add-permission-for-command-blocks.patch +++ b/patches/server/0366-Add-permission-for-command-blocks.patch @@ -18,7 +18,7 @@ index 4623c8acd125dff4919c4e2045b848310d785da5..86e4559da2344f228ef4d1c4ac3c115f return false; } else if (this.player.blockActionRestricted(this.level, pos, this.gameModeForPlayer)) { diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index a7d1b4d4668b90445e1cba786be74fbd069d3330..1aec0f76636b1bdc5bb0b300690099ad341873e8 100644 +index 71785d4b2204030dc718c10494840013d5ea72fa..51e718812f2696f05824c735962aa885069d68e3 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -816,7 +816,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl diff --git a/patches/server/0368-Fix-Per-World-Difficulty-Remembering-Difficulty.patch b/patches/server/0368-Fix-Per-World-Difficulty-Remembering-Difficulty.patch index cf9db52420..329a3d31a9 100644 --- a/patches/server/0368-Fix-Per-World-Difficulty-Remembering-Difficulty.patch +++ b/patches/server/0368-Fix-Per-World-Difficulty-Remembering-Difficulty.patch @@ -76,7 +76,7 @@ index fe1975675189c6d1a63c42b7959fa40b5ac95ef8..9a3e73a5c206b78dfcf6f41a47b61434 @Override diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 1aec0f76636b1bdc5bb0b300690099ad341873e8..11f9853bd48cec693c99edb3d43f0356a0852d5e 100644 +index 51e718812f2696f05824c735962aa885069d68e3..20d7b673df5e2def564c9b0daee1e943d7522deb 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -3366,7 +3366,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl diff --git a/patches/server/0373-Do-not-accept-invalid-client-settings.patch b/patches/server/0373-Do-not-accept-invalid-client-settings.patch index c59997797f..5a6f7f0a1f 100644 --- a/patches/server/0373-Do-not-accept-invalid-client-settings.patch +++ b/patches/server/0373-Do-not-accept-invalid-client-settings.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Do not accept invalid client settings diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 11f9853bd48cec693c99edb3d43f0356a0852d5e..417930ad0ffe395e9211a6bf1e6c192af659471b 100644 +index 20d7b673df5e2def564c9b0daee1e943d7522deb..d30febf2f37f2b9c864184f272d5949cbe2dc1e8 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -3352,6 +3352,13 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl diff --git a/patches/server/0401-Fix-Entity-Teleportation-and-cancel-velocity-if-tele.patch b/patches/server/0401-Fix-Entity-Teleportation-and-cancel-velocity-if-tele.patch index fa6d3ee774..37e4591ad7 100644 --- a/patches/server/0401-Fix-Entity-Teleportation-and-cancel-velocity-if-tele.patch +++ b/patches/server/0401-Fix-Entity-Teleportation-and-cancel-velocity-if-tele.patch @@ -9,7 +9,7 @@ as this is how Vanilla teleports entities. Cancel any pending motion when teleported. diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 417930ad0ffe395e9211a6bf1e6c192af659471b..6f96f1522ae552940cf279194bd84630c3e4853c 100644 +index d30febf2f37f2b9c864184f272d5949cbe2dc1e8..748458bf569e7b96fd7cea6bd8a29d1735eb7d25 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -680,7 +680,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl diff --git a/patches/server/0411-Fix-for-large-move-vectors-crashing-server.patch b/patches/server/0411-Fix-for-large-move-vectors-crashing-server.patch index c2cff41458..12c0f652ae 100644 --- a/patches/server/0411-Fix-for-large-move-vectors-crashing-server.patch +++ b/patches/server/0411-Fix-for-large-move-vectors-crashing-server.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Fix for large move vectors crashing server Check movement distance also based on current position. diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 6f96f1522ae552940cf279194bd84630c3e4853c..bfcf0a27ab8d90742b83e9c200f2ec867e2424fe 100644 +index 748458bf569e7b96fd7cea6bd8a29d1735eb7d25..8be41f7ea383a27331109813e04d807f38bf6736 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -499,9 +499,9 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl @@ -33,7 +33,7 @@ index 6f96f1522ae552940cf279194bd84630c3e4853c..bfcf0a27ab8d90742b83e9c200f2ec86 + double currDeltaZ = toZ - fromZ; + double d10 = Math.max(d6 * d6 + d7 * d7 + d8 * d8, (currDeltaX * currDeltaX + currDeltaY * currDeltaY + currDeltaZ * currDeltaZ) - 1); + double otherFieldX = d3 - this.vehicleLastGoodX; -+ double otherFieldY = d4 - this.vehicleLastGoodY - 1.0E-6D; ++ double otherFieldY = d4 - this.vehicleLastGoodY; + double otherFieldZ = d5 - this.vehicleLastGoodZ; + d10 = Math.max(d10, (otherFieldX * otherFieldX + otherFieldY * otherFieldY + otherFieldZ * otherFieldZ) - 1); + // Paper end - fix large move vectors killing the server @@ -48,7 +48,7 @@ index 6f96f1522ae552940cf279194bd84630c3e4853c..bfcf0a27ab8d90742b83e9c200f2ec86 - d7 = d4 - this.vehicleLastGoodY; - d8 = d5 - this.vehicleLastGoodZ; + d6 = d3 - this.vehicleLastGoodX; // Paper - diff on change, used for checking large move vectors above -+ d7 = d4 - this.vehicleLastGoodY - 1.0E-6D; // Paper - diff on change, used for checking large move vectors above ++ d7 = d4 - this.vehicleLastGoodY; // Paper - diff on change, used for checking large move vectors above + d8 = d5 - this.vehicleLastGoodZ; // Paper - diff on change, used for checking large move vectors above boolean flag1 = entity.verticalCollisionBelow; diff --git a/patches/server/0435-Limit-recipe-packets.patch b/patches/server/0435-Limit-recipe-packets.patch index 66c9b8f393..f12a292853 100644 --- a/patches/server/0435-Limit-recipe-packets.patch +++ b/patches/server/0435-Limit-recipe-packets.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Limit recipe packets diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index bfcf0a27ab8d90742b83e9c200f2ec867e2424fe..65bc724ba824e4b65005f0886de768506a9b4160 100644 +index 8be41f7ea383a27331109813e04d807f38bf6736..bf4b64f07f21d816eb866d23b290c2f5e5471c70 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -279,6 +279,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl diff --git a/patches/server/0446-Allow-disabling-mob-spawner-spawn-egg-transformation.patch b/patches/server/0446-Allow-disabling-mob-spawner-spawn-egg-transformation.patch new file mode 100644 index 0000000000..140d605ba0 --- /dev/null +++ b/patches/server/0446-Allow-disabling-mob-spawner-spawn-egg-transformation.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: BrodyBeckwith <[email protected]> +Date: Fri, 9 Oct 2020 20:30:12 -0400 +Subject: [PATCH] Allow disabling mob spawner spawn egg transformation + + +diff --git a/src/main/java/net/minecraft/world/item/SpawnEggItem.java b/src/main/java/net/minecraft/world/item/SpawnEggItem.java +index ecc3193b4276a083461780eddab9f7b1c34175a8..cc7e9b87e919b4ef8cf77cd780c890fd9a9cfa50 100644 +--- a/src/main/java/net/minecraft/world/item/SpawnEggItem.java ++++ b/src/main/java/net/minecraft/world/item/SpawnEggItem.java +@@ -63,6 +63,8 @@ public class SpawnEggItem extends Item { + EntityType entitytypes; + + if (tileentity instanceof Spawner) { ++ if (world.paperConfig().entities.spawning.disableMobSpawnerSpawnEggTransformation) return InteractionResult.FAIL; // Paper - Allow disabling mob spawner spawn egg transformation ++ + Spawner spawner = (Spawner) tileentity; + + entitytypes = this.getType(world.registryAccess(), itemstack); diff --git a/patches/server/0447-Fix-Not-a-string-Map-Conversion-spam.patch b/patches/server/0447-Fix-Not-a-string-Map-Conversion-spam.patch new file mode 100644 index 0000000000..8952de7633 --- /dev/null +++ b/patches/server/0447-Fix-Not-a-string-Map-Conversion-spam.patch @@ -0,0 +1,45 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Aikar <[email protected]> +Date: Thu, 8 Oct 2020 00:00:25 -0400 +Subject: [PATCH] Fix "Not a string" Map Conversion spam + +The maps did convert successfully, but had noisy logs due to Spigot +implementing this logic incorrectly. + +This stops the spam by converting the old format to new before +requesting the world. + +Track spigot issue to see when fixed: https://hub.spigotmc.org/jira/browse/SPIGOT-6181 + +diff --git a/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java b/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java +index c21ae4975206398e7d20b37a749b830b9219c746..a89f0b652c515efbc24ffc9af47fa65082946e54 100644 +--- a/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java ++++ b/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java +@@ -123,7 +123,26 @@ public class MapItemSavedData extends SavedData { + } + + public static MapItemSavedData load(CompoundTag nbt, HolderLookup.Provider registries) { +- DataResult<ResourceKey<Level>> dataresult = DimensionType.parseLegacy(new Dynamic(NbtOps.INSTANCE, nbt.get("dimension"))); // CraftBukkit - decompile error ++ // Paper start - fix "Not a string" spam ++ Tag dimension = nbt.get("dimension"); ++ if (dimension instanceof final net.minecraft.nbt.NumericTag numericTag && numericTag.getAsInt() >= CraftWorld.CUSTOM_DIMENSION_OFFSET) { ++ long least = nbt.getLong("UUIDLeast"); ++ long most = nbt.getLong("UUIDMost"); ++ ++ if (least != 0L && most != 0L) { ++ UUID uuid = new UUID(most, least); ++ CraftWorld world = (CraftWorld) Bukkit.getWorld(uuid); ++ if (world != null) { ++ dimension = net.minecraft.nbt.StringTag.valueOf("minecraft:" + world.getName().toLowerCase(java.util.Locale.ENGLISH)); ++ } else { ++ dimension = net.minecraft.nbt.StringTag.valueOf("bukkit:_invalidworld_"); ++ } ++ } else { ++ dimension = net.minecraft.nbt.StringTag.valueOf("bukkit:_invalidworld_"); ++ } ++ } ++ DataResult<ResourceKey<Level>> dataresult = DimensionType.parseLegacy(new Dynamic(NbtOps.INSTANCE, dimension)); // CraftBukkit - decompile error ++ // Paper end - fix "Not a string" spam + Logger logger = MapItemSavedData.LOGGER; + + Objects.requireNonNull(logger); diff --git a/patches/server/0448-Add-PlayerFlowerPotManipulateEvent.patch b/patches/server/0448-Add-PlayerFlowerPotManipulateEvent.patch new file mode 100644 index 0000000000..8b8fb61ee2 --- /dev/null +++ b/patches/server/0448-Add-PlayerFlowerPotManipulateEvent.patch @@ -0,0 +1,48 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: MisterVector <[email protected]> +Date: Tue, 13 Aug 2019 19:45:06 -0700 +Subject: [PATCH] Add PlayerFlowerPotManipulateEvent + + +diff --git a/src/main/java/net/minecraft/world/level/block/FlowerPotBlock.java b/src/main/java/net/minecraft/world/level/block/FlowerPotBlock.java +index 4c7723b9f369391ab253c4a60510e318fb7cfce3..f45a8741e6707f033b0205eee03c59aa889565e1 100644 +--- a/src/main/java/net/minecraft/world/level/block/FlowerPotBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/FlowerPotBlock.java +@@ -63,6 +63,18 @@ public class FlowerPotBlock extends Block { + } else if (!this.isEmpty()) { + return InteractionResult.CONSUME; + } else { ++ // Paper start - Add PlayerFlowerPotManipulateEvent ++ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(world, pos); ++ org.bukkit.inventory.ItemStack placedStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(stack); ++ ++ io.papermc.paper.event.player.PlayerFlowerPotManipulateEvent event = new io.papermc.paper.event.player.PlayerFlowerPotManipulateEvent((org.bukkit.entity.Player) player.getBukkitEntity(), block, placedStack, true); ++ if (!event.callEvent()) { ++ // Update client ++ player.containerMenu.sendAllDataToRemote(); ++ ++ return InteractionResult.CONSUME; ++ } ++ // Paper end - Add PlayerFlowerPotManipulateEvent + world.setBlock(pos, blockState, 3); + world.gameEvent(player, GameEvent.BLOCK_CHANGE, pos); + player.awardStat(Stats.POT_FLOWER); +@@ -77,6 +89,18 @@ public class FlowerPotBlock extends Block { + return InteractionResult.CONSUME; + } else { + ItemStack itemStack = new ItemStack(this.potted); ++ // Paper start - Add PlayerFlowerPotManipulateEvent ++ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(world, pos); ++ org.bukkit.inventory.ItemStack pottedStack = new org.bukkit.inventory.ItemStack(org.bukkit.craftbukkit.block.CraftBlockType.minecraftToBukkit(this.potted)); ++ ++ io.papermc.paper.event.player.PlayerFlowerPotManipulateEvent event = new io.papermc.paper.event.player.PlayerFlowerPotManipulateEvent((org.bukkit.entity.Player) player.getBukkitEntity(), block, pottedStack, false); ++ if (!event.callEvent()) { ++ // Update client ++ player.containerMenu.sendAllDataToRemote(); ++ ++ return InteractionResult.PASS; ++ } ++ // Paper end - Add PlayerFlowerPotManipulateEvent + if (!player.addItem(itemStack)) { + player.drop(itemStack, false); + } diff --git a/patches/server/0449-Fix-interact-event-not-being-called-sometimes.patch b/patches/server/0449-Fix-interact-event-not-being-called-sometimes.patch new file mode 100644 index 0000000000..3c9078dd88 --- /dev/null +++ b/patches/server/0449-Fix-interact-event-not-being-called-sometimes.patch @@ -0,0 +1,48 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: TheMolkaPL <[email protected]> +Date: Sun, 21 Jun 2020 17:21:46 +0200 +Subject: [PATCH] Fix interact event not being called sometimes + +* Call PlayerInteractEvent when left-clicking on a block in adventure + mode. +* Call PlayerInteractEvent when left-clicking an Entity that is out of + range in adventure/survival (entity reach is 3.0). + +Co-authored-by: Moulberry <[email protected]> + +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index bf4b64f07f21d816eb866d23b290c2f5e5471c70..3d4ab22077eb358168b2959c3d1beb10b7104ab7 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -1842,7 +1842,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + } else if (enuminteractionresult instanceof InteractionResult.Success) { + InteractionResult.Success enuminteractionresult_d = (InteractionResult.Success) enuminteractionresult; + +- if (enuminteractionresult_d.swingSource() == InteractionResult.SwingSource.SERVER) { ++ if (enuminteractionresult_d.swingSource() == InteractionResult.SwingSource.SERVER && !this.player.gameMode.interactResult) { + this.player.swing(enumhand, true); + } + } +@@ -2466,13 +2466,20 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + double d3 = Math.max(this.player.blockInteractionRange(), this.player.entityInteractionRange()); + // SPIGOT-5607: Only call interact event if no block or entity is being clicked. Use bukkit ray trace method, because it handles blocks and entities at the same time + // SPIGOT-7429: Make sure to call PlayerInteractEvent for spectators and non-pickable entities +- org.bukkit.util.RayTraceResult result = this.player.level().getWorld().rayTrace(origin, origin.getDirection(), d3, org.bukkit.FluidCollisionMode.NEVER, false, 0.1, entity -> { ++ org.bukkit.util.RayTraceResult result = this.player.level().getWorld().rayTrace(origin, origin.getDirection(), d3, org.bukkit.FluidCollisionMode.NEVER, false, 0.0, entity -> { // Paper - Call interact event; change raySize from 0.1 to 0.0 + Entity handle = ((CraftEntity) entity).getHandle(); + return entity != this.player.getBukkitEntity() && this.player.getBukkitEntity().canSee(entity) && !handle.isSpectator() && handle.isPickable() && !handle.isPassengerOfSameVehicle(this.player); + }); + if (result == null) { + CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_AIR, this.player.getInventory().getSelected(), InteractionHand.MAIN_HAND); +- } ++ } else { // Paper start - Call interact event ++ GameType gameType = this.player.gameMode.getGameModeForPlayer(); ++ if (gameType == GameType.ADVENTURE && result.getHitBlock() != null) { ++ CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_BLOCK, ((org.bukkit.craftbukkit.block.CraftBlock) result.getHitBlock()).getPosition(), org.bukkit.craftbukkit.block.CraftBlock.blockFaceToNotch(result.getHitBlockFace()), this.player.getInventory().getSelected(), InteractionHand.MAIN_HAND); ++ } else if (gameType != GameType.CREATIVE && result.getHitEntity() != null && origin.toVector().distanceSquared(result.getHitPosition()) > this.player.entityInteractionRange() * this.player.entityInteractionRange()) { ++ CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_AIR, this.player.getInventory().getSelected(), InteractionHand.MAIN_HAND); ++ } ++ } // Paper end - Call interact event + + // Arm swing animation + PlayerAnimationEvent event = new PlayerAnimationEvent(this.getCraftPlayer(), (packet.getHand() == InteractionHand.MAIN_HAND) ? PlayerAnimationType.ARM_SWING : PlayerAnimationType.OFF_ARM_SWING); diff --git a/patches/server/0450-Zombie-API-breaking-doors.patch b/patches/server/0450-Zombie-API-breaking-doors.patch new file mode 100644 index 0000000000..b0741f110f --- /dev/null +++ b/patches/server/0450-Zombie-API-breaking-doors.patch @@ -0,0 +1,24 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Wed, 18 Nov 2020 11:32:46 -0800 +Subject: [PATCH] Zombie API - breaking doors + +== AT == +public net.minecraft.world.entity.monster.Zombie supportsBreakDoorGoal()Z + +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftZombie.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftZombie.java +index 4412c913123f7521f449c98b60378e8d3b1671ce..dfc2b40e20069705f92d86a6898e3e8348bf4dcd 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftZombie.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftZombie.java +@@ -122,6 +122,11 @@ public class CraftZombie extends CraftMonster implements Zombie { + public void setShouldBurnInDay(boolean shouldBurnInDay) { + getHandle().setShouldBurnInDay(shouldBurnInDay); + } ++ ++ @Override ++ public boolean supportsBreakingDoors() { ++ return true; // All zombies are now capable of breaking doors, see https://bugs.mojang.com/browse/MC-137053 ++ } + // Paper end + + @Override diff --git a/patches/server/0451-Fix-nerfed-slime-when-splitting.patch b/patches/server/0451-Fix-nerfed-slime-when-splitting.patch new file mode 100644 index 0000000000..c35358b895 --- /dev/null +++ b/patches/server/0451-Fix-nerfed-slime-when-splitting.patch @@ -0,0 +1,18 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Mon, 24 Aug 2020 08:39:06 -0700 +Subject: [PATCH] Fix nerfed slime when splitting + + +diff --git a/src/main/java/net/minecraft/world/entity/monster/Slime.java b/src/main/java/net/minecraft/world/entity/monster/Slime.java +index 26f4db572dc6c25a9815b8f352d8829e252fa1a2..129f0cbc0469cb2804db6088b53347d88d91f4eb 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/Slime.java ++++ b/src/main/java/net/minecraft/world/entity/monster/Slime.java +@@ -250,6 +250,7 @@ public class Slime extends Mob implements Enemy { + float f3 = ((float) (l / 2) - 0.5F) * f1; + + Slime converted = this.convertTo(this.getType(), new ConversionParams(ConversionType.SPLIT_ON_DEATH, false, false, scoreboardteam), EntitySpawnReason.TRIGGERED, (entityslime) -> { // CraftBukkit ++ entityslime.aware = this.aware; // Paper - Fix nerfed slime when splitting + entityslime.setSize(j, true); + entityslime.moveTo(this.getX() + (double) f2, this.getY() + 0.5D, this.getZ() + (double) f3, this.random.nextFloat() * 360.0F, 0.0F); + // CraftBukkit start diff --git a/patches/server/0452-Add-EntityLoadCrossbowEvent.patch b/patches/server/0452-Add-EntityLoadCrossbowEvent.patch new file mode 100644 index 0000000000..be830ab067 --- /dev/null +++ b/patches/server/0452-Add-EntityLoadCrossbowEvent.patch @@ -0,0 +1,68 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: JRoy <[email protected]> +Date: Wed, 7 Oct 2020 12:04:01 -0400 +Subject: [PATCH] Add EntityLoadCrossbowEvent + + +diff --git a/src/main/java/net/minecraft/world/item/CrossbowItem.java b/src/main/java/net/minecraft/world/item/CrossbowItem.java +index a81e2f1e5abc20a95c562c1b9b1f7af489eaaaab..be1902a307a54434644b242b429ad47c271d2a0c 100644 +--- a/src/main/java/net/minecraft/world/item/CrossbowItem.java ++++ b/src/main/java/net/minecraft/world/item/CrossbowItem.java +@@ -90,7 +90,14 @@ public class CrossbowItem extends ProjectileWeaponItem { + public boolean releaseUsing(ItemStack stack, Level world, LivingEntity user, int remainingUseTicks) { + int i = this.getUseDuration(stack, user) - remainingUseTicks; + float f = getPowerForTime(i, stack, user); +- if (f >= 1.0F && !isCharged(stack) && tryLoadProjectiles(user, stack)) { ++ // Paper start - Add EntityLoadCrossbowEvent ++ if (f >= 1.0F && !isCharged(stack)) { ++ final io.papermc.paper.event.entity.EntityLoadCrossbowEvent event = new io.papermc.paper.event.entity.EntityLoadCrossbowEvent(user.getBukkitLivingEntity(), stack.asBukkitMirror(), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(user.getUsedItemHand())); ++ if (!event.callEvent() || !tryLoadProjectiles(user, stack, event.shouldConsumeItem()) || !event.shouldConsumeItem()) { ++ if (user instanceof ServerPlayer player) player.containerMenu.sendAllDataToRemote(); ++ return false; ++ } ++ // Paper end - Add EntityLoadCrossbowEvent + CrossbowItem.ChargingSounds chargingSounds = this.getChargingSounds(stack); + chargingSounds.end() + .ifPresent( +@@ -111,8 +118,14 @@ public class CrossbowItem extends ProjectileWeaponItem { + } + } + +- private static boolean tryLoadProjectiles(LivingEntity shooter, ItemStack crossbow) { +- List<ItemStack> list = draw(crossbow, shooter.getProjectile(crossbow), shooter); ++ @io.papermc.paper.annotation.DoNotUse // Paper - Add EntityLoadCrossbowEvent ++ private static boolean tryLoadProjectiles(LivingEntity shooter, ItemStack crossbow) { ++ // Paper start - Add EntityLoadCrossbowEvent ++ return CrossbowItem.tryLoadProjectiles(shooter, crossbow, true); ++ } ++ private static boolean tryLoadProjectiles(LivingEntity shooter, ItemStack crossbow, boolean consume) { ++ List<ItemStack> list = draw(crossbow, shooter.getProjectile(crossbow), shooter, consume); ++ // Paper end - Add EntityLoadCrossbowEvent + if (!list.isEmpty()) { + crossbow.set(DataComponents.CHARGED_PROJECTILES, ChargedProjectiles.of(list)); + return true; +diff --git a/src/main/java/net/minecraft/world/item/ProjectileWeaponItem.java b/src/main/java/net/minecraft/world/item/ProjectileWeaponItem.java +index a7d0ac6513fd888e222b3128afc1a227ea91f1d2..78ba170a83f8c026bd110eae494c52577182ed61 100644 +--- a/src/main/java/net/minecraft/world/item/ProjectileWeaponItem.java ++++ b/src/main/java/net/minecraft/world/item/ProjectileWeaponItem.java +@@ -109,6 +109,11 @@ public abstract class ProjectileWeaponItem extends Item { + } + + protected static List<ItemStack> draw(ItemStack stack, ItemStack projectileStack, LivingEntity shooter) { ++ // Paper start ++ return draw(stack, projectileStack, shooter, true); ++ } ++ protected static List<ItemStack> draw(ItemStack stack, ItemStack projectileStack, LivingEntity shooter, boolean consume) { ++ // Paper end + if (projectileStack.isEmpty()) { + return List.of(); + } else { +@@ -128,7 +133,7 @@ public abstract class ProjectileWeaponItem extends Item { + ItemStack itemstack2 = projectileStack.copy(); + + for (int k = 0; k < j; ++k) { +- ItemStack itemstack3 = ProjectileWeaponItem.useAmmo(stack, k == 0 ? projectileStack : itemstack2, shooter, k > 0); ++ ItemStack itemstack3 = ProjectileWeaponItem.useAmmo(stack, k == 0 ? projectileStack : itemstack2, shooter, k > 0 || !consume); // Paper + + if (!itemstack3.isEmpty()) { + list.add(itemstack3); diff --git a/patches/server/0453-Add-WorldGameRuleChangeEvent.patch b/patches/server/0453-Add-WorldGameRuleChangeEvent.patch new file mode 100644 index 0000000000..a35834efa7 --- /dev/null +++ b/patches/server/0453-Add-WorldGameRuleChangeEvent.patch @@ -0,0 +1,100 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sun, 20 Dec 2020 16:41:44 -0800 +Subject: [PATCH] Add WorldGameRuleChangeEvent + + +diff --git a/src/main/java/net/minecraft/server/commands/GameRuleCommand.java b/src/main/java/net/minecraft/server/commands/GameRuleCommand.java +index 6fc70f07f9ba964ff6f5176367dab788decfc917..da0600ab3b0a983ac36cb777d21160bdaced7c52 100644 +--- a/src/main/java/net/minecraft/server/commands/GameRuleCommand.java ++++ b/src/main/java/net/minecraft/server/commands/GameRuleCommand.java +@@ -36,7 +36,7 @@ public class GameRuleCommand { + CommandSourceStack commandlistenerwrapper = (CommandSourceStack) context.getSource(); + T t0 = commandlistenerwrapper.getLevel().getGameRules().getRule(key); // CraftBukkit + +- t0.setFromArgument(context, "value"); ++ t0.setFromArgument(context, "value", key); // Paper - Add WorldGameRuleChangeEvent + commandlistenerwrapper.sendSuccess(() -> { + return Component.translatable("commands.gamerule.set", key.getId(), t0.toString()); + }, true); +diff --git a/src/main/java/net/minecraft/world/level/GameRules.java b/src/main/java/net/minecraft/world/level/GameRules.java +index 7ea92a0b0f5d4eb6bd873e61c42bc0499d5d2028..09299e45552eb998fd02123c3921c0653f85083d 100644 +--- a/src/main/java/net/minecraft/world/level/GameRules.java ++++ b/src/main/java/net/minecraft/world/level/GameRules.java +@@ -322,10 +322,10 @@ public class GameRules { + this.type = type; + } + +- protected abstract void updateFromArgument(CommandContext<CommandSourceStack> context, String name); ++ protected abstract void updateFromArgument(CommandContext<CommandSourceStack> context, String name, GameRules.Key<T> gameRuleKey); // Paper - Add WorldGameRuleChangeEvent + +- public void setFromArgument(CommandContext<CommandSourceStack> context, String name) { +- this.updateFromArgument(context, name); ++ public void setFromArgument(CommandContext<CommandSourceStack> context, String name, GameRules.Key<T> gameRuleKey) { // Paper - Add WorldGameRuleChangeEvent ++ this.updateFromArgument(context, name, gameRuleKey); // Paper - Add WorldGameRuleChangeEvent + this.onChanged(((CommandSourceStack) context.getSource()).getLevel()); // CraftBukkit - per-world + } + +@@ -383,8 +383,11 @@ public class GameRules { + } + + @Override +- protected void updateFromArgument(CommandContext<CommandSourceStack> context, String name) { +- this.value = BoolArgumentType.getBool(context, name); ++ protected void updateFromArgument(CommandContext<CommandSourceStack> context, String name, GameRules.Key<BooleanValue> gameRuleKey) { // Paper start - Add WorldGameRuleChangeEvent ++ io.papermc.paper.event.world.WorldGameRuleChangeEvent event = new io.papermc.paper.event.world.WorldGameRuleChangeEvent(context.getSource().getBukkitWorld(), context.getSource().getBukkitSender(), (org.bukkit.GameRule<Boolean>) org.bukkit.GameRule.getByName(gameRuleKey.toString()), String.valueOf(BoolArgumentType.getBool(context, name))); ++ if (!event.callEvent()) return; ++ this.value = Boolean.parseBoolean(event.getValue()); ++ // Paper end - Add WorldGameRuleChangeEvent + } + + public boolean get() { +@@ -456,8 +459,11 @@ public class GameRules { + } + + @Override +- protected void updateFromArgument(CommandContext<CommandSourceStack> context, String name) { +- this.value = IntegerArgumentType.getInteger(context, name); ++ protected void updateFromArgument(CommandContext<CommandSourceStack> context, String name, GameRules.Key<IntegerValue> gameRuleKey) { // Paper start - Add WorldGameRuleChangeEvent ++ io.papermc.paper.event.world.WorldGameRuleChangeEvent event = new io.papermc.paper.event.world.WorldGameRuleChangeEvent(context.getSource().getBukkitWorld(), context.getSource().getBukkitSender(), (org.bukkit.GameRule<Integer>) org.bukkit.GameRule.getByName(gameRuleKey.toString()), String.valueOf(IntegerArgumentType.getInteger(context, name))); ++ if (!event.callEvent()) return; ++ this.value = Integer.parseInt(event.getValue()); ++ // Paper end - Add WorldGameRuleChangeEvent + } + + public int get() { +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index b877904bd18c96a4a7e49fb3e1aba2b6109f15cd..69464a4fe467128121fe1a9ba08c9c7154e22c7f 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -1884,9 +1884,14 @@ public class CraftWorld extends CraftRegionAccessor implements World { + if (rule == null || value == null) return false; + + if (!this.isGameRule(rule)) return false; ++ // Paper start - Add WorldGameRuleChangeEvent ++ GameRule<?> gameRule = GameRule.getByName(rule); ++ io.papermc.paper.event.world.WorldGameRuleChangeEvent event = new io.papermc.paper.event.world.WorldGameRuleChangeEvent(this, null, gameRule, value); ++ if (!event.callEvent()) return false; ++ // Paper end - Add WorldGameRuleChangeEvent + + GameRules.Value<?> handle = this.getHandle().getGameRules().getRule(this.getGameRulesNMS().get(rule)); +- handle.deserialize(value); ++ handle.deserialize(event.getValue()); // Paper - Add WorldGameRuleChangeEvent + handle.onChanged(this.getHandle()); + return true; + } +@@ -1921,9 +1926,13 @@ public class CraftWorld extends CraftRegionAccessor implements World { + Preconditions.checkArgument(newValue != null, "GameRule value cannot be null"); + + if (!this.isGameRule(rule.getName())) return false; ++ // Paper start - Add WorldGameRuleChangeEvent ++ io.papermc.paper.event.world.WorldGameRuleChangeEvent event = new io.papermc.paper.event.world.WorldGameRuleChangeEvent(this, null, rule, String.valueOf(newValue)); ++ if (!event.callEvent()) return false; ++ // Paper end - Add WorldGameRuleChangeEvent + + GameRules.Value<?> handle = this.getHandle().getGameRules().getRule(this.getGameRulesNMS().get(rule.getName())); +- handle.deserialize(newValue.toString()); ++ handle.deserialize(event.getValue()); // Paper - Add WorldGameRuleChangeEvent + handle.onChanged(this.getHandle()); + return true; + } diff --git a/patches/server/0454-Add-ServerResourcesReloadedEvent.patch b/patches/server/0454-Add-ServerResourcesReloadedEvent.patch new file mode 100644 index 0000000000..b6ef8ff126 --- /dev/null +++ b/patches/server/0454-Add-ServerResourcesReloadedEvent.patch @@ -0,0 +1,54 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Wed, 2 Dec 2020 20:04:01 -0800 +Subject: [PATCH] Add ServerResourcesReloadedEvent + + +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index c036c391172ffb4b14b7a5f77776a68e3670775c..d6fe710a4e3784ba38776a0496eac7fa702aed88 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -2150,7 +2150,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa + return this.functionManager; + } + ++ // Paper start - Add ServerResourcesReloadedEvent ++ @Deprecated @io.papermc.paper.annotation.DoNotUse + public CompletableFuture<Void> reloadResources(Collection<String> dataPacks) { ++ return this.reloadResources(dataPacks, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause.PLUGIN); ++ } ++ public CompletableFuture<Void> reloadResources(Collection<String> dataPacks, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause cause) { ++ // Paper end - Add ServerResourcesReloadedEvent + CompletableFuture<Void> completablefuture = CompletableFuture.supplyAsync(() -> { + Stream<String> stream = dataPacks.stream(); // CraftBukkit - decompile error + PackRepository resourcepackrepository = this.packRepository; +@@ -2185,6 +2191,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa + this.structureTemplateManager.onResourceManagerReload(this.resources.resourceManager); + this.fuelValues = FuelValues.vanillaBurnTimes(this.registries.compositeAccess(), this.worldData.enabledFeatures()); + org.bukkit.craftbukkit.block.data.CraftBlockData.reloadCache(); // Paper - cache block data strings; they can be defined by datapacks so refresh it here ++ new io.papermc.paper.event.server.ServerResourcesReloadedEvent(cause).callEvent(); // Paper - Add ServerResourcesReloadedEvent; fire after everything has been reloaded + }, this); + + if (this.isSameThread()) { +diff --git a/src/main/java/net/minecraft/server/commands/ReloadCommand.java b/src/main/java/net/minecraft/server/commands/ReloadCommand.java +index fa18d018a8458b30c0048f7e59aea39f928d974a..c020c86194723a5c89816f91e0b7c5eeaf132b7e 100644 +--- a/src/main/java/net/minecraft/server/commands/ReloadCommand.java ++++ b/src/main/java/net/minecraft/server/commands/ReloadCommand.java +@@ -20,7 +20,7 @@ public class ReloadCommand { + public ReloadCommand() {} + + public static void reloadPacks(Collection<String> dataPacks, CommandSourceStack source) { +- source.getServer().reloadResources(dataPacks).exceptionally((throwable) -> { ++ source.getServer().reloadResources(dataPacks, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause.COMMAND).exceptionally((throwable) -> { // Paper - Add ServerResourcesReloadedEvent + ReloadCommand.LOGGER.warn("Failed to execute reload", throwable); + source.sendFailure(Component.translatable("commands.reload.failure")); + return null; +@@ -50,7 +50,7 @@ public class ReloadCommand { + WorldData savedata = minecraftserver.getWorldData(); + Collection<String> collection = resourcepackrepository.getSelectedIds(); + Collection<String> collection1 = ReloadCommand.discoverNewPacks(resourcepackrepository, savedata, collection); +- minecraftserver.reloadResources(collection1); ++ minecraftserver.reloadResources(collection1, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause.PLUGIN); // Paper - Add ServerResourcesReloadedEvent + } + // CraftBukkit end + diff --git a/patches/server/0455-Add-world-settings-for-mobs-picking-up-loot.patch b/patches/server/0455-Add-world-settings-for-mobs-picking-up-loot.patch new file mode 100644 index 0000000000..284e61124a --- /dev/null +++ b/patches/server/0455-Add-world-settings-for-mobs-picking-up-loot.patch @@ -0,0 +1,32 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sat, 28 Nov 2020 18:43:52 -0800 +Subject: [PATCH] Add world settings for mobs picking up loot + + +diff --git a/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java b/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java +index b7b567192f14ca8bac24b533dcbb5b5bd4f5a3aa..7c80c79a87ec438d0891c5c977e162f272d80039 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java ++++ b/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java +@@ -152,7 +152,7 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo + this.populateDefaultEquipmentSlots(randomsource, difficulty); + this.populateDefaultEquipmentEnchantments(world, randomsource, difficulty); + this.reassessWeaponGoal(); +- this.setCanPickUpLoot(randomsource.nextFloat() < 0.55F * difficulty.getSpecialMultiplier()); ++ this.setCanPickUpLoot(this.level().paperConfig().entities.behavior.mobsCanAlwaysPickUpLoot.skeletons || randomsource.nextFloat() < 0.55F * difficulty.getSpecialMultiplier()); // Paper - Add world settings for mobs picking up loot + if (this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) { + LocalDate localdate = LocalDate.now(); + int i = localdate.get(ChronoField.DAY_OF_MONTH); +diff --git a/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java +index c182bdcc5da5652f8b34b4cb8d28651cf79009fe..34e46a64b3638f749a571d080fd8e7ac1f57edba 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java ++++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java +@@ -518,7 +518,7 @@ public class Zombie extends Monster { + float f = difficulty.getSpecialMultiplier(); + + if (spawnReason != EntitySpawnReason.CONVERSION) { +- this.setCanPickUpLoot(randomsource.nextFloat() < 0.55F * f); ++ this.setCanPickUpLoot(this.level().paperConfig().entities.behavior.mobsCanAlwaysPickUpLoot.zombies || randomsource.nextFloat() < 0.55F * f); // Paper - Add world settings for mobs picking up loot + } + + if (object == null) { diff --git a/patches/server/0456-Add-BlockFailedDispenseEvent.patch b/patches/server/0456-Add-BlockFailedDispenseEvent.patch new file mode 100644 index 0000000000..7d8f896e06 --- /dev/null +++ b/patches/server/0456-Add-BlockFailedDispenseEvent.patch @@ -0,0 +1,50 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: TheViperShow <[email protected]> +Date: Wed, 22 Apr 2020 09:40:38 +0200 +Subject: [PATCH] Add BlockFailedDispenseEvent + + +diff --git a/src/main/java/net/minecraft/world/level/block/DispenserBlock.java b/src/main/java/net/minecraft/world/level/block/DispenserBlock.java +index 975c02461bb88d71a0e3efe91838fad3fd346587..d915ef1030728a3f6ff303977784097b712238d4 100644 +--- a/src/main/java/net/minecraft/world/level/block/DispenserBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/DispenserBlock.java +@@ -98,8 +98,10 @@ public class DispenserBlock extends BaseEntityBlock { + int i = tileentitydispenser.getRandomSlot(world.random); + + if (i < 0) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFailedDispenseEvent(world, pos)) { // Paper - Add BlockFailedDispenseEvent + world.levelEvent(1001, pos, 0); + world.gameEvent((Holder) GameEvent.BLOCK_ACTIVATE, pos, GameEvent.Context.of(tileentitydispenser.getBlockState())); ++ } // Paper - Add BlockFailedDispenseEvent + } else { + ItemStack itemstack = tileentitydispenser.getItem(i); + DispenseItemBehavior idispensebehavior = this.getDispenseMethod(world, itemstack); +diff --git a/src/main/java/net/minecraft/world/level/block/DropperBlock.java b/src/main/java/net/minecraft/world/level/block/DropperBlock.java +index a08e8571f3a83afc80c2f1758a9029cd28ed6947..91b514967405115f22edf4255775361a672e5c2f 100644 +--- a/src/main/java/net/minecraft/world/level/block/DropperBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/DropperBlock.java +@@ -60,6 +60,7 @@ public class DropperBlock extends DispenserBlock { + int i = tileentitydispenser.getRandomSlot(world.random); + + if (i < 0) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFailedDispenseEvent(world, pos)) // Paper - Add BlockFailedDispenseEvent + world.levelEvent(1001, pos, 0); + } else { + ItemStack itemstack = tileentitydispenser.getItem(i); +diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +index f8751bf07479d6619ec1acb19f84a9af00ecd308..696867479e74c3c94e4131f2bbb97c857ed5e9dd 100644 +--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java ++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +@@ -2125,4 +2125,12 @@ public class CraftEventFactory { + return org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getPotion()); + } + // Paper end - WitchReadyPotionEvent ++ ++ // Paper start ++ public static boolean handleBlockFailedDispenseEvent(ServerLevel serverLevel, BlockPos pos) { ++ org.bukkit.block.Block block = CraftBlock.at(serverLevel, pos); ++ io.papermc.paper.event.block.BlockFailedDispenseEvent event = new io.papermc.paper.event.block.BlockFailedDispenseEvent(block); ++ return event.callEvent(); ++ } ++ // Paper end + } diff --git a/patches/server/0457-Add-PlayerLecternPageChangeEvent.patch b/patches/server/0457-Add-PlayerLecternPageChangeEvent.patch new file mode 100644 index 0000000000..15052d962d --- /dev/null +++ b/patches/server/0457-Add-PlayerLecternPageChangeEvent.patch @@ -0,0 +1,46 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Mon, 23 Nov 2020 12:58:51 -0800 +Subject: [PATCH] Add PlayerLecternPageChangeEvent + + +diff --git a/src/main/java/net/minecraft/world/inventory/LecternMenu.java b/src/main/java/net/minecraft/world/inventory/LecternMenu.java +index 1b3119751617366cf753008d38be566cd7ee2453..df4ae5d37b9aa5b8fb26c5773a47a5a85f831982 100644 +--- a/src/main/java/net/minecraft/world/inventory/LecternMenu.java ++++ b/src/main/java/net/minecraft/world/inventory/LecternMenu.java +@@ -64,6 +64,7 @@ public class LecternMenu extends AbstractContainerMenu { + @Override + public boolean clickMenuButton(net.minecraft.world.entity.player.Player player, int id) { + int j; ++ io.papermc.paper.event.player.PlayerLecternPageChangeEvent playerLecternPageChangeEvent; CraftInventoryLectern bukkitView; // Paper - Add PlayerLecternPageChangeEvent + + if (id >= 100) { + j = id - 100; +@@ -73,11 +74,25 @@ public class LecternMenu extends AbstractContainerMenu { + switch (id) { + case 1: + j = this.lecternData.get(0); +- this.setData(0, j - 1); ++ // Paper start - Add PlayerLecternPageChangeEvent ++ bukkitView = (CraftInventoryLectern) getBukkitView().getTopInventory(); ++ playerLecternPageChangeEvent = new io.papermc.paper.event.player.PlayerLecternPageChangeEvent((org.bukkit.entity.Player) player.getBukkitEntity(), bukkitView.getHolder(), bukkitView.getBook(), io.papermc.paper.event.player.PlayerLecternPageChangeEvent.PageChangeDirection.LEFT, j, j - 1); ++ if (!playerLecternPageChangeEvent.callEvent()) { ++ return false; ++ } ++ this.setData(0, playerLecternPageChangeEvent.getNewPage()); ++ // Paper end - Add PlayerLecternPageChangeEvent + return true; + case 2: + j = this.lecternData.get(0); +- this.setData(0, j + 1); ++ // Paper start - Add PlayerLecternPageChangeEvent ++ bukkitView = (CraftInventoryLectern) getBukkitView().getTopInventory(); ++ playerLecternPageChangeEvent = new io.papermc.paper.event.player.PlayerLecternPageChangeEvent((org.bukkit.entity.Player) player.getBukkitEntity(), bukkitView.getHolder(), bukkitView.getBook(), io.papermc.paper.event.player.PlayerLecternPageChangeEvent.PageChangeDirection.RIGHT, j, j + 1); ++ if (!playerLecternPageChangeEvent.callEvent()) { ++ return false; ++ } ++ this.setData(0, playerLecternPageChangeEvent.getNewPage()); ++ // Paper end - Add PlayerLecternPageChangeEvent + return true; + case 3: + if (!player.mayBuild()) { diff --git a/patches/server/0458-Add-PlayerLoomPatternSelectEvent.patch b/patches/server/0458-Add-PlayerLoomPatternSelectEvent.patch new file mode 100644 index 0000000000..b523b29e0f --- /dev/null +++ b/patches/server/0458-Add-PlayerLoomPatternSelectEvent.patch @@ -0,0 +1,45 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Wed, 25 Nov 2020 16:33:27 -0800 +Subject: [PATCH] Add PlayerLoomPatternSelectEvent + + +diff --git a/src/main/java/net/minecraft/world/inventory/LoomMenu.java b/src/main/java/net/minecraft/world/inventory/LoomMenu.java +index 58470d8e9a4ceb1eca05b342481ed8260588e225..1b7cf165ab0818792870f43719a6324b282bb57a 100644 +--- a/src/main/java/net/minecraft/world/inventory/LoomMenu.java ++++ b/src/main/java/net/minecraft/world/inventory/LoomMenu.java +@@ -162,8 +162,32 @@ public class LoomMenu extends AbstractContainerMenu { + @Override + public boolean clickMenuButton(net.minecraft.world.entity.player.Player player, int id) { + if (id >= 0 && id < this.selectablePatterns.size()) { +- this.selectedBannerPatternIndex.set(id); +- this.setupResultSlot((Holder) this.selectablePatterns.get(id)); ++ // Paper start - Add PlayerLoomPatternSelectEvent ++ int selectablePatternIndex = id; ++ io.papermc.paper.event.player.PlayerLoomPatternSelectEvent event = new io.papermc.paper.event.player.PlayerLoomPatternSelectEvent((Player) player.getBukkitEntity(), ((CraftInventoryLoom) getBukkitView().getTopInventory()), org.bukkit.craftbukkit.block.banner.CraftPatternType.minecraftHolderToBukkit(this.selectablePatterns.get(selectablePatternIndex))); ++ if (!event.callEvent()) { ++ player.containerMenu.sendAllDataToRemote(); ++ return false; ++ } ++ final Holder<BannerPattern> eventPattern = org.bukkit.craftbukkit.block.banner.CraftPatternType.bukkitToMinecraftHolder(event.getPatternType()); ++ Holder<BannerPattern> selectedPattern = null; ++ for (int i = 0; i < this.selectablePatterns.size(); i++) { ++ final Holder<BannerPattern> holder = this.selectablePatterns.get(i); ++ if (eventPattern.equals(holder)) { ++ selectablePatternIndex = i; ++ selectedPattern = holder; ++ break; ++ } ++ } ++ if (selectedPattern == null) { ++ selectedPattern = eventPattern; ++ selectablePatternIndex = -1; ++ } ++ ++ player.containerMenu.sendAllDataToRemote(); ++ this.selectedBannerPatternIndex.set(selectablePatternIndex); ++ this.setupResultSlot(java.util.Objects.requireNonNull(selectedPattern, "selectedPattern was null, this is unexpected")); ++ // Paper end - Add PlayerLoomPatternSelectEvent + return true; + } else { + return false; diff --git a/patches/server/0459-Configurable-door-breaking-difficulty.patch b/patches/server/0459-Configurable-door-breaking-difficulty.patch new file mode 100644 index 0000000000..94b37c735f --- /dev/null +++ b/patches/server/0459-Configurable-door-breaking-difficulty.patch @@ -0,0 +1,37 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sun, 3 Jan 2021 22:27:43 -0800 +Subject: [PATCH] Configurable door breaking difficulty + +== AT == +public net.minecraft.world.entity.monster.Vindicator DOOR_BREAKING_PREDICATE +public net.minecraft.world.entity.monster.Zombie DOOR_BREAKING_PREDICATE + +Co-authored-by: Doc <[email protected]> + +diff --git a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java +index b06eedb1cb13771bbc7d0b812a9df864d1f73142..96b105697c91314148fd1b783501389214b1a3f0 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java ++++ b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java +@@ -184,7 +184,7 @@ public class Vindicator extends AbstractIllager { + + static class VindicatorBreakDoorGoal extends BreakDoorGoal { + public VindicatorBreakDoorGoal(Mob mob) { +- super(mob, 6, Vindicator.DOOR_BREAKING_PREDICATE); ++ super(mob, 6, com.google.common.base.Predicates.in(mob.level().paperConfig().entities.behavior.doorBreakingDifficulty.getOrDefault(mob.getType(), mob.level().paperConfig().entities.behavior.doorBreakingDifficulty.get(EntityType.VINDICATOR)))); // Paper - Configurable door breaking difficulty + this.setFlags(EnumSet.of(Goal.Flag.MOVE)); + } + +diff --git a/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java +index 34e46a64b3638f749a571d080fd8e7ac1f57edba..a835ec6e063dd247a008da84446f8647f38d89d4 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java ++++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java +@@ -103,7 +103,7 @@ public class Zombie extends Monster { + + public Zombie(EntityType<? extends Zombie> type, Level world) { + super(type, world); +- this.breakDoorGoal = new BreakDoorGoal(this, Zombie.DOOR_BREAKING_PREDICATE); ++ this.breakDoorGoal = new BreakDoorGoal(this, com.google.common.base.Predicates.in(world.paperConfig().entities.behavior.doorBreakingDifficulty.getOrDefault(type, world.paperConfig().entities.behavior.doorBreakingDifficulty.get(EntityType.ZOMBIE)))); // Paper - Configurable door breaking difficulty + } + + public Zombie(Level world) { diff --git a/patches/server/0460-Empty-commands-shall-not-be-dispatched.patch b/patches/server/0460-Empty-commands-shall-not-be-dispatched.patch new file mode 100644 index 0000000000..9d321da35f --- /dev/null +++ b/patches/server/0460-Empty-commands-shall-not-be-dispatched.patch @@ -0,0 +1,18 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Mariell Hoversholm <[email protected]> +Date: Wed, 6 Jan 2021 23:38:43 +0100 +Subject: [PATCH] Empty commands shall not be dispatched + + +diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java +index b34abf66c7dc756e88e08637af976f7794144d06..2dcb1a1bdba1d1cad3caa117d85a8619ed9e67b4 100644 +--- a/src/main/java/net/minecraft/commands/Commands.java ++++ b/src/main/java/net/minecraft/commands/Commands.java +@@ -291,6 +291,7 @@ public class Commands { + command = event.getCommand(); + + String[] args = command.split(" "); ++ if (args.length == 0) return; // Paper - empty commands shall not be dispatched + + String cmd = args[0]; + if (cmd.startsWith("minecraft:")) cmd = cmd.substring("minecraft:".length()); diff --git a/patches/server/0461-Remove-stale-POIs.patch b/patches/server/0461-Remove-stale-POIs.patch new file mode 100644 index 0000000000..1f2723ec86 --- /dev/null +++ b/patches/server/0461-Remove-stale-POIs.patch @@ -0,0 +1,22 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Shane Freeder <[email protected]> +Date: Sat, 9 Jan 2021 14:17:07 +0100 +Subject: [PATCH] Remove stale POIs + + +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index 1ed0940d8bdc4210992a335b3691c1cd3a13580a..e56f9e823f9460854c6f0f2f05422136c12aabba 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -1786,6 +1786,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + }); + optional1.ifPresent((holder) -> { + this.getServer().execute(() -> { ++ // Paper start - Remove stale POIs ++ if (optional.isEmpty() && this.getPoiManager().exists(blockposition1, poiType -> true)) { ++ this.getPoiManager().remove(blockposition1); ++ } ++ // Paper end - Remove stale POIs + this.getPoiManager().add(blockposition1, holder); + DebugPackets.sendPoiAddedPacket(this, blockposition1); + }); diff --git a/patches/server/0462-Fix-villager-boat-exploit.patch b/patches/server/0462-Fix-villager-boat-exploit.patch new file mode 100644 index 0000000000..f5548b98cc --- /dev/null +++ b/patches/server/0462-Fix-villager-boat-exploit.patch @@ -0,0 +1,25 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jason Penilla <[email protected]> +Date: Mon, 11 Jan 2021 12:43:51 -0800 +Subject: [PATCH] Fix villager boat exploit + + +diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java +index 8566e28d18a5b1221ba4e5d55e7788f80f956296..fbe10666c4ec6caa95aa38a6f99ccca700e9a4d2 100644 +--- a/src/main/java/net/minecraft/server/players/PlayerList.java ++++ b/src/main/java/net/minecraft/server/players/PlayerList.java +@@ -544,6 +544,14 @@ public abstract class PlayerList { + PlayerList.LOGGER.debug("Removing player mount"); + entityplayer.stopRiding(); + entity.getPassengersAndSelf().forEach((entity1) -> { ++ // Paper start - Fix villager boat exploit ++ if (entity1 instanceof net.minecraft.world.entity.npc.AbstractVillager villager) { ++ final net.minecraft.world.entity.player.Player human = villager.getTradingPlayer(); ++ if (human != null) { ++ villager.setTradingPlayer(null); ++ } ++ } ++ // Paper end - Fix villager boat exploit + entity1.setRemoved(Entity.RemovalReason.UNLOADED_WITH_PLAYER, EntityRemoveEvent.Cause.PLAYER_QUIT); // CraftBukkit - add Bukkit remove cause + }); + } diff --git a/patches/server/0463-Add-sendOpLevel-API.patch b/patches/server/0463-Add-sendOpLevel-API.patch new file mode 100644 index 0000000000..44d11dcef6 --- /dev/null +++ b/patches/server/0463-Add-sendOpLevel-API.patch @@ -0,0 +1,53 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Mariell Hoversholm <[email protected]> +Date: Tue, 29 Dec 2020 15:03:03 +0100 +Subject: [PATCH] Add sendOpLevel API + + +diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java +index fbe10666c4ec6caa95aa38a6f99ccca700e9a4d2..061bba184c8bc2569ce1d413435ec1360fa14007 100644 +--- a/src/main/java/net/minecraft/server/players/PlayerList.java ++++ b/src/main/java/net/minecraft/server/players/PlayerList.java +@@ -1025,6 +1025,11 @@ public abstract class PlayerList { + } + + private void sendPlayerPermissionLevel(ServerPlayer player, int permissionLevel) { ++ // Paper start - Add sendOpLevel API ++ this.sendPlayerPermissionLevel(player, permissionLevel, true); ++ } ++ public void sendPlayerPermissionLevel(ServerPlayer player, int permissionLevel, boolean recalculatePermissions) { ++ // Paper end - Add sendOpLevel API + if (player.connection != null) { + byte b0; + +@@ -1039,8 +1044,10 @@ public abstract class PlayerList { + player.connection.send(new ClientboundEntityEventPacket(player, b0)); + } + ++ if (recalculatePermissions) { // Paper - Add sendOpLevel API + player.getBukkitEntity().recalculatePermissions(); // CraftBukkit + this.server.getCommands().sendCommands(player); ++ } // Paper - Add sendOpLevel API + } + + public boolean isWhiteListed(GameProfile profile) { +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +index 0aaea6091c5828782ed606d250423f3d75b9b27f..c93ec7e97c9af44ed75e7ea4fb1c6c58c2b6bc1a 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -684,6 +684,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + } + // Paper end + ++ // Paper start - Add sendOpLevel API ++ @Override ++ public void sendOpLevel(byte level) { ++ Preconditions.checkArgument(level >= 0 && level <= 4, "Level must be within [0, 4]"); ++ ++ this.getHandle().getServer().getPlayerList().sendPlayerPermissionLevel(this.getHandle(), level, false); ++ } ++ // Paper end - Add sendOpLevel API ++ + @Override + public void setCompassTarget(Location loc) { + Preconditions.checkArgument(loc != null, "Location cannot be null"); diff --git a/patches/server/0464-Add-RegistryAccess-for-managing-Registries.patch b/patches/server/0464-Add-RegistryAccess-for-managing-Registries.patch new file mode 100644 index 0000000000..9c82b85992 --- /dev/null +++ b/patches/server/0464-Add-RegistryAccess-for-managing-Registries.patch @@ -0,0 +1,1405 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Mon, 27 Feb 2023 18:28:39 -0800 +Subject: [PATCH] Add RegistryAccess for managing Registries + +RegistryAccess is independant from CraftServer and +doesn't require one to be created allowing the +org.bukkit.Registry class to be loaded earlier. + +== AT == +public net.minecraft.server.RegistryLayer STATIC_ACCESS + +diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistries.java b/src/main/java/io/papermc/paper/registry/PaperRegistries.java +new file mode 100644 +index 0000000000000000000000000000000000000000..cd8a6a4c2a63029f8f859765088c227bbd456813 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/registry/PaperRegistries.java +@@ -0,0 +1,154 @@ ++package io.papermc.paper.registry; ++ ++import com.google.common.base.Preconditions; ++import io.papermc.paper.adventure.PaperAdventure; ++import io.papermc.paper.registry.entry.RegistryEntry; ++import java.util.Collections; ++import java.util.IdentityHashMap; ++import java.util.List; ++import java.util.Map; ++import java.util.Objects; ++import net.minecraft.core.Registry; ++import net.minecraft.core.registries.Registries; ++import net.minecraft.resources.ResourceKey; ++import org.bukkit.Art; ++import org.bukkit.Fluid; ++import org.bukkit.GameEvent; ++import org.bukkit.JukeboxSong; ++import org.bukkit.Keyed; ++import org.bukkit.MusicInstrument; ++import org.bukkit.Sound; ++import org.bukkit.attribute.Attribute; ++import org.bukkit.block.Biome; ++import org.bukkit.block.BlockType; ++import org.bukkit.block.banner.PatternType; ++import org.bukkit.craftbukkit.CraftArt; ++import org.bukkit.craftbukkit.CraftFluid; ++import org.bukkit.craftbukkit.CraftGameEvent; ++import org.bukkit.craftbukkit.CraftJukeboxSong; ++import org.bukkit.craftbukkit.CraftMusicInstrument; ++import org.bukkit.craftbukkit.CraftSound; ++import org.bukkit.craftbukkit.attribute.CraftAttribute; ++import org.bukkit.craftbukkit.block.CraftBiome; ++import org.bukkit.craftbukkit.block.CraftBlockType; ++import org.bukkit.craftbukkit.block.banner.CraftPatternType; ++import org.bukkit.craftbukkit.damage.CraftDamageType; ++import org.bukkit.craftbukkit.enchantments.CraftEnchantment; ++import org.bukkit.craftbukkit.entity.CraftCat; ++import org.bukkit.craftbukkit.entity.CraftFrog; ++import org.bukkit.craftbukkit.entity.CraftVillager; ++import org.bukkit.craftbukkit.entity.CraftWolf; ++import org.bukkit.craftbukkit.generator.structure.CraftStructure; ++import org.bukkit.craftbukkit.generator.structure.CraftStructureType; ++import org.bukkit.craftbukkit.inventory.CraftItemType; ++import org.bukkit.craftbukkit.inventory.CraftMenuType; ++import org.bukkit.craftbukkit.inventory.trim.CraftTrimMaterial; ++import org.bukkit.craftbukkit.inventory.trim.CraftTrimPattern; ++import org.bukkit.craftbukkit.legacy.FieldRename; ++import org.bukkit.craftbukkit.map.CraftMapCursor; ++import org.bukkit.craftbukkit.potion.CraftPotionEffectType; ++import org.bukkit.craftbukkit.util.CraftNamespacedKey; ++import org.bukkit.damage.DamageType; ++import org.bukkit.enchantments.Enchantment; ++import org.bukkit.entity.Cat; ++import org.bukkit.entity.Frog; ++import org.bukkit.entity.Villager; ++import org.bukkit.entity.Wolf; ++import org.bukkit.entity.memory.MemoryKey; ++import org.bukkit.generator.structure.Structure; ++import org.bukkit.generator.structure.StructureType; ++import org.bukkit.inventory.ItemType; ++import org.bukkit.inventory.MenuType; ++import org.bukkit.inventory.meta.trim.TrimMaterial; ++import org.bukkit.inventory.meta.trim.TrimPattern; ++import org.bukkit.map.MapCursor; ++import org.bukkit.potion.PotionEffectType; ++import org.jspecify.annotations.Nullable; ++ ++import static io.papermc.paper.registry.entry.RegistryEntry.apiOnly; ++import static io.papermc.paper.registry.entry.RegistryEntry.entry; ++ ++public final class PaperRegistries { ++ ++ static final List<RegistryEntry<?, ?>> REGISTRY_ENTRIES; ++ private static final Map<RegistryKey<?>, RegistryEntry<?, ?>> BY_REGISTRY_KEY; ++ private static final Map<ResourceKey<?>, RegistryEntry<?, ?>> BY_RESOURCE_KEY; ++ static { ++ REGISTRY_ENTRIES = List.of( ++ // built-ins ++ entry(Registries.GAME_EVENT, RegistryKey.GAME_EVENT, GameEvent.class, CraftGameEvent::new), ++ entry(Registries.STRUCTURE_TYPE, RegistryKey.STRUCTURE_TYPE, StructureType.class, CraftStructureType::new), ++ entry(Registries.MOB_EFFECT, RegistryKey.MOB_EFFECT, PotionEffectType.class, CraftPotionEffectType::new), ++ entry(Registries.BLOCK, RegistryKey.BLOCK, BlockType.class, CraftBlockType::new), ++ entry(Registries.ITEM, RegistryKey.ITEM, ItemType.class, CraftItemType::new), ++ entry(Registries.CAT_VARIANT, RegistryKey.CAT_VARIANT, Cat.Type.class, CraftCat.CraftType::new), ++ entry(Registries.FROG_VARIANT, RegistryKey.FROG_VARIANT, Frog.Variant.class, CraftFrog.CraftVariant::new), ++ entry(Registries.VILLAGER_PROFESSION, RegistryKey.VILLAGER_PROFESSION, Villager.Profession.class, CraftVillager.CraftProfession::new), ++ entry(Registries.VILLAGER_TYPE, RegistryKey.VILLAGER_TYPE, Villager.Type.class, CraftVillager.CraftType::new), ++ entry(Registries.MAP_DECORATION_TYPE, RegistryKey.MAP_DECORATION_TYPE, MapCursor.Type.class, CraftMapCursor.CraftType::new), ++ entry(Registries.MENU, RegistryKey.MENU, MenuType.class, CraftMenuType::new), ++ entry(Registries.ATTRIBUTE, RegistryKey.ATTRIBUTE, Attribute.class, CraftAttribute::new), ++ entry(Registries.FLUID, RegistryKey.FLUID, Fluid.class, CraftFluid::new), ++ entry(Registries.SOUND_EVENT, RegistryKey.SOUND_EVENT, Sound.class, CraftSound::new), ++ ++ // data-drivens ++ entry(Registries.BIOME, RegistryKey.BIOME, Biome.class, CraftBiome::new).delayed(), ++ entry(Registries.STRUCTURE, RegistryKey.STRUCTURE, Structure.class, CraftStructure::new).delayed(), ++ entry(Registries.TRIM_MATERIAL, RegistryKey.TRIM_MATERIAL, TrimMaterial.class, CraftTrimMaterial::new).delayed(), ++ entry(Registries.TRIM_PATTERN, RegistryKey.TRIM_PATTERN, TrimPattern.class, CraftTrimPattern::new).delayed(), ++ entry(Registries.DAMAGE_TYPE, RegistryKey.DAMAGE_TYPE, DamageType.class, CraftDamageType::new).delayed(), ++ entry(Registries.WOLF_VARIANT, RegistryKey.WOLF_VARIANT, Wolf.Variant.class, CraftWolf.CraftVariant::new).delayed(), ++ entry(Registries.ENCHANTMENT, RegistryKey.ENCHANTMENT, Enchantment.class, CraftEnchantment::new).withSerializationUpdater(FieldRename.ENCHANTMENT_RENAME).delayed(), ++ entry(Registries.JUKEBOX_SONG, RegistryKey.JUKEBOX_SONG, JukeboxSong.class, CraftJukeboxSong::new).delayed(), ++ entry(Registries.BANNER_PATTERN, RegistryKey.BANNER_PATTERN, PatternType.class, CraftPatternType::new).delayed(), ++ entry(Registries.PAINTING_VARIANT, RegistryKey.PAINTING_VARIANT, Art.class, CraftArt::new).delayed(), ++ entry(Registries.INSTRUMENT, RegistryKey.INSTRUMENT, MusicInstrument.class, CraftMusicInstrument::new).delayed(), ++ ++ // api-only ++ apiOnly(Registries.ENTITY_TYPE, RegistryKey.ENTITY_TYPE, PaperSimpleRegistry::entityType), ++ apiOnly(Registries.PARTICLE_TYPE, RegistryKey.PARTICLE_TYPE, PaperSimpleRegistry::particleType), ++ apiOnly(Registries.POTION, RegistryKey.POTION, PaperSimpleRegistry::potion), ++ apiOnly(Registries.MEMORY_MODULE_TYPE, RegistryKey.MEMORY_MODULE_TYPE, () -> (org.bukkit.Registry<MemoryKey<?>>) (org.bukkit.Registry) org.bukkit.Registry.MEMORY_MODULE_TYPE) ++ ); ++ final Map<RegistryKey<?>, RegistryEntry<?, ?>> byRegistryKey = new IdentityHashMap<>(REGISTRY_ENTRIES.size()); ++ final Map<ResourceKey<?>, RegistryEntry<?, ?>> byResourceKey = new IdentityHashMap<>(REGISTRY_ENTRIES.size()); ++ for (final RegistryEntry<?, ?> entry : REGISTRY_ENTRIES) { ++ Preconditions.checkState(byRegistryKey.put(entry.apiKey(), entry) == null, "Duplicate api registry key: %s", entry.apiKey()); ++ Preconditions.checkState(byResourceKey.put(entry.mcKey(), entry) == null, "Duplicate mc registry key: %s", entry.mcKey()); ++ } ++ BY_REGISTRY_KEY = Collections.unmodifiableMap(byRegistryKey); ++ BY_RESOURCE_KEY = Collections.unmodifiableMap(byResourceKey); ++ } ++ ++ @SuppressWarnings("unchecked") ++ public static <M, T extends Keyed> @Nullable RegistryEntry<M, T> getEntry(final ResourceKey<? extends Registry<M>> resourceKey) { ++ return (RegistryEntry<M, T>) BY_RESOURCE_KEY.get(resourceKey); ++ } ++ ++ @SuppressWarnings("unchecked") ++ public static <M, T extends Keyed> @Nullable RegistryEntry<M, T> getEntry(final RegistryKey<? super T> registryKey) { ++ return (RegistryEntry<M, T>) BY_REGISTRY_KEY.get(registryKey); ++ } ++ ++ @SuppressWarnings("unchecked") ++ public static <M, T> RegistryKey<T> registryFromNms(final ResourceKey<? extends Registry<M>> registryResourceKey) { ++ return (RegistryKey<T>) Objects.requireNonNull(BY_RESOURCE_KEY.get(registryResourceKey), registryResourceKey + " doesn't have an api RegistryKey").apiKey(); ++ } ++ ++ @SuppressWarnings("unchecked") ++ public static <M, T> ResourceKey<? extends Registry<M>> registryToNms(final RegistryKey<T> registryKey) { ++ return (ResourceKey<? extends Registry<M>>) Objects.requireNonNull(BY_REGISTRY_KEY.get(registryKey), registryKey + " doesn't have an mc registry ResourceKey").mcKey(); ++ } ++ ++ public static <M, T> TypedKey<T> fromNms(final ResourceKey<M> resourceKey) { ++ return TypedKey.create(registryFromNms(resourceKey.registryKey()), CraftNamespacedKey.fromMinecraft(resourceKey.location())); ++ } ++ ++ @SuppressWarnings({"unchecked", "RedundantCast"}) ++ public static <M, T> ResourceKey<M> toNms(final TypedKey<T> typedKey) { ++ return ResourceKey.create((ResourceKey<? extends Registry<M>>) PaperRegistries.registryToNms(typedKey.registryKey()), PaperAdventure.asVanilla(typedKey.key())); ++ } ++ ++ private PaperRegistries() { ++ } ++} +diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java b/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java +new file mode 100644 +index 0000000000000000000000000000000000000000..4bf7915867dbe762ef0b070d67d5f7b7d1ee4f03 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java +@@ -0,0 +1,122 @@ ++package io.papermc.paper.registry; ++ ++import io.papermc.paper.registry.entry.ApiRegistryEntry; ++import io.papermc.paper.registry.entry.RegistryEntry; ++import io.papermc.paper.registry.legacy.DelayedRegistry; ++import io.papermc.paper.registry.legacy.DelayedRegistryEntry; ++import io.papermc.paper.registry.legacy.LegacyRegistryIdentifiers; ++import java.util.Map; ++import java.util.NoSuchElementException; ++import java.util.Set; ++import java.util.concurrent.ConcurrentHashMap; ++import java.util.stream.Collectors; ++import net.minecraft.resources.ResourceKey; ++import org.bukkit.Keyed; ++import org.bukkit.Registry; ++import org.jetbrains.annotations.VisibleForTesting; ++import org.jspecify.annotations.Nullable; ++ ++public class PaperRegistryAccess implements RegistryAccess { ++ ++ // We store the API registries in a memoized supplier, so they can be created on-demand. ++ // These suppliers are added to this map right after the instance of nms.Registry is created before it is loaded. ++ // We want to do registration there, so we have access to the nms.Registry instance in order to wrap it in a CraftRegistry instance. ++ // The memoized Supplier is needed because we *can't* instantiate any CraftRegistry class until **all** the BuiltInRegistries have been added ++ // to this map because that would class-load org.bukkit.Registry which would query this map. ++ private final Map<RegistryKey<?>, RegistryHolder<?>> registries = new ConcurrentHashMap<>(); // is "identity" because RegistryKey overrides equals and hashCode ++ ++ public static PaperRegistryAccess instance() { ++ return (PaperRegistryAccess) RegistryAccessHolder.INSTANCE.orElseThrow(() -> new IllegalStateException("No RegistryAccess implementation found")); ++ } ++ ++ @VisibleForTesting ++ public Set<RegistryKey<?>> getLoadedServerBackedRegistries() { ++ return this.registries.keySet().stream().filter(registryHolder -> !(PaperRegistries.getEntry(registryHolder) instanceof ApiRegistryEntry)).collect(Collectors.toUnmodifiableSet()); ++ } ++ ++ @SuppressWarnings("unchecked") ++ @Deprecated(forRemoval = true) ++ @Override ++ public <T extends Keyed> @Nullable Registry<T> getRegistry(final Class<T> type) { ++ final RegistryKey<T> registryKey = byType(type); ++ // If our mapping from Class -> RegistryKey did not contain the passed type it was either a completely invalid type or a registry ++ // that merely exists as a SimpleRegistry in the org.bukkit.Registry type. We cannot return a registry for these, return null ++ // as per method contract in Bukkit#getRegistry. ++ if (registryKey == null) return null; ++ ++ final RegistryEntry<?, T> entry = PaperRegistries.getEntry(registryKey); ++ final RegistryHolder<T> registry = (RegistryHolder<T>) this.registries.get(registryKey); ++ if (registry != null) { ++ // if the registry exists, return right away. Since this is the "legacy" method, we return DelayedRegistry ++ // for the non-builtin Registry instances stored as fields in Registry. ++ return registry.get(); ++ } else if (entry instanceof DelayedRegistryEntry<?, T>) { ++ // if the registry doesn't exist and the entry is marked as "delayed", we create a registry holder that is empty ++ // which will later be filled with the actual registry. This is so the fields on org.bukkit.Registry can be populated with ++ // registries that don't exist at the time org.bukkit.Registry is statically initialized. ++ final RegistryHolder<T> delayedHolder = new RegistryHolder.Delayed<>(); ++ this.registries.put(registryKey, delayedHolder); ++ return delayedHolder.get(); ++ } else { ++ // if the registry doesn't exist yet or doesn't have a delayed entry, just return null ++ return null; ++ } ++ } ++ ++ @SuppressWarnings("unchecked") ++ @Override ++ public <T extends Keyed> Registry<T> getRegistry(final RegistryKey<T> key) { ++ if (PaperRegistries.getEntry(key) == null) { ++ throw new NoSuchElementException(key + " is not a valid registry key"); ++ } ++ final @Nullable RegistryHolder<T> registryHolder = (RegistryHolder<T>) this.registries.get(key); ++ if (registryHolder == null) { ++ throw new IllegalArgumentException(key + " points to a registry that is not available yet"); ++ } ++ // since this is the getRegistry method that uses the modern RegistryKey, we unwrap any DelayedRegistry instances ++ // that might be returned here. I don't think reference equality is required when doing getRegistry(RegistryKey.WOLF_VARIANT) == Registry.WOLF_VARIANT ++ return possiblyUnwrap(registryHolder.get()); ++ } ++ ++ private static <T extends Keyed> Registry<T> possiblyUnwrap(final Registry<T> registry) { ++ if (registry instanceof final DelayedRegistry<T, ?> delayedRegistry) { // if not coming from legacy, unwrap the delayed registry ++ return delayedRegistry.delegate(); ++ } ++ return registry; ++ } ++ ++ public <M> void registerReloadableRegistry(final ResourceKey<? extends net.minecraft.core.Registry<M>> resourceKey, final net.minecraft.core.Registry<M> registry) { ++ this.registerRegistry(resourceKey, registry, true); ++ } ++ ++ public <M> void registerRegistry(final ResourceKey<? extends net.minecraft.core.Registry<M>> resourceKey, final net.minecraft.core.Registry<M> registry) { ++ this.registerRegistry(resourceKey, registry, false); ++ } ++ ++ @SuppressWarnings("unchecked") // this method should be called right after any new MappedRegistry instances are created to later be used by the server. ++ private <M, B extends Keyed, R extends Registry<B>> void registerRegistry(final ResourceKey<? extends net.minecraft.core.Registry<M>> resourceKey, final net.minecraft.core.Registry<M> registry, final boolean replace) { ++ final @Nullable RegistryEntry<M, B> entry = PaperRegistries.getEntry(resourceKey); ++ if (entry == null) { // skip registries that don't have API entries ++ return; ++ } ++ final @Nullable RegistryHolder<B> registryHolder = (RegistryHolder<B>) this.registries.get(entry.apiKey()); ++ if (registryHolder == null || replace) { ++ // if the holder doesn't exist yet, or is marked as "replaceable", put it in the map. ++ this.registries.put(entry.apiKey(), entry.createRegistryHolder(registry)); ++ } else { ++ if (registryHolder instanceof RegistryHolder.Delayed<?, ?> && entry instanceof final DelayedRegistryEntry<M, B> delayedEntry) { ++ // if the registry holder is delayed, and the entry is marked as "delayed", then load the holder with the CraftRegistry instance that wraps the actual nms Registry. ++ ((RegistryHolder.Delayed<B, R>) registryHolder).loadFrom(delayedEntry, registry); ++ } else { ++ throw new IllegalArgumentException(resourceKey + " has already been created"); ++ } ++ } ++ } ++ ++ @SuppressWarnings("unchecked") ++ @Deprecated ++ @VisibleForTesting ++ public static <T extends Keyed> @Nullable RegistryKey<T> byType(final Class<T> type) { ++ return (RegistryKey<T>) LegacyRegistryIdentifiers.CLASS_TO_KEY_MAP.get(type); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/registry/PaperSimpleRegistry.java b/src/main/java/io/papermc/paper/registry/PaperSimpleRegistry.java +new file mode 100644 +index 0000000000000000000000000000000000000000..6d134ace042758da722960cbcb48e52508dafd61 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/registry/PaperSimpleRegistry.java +@@ -0,0 +1,38 @@ ++package io.papermc.paper.registry; ++ ++import java.util.function.Predicate; ++import net.minecraft.core.registries.BuiltInRegistries; ++import org.bukkit.Keyed; ++import org.bukkit.Particle; ++import org.bukkit.Registry; ++import org.bukkit.entity.EntityType; ++import org.bukkit.potion.PotionType; ++import org.jspecify.annotations.NullMarked; ++ ++@NullMarked ++public class PaperSimpleRegistry<T extends Enum<T> & Keyed, M> extends Registry.SimpleRegistry<T> { ++ ++ static Registry<EntityType> entityType() { ++ return new PaperSimpleRegistry<>(EntityType.class, entity -> entity != EntityType.UNKNOWN, BuiltInRegistries.ENTITY_TYPE); ++ } ++ ++ static Registry<Particle> particleType() { ++ return new PaperSimpleRegistry<>(Particle.class, BuiltInRegistries.PARTICLE_TYPE); ++ } ++ ++ static Registry<PotionType> potion() { ++ return new PaperSimpleRegistry<>(PotionType.class, BuiltInRegistries.POTION); ++ } ++ ++ private final net.minecraft.core.Registry<M> nmsRegistry; ++ ++ protected PaperSimpleRegistry(final Class<T> type, final net.minecraft.core.Registry<M> nmsRegistry) { ++ super(type); ++ this.nmsRegistry = nmsRegistry; ++ } ++ ++ public PaperSimpleRegistry(final Class<T> type, final Predicate<T> predicate, final net.minecraft.core.Registry<M> nmsRegistry) { ++ super(type, predicate); ++ this.nmsRegistry = nmsRegistry; ++ } ++} +diff --git a/src/main/java/io/papermc/paper/registry/RegistryHolder.java b/src/main/java/io/papermc/paper/registry/RegistryHolder.java +new file mode 100644 +index 0000000000000000000000000000000000000000..1b52d4bc868e1e5f84c8416301e193bb9cd315b2 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/registry/RegistryHolder.java +@@ -0,0 +1,44 @@ ++package io.papermc.paper.registry; ++ ++import com.google.common.base.Suppliers; ++import io.papermc.paper.registry.legacy.DelayedRegistry; ++import io.papermc.paper.registry.legacy.DelayedRegistryEntry; ++import java.util.function.Supplier; ++import org.bukkit.Keyed; ++import org.bukkit.Registry; ++ ++public interface RegistryHolder<B extends Keyed> { ++ ++ Registry<B> get(); ++ ++ final class Memoized<B extends Keyed, R extends Registry<B>> implements RegistryHolder<B> { ++ ++ private final Supplier<R> memoizedSupplier; ++ ++ public Memoized(final Supplier<? extends R> supplier) { ++ this.memoizedSupplier = Suppliers.memoize(supplier::get); ++ } ++ ++ public Registry<B> get() { ++ return this.memoizedSupplier.get(); ++ } ++ } ++ ++ final class Delayed<B extends Keyed, R extends Registry<B>> implements RegistryHolder<B> { ++ ++ private final DelayedRegistry<B, R> delayedRegistry = new DelayedRegistry<>(); ++ ++ @Override ++ public DelayedRegistry<B, R> get() { ++ return this.delayedRegistry; ++ } ++ ++ <M> void loadFrom(final DelayedRegistryEntry<M, B> delayedEntry, final net.minecraft.core.Registry<M> registry) { ++ final RegistryHolder<B> delegateHolder = delayedEntry.delegate().createRegistryHolder(registry); ++ if (!(delegateHolder instanceof RegistryHolder.Memoized<B, ?>)) { ++ throw new IllegalArgumentException(delegateHolder + " must be a memoized holder"); ++ } ++ this.delayedRegistry.load(((Memoized<B, R>) delegateHolder).memoizedSupplier); ++ } ++ } ++} +diff --git a/src/main/java/io/papermc/paper/registry/entry/ApiRegistryEntry.java b/src/main/java/io/papermc/paper/registry/entry/ApiRegistryEntry.java +new file mode 100644 +index 0000000000000000000000000000000000000000..2295b0d145cbaabef5d29482c817575dcbe2ba54 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/registry/entry/ApiRegistryEntry.java +@@ -0,0 +1,27 @@ ++package io.papermc.paper.registry.entry; ++ ++import io.papermc.paper.registry.RegistryHolder; ++import io.papermc.paper.registry.RegistryKey; ++import java.util.function.Supplier; ++import net.minecraft.core.Registry; ++import net.minecraft.resources.ResourceKey; ++import org.bukkit.Keyed; ++ ++public class ApiRegistryEntry<M, B extends Keyed> extends BaseRegistryEntry<M, B> { ++ ++ private final Supplier<org.bukkit.Registry<B>> registrySupplier; ++ ++ protected ApiRegistryEntry( ++ final ResourceKey<? extends Registry<M>> mcKey, ++ final RegistryKey<B> apiKey, ++ final Supplier<org.bukkit.Registry<B>> registrySupplier ++ ) { ++ super(mcKey, apiKey); ++ this.registrySupplier = registrySupplier; ++ } ++ ++ @Override ++ public RegistryHolder<B> createRegistryHolder(final Registry<M> nmsRegistry) { ++ return new RegistryHolder.Memoized<>(this.registrySupplier); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/registry/entry/BaseRegistryEntry.java b/src/main/java/io/papermc/paper/registry/entry/BaseRegistryEntry.java +new file mode 100644 +index 0000000000000000000000000000000000000000..ceb217dbbb84e8bd51365dd47bf91971e364d298 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/registry/entry/BaseRegistryEntry.java +@@ -0,0 +1,27 @@ ++package io.papermc.paper.registry.entry; ++ ++import io.papermc.paper.registry.RegistryKey; ++import net.minecraft.core.Registry; ++import net.minecraft.resources.ResourceKey; ++import org.bukkit.Keyed; ++ ++public abstract class BaseRegistryEntry<M, B extends Keyed> implements RegistryEntry<M, B> { // TODO remove Keyed ++ ++ private final ResourceKey<? extends Registry<M>> minecraftRegistryKey; ++ private final RegistryKey<B> apiRegistryKey; ++ ++ protected BaseRegistryEntry(final ResourceKey<? extends Registry<M>> minecraftRegistryKey, final RegistryKey<B> apiRegistryKey) { ++ this.minecraftRegistryKey = minecraftRegistryKey; ++ this.apiRegistryKey = apiRegistryKey; ++ } ++ ++ @Override ++ public final ResourceKey<? extends Registry<M>> mcKey() { ++ return this.minecraftRegistryKey; ++ } ++ ++ @Override ++ public final RegistryKey<B> apiKey() { ++ return this.apiRegistryKey; ++ } ++} +diff --git a/src/main/java/io/papermc/paper/registry/entry/CraftRegistryEntry.java b/src/main/java/io/papermc/paper/registry/entry/CraftRegistryEntry.java +new file mode 100644 +index 0000000000000000000000000000000000000000..45cbc425da64f0bd3290600869ad425d9e6e912b +--- /dev/null ++++ b/src/main/java/io/papermc/paper/registry/entry/CraftRegistryEntry.java +@@ -0,0 +1,48 @@ ++package io.papermc.paper.registry.entry; ++ ++import com.google.common.base.Preconditions; ++import io.papermc.paper.registry.RegistryHolder; ++import io.papermc.paper.registry.RegistryKey; ++import java.util.function.BiFunction; ++import net.minecraft.core.Registry; ++import net.minecraft.resources.ResourceKey; ++import org.bukkit.Keyed; ++import org.bukkit.NamespacedKey; ++import org.bukkit.craftbukkit.CraftRegistry; ++import org.bukkit.craftbukkit.util.ApiVersion; ++ ++public class CraftRegistryEntry<M, B extends Keyed> extends BaseRegistryEntry<M, B> { // TODO remove Keyed ++ ++ private static final BiFunction<NamespacedKey, ApiVersion, NamespacedKey> EMPTY = (namespacedKey, apiVersion) -> namespacedKey; ++ ++ protected final Class<?> classToPreload; ++ protected final BiFunction<NamespacedKey, M, B> minecraftToBukkit; ++ protected BiFunction<NamespacedKey, ApiVersion, NamespacedKey> updater = EMPTY; ++ ++ protected CraftRegistryEntry( ++ final ResourceKey<? extends Registry<M>> mcKey, ++ final RegistryKey<B> apiKey, ++ final Class<?> classToPreload, ++ final BiFunction<NamespacedKey, M, B> minecraftToBukkit ++ ) { ++ super(mcKey, apiKey); ++ Preconditions.checkArgument(!classToPreload.getPackageName().startsWith("net.minecraft"), classToPreload + " should not be in the net.minecraft package as the class-to-preload"); ++ this.classToPreload = classToPreload; ++ this.minecraftToBukkit = minecraftToBukkit; ++ } ++ ++ @Override ++ public RegistryEntry<M, B> withSerializationUpdater(final BiFunction<NamespacedKey, ApiVersion, NamespacedKey> updater) { ++ this.updater = updater; ++ return this; ++ } ++ ++ @Override ++ public RegistryHolder<B> createRegistryHolder(final Registry<M> nmsRegistry) { ++ return new RegistryHolder.Memoized<>(() -> this.createApiRegistry(nmsRegistry)); ++ } ++ ++ private CraftRegistry<B, M> createApiRegistry(final Registry<M> nmsRegistry) { ++ return new CraftRegistry<>(this.classToPreload, nmsRegistry, this.minecraftToBukkit, this.updater); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/registry/entry/RegistryEntry.java b/src/main/java/io/papermc/paper/registry/entry/RegistryEntry.java +new file mode 100644 +index 0000000000000000000000000000000000000000..2889d87f0989ae5744cd4c1e57240830aa574155 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/registry/entry/RegistryEntry.java +@@ -0,0 +1,48 @@ ++package io.papermc.paper.registry.entry; ++ ++import io.papermc.paper.registry.RegistryHolder; ++import io.papermc.paper.registry.RegistryKey; ++import io.papermc.paper.registry.legacy.DelayedRegistryEntry; ++import java.util.function.BiFunction; ++import java.util.function.Supplier; ++import net.minecraft.core.Registry; ++import net.minecraft.resources.ResourceKey; ++import org.bukkit.Keyed; ++import org.bukkit.NamespacedKey; ++import org.bukkit.craftbukkit.util.ApiVersion; ++ ++public interface RegistryEntry<M, B extends Keyed> extends RegistryEntryInfo<M, B> { // TODO remove Keyed ++ ++ RegistryHolder<B> createRegistryHolder(Registry<M> nmsRegistry); ++ ++ default RegistryEntry<M, B> withSerializationUpdater(final BiFunction<NamespacedKey, ApiVersion, NamespacedKey> updater) { ++ return this; ++ } ++ ++ /** ++ * This should only be used if the registry instance needs to exist early due to the need ++ * to populate a field in {@link org.bukkit.Registry}. Data-driven registries shouldn't exist ++ * as fields, but instead be obtained via {@link io.papermc.paper.registry.RegistryAccess#getRegistry(RegistryKey)} ++ */ ++ @Deprecated ++ default RegistryEntry<M, B> delayed() { ++ return new DelayedRegistryEntry<>(this); ++ } ++ ++ static <M, B extends Keyed> RegistryEntry<M, B> entry( ++ final ResourceKey<? extends Registry<M>> mcKey, ++ final RegistryKey<B> apiKey, ++ final Class<?> classToPreload, ++ final BiFunction<NamespacedKey, M, B> minecraftToBukkit ++ ) { ++ return new CraftRegistryEntry<>(mcKey, apiKey, classToPreload, minecraftToBukkit); ++ } ++ ++ static <M, B extends Keyed> RegistryEntry<M, B> apiOnly( ++ final ResourceKey<? extends Registry<M>> mcKey, ++ final RegistryKey<B> apiKey, ++ final Supplier<org.bukkit.Registry<B>> apiRegistrySupplier ++ ) { ++ return new ApiRegistryEntry<>(mcKey, apiKey, apiRegistrySupplier); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/registry/entry/RegistryEntryInfo.java b/src/main/java/io/papermc/paper/registry/entry/RegistryEntryInfo.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0ae855e80fc9fddfc1feb33c7a9748204828b7cc +--- /dev/null ++++ b/src/main/java/io/papermc/paper/registry/entry/RegistryEntryInfo.java +@@ -0,0 +1,12 @@ ++package io.papermc.paper.registry.entry; ++ ++import io.papermc.paper.registry.RegistryKey; ++import net.minecraft.core.Registry; ++import net.minecraft.resources.ResourceKey; ++ ++public interface RegistryEntryInfo<M, B> { ++ ++ ResourceKey<? extends Registry<M>> mcKey(); ++ ++ RegistryKey<B> apiKey(); ++} +diff --git a/src/main/java/io/papermc/paper/registry/entry/package-info.java b/src/main/java/io/papermc/paper/registry/entry/package-info.java +new file mode 100644 +index 0000000000000000000000000000000000000000..f04e93aa5ca41ce1cf3b152f827911fdf0dd10cb +--- /dev/null ++++ b/src/main/java/io/papermc/paper/registry/entry/package-info.java +@@ -0,0 +1,4 @@ ++@NullMarked ++package io.papermc.paper.registry.entry; ++ ++import org.jspecify.annotations.NullMarked; +diff --git a/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistry.java b/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistry.java +new file mode 100644 +index 0000000000000000000000000000000000000000..ca829b162d4369f845e59b62bb8779fd83fe2ef3 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistry.java +@@ -0,0 +1,57 @@ ++package io.papermc.paper.registry.legacy; ++ ++import java.util.Iterator; ++import java.util.function.Supplier; ++import java.util.stream.Stream; ++import org.bukkit.Keyed; ++import org.bukkit.NamespacedKey; ++import org.bukkit.Registry; ++import org.jspecify.annotations.Nullable; ++ ++/** ++ * This is to support the now-deprecated fields in {@link Registry} for ++ * data-driven registries. ++ */ ++public final class DelayedRegistry<T extends Keyed, R extends Registry<T>> implements Registry<T> { ++ ++ private @Nullable Supplier<? extends R> delegate; ++ ++ public void load(final Supplier<? extends R> registry) { ++ if (this.delegate != null) { ++ throw new IllegalStateException("Registry already loaded!"); ++ } ++ this.delegate = registry; ++ } ++ ++ public Registry<T> delegate() { ++ if (this.delegate == null) { ++ throw new IllegalStateException("You are trying to access this registry too early!"); ++ } ++ return this.delegate.get(); ++ } ++ ++ @Override ++ public @Nullable T get(final NamespacedKey key) { ++ return this.delegate().get(key); ++ } ++ ++ @Override ++ public T getOrThrow(final NamespacedKey key) { ++ return this.delegate().getOrThrow(key); ++ } ++ ++ @Override ++ public Iterator<T> iterator() { ++ return this.delegate().iterator(); ++ } ++ ++ @Override ++ public Stream<T> stream() { ++ return this.delegate().stream(); ++ } ++ ++ @Override ++ public @Nullable NamespacedKey getKey(final T value) { ++ return this.delegate().getKey(value); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistryEntry.java b/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistryEntry.java +new file mode 100644 +index 0000000000000000000000000000000000000000..110b8d559f49f9e4f181b47663962a139a273a72 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistryEntry.java +@@ -0,0 +1,26 @@ ++package io.papermc.paper.registry.legacy; ++ ++import io.papermc.paper.registry.RegistryHolder; ++import io.papermc.paper.registry.RegistryKey; ++import io.papermc.paper.registry.entry.RegistryEntry; ++import net.minecraft.core.Registry; ++import net.minecraft.resources.ResourceKey; ++import org.bukkit.Keyed; ++ ++public record DelayedRegistryEntry<M, T extends Keyed>(RegistryEntry<M, T> delegate) implements RegistryEntry<M, T> { ++ ++ @Override ++ public ResourceKey<? extends Registry<M>> mcKey() { ++ return this.delegate.mcKey(); ++ } ++ ++ @Override ++ public RegistryKey<T> apiKey() { ++ return this.delegate.apiKey(); ++ } ++ ++ @Override ++ public RegistryHolder<T> createRegistryHolder(final Registry<M> nmsRegistry) { ++ return this.delegate.createRegistryHolder(nmsRegistry); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/registry/legacy/LegacyRegistryIdentifiers.java b/src/main/java/io/papermc/paper/registry/legacy/LegacyRegistryIdentifiers.java +new file mode 100644 +index 0000000000000000000000000000000000000000..83870816cd4c54f94a3c603ffe41c11e457f3dec +--- /dev/null ++++ b/src/main/java/io/papermc/paper/registry/legacy/LegacyRegistryIdentifiers.java +@@ -0,0 +1,33 @@ ++package io.papermc.paper.registry.legacy; ++ ++import com.google.common.collect.ImmutableMap; ++import io.leangen.geantyref.GenericTypeReflector; ++import io.papermc.paper.registry.RegistryKey; ++import java.lang.reflect.Field; ++import java.lang.reflect.ParameterizedType; ++import java.util.Map; ++ ++@Deprecated ++public final class LegacyRegistryIdentifiers { ++ ++ public static final Map<Class<?>, RegistryKey<?>> CLASS_TO_KEY_MAP; ++ ++ static { ++ final ImmutableMap.Builder<Class<?>, RegistryKey<?>> builder = ImmutableMap.builder(); ++ try { ++ for (final Field field : RegistryKey.class.getFields()) { ++ if (field.getType() == RegistryKey.class) { ++ // get the legacy type from the RegistryKey generic parameter on the field ++ final Class<?> legacyType = GenericTypeReflector.erase(((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]); ++ builder.put(legacyType, (RegistryKey<?>) field.get(null)); ++ } ++ } ++ } catch (final ReflectiveOperationException ex) { ++ throw new RuntimeException(ex); ++ } ++ CLASS_TO_KEY_MAP = builder.build(); ++ } ++ ++ private LegacyRegistryIdentifiers() { ++ } ++} +diff --git a/src/main/java/io/papermc/paper/registry/legacy/package-info.java b/src/main/java/io/papermc/paper/registry/legacy/package-info.java +new file mode 100644 +index 0000000000000000000000000000000000000000..063a8d7a7313d5d685d51df54d9502d72266da49 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/registry/legacy/package-info.java +@@ -0,0 +1,4 @@ ++@NullMarked ++package io.papermc.paper.registry.legacy; ++ ++import org.jspecify.annotations.NullMarked; +diff --git a/src/main/java/io/papermc/paper/registry/package-info.java b/src/main/java/io/papermc/paper/registry/package-info.java +new file mode 100644 +index 0000000000000000000000000000000000000000..ca07ef31161e938d48214992b34cafb712a51513 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/registry/package-info.java +@@ -0,0 +1,4 @@ ++@NullMarked ++package io.papermc.paper.registry; ++ ++import org.jspecify.annotations.NullMarked; +diff --git a/src/main/java/net/minecraft/core/registries/BuiltInRegistries.java b/src/main/java/net/minecraft/core/registries/BuiltInRegistries.java +index 3f72e30b57fb2a4231e22a2234729408c1240af4..4638ba98dbbdb0f880337347be85a6e0fbed2191 100644 +--- a/src/main/java/net/minecraft/core/registries/BuiltInRegistries.java ++++ b/src/main/java/net/minecraft/core/registries/BuiltInRegistries.java +@@ -323,6 +323,7 @@ public class BuiltInRegistries { + ResourceKey<? extends Registry<T>> key, R registry, BuiltInRegistries.RegistryBootstrap<T> initializer + ) { + Bootstrap.checkBootstrapCalled(() -> "registry " + key.location()); ++ io.papermc.paper.registry.PaperRegistryAccess.instance().registerRegistry(registry.key(), registry); // Paper - initialize API registry + ResourceLocation resourceLocation = key.location(); + LOADERS.put(resourceLocation, () -> initializer.run(registry)); + WRITABLE_REGISTRY.register((ResourceKey)key, registry, RegistrationInfo.BUILT_IN); // Paper - decompile fix +diff --git a/src/main/java/net/minecraft/resources/RegistryDataLoader.java b/src/main/java/net/minecraft/resources/RegistryDataLoader.java +index b9b9ec93442423e99def9b2c51aedc955a7799d5..b8c1840eeda982c0c6350e49fae2784a599ef3ce 100644 +--- a/src/main/java/net/minecraft/resources/RegistryDataLoader.java ++++ b/src/main/java/net/minecraft/resources/RegistryDataLoader.java +@@ -349,6 +349,7 @@ public class RegistryDataLoader { + + RegistryDataLoader.Loader<T> create(Lifecycle lifecycle, Map<ResourceKey<?>, Exception> errors) { + WritableRegistry<T> writableRegistry = new MappedRegistry<>(this.key, lifecycle); ++ io.papermc.paper.registry.PaperRegistryAccess.instance().registerRegistry(this.key, writableRegistry); // Paper - initialize API registry + return new RegistryDataLoader.Loader<>(this, writableRegistry, errors); + } + +diff --git a/src/main/java/net/minecraft/server/ReloadableServerRegistries.java b/src/main/java/net/minecraft/server/ReloadableServerRegistries.java +index 257cfce009fb6fcd24d1fddfd8001e9b2a8ae1ae..185752185549ebd5f431932b63d8e5fea50a2cb2 100644 +--- a/src/main/java/net/minecraft/server/ReloadableServerRegistries.java ++++ b/src/main/java/net/minecraft/server/ReloadableServerRegistries.java +@@ -64,6 +64,7 @@ public class ReloadableServerRegistries { + ) { + return CompletableFuture.supplyAsync(() -> { + WritableRegistry<T> writableRegistry = new MappedRegistry<>(type.registryKey(), Lifecycle.experimental()); ++ io.papermc.paper.registry.PaperRegistryAccess.instance().registerReloadableRegistry(type.registryKey(), writableRegistry); // Paper - register reloadable registry + Map<ResourceLocation, T> map = new HashMap<>(); + SimpleJsonResourceReloadListener.scanDirectory(resourceManager, type.registryKey(), ops, type.codec(), map); + map.forEach((id, value) -> writableRegistry.register(ResourceKey.create(type.registryKey(), id), (T)value, DEFAULT_REGISTRATION_INFO)); +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java b/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java +index 0d091c7b328fb70a82f5b5ded7dec61b7cd3d59a..249f0dcad04a35244da6dab837a461bb42aad00a 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java +@@ -127,96 +127,12 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> { + + ", this can happen if a plugin creates its own registry entry with out properly registering it."); + } + +- /** +- * Note: Newly added registries should also be added to RegistriesArgumentProvider in the test package +- * +- * @param bukkitClass the bukkit class of the registry +- * @param registryHolder the minecraft registry holder +- * @return the bukkit registry of the provided class +- */ +- public static <B extends Keyed> Registry<?> createRegistry(Class<? super B> bukkitClass, RegistryAccess registryHolder) { +- if (bukkitClass == Art.class) { +- return new CraftRegistry<>(Art.class, registryHolder.lookupOrThrow(Registries.PAINTING_VARIANT), CraftArt::new, FieldRename.NONE); +- } +- if (bukkitClass == Attribute.class) { +- return new CraftRegistry<>(Attribute.class, registryHolder.lookupOrThrow(Registries.ATTRIBUTE), CraftAttribute::new, FieldRename.ATTRIBUTE_RENAME); +- } +- if (bukkitClass == Biome.class) { +- return new CraftRegistry<>(Biome.class, registryHolder.lookupOrThrow(Registries.BIOME), CraftBiome::new, FieldRename.BIOME_RENAME); +- } +- if (bukkitClass == Enchantment.class) { +- return new CraftRegistry<>(Enchantment.class, registryHolder.lookupOrThrow(Registries.ENCHANTMENT), CraftEnchantment::new, FieldRename.ENCHANTMENT_RENAME); +- } +- if (bukkitClass == Fluid.class) { +- return new CraftRegistry<>(Fluid.class, registryHolder.lookupOrThrow(Registries.FLUID), CraftFluid::new, FieldRename.NONE); +- } +- if (bukkitClass == GameEvent.class) { +- return new CraftRegistry<>(GameEvent.class, registryHolder.lookupOrThrow(Registries.GAME_EVENT), CraftGameEvent::new, FieldRename.NONE); +- } +- if (bukkitClass == MusicInstrument.class) { +- return new CraftRegistry<>(MusicInstrument.class, registryHolder.lookupOrThrow(Registries.INSTRUMENT), CraftMusicInstrument::new, FieldRename.NONE); +- } +- if (bukkitClass == MenuType.class) { +- return new CraftRegistry<>(MenuType.class, registryHolder.lookupOrThrow(Registries.MENU), CraftMenuType::new, FieldRename.NONE); +- } +- if (bukkitClass == PotionEffectType.class) { +- return new CraftRegistry<>(PotionEffectType.class, registryHolder.lookupOrThrow(Registries.MOB_EFFECT), CraftPotionEffectType::new, FieldRename.NONE); +- } +- if (bukkitClass == Sound.class) { +- return new CraftRegistry<>(Sound.class, registryHolder.lookupOrThrow(Registries.SOUND_EVENT), CraftSound::new, FieldRename.NONE); +- } +- if (bukkitClass == Structure.class) { +- return new CraftRegistry<>(Structure.class, registryHolder.lookupOrThrow(Registries.STRUCTURE), CraftStructure::new, FieldRename.NONE); +- } +- if (bukkitClass == StructureType.class) { +- return new CraftRegistry<>(StructureType.class, registryHolder.lookupOrThrow(Registries.STRUCTURE_TYPE), CraftStructureType::new, FieldRename.NONE); +- } +- if (bukkitClass == Villager.Type.class) { +- return new CraftRegistry<>(Villager.Type.class, registryHolder.lookupOrThrow(Registries.VILLAGER_TYPE), CraftVillager.CraftType::new, FieldRename.NONE); +- } +- if (bukkitClass == Villager.Profession.class) { +- return new CraftRegistry<>(Villager.Profession.class, registryHolder.lookupOrThrow(Registries.VILLAGER_PROFESSION), CraftVillager.CraftProfession::new, FieldRename.NONE); +- } +- if (bukkitClass == TrimMaterial.class) { +- return new CraftRegistry<>(TrimMaterial.class, registryHolder.lookupOrThrow(Registries.TRIM_MATERIAL), CraftTrimMaterial::new, FieldRename.NONE); +- } +- if (bukkitClass == TrimPattern.class) { +- return new CraftRegistry<>(TrimPattern.class, registryHolder.lookupOrThrow(Registries.TRIM_PATTERN), CraftTrimPattern::new, FieldRename.NONE); +- } +- if (bukkitClass == DamageType.class) { +- return new CraftRegistry<>(DamageType.class, registryHolder.lookupOrThrow(Registries.DAMAGE_TYPE), CraftDamageType::new, FieldRename.NONE); +- } +- if (bukkitClass == JukeboxSong.class) { +- return new CraftRegistry<>(JukeboxSong.class, registryHolder.lookupOrThrow(Registries.JUKEBOX_SONG), CraftJukeboxSong::new, FieldRename.NONE); +- } +- if (bukkitClass == Wolf.Variant.class) { +- return new CraftRegistry<>(Wolf.Variant.class, registryHolder.lookupOrThrow(Registries.WOLF_VARIANT), CraftWolf.CraftVariant::new, FieldRename.NONE); +- } +- if (bukkitClass == BlockType.class) { +- return new CraftRegistry<>(BlockType.class, registryHolder.lookupOrThrow(Registries.BLOCK), CraftBlockType::new, FieldRename.NONE); +- } +- if (bukkitClass == ItemType.class) { +- return new CraftRegistry<>(ItemType.class, registryHolder.lookupOrThrow(Registries.ITEM), CraftItemType::new, FieldRename.NONE); +- } +- if (bukkitClass == Frog.Variant.class) { +- return new CraftRegistry<>(Frog.Variant.class, registryHolder.lookupOrThrow(Registries.FROG_VARIANT), CraftFrog.CraftVariant::new, FieldRename.NONE); +- } +- if (bukkitClass == Cat.Type.class) { +- return new CraftRegistry<>(Cat.Type.class, registryHolder.lookupOrThrow(Registries.CAT_VARIANT), CraftCat.CraftType::new, FieldRename.NONE); +- } +- if (bukkitClass == MapCursor.Type.class) { +- return new CraftRegistry<>(MapCursor.Type.class, registryHolder.lookupOrThrow(Registries.MAP_DECORATION_TYPE), CraftMapCursor.CraftType::new, FieldRename.NONE); +- } +- if (bukkitClass == PatternType.class) { +- return new CraftRegistry<>(PatternType.class, registryHolder.lookupOrThrow(Registries.BANNER_PATTERN), CraftPatternType::new, FieldRename.NONE); +- } +- +- return null; +- } ++ // Paper - move to PaperRegistries + ++ // Paper - NOTE: As long as all uses of the method below relate to *serialization* via ConfigurationSerializable, it's fine + public static <B extends Keyed> B get(Registry<B> bukkit, NamespacedKey namespacedKey, ApiVersion apiVersion) { + if (bukkit instanceof CraftRegistry<B, ?> craft) { +- return craft.get(namespacedKey, apiVersion); ++ return craft.get(craft.serializationUpdater.apply(namespacedKey, apiVersion)); // Paper + } + + if (bukkit instanceof Registry.SimpleRegistry<?> simple) { +@@ -234,23 +150,21 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> { + return bukkit.get(namespacedKey); + } + +- private final Class<? super B> bukkitClass; ++ private final Class<?> bukkitClass; // Paper - relax preload class + private final Map<NamespacedKey, B> cache = new HashMap<>(); + private final net.minecraft.core.Registry<M> minecraftRegistry; + private final BiFunction<NamespacedKey, M, B> minecraftToBukkit; +- private final BiFunction<NamespacedKey, ApiVersion, NamespacedKey> updater; ++ private final BiFunction<NamespacedKey, ApiVersion, NamespacedKey> serializationUpdater; // Paper - rename to make it *clear* what it is *only* for + private boolean init; + +- public CraftRegistry(Class<? super B> bukkitClass, net.minecraft.core.Registry<M> minecraftRegistry, BiFunction<NamespacedKey, M, B> minecraftToBukkit, BiFunction<NamespacedKey, ApiVersion, NamespacedKey> updater) { ++ public CraftRegistry(Class<?> bukkitClass, net.minecraft.core.Registry<M> minecraftRegistry, BiFunction<NamespacedKey, M, B> minecraftToBukkit, BiFunction<NamespacedKey, ApiVersion, NamespacedKey> serializationUpdater) { // Paper - relax preload class + this.bukkitClass = bukkitClass; + this.minecraftRegistry = minecraftRegistry; + this.minecraftToBukkit = minecraftToBukkit; +- this.updater = updater; ++ this.serializationUpdater = serializationUpdater; + } + +- public B get(NamespacedKey namespacedKey, ApiVersion apiVersion) { +- return this.get(this.updater.apply(namespacedKey, apiVersion)); +- } ++ // Paper - inline into CraftRegistry#get(Registry, NamespacedKey, ApiVersion) above + + @Override + public B get(NamespacedKey namespacedKey) { +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index f75d73402cf633254fe1ef4e919f09db48165190..5ae8f646083fb580ac8d28fcbfe8ed2208b45d09 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -284,7 +284,7 @@ public final class CraftServer implements Server { + protected final DedicatedServer console; + protected final DedicatedPlayerList playerList; + private final Map<String, World> worlds = new LinkedHashMap<String, World>(); +- private final Map<Class<?>, Registry<?>> registries = new HashMap<>(); ++ // private final Map<Class<?>, Registry<?>> registries = new HashMap<>(); // Paper - replace with RegistryAccess + private YamlConfiguration configuration; + private YamlConfiguration commandsConfiguration; + private final Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); +@@ -431,6 +431,7 @@ public final class CraftServer implements Server { + } + + private void loadCompatibilities() { ++ if (true) return; // Paper - Big nope + ConfigurationSection compatibilities = this.configuration.getConfigurationSection("settings.compatibility"); + if (compatibilities == null) { + this.activeCompatibilities = Collections.emptySet(); +@@ -2754,7 +2755,7 @@ public final class CraftServer implements Server { + + @Override + public <T extends Keyed> Registry<T> getRegistry(Class<T> aClass) { +- return (Registry<T>) this.registries.computeIfAbsent(aClass, key -> CraftRegistry.createRegistry(aClass, this.console.registryAccess())); ++ return io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(aClass); // Paper - replace with RegistryAccess + } + + @Deprecated +diff --git a/src/main/java/org/bukkit/craftbukkit/legacy/FieldRename.java b/src/main/java/org/bukkit/craftbukkit/legacy/FieldRename.java +index 091e11934bddb180f0b2e51efb3921c62275d41d..12fe2f8d0dcb715545e071023490a32125b9c4a4 100644 +--- a/src/main/java/org/bukkit/craftbukkit/legacy/FieldRename.java ++++ b/src/main/java/org/bukkit/craftbukkit/legacy/FieldRename.java +@@ -51,11 +51,14 @@ public class FieldRename { + }; + } + +- @RequireCompatibility("allow-old-keys-in-registry") +- public static <T extends Keyed> T get(Registry<T> registry, NamespacedKey namespacedKey) { +- // We don't have version-specific changes, so just use current, and don't inject a version +- return CraftRegistry.get(registry, namespacedKey, ApiVersion.CURRENT); +- } ++ // Paper start - absolutely not, having this as an expectation for plugin developers opens a huge ++ // can of worms in the future, especially if mojang comes back and reuses some old key ++ //@RequireCompatibility("allow-old-keys-in-registry") ++ //public static <T extends Keyed> T get(Registry<T> registry, NamespacedKey namespacedKey) { ++ // // We don't have version-specific changes, so just use current, and don't inject a version ++ // return CraftRegistry.get(registry, namespacedKey, ApiVersion.CURRENT); ++ //} ++ // Paper end + + // PatternType + private static final FieldRenameData PATTERN_TYPE_DATA = FieldRenameData.Builder.newBuilder() +diff --git a/src/main/java/org/bukkit/craftbukkit/util/Commodore.java b/src/main/java/org/bukkit/craftbukkit/util/Commodore.java +index 61d617d4abb9c9cf5c711459aa98c8b173597a9a..c9b789c2a904c2caff516ee9aeff4a6b368766f4 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/Commodore.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/Commodore.java +@@ -220,20 +220,10 @@ public class Commodore { + + public byte[] convert(byte[] b, final String pluginName, final ApiVersion pluginVersion, final Set<String> activeCompatibilities) { + final boolean modern = pluginVersion.isNewerThanOrSameAs(ApiVersion.FLATTENING); +- final boolean enumCompatibility = pluginVersion.isOlderThanOrSameAs(ApiVersion.getOrCreateVersion("1.20.6")) && activeCompatibilities.contains("enum-compatibility-mode"); + ClassReader cr = new ClassReader(b); + ClassWriter cw = new ClassWriter(cr, 0); + +- List<String> methodEnumSignatures = Commodore.getMethodSignatures(b); +- Multimap<String, String> enumLessToEnum = HashMultimap.create(); +- for (String method : methodEnumSignatures) { +- enumLessToEnum.put(method.replace("Ljava/lang/Enum;", "Ljava/lang/Object;"), method); +- } +- + ClassVisitor visitor = cw; +- if (enumCompatibility) { +- visitor = new LimitedClassRemapper(cw, new SimpleRemapper(Commodore.ENUM_RENAMES)); +- } + + visitor = io.papermc.paper.pluginremap.reflect.ReflectionRemapper.visitor(visitor); // Paper + cr.accept(new ClassRemapper(new ClassVisitor(Opcodes.ASM9, visitor) { +@@ -300,15 +290,6 @@ public class Commodore { + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { +- if (enumCompatibility && (access & Opcodes.ACC_SYNTHETIC) != 0 && (access & Opcodes.ACC_BRIDGE) != 0 && desc.contains("Ljava/lang/Object;")) { +- // SPIGOT-7820: Do not use object method if enum method is present +- // The object method does only redirect to the enum method +- Collection<String> result = enumLessToEnum.get(desc.replace("Ljava/lang/Enum;", "Ljava/lang/Object;") + " " + name); +- if (result.size() == 2) { +- name = name + "_BUKKIT_UNUSED"; +- } +- } +- + return new MethodVisitor(this.api, super.visitMethod(access, name, desc, signature, exceptions)) { + // Paper start - Plugin rewrites + @Override +diff --git a/src/main/resources/META-INF/services/io.papermc.paper.registry.RegistryAccess b/src/main/resources/META-INF/services/io.papermc.paper.registry.RegistryAccess +new file mode 100644 +index 0000000000000000000000000000000000000000..8a083d45004f82fc9c51c219fb20f34624adb501 +--- /dev/null ++++ b/src/main/resources/META-INF/services/io.papermc.paper.registry.RegistryAccess +@@ -0,0 +1 @@ ++io.papermc.paper.registry.PaperRegistryAccess +diff --git a/src/main/resources/configurations/bukkit.yml b/src/main/resources/configurations/bukkit.yml +index 543e37737bc6fdca23ed9ed0606805d345515a5a..eef7c125b2689f29cae5464659eacdf33f5695b2 100644 +--- a/src/main/resources/configurations/bukkit.yml ++++ b/src/main/resources/configurations/bukkit.yml +@@ -23,9 +23,6 @@ settings: + shutdown-message: Server closed + minimum-api: none + use-map-color-cache: true +- compatibility: +- allow-old-keys-in-registry: false +- enum-compatibility-mode: false + spawn-limits: + monsters: 70 + animals: 10 +diff --git a/src/test/java/io/papermc/paper/registry/LegacyRegistryIdentifierTest.java b/src/test/java/io/papermc/paper/registry/LegacyRegistryIdentifierTest.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a80b0ded74c0be657e734de61cbf5e32e16c26a8 +--- /dev/null ++++ b/src/test/java/io/papermc/paper/registry/LegacyRegistryIdentifierTest.java +@@ -0,0 +1,21 @@ ++package io.papermc.paper.registry; ++ ++import org.bukkit.GameEvent; ++import org.bukkit.MusicInstrument; ++import org.bukkit.inventory.meta.trim.TrimPattern; ++import org.bukkit.support.environment.VanillaFeature; ++import org.junit.jupiter.api.Test; ++ ++import static org.junit.jupiter.api.Assertions.assertSame; ++ ++@Deprecated ++@VanillaFeature ++class LegacyRegistryIdentifierTest { ++ ++ @Test ++ void testSeveralConversions() { ++ assertSame(RegistryKey.GAME_EVENT, PaperRegistryAccess.byType(GameEvent.class)); ++ assertSame(RegistryKey.TRIM_PATTERN, PaperRegistryAccess.byType(TrimPattern.class)); ++ assertSame(RegistryKey.INSTRUMENT, PaperRegistryAccess.byType(MusicInstrument.class)); ++ } ++} +diff --git a/src/test/java/io/papermc/paper/registry/RegistryKeyTest.java b/src/test/java/io/papermc/paper/registry/RegistryKeyTest.java +index d8857a05858585113bc7efde3416748effb53d01..41c38b1b6d25c7a7ed08d70b19f5f4a70fc2df94 100644 +--- a/src/test/java/io/papermc/paper/registry/RegistryKeyTest.java ++++ b/src/test/java/io/papermc/paper/registry/RegistryKeyTest.java +@@ -1,15 +1,19 @@ + package io.papermc.paper.registry; + ++import io.papermc.paper.registry.entry.RegistryEntry; + import java.util.Optional; + import java.util.stream.Stream; + import net.minecraft.core.Registry; + import net.minecraft.resources.ResourceKey; + import net.minecraft.resources.ResourceLocation; +-import org.bukkit.support.AbstractTestingBase; ++import org.bukkit.support.RegistryHelper; ++import org.bukkit.support.environment.AllFeatures; ++import org.checkerframework.checker.nullness.qual.Nullable; + import org.junit.jupiter.api.BeforeAll; + import org.junit.jupiter.params.ParameterizedTest; + import org.junit.jupiter.params.provider.MethodSource; + ++import static org.junit.jupiter.api.Assertions.assertNotNull; + import static org.junit.jupiter.api.Assertions.assertTrue; + + @AllFeatures +@@ -29,6 +33,12 @@ class RegistryKeyTest { + void testApiRegistryKeysExist(final RegistryKey<?> key) { + final Optional<Registry<Object>> registry = RegistryHelper.getRegistry().lookup(ResourceKey.createRegistryKey(ResourceLocation.parse(key.key().asString()))); + assertTrue(registry.isPresent(), "Missing vanilla registry for " + key.key().asString()); ++ } + ++ @ParameterizedTest ++ @MethodSource("data") ++ void testRegistryEntryExists(final RegistryKey<?> key) { ++ final @Nullable RegistryEntry<?, ?> entry = PaperRegistries.getEntry(key); ++ assertNotNull(entry, "Missing PaperRegistries entry for " + key); + } + } +diff --git a/src/test/java/org/bukkit/registry/RegistryArgumentAddedTest.java b/src/test/java/org/bukkit/registry/RegistryArgumentAddedTest.java +index b65a3ee68c177da7ef5a57608187dc1672257c7f..c1016e0eb00e952551370c874e8d678fef8ba3dc 100644 +--- a/src/test/java/org/bukkit/registry/RegistryArgumentAddedTest.java ++++ b/src/test/java/org/bukkit/registry/RegistryArgumentAddedTest.java +@@ -22,14 +22,17 @@ public class RegistryArgumentAddedTest { + // Make sure every registry is created + Class.forName(Registry.class.getName()); + +- Set<Class<?>> loadedRegistries = new HashSet<>(AllFeaturesExtension.getRealRegistries().keySet()); +- Set<Class<?>> notFound = new HashSet<>(); ++ // Paper start ++ Set<io.papermc.paper.registry.RegistryKey<?>> loadedRegistries = java.util.Collections.newSetFromMap(new java.util.IdentityHashMap<>()); ++ loadedRegistries.addAll(io.papermc.paper.registry.PaperRegistryAccess.instance().getLoadedServerBackedRegistries()); ++ // Paper end ++ Set<io.papermc.paper.registry.RegistryKey<?>> notFound = new HashSet<>(); // Paper + + RegistriesArgumentProvider + .getData() + .map(Arguments::get) + .map(array -> array[0]) +- .map(clazz -> (Class<?>) clazz) ++ .map(clazz -> (io.papermc.paper.registry.RegistryKey<?>) clazz) // Paper + .forEach(clazz -> { + if (!loadedRegistries.remove(clazz)) { + notFound.add(clazz); +diff --git a/src/test/java/org/bukkit/registry/RegistryConversionTest.java b/src/test/java/org/bukkit/registry/RegistryConversionTest.java +index e97328b95973db52d44bc4d0aefd8eb69f2ebdea..01e351f4e292efe78fc1a1db0f31b2c0a313b101 100644 +--- a/src/test/java/org/bukkit/registry/RegistryConversionTest.java ++++ b/src/test/java/org/bukkit/registry/RegistryConversionTest.java +@@ -41,9 +41,9 @@ public class RegistryConversionTest { + + @Order(1) + @RegistriesTest +- public void testHandleableImplementation(Class<? extends Keyed> clazz) { ++ public void testHandleableImplementation(io.papermc.paper.registry.RegistryKey<? extends Keyed> type, Class<? extends Keyed> clazz) { // Paper + Set<Class<? extends Keyed>> notImplemented = new HashSet<>(); +- Registry<? extends Keyed> registry = Bukkit.getRegistry(clazz); ++ Registry<? extends Keyed> registry = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(type); // Paper + + for (Keyed item : registry) { + if (!(item instanceof Handleable<?>)) { +@@ -63,7 +63,7 @@ public class RegistryConversionTest { + + @Order(2) + @RegistriesTest +- public void testMinecraftToBukkitPresent(Class<? extends Keyed> clazz, ResourceKey<net.minecraft.core.Registry<?>> registryKey, ++ public void testMinecraftToBukkitPresent(io.papermc.paper.registry.RegistryKey<? extends Keyed> type, Class<? extends Keyed> clazz, ResourceKey<net.minecraft.core.Registry<?>> registryKey, + Class<? extends Keyed> craftClazz, Class<?> minecraftClazz, boolean newMethod) { + String methodName = (newMethod) ? RegistryConversionTest.MINECRAFT_TO_BUKKIT_NEW : RegistryConversionTest.MINECRAFT_TO_BUKKIT; + Method method = null; +@@ -112,7 +112,7 @@ public class RegistryConversionTest { + + @Order(2) + @RegistriesTest +- public void testBukkitToMinecraftPresent(Class<? extends Keyed> clazz, ResourceKey<net.minecraft.core.Registry<?>> registryKey, ++ public void testBukkitToMinecraftPresent(io.papermc.paper.registry.RegistryKey<? extends Keyed> type, Class<? extends Keyed> clazz, ResourceKey<net.minecraft.core.Registry<?>> registryKey, + Class<? extends Keyed> craftClazz, Class<?> minecraftClazz, boolean newMethod) { + String methodName = (newMethod) ? RegistryConversionTest.BUKKIT_TO_MINECRAFT_NEW : RegistryConversionTest.BUKKIT_TO_MINECRAFT; + Method method = null; +@@ -159,9 +159,9 @@ public class RegistryConversionTest { + """, minecraftClazz.getName(), methodName, clazz.getSimpleName()); + } + +- @Order(2) ++ @Order(3) + @RegistriesTest +- public void testMinecraftToBukkitNullValue(Class<? extends Keyed> clazz) throws IllegalAccessException { ++ public void testMinecraftToBukkitNullValue(io.papermc.paper.registry.RegistryKey<? extends Keyed> type, Class<? extends Keyed> clazz) throws IllegalAccessException { // Paper + this.checkValidMinecraftToBukkit(clazz); + + try { +@@ -180,7 +180,7 @@ public class RegistryConversionTest { + + @Order(3) + @RegistriesTest +- public void testBukkitToMinecraftNullValue(Class<? extends Keyed> clazz) throws IllegalAccessException { ++ public void testBukkitToMinecraftNullValue(io.papermc.paper.registry.RegistryKey<? extends Keyed> type, Class<? extends Keyed> clazz) throws IllegalAccessException { // Paper + this.checkValidBukkitToMinecraft(clazz); + + try { +@@ -199,14 +199,14 @@ public class RegistryConversionTest { + + @Order(3) + @RegistriesTest +- public void testMinecraftToBukkit(Class<? extends Keyed> clazz) { ++ public void testMinecraftToBukkit(io.papermc.paper.registry.RegistryKey<? extends Keyed> type, Class<? extends Keyed> clazz) { // Paper + this.checkValidMinecraftToBukkit(clazz); + this.checkValidHandle(clazz); + + Map<Object, Object> notMatching = new HashMap<>(); + Method method = RegistryConversionTest.MINECRAFT_TO_BUKKIT_METHODS.get(clazz); + +- RegistryArgumentProvider.getValues(clazz).map(Arguments::get).forEach(arguments -> { ++ RegistryArgumentProvider.getValues(type).map(Arguments::get).forEach(arguments -> { // Paper + Keyed bukkit = (Keyed) arguments[0]; + Object minecraft = arguments[1]; + +@@ -230,14 +230,14 @@ public class RegistryConversionTest { + + @Order(3) + @RegistriesTest +- public void testBukkitToMinecraft(Class<? extends Keyed> clazz) { ++ public void testBukkitToMinecraft(io.papermc.paper.registry.RegistryKey<? extends Keyed> type, Class<? extends Keyed> clazz) { // Paper + this.checkValidBukkitToMinecraft(clazz); + this.checkValidHandle(clazz); + + Map<Object, Object> notMatching = new HashMap<>(); + Method method = RegistryConversionTest.BUKKIT_TO_MINECRAFT_METHODS.get(clazz); + +- RegistryArgumentProvider.getValues(clazz).map(Arguments::get).forEach(arguments -> { ++ RegistryArgumentProvider.getValues(type).map(Arguments::get).forEach(arguments -> { // Paper + Keyed bukkit = (Keyed) arguments[0]; + Object minecraft = arguments[1]; + +@@ -265,7 +265,7 @@ public class RegistryConversionTest { + */ + @Order(3) + @RegistriesTest +- public void testMinecraftToBukkitNoValidMinecraft(Class<? extends Keyed> clazz, ResourceKey<net.minecraft.core.Registry<?>> registryKey, ++ public void testMinecraftToBukkitNoValidMinecraft(io.papermc.paper.registry.RegistryKey<? extends Keyed> type, Class<? extends Keyed> clazz, ResourceKey<net.minecraft.core.Registry<?>> registryKey, // Paper + Class<? extends Keyed> craftClazz, Class<?> minecraftClazz) throws IllegalAccessException { + this.checkValidMinecraftToBukkit(clazz); + +diff --git a/src/test/java/org/bukkit/support/extension/AllFeaturesExtension.java b/src/test/java/org/bukkit/support/extension/AllFeaturesExtension.java +index e9eb521419bbacb03d7000ace355f2a9f5a6a9c5..8fef8421e3cf87913746a314a477634bd3e99300 100644 +--- a/src/test/java/org/bukkit/support/extension/AllFeaturesExtension.java ++++ b/src/test/java/org/bukkit/support/extension/AllFeaturesExtension.java +@@ -39,22 +39,7 @@ public class AllFeaturesExtension extends BaseExtension { + + Bukkit.setServer(server); + +- when(server.getRegistry(any())) +- .then(invocation -> { +- Class<? extends Keyed> keyed = invocation.getArgument(0); +- if (spyRegistries.containsKey(keyed)) { +- return spyRegistries.get(keyed); +- } +- +- Registry<?> registry = CraftRegistry.createRegistry(keyed, RegistryHelper.getRegistry()); +- realRegistries.put(keyed, registry); +- +- Registry<?> spy = mock(registry.getClass(), withSettings().stubOnly().spiedInstance(registry).defaultAnswer(CALLS_REAL_METHODS)); +- +- spyRegistries.put(keyed, spy); +- +- return spy; +- }); ++ // Paper - Add RegistryAccess for managing registries - replaced with registry access + + CraftRegistry.setMinecraftRegistry(RegistryHelper.getRegistry()); + } +diff --git a/src/test/java/org/bukkit/support/extension/LegacyExtension.java b/src/test/java/org/bukkit/support/extension/LegacyExtension.java +index 94cf52cf7603e6814682c92b26fcf03a8b927838..c9c3227c3b7fa36ed80f2dc828885a0128e1e3d0 100644 +--- a/src/test/java/org/bukkit/support/extension/LegacyExtension.java ++++ b/src/test/java/org/bukkit/support/extension/LegacyExtension.java +@@ -30,11 +30,7 @@ public class LegacyExtension extends BaseExtension { + + Bukkit.setServer(server); + +- when(server.getRegistry(any())) +- .then(invocation -> { +- Class<? extends Keyed> keyed = invocation.getArgument(0); +- return registries.computeIfAbsent(keyed, k -> CraftRegistry.createRegistry(keyed, RegistryHelper.getRegistry())); +- }); ++ // Paper - Add RegistryAccess for managing registries - replaced with registry access + + CraftRegistry.setMinecraftRegistry(RegistryHelper.getRegistry()); + } +diff --git a/src/test/java/org/bukkit/support/extension/SlowExtension.java b/src/test/java/org/bukkit/support/extension/SlowExtension.java +index e0ce6836d4365c36303f6c675a75ef6a9b047b92..87364f223fbd6185b041138550fcb6e3ed07d1ae 100644 +--- a/src/test/java/org/bukkit/support/extension/SlowExtension.java ++++ b/src/test/java/org/bukkit/support/extension/SlowExtension.java +@@ -30,11 +30,7 @@ public class SlowExtension extends BaseExtension { + + Bukkit.setServer(server); + +- when(server.getRegistry(any())) +- .then(invocation -> { +- Class<? extends Keyed> keyed = invocation.getArgument(0); +- return registries.computeIfAbsent(keyed, k -> CraftRegistry.createRegistry(keyed, RegistryHelper.getRegistry())); +- }); ++ // Paper - Add RegistryAccess for managing registries - replaced with registry access + + CraftRegistry.setMinecraftRegistry(RegistryHelper.getRegistry()); + } +diff --git a/src/test/java/org/bukkit/support/extension/VanillaFeatureExtension.java b/src/test/java/org/bukkit/support/extension/VanillaFeatureExtension.java +index bbd5dd5b27937ddc3d8c57f2b604331495b0f311..626c3033e36897846fe84a77d05e2e91a15598e5 100644 +--- a/src/test/java/org/bukkit/support/extension/VanillaFeatureExtension.java ++++ b/src/test/java/org/bukkit/support/extension/VanillaFeatureExtension.java +@@ -30,11 +30,7 @@ public class VanillaFeatureExtension extends BaseExtension { + + Bukkit.setServer(server); + +- when(server.getRegistry(any())) +- .then(invocation -> { +- Class<? extends Keyed> keyed = invocation.getArgument(0); +- return registries.computeIfAbsent(keyed, k -> CraftRegistry.createRegistry(keyed, RegistryHelper.getRegistry())); +- }); ++ // Paper - Add RegistryAccess for managing registries - replaced with registry access + + CraftRegistry.setMinecraftRegistry(RegistryHelper.getRegistry()); + } +diff --git a/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java b/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java +index bfec6280e8b753a29ad2d9eb88808beb79ec65ea..b717a5ffa567781b0687bbe238b62844214db284 100644 +--- a/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java ++++ b/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java +@@ -1,6 +1,7 @@ + package org.bukkit.support.provider; + + import com.google.common.collect.Lists; ++import io.papermc.paper.registry.RegistryKey; + import java.util.List; + import java.util.stream.Stream; + import net.minecraft.core.registries.Registries; +@@ -73,41 +74,40 @@ public class RegistriesArgumentProvider implements ArgumentsProvider { + private static final List<Arguments> DATA = Lists.newArrayList(); + + static { +- // Order: Bukkit class, Minecraft Registry key, CraftBukkit class, Minecraft class +- register(Art.class, Registries.PAINTING_VARIANT, CraftArt.class, PaintingVariant.class); +- register(Attribute.class, Registries.ATTRIBUTE, CraftAttribute.class, net.minecraft.world.entity.ai.attributes.Attribute.class); +- register(Biome.class, Registries.BIOME, CraftBiome.class, net.minecraft.world.level.biome.Biome.class); +- register(Enchantment.class, Registries.ENCHANTMENT, CraftEnchantment.class, net.minecraft.world.item.enchantment.Enchantment.class); +- register(Fluid.class, Registries.FLUID, CraftFluid.class, net.minecraft.world.level.material.Fluid.class); +- register(GameEvent.class, Registries.GAME_EVENT, CraftGameEvent.class, net.minecraft.world.level.gameevent.GameEvent.class); +- register(MusicInstrument.class, Registries.INSTRUMENT, CraftMusicInstrument.class, Instrument.class); +- register(MenuType.class, Registries.MENU, CraftMenuType.class, net.minecraft.world.inventory.MenuType.class); +- register(PotionEffectType.class, Registries.MOB_EFFECT, CraftPotionEffectType.class, MobEffect.class); +- register(Sound.class, Registries.SOUND_EVENT, CraftSound.class, SoundEvent.class); +- register(Structure.class, Registries.STRUCTURE, CraftStructure.class, net.minecraft.world.level.levelgen.structure.Structure.class); +- register(StructureType.class, Registries.STRUCTURE_TYPE, CraftStructureType.class, net.minecraft.world.level.levelgen.structure.StructureType.class); +- register(Villager.Type.class, Registries.VILLAGER_TYPE, CraftVillager.CraftType.class, VillagerType.class); +- register(Villager.Profession.class, Registries.VILLAGER_PROFESSION, CraftVillager.CraftProfession.class, VillagerProfession.class); +- register(TrimMaterial.class, Registries.TRIM_MATERIAL, CraftTrimMaterial.class, net.minecraft.world.item.equipment.trim.TrimMaterial.class); +- register(TrimPattern.class, Registries.TRIM_PATTERN, CraftTrimPattern.class, net.minecraft.world.item.equipment.trim.TrimPattern.class); +- register(DamageType.class, Registries.DAMAGE_TYPE, CraftDamageType.class, net.minecraft.world.damagesource.DamageType.class); +- register(JukeboxSong.class, Registries.JUKEBOX_SONG, CraftJukeboxSong.class, net.minecraft.world.item.JukeboxSong.class); +- register(Wolf.Variant.class, Registries.WOLF_VARIANT, CraftWolf.CraftVariant.class, WolfVariant.class); +- register(ItemType.class, Registries.ITEM, CraftItemType.class, net.minecraft.world.item.Item.class, true); +- register(BlockType.class, Registries.BLOCK, CraftBlockType.class, net.minecraft.world.level.block.Block.class, true); +- register(Frog.Variant.class, Registries.FROG_VARIANT, CraftFrog.CraftVariant.class, FrogVariant.class); +- register(Cat.Type.class, Registries.CAT_VARIANT, CraftCat.CraftType.class, CatVariant.class); +- register(MapCursor.Type.class, Registries.MAP_DECORATION_TYPE, CraftMapCursor.CraftType.class, MapDecorationType.class); +- register(PatternType.class, Registries.BANNER_PATTERN, CraftPatternType.class, BannerPattern.class); +- ++ // Order: RegistryKey, Bukkit class, Minecraft Registry key, CraftBukkit class, Minecraft class ++ register(RegistryKey.PAINTING_VARIANT, Art.class, Registries.PAINTING_VARIANT, CraftArt.class, PaintingVariant.class); ++ register(RegistryKey.ATTRIBUTE, Attribute.class, Registries.ATTRIBUTE, CraftAttribute.class, net.minecraft.world.entity.ai.attributes.Attribute.class); ++ register(RegistryKey.BIOME, Biome.class, Registries.BIOME, CraftBiome.class, net.minecraft.world.level.biome.Biome.class); ++ register(RegistryKey.ENCHANTMENT, Enchantment.class, Registries.ENCHANTMENT, CraftEnchantment.class, net.minecraft.world.item.enchantment.Enchantment.class); ++ register(RegistryKey.FLUID, Fluid.class, Registries.FLUID, CraftFluid.class, net.minecraft.world.level.material.Fluid.class); ++ register(RegistryKey.GAME_EVENT, GameEvent.class, Registries.GAME_EVENT, CraftGameEvent.class, net.minecraft.world.level.gameevent.GameEvent.class); ++ register(RegistryKey.INSTRUMENT, MusicInstrument.class, Registries.INSTRUMENT, CraftMusicInstrument.class, Instrument.class); ++ register(RegistryKey.MOB_EFFECT, PotionEffectType.class, Registries.MOB_EFFECT, CraftPotionEffectType.class, MobEffect.class); ++ register(RegistryKey.SOUND_EVENT, Sound.class, Registries.SOUND_EVENT, CraftSound.class, SoundEvent.class); ++ register(RegistryKey.STRUCTURE, Structure.class, Registries.STRUCTURE, CraftStructure.class, net.minecraft.world.level.levelgen.structure.Structure.class); ++ register(RegistryKey.STRUCTURE_TYPE, StructureType.class, Registries.STRUCTURE_TYPE, CraftStructureType.class, net.minecraft.world.level.levelgen.structure.StructureType.class); ++ register(RegistryKey.VILLAGER_TYPE, Villager.Type.class, Registries.VILLAGER_TYPE, CraftVillager.CraftType.class, VillagerType.class); ++ register(RegistryKey.VILLAGER_PROFESSION, Villager.Profession.class, Registries.VILLAGER_PROFESSION, CraftVillager.CraftProfession.class, VillagerProfession.class); ++ register(RegistryKey.TRIM_MATERIAL, TrimMaterial.class, Registries.TRIM_MATERIAL, CraftTrimMaterial.class, net.minecraft.world.item.equipment.trim.TrimMaterial.class); ++ register(RegistryKey.TRIM_PATTERN, TrimPattern.class, Registries.TRIM_PATTERN, CraftTrimPattern.class, net.minecraft.world.item.equipment.trim.TrimPattern.class); ++ register(RegistryKey.DAMAGE_TYPE, DamageType.class, Registries.DAMAGE_TYPE, CraftDamageType.class, net.minecraft.world.damagesource.DamageType.class); ++ register(RegistryKey.JUKEBOX_SONG, JukeboxSong.class, Registries.JUKEBOX_SONG, CraftJukeboxSong.class, net.minecraft.world.item.JukeboxSong.class); ++ register(RegistryKey.WOLF_VARIANT, Wolf.Variant.class, Registries.WOLF_VARIANT, CraftWolf.CraftVariant.class, WolfVariant.class); ++ register(RegistryKey.ITEM, ItemType.class, Registries.ITEM, CraftItemType.class, net.minecraft.world.item.Item.class, true); ++ register(RegistryKey.BLOCK, BlockType.class, Registries.BLOCK, CraftBlockType.class, net.minecraft.world.level.block.Block.class, true); ++ register(RegistryKey.FROG_VARIANT, Frog.Variant.class, Registries.FROG_VARIANT, CraftFrog.CraftVariant.class, FrogVariant.class); ++ register(RegistryKey.CAT_VARIANT, Cat.Type.class, Registries.CAT_VARIANT, CraftCat.CraftType.class, CatVariant.class); ++ register(RegistryKey.MAP_DECORATION_TYPE, MapCursor.Type.class, Registries.MAP_DECORATION_TYPE, CraftMapCursor.CraftType.class, MapDecorationType.class); ++ register(RegistryKey.BANNER_PATTERN, PatternType.class, Registries.BANNER_PATTERN, CraftPatternType.class, BannerPattern.class); ++ register(RegistryKey.MENU, MenuType.class, Registries.MENU, CraftMenuType.class, net.minecraft.world.inventory.MenuType.class); + } + +- private static void register(Class bukkit, ResourceKey registry, Class craft, Class minecraft) { +- RegistriesArgumentProvider.register(bukkit, registry, craft, minecraft, false); ++ private static void register(RegistryKey registryKey, Class bukkit, ResourceKey registry, Class craft, Class minecraft) { // Paper ++ RegistriesArgumentProvider.register(registryKey, bukkit, registry, craft, minecraft, false); + } + +- private static void register(Class bukkit, ResourceKey registry, Class craft, Class minecraft, boolean newClass) { +- RegistriesArgumentProvider.DATA.add(Arguments.of(bukkit, registry, craft, minecraft, newClass)); ++ private static void register(RegistryKey registryKey, Class bukkit, ResourceKey registry, Class craft, Class minecraft, boolean newClass) { // Paper ++ RegistriesArgumentProvider.DATA.add(Arguments.of(registryKey, bukkit, registry, craft, minecraft, newClass)); + } + + @Override +diff --git a/src/test/java/org/bukkit/support/provider/RegistryArgumentProvider.java b/src/test/java/org/bukkit/support/provider/RegistryArgumentProvider.java +index f2ceb5e67536dfa5de792dc64a7898fd2e8aa810..beb5fc9e721f5de54064c3d241df9ca9f4cd4f65 100644 +--- a/src/test/java/org/bukkit/support/provider/RegistryArgumentProvider.java ++++ b/src/test/java/org/bukkit/support/provider/RegistryArgumentProvider.java +@@ -22,11 +22,11 @@ public class RegistryArgumentProvider implements ArgumentsProvider, AnnotationCo + + @Override + public Stream<? extends Arguments> provideArguments(ExtensionContext extensionContext) throws Exception { +- return RegistryArgumentProvider.getValues(this.registryType); ++ return RegistryArgumentProvider.getValues(io.papermc.paper.registry.PaperRegistryAccess.byType(this.registryType)); // Paper + } + +- public static Stream<? extends Arguments> getValues(Class<? extends Keyed> registryType) { +- Registry<?> registry = Bukkit.getRegistry(registryType); ++ public static Stream<? extends Arguments> getValues(io.papermc.paper.registry.RegistryKey<? extends Keyed> registryType) { // Paper ++ Registry<?> registry = io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(registryType); // Paper + return registry.stream().map(keyed -> (Handleable<?>) keyed) + .map(handleAble -> Arguments.of(handleAble, handleAble.getHandle())); + } diff --git a/patches/server/0465-Add-StructuresLocateEvent.patch b/patches/server/0465-Add-StructuresLocateEvent.patch new file mode 100644 index 0000000000..6f3d0638ff --- /dev/null +++ b/patches/server/0465-Add-StructuresLocateEvent.patch @@ -0,0 +1,36 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: dfsek <[email protected]> +Date: Wed, 16 Sep 2020 01:12:29 -0700 +Subject: [PATCH] Add StructuresLocateEvent + +Co-authored-by: Jake Potrebic <[email protected]> + +diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java +index 0acf8b62ddb5e005f8f861558934e8afc8673725..416b1afcbab093f45900a4d55708609ba5a7de7a 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java ++++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java +@@ -127,6 +127,24 @@ public abstract class ChunkGenerator { + + @Nullable + public Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel world, HolderSet<Structure> structures, BlockPos center, int radius, boolean skipReferencedStructures) { ++ // Paper start - StructuresLocateEvent ++ final org.bukkit.World bukkitWorld = world.getWorld(); ++ final org.bukkit.Location origin = io.papermc.paper.util.MCUtil.toLocation(world, center); ++ final List<org.bukkit.generator.structure.Structure> apiStructures = structures.stream().map(Holder::value).map(nms -> org.bukkit.craftbukkit.generator.structure.CraftStructure.minecraftToBukkit(nms)).toList(); ++ if (!apiStructures.isEmpty()) { ++ final io.papermc.paper.event.world.StructuresLocateEvent event = new io.papermc.paper.event.world.StructuresLocateEvent(bukkitWorld, origin, apiStructures, radius, skipReferencedStructures); ++ if (!event.callEvent()) { ++ return null; ++ } ++ if (event.getResult() != null) { ++ return Pair.of(io.papermc.paper.util.MCUtil.toBlockPos(event.getResult().pos()), world.registryAccess().lookupOrThrow(Registries.STRUCTURE).wrapAsHolder(org.bukkit.craftbukkit.generator.structure.CraftStructure.bukkitToMinecraft(event.getResult().structure()))); ++ } ++ center = io.papermc.paper.util.MCUtil.toBlockPosition(event.getOrigin()); ++ radius = event.getRadius(); ++ skipReferencedStructures = event.shouldFindUnexplored(); ++ structures = HolderSet.direct(api -> world.registryAccess().lookupOrThrow(Registries.STRUCTURE).wrapAsHolder(org.bukkit.craftbukkit.generator.structure.CraftStructure.bukkitToMinecraft(api)), event.getStructures()); ++ } ++ // Paper end + ChunkGeneratorStructureState chunkgeneratorstructurestate = world.getChunkSource().getGeneratorState(); + Map<StructurePlacement, Set<Holder<Structure>>> map = new Object2ObjectArrayMap(); + Iterator iterator = structures.iterator(); diff --git a/patches/server/0466-Collision-option-for-requiring-a-player-participant.patch b/patches/server/0466-Collision-option-for-requiring-a-player-participant.patch new file mode 100644 index 0000000000..c5dba4f028 --- /dev/null +++ b/patches/server/0466-Collision-option-for-requiring-a-player-participant.patch @@ -0,0 +1,42 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Mariell Hoversholm <[email protected]> +Date: Sat, 14 Nov 2020 16:48:37 +0100 +Subject: [PATCH] Collision option for requiring a player participant + + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 341cfedfb06ac0a18b8cca1df5aa4f72b1455737..e556788a7c5e38c6ed293d566f75ef6501b7464c 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -2048,6 +2048,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + public void push(Entity entity) { + if (!this.isPassengerOfSameVehicle(entity)) { + if (!entity.noPhysics && !this.noPhysics) { ++ if (this.level.paperConfig().collisions.onlyPlayersCollide && !(entity instanceof ServerPlayer || this instanceof ServerPlayer)) return; // Paper - Collision option for requiring a player participant + double d0 = entity.getX() - this.getX(); + double d1 = entity.getZ() - this.getZ(); + double d2 = Mth.absMax(d0, d1); +diff --git a/src/main/java/net/minecraft/world/entity/vehicle/AbstractBoat.java b/src/main/java/net/minecraft/world/entity/vehicle/AbstractBoat.java +index e2bbb4b519010cbabc0796c5b2f749b4fac32bb7..8a5b3ebc875e5c71ca0d57a52e01b74cfee408b6 100644 +--- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractBoat.java ++++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractBoat.java +@@ -196,6 +196,7 @@ public abstract class AbstractBoat extends VehicleEntity implements Leashable { + + @Override + public void push(Entity entity) { ++ if (!this.level().paperConfig().collisions.allowVehicleCollisions && this.level().paperConfig().collisions.onlyPlayersCollide && !(entity instanceof Player)) return; // Paper - Collision option for requiring a player participant + if (entity instanceof AbstractBoat) { + if (entity.getBoundingBox().minY < this.getBoundingBox().maxY) { + // CraftBukkit start +diff --git a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java +index d01a588aea379c962dd63f1428f92cf2442f4d45..7e7a78ebbf128f7d29cf9eb349f25230c9c7d5d0 100644 +--- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java ++++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java +@@ -562,6 +562,7 @@ public abstract class AbstractMinecart extends VehicleEntity { + public void push(Entity entity) { + if (!this.level().isClientSide) { + if (!entity.noPhysics && !this.noPhysics) { ++ if (!this.level().paperConfig().collisions.allowVehicleCollisions && this.level().paperConfig().collisions.onlyPlayersCollide && !(entity instanceof Player)) return; // Paper - Collision option for requiring a player participant + if (!this.hasPassenger(entity)) { + // CraftBukkit start + VehicleEntityCollisionEvent collisionEvent = new VehicleEntityCollisionEvent((Vehicle) this.getBukkitEntity(), entity.getBukkitEntity()); diff --git a/patches/server/0467-Return-chat-component-with-empty-text-instead-of-thr.patch b/patches/server/0467-Return-chat-component-with-empty-text-instead-of-thr.patch new file mode 100644 index 0000000000..20c17b39c0 --- /dev/null +++ b/patches/server/0467-Return-chat-component-with-empty-text-instead-of-thr.patch @@ -0,0 +1,25 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: CDFN <[email protected]> +Date: Tue, 7 Jul 2020 17:53:23 +0200 +Subject: [PATCH] Return chat component with empty text instead of throwing + exception + + +diff --git a/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java b/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java +index 8a03f31d07e467288dd518c99f3f29bc77eac058..3b92fb173f623a05ae99c86d98f2ecdf907f58c4 100644 +--- a/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java ++++ b/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java +@@ -93,7 +93,12 @@ public abstract class AbstractContainerMenu { + } + private Component title; + public final Component getTitle() { +- Preconditions.checkState(this.title != null, "Title not set"); ++ // Paper start - return chat component with empty text instead of throwing error ++ // Preconditions.checkState(this.title != null, "Title not set"); ++ if (this.title == null){ ++ return Component.literal(""); ++ } ++ // Paper end - return chat component with empty text instead of throwing error + return this.title; + } + public final void setTitle(Component title) { diff --git a/patches/server/0468-Make-schedule-command-per-world.patch b/patches/server/0468-Make-schedule-command-per-world.patch new file mode 100644 index 0000000000..3ac2f8e3f4 --- /dev/null +++ b/patches/server/0468-Make-schedule-command-per-world.patch @@ -0,0 +1,28 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Mon, 4 Jan 2021 19:52:44 -0800 +Subject: [PATCH] Make schedule command per-world + + +diff --git a/src/main/java/net/minecraft/server/commands/ScheduleCommand.java b/src/main/java/net/minecraft/server/commands/ScheduleCommand.java +index 10b059a93762d9711fb4aedff572edfaebcb8264..1090759025969c04267de8b96ad045b1da0cb246 100644 +--- a/src/main/java/net/minecraft/server/commands/ScheduleCommand.java ++++ b/src/main/java/net/minecraft/server/commands/ScheduleCommand.java +@@ -33,7 +33,7 @@ public class ScheduleCommand { + }); + private static final SimpleCommandExceptionType ERROR_MACRO = new SimpleCommandExceptionType(Component.translatableEscape("commands.schedule.macro")); + private static final SuggestionProvider<CommandSourceStack> SUGGEST_SCHEDULE = (commandcontext, suggestionsbuilder) -> { +- return SharedSuggestionProvider.suggest((Iterable) ((CommandSourceStack) commandcontext.getSource()).getServer().getWorldData().overworldData().getScheduledEvents().getEventsIds(), suggestionsbuilder); ++ return SharedSuggestionProvider.suggest((Iterable) ((net.minecraft.commands.CommandSourceStack) commandcontext.getSource()).getLevel().serverLevelData.getScheduledEvents().getEventsIds(), suggestionsbuilder); // Paper - Make schedule command per-world + }; + + public ScheduleCommand() {} +@@ -93,7 +93,7 @@ public class ScheduleCommand { + } + + private static int remove(CommandSourceStack source, String eventName) throws CommandSyntaxException { +- int i = source.getServer().getWorldData().overworldData().getScheduledEvents().remove(eventName); ++ int i = source.getLevel().serverLevelData.getScheduledEvents().remove(eventName); // Paper - Make schedule command per-world + + if (i == 0) { + throw ScheduleCommand.ERROR_CANT_REMOVE.create(eventName); diff --git a/patches/server/0469-Configurable-max-leash-distance.patch b/patches/server/0469-Configurable-max-leash-distance.patch new file mode 100644 index 0000000000..39054ac60f --- /dev/null +++ b/patches/server/0469-Configurable-max-leash-distance.patch @@ -0,0 +1,32 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sun, 3 Jan 2021 21:04:03 -0800 +Subject: [PATCH] Configurable max leash distance + + +diff --git a/src/main/java/net/minecraft/world/entity/Leashable.java b/src/main/java/net/minecraft/world/entity/Leashable.java +index 4692704b58d1b73a45f1d587782dc706da94a6e6..5e67402340a81a965b0b3af88307d36500c2dc6b 100644 +--- a/src/main/java/net/minecraft/world/entity/Leashable.java ++++ b/src/main/java/net/minecraft/world/entity/Leashable.java +@@ -201,7 +201,7 @@ public interface Leashable { + return; + } + +- if ((double) f > 10.0D) { ++ if ((double) f > entity.level().paperConfig().misc.maxLeashDistance.or(LEASH_TOO_FAR_DIST)) { // Paper - Configurable max leash distance + ((Leashable) entity).leashTooFarBehaviour(); + } else if ((double) f > 6.0D) { + ((Leashable) entity).elasticRangeLeashBehaviour(entity1, f); +diff --git a/src/main/java/net/minecraft/world/entity/TamableAnimal.java b/src/main/java/net/minecraft/world/entity/TamableAnimal.java +index 144ef3cc71bb265646e48f3a5f10d971b5768154..9c6e2124da7c2358d2d524c52d3e77422f15f6e5 100644 +--- a/src/main/java/net/minecraft/world/entity/TamableAnimal.java ++++ b/src/main/java/net/minecraft/world/entity/TamableAnimal.java +@@ -101,7 +101,7 @@ public abstract class TamableAnimal extends Animal implements OwnableEntity { + @Override + public boolean handleLeashAtDistance(Entity leashHolder, float distance) { + if (this.isInSittingPose()) { +- if (distance > 10.0F) { ++ if (distance > (float) this.level().paperConfig().misc.maxLeashDistance.or(Leashable.LEASH_TOO_FAR_DIST)) { // Paper - Configurable max leash distance + this.dropLeash(); + } + diff --git a/patches/server/0470-Add-BlockPreDispenseEvent.patch b/patches/server/0470-Add-BlockPreDispenseEvent.patch new file mode 100644 index 0000000000..f2725046a1 --- /dev/null +++ b/patches/server/0470-Add-BlockPreDispenseEvent.patch @@ -0,0 +1,46 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Madeline Miller <[email protected]> +Date: Sun, 17 Jan 2021 13:16:09 +1000 +Subject: [PATCH] Add BlockPreDispenseEvent + + +diff --git a/src/main/java/net/minecraft/world/level/block/DispenserBlock.java b/src/main/java/net/minecraft/world/level/block/DispenserBlock.java +index d915ef1030728a3f6ff303977784097b712238d4..6a8e0df7d0150ad8dbbffcd5f49c4623a259e680 100644 +--- a/src/main/java/net/minecraft/world/level/block/DispenserBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/DispenserBlock.java +@@ -107,6 +107,7 @@ public class DispenserBlock extends BaseEntityBlock { + DispenseItemBehavior idispensebehavior = this.getDispenseMethod(world, itemstack); + + if (idispensebehavior != DispenseItemBehavior.NOOP) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockPreDispenseEvent(world, pos, itemstack, i)) return; // Paper - Add BlockPreDispenseEvent + DispenserBlock.eventFired = false; // CraftBukkit - reset event status + tileentitydispenser.setItem(i, idispensebehavior.dispense(sourceblock, itemstack)); + } +diff --git a/src/main/java/net/minecraft/world/level/block/DropperBlock.java b/src/main/java/net/minecraft/world/level/block/DropperBlock.java +index 91b514967405115f22edf4255775361a672e5c2f..ddecf443df3679e3098eb54edd19585a0512e342 100644 +--- a/src/main/java/net/minecraft/world/level/block/DropperBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/DropperBlock.java +@@ -71,6 +71,7 @@ public class DropperBlock extends DispenserBlock { + ItemStack itemstack1; + + if (iinventory == null) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockPreDispenseEvent(world, pos, itemstack, i)) return; // Paper - Add BlockPreDispenseEvent + itemstack1 = DropperBlock.DISPENSE_BEHAVIOUR.dispense(sourceblock, itemstack); + } else { + // CraftBukkit start - Fire event when pushing items into other inventories +diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +index 696867479e74c3c94e4131f2bbb97c857ed5e9dd..6d1d3451054af05e2381d70d71b99869f37c0901 100644 +--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java ++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +@@ -2132,5 +2132,11 @@ public class CraftEventFactory { + io.papermc.paper.event.block.BlockFailedDispenseEvent event = new io.papermc.paper.event.block.BlockFailedDispenseEvent(block); + return event.callEvent(); + } ++ ++ public static boolean handleBlockPreDispenseEvent(ServerLevel serverLevel, BlockPos pos, ItemStack itemStack, int slot) { ++ org.bukkit.block.Block block = CraftBlock.at(serverLevel, pos); ++ io.papermc.paper.event.block.BlockPreDispenseEvent event = new io.papermc.paper.event.block.BlockPreDispenseEvent(block, org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), slot); ++ return event.callEvent(); ++ } + // Paper end + } diff --git a/patches/server/0471-Add-PlayerChangeBeaconEffectEvent.patch b/patches/server/0471-Add-PlayerChangeBeaconEffectEvent.patch new file mode 100644 index 0000000000..f8164b3839 --- /dev/null +++ b/patches/server/0471-Add-PlayerChangeBeaconEffectEvent.patch @@ -0,0 +1,38 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Wed, 24 Jun 2020 15:14:51 -0600 +Subject: [PATCH] Add PlayerChangeBeaconEffectEvent + + +diff --git a/src/main/java/net/minecraft/world/inventory/BeaconMenu.java b/src/main/java/net/minecraft/world/inventory/BeaconMenu.java +index cb063d6e7f4d34eab3a5f52b11f671e0c99bbd40..c257660c79516a5919032b771fc3ac9575e9db9d 100644 +--- a/src/main/java/net/minecraft/world/inventory/BeaconMenu.java ++++ b/src/main/java/net/minecraft/world/inventory/BeaconMenu.java +@@ -157,12 +157,25 @@ public class BeaconMenu extends AbstractContainerMenu { + return BeaconMenu.decodeEffect(this.beaconData.get(2)); + } + ++ // Paper start - Add PlayerChangeBeaconEffectEvent ++ private static @Nullable org.bukkit.potion.PotionEffectType convert(Optional<Holder<MobEffect>> optionalEffect) { ++ return optionalEffect.map(org.bukkit.craftbukkit.potion.CraftPotionEffectType::minecraftHolderToBukkit).orElse(null); ++ } ++ // Paper end - Add PlayerChangeBeaconEffectEvent ++ + public void updateEffects(Optional<Holder<MobEffect>> primary, Optional<Holder<MobEffect>> secondary) { + if (this.paymentSlot.hasItem()) { +- this.beaconData.set(1, BeaconMenu.encodeEffect((Holder) primary.orElse(null)));// CraftBukkit - decompile error +- this.beaconData.set(2, BeaconMenu.encodeEffect((Holder) secondary.orElse(null)));// CraftBukkit - decompile error ++ // Paper start - Add PlayerChangeBeaconEffectEvent ++ io.papermc.paper.event.player.PlayerChangeBeaconEffectEvent event = new io.papermc.paper.event.player.PlayerChangeBeaconEffectEvent((org.bukkit.entity.Player) this.player.player.getBukkitEntity(), convert(primary), convert(secondary), this.access.getLocation().getBlock()); ++ if (event.callEvent()) { ++ // Paper end - Add PlayerChangeBeaconEffectEvent ++ this.beaconData.set(1, BeaconMenu.encodeEffect(event.getPrimary() == null ? null : org.bukkit.craftbukkit.potion.CraftPotionEffectType.bukkitToMinecraftHolder(event.getPrimary())));// CraftBukkit - decompile error ++ this.beaconData.set(2, BeaconMenu.encodeEffect(event.getSecondary() == null ? null : org.bukkit.craftbukkit.potion.CraftPotionEffectType.bukkitToMinecraftHolder(event.getSecondary())));// CraftBukkit - decompile error ++ if (event.willConsumeItem()) { // Paper + this.paymentSlot.remove(1); ++ } // Paper + this.access.execute(Level::blockEntityChanged); ++ } // Paper end - Add PlayerChangeBeaconEffectEvent + } + + } diff --git a/patches/server/0472-Add-toggle-for-always-placing-the-dragon-egg.patch b/patches/server/0472-Add-toggle-for-always-placing-the-dragon-egg.patch new file mode 100644 index 0000000000..0b28271dd1 --- /dev/null +++ b/patches/server/0472-Add-toggle-for-always-placing-the-dragon-egg.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath <[email protected]> +Date: Thu, 26 Nov 2020 11:47:24 +0000 +Subject: [PATCH] Add toggle for always placing the dragon egg + + +diff --git a/src/main/java/net/minecraft/world/level/dimension/end/EndDragonFight.java b/src/main/java/net/minecraft/world/level/dimension/end/EndDragonFight.java +index 323fbf684b7062c1b9084f1718538a3b3414d5bf..0e1eceb2b83aaafccbb5d58cf5098cfbc6f25a54 100644 +--- a/src/main/java/net/minecraft/world/level/dimension/end/EndDragonFight.java ++++ b/src/main/java/net/minecraft/world/level/dimension/end/EndDragonFight.java +@@ -410,7 +410,7 @@ public class EndDragonFight { + this.dragonEvent.setVisible(false); + this.spawnExitPortal(true); + this.spawnNewGateway(); +- if (!this.previouslyKilled) { ++ if (this.level.paperConfig().entities.behavior.enderDragonsDeathAlwaysPlacesDragonEgg || !this.previouslyKilled) { // Paper - Add toggle for always placing the dragon egg + this.level.setBlockAndUpdate(this.level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, EndPodiumFeature.getLocation(this.origin)), Blocks.DRAGON_EGG.defaultBlockState()); + } + diff --git a/patches/server/0473-Add-PlayerStonecutterRecipeSelectEvent.patch b/patches/server/0473-Add-PlayerStonecutterRecipeSelectEvent.patch new file mode 100644 index 0000000000..66c297aace --- /dev/null +++ b/patches/server/0473-Add-PlayerStonecutterRecipeSelectEvent.patch @@ -0,0 +1,57 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Fri, 27 Nov 2020 17:14:27 -0800 +Subject: [PATCH] Add PlayerStonecutterRecipeSelectEvent + +Co-Authored-By: MiniDigger <[email protected]> + +diff --git a/src/main/java/net/minecraft/world/inventory/StonecutterMenu.java b/src/main/java/net/minecraft/world/inventory/StonecutterMenu.java +index 46d6bc61ba54ab23eb35915073cfd5a3ab0111f1..74ea3f0154733ed4f096d88403fe011cca663e02 100644 +--- a/src/main/java/net/minecraft/world/inventory/StonecutterMenu.java ++++ b/src/main/java/net/minecraft/world/inventory/StonecutterMenu.java +@@ -64,7 +64,7 @@ public class StonecutterMenu extends AbstractContainerMenu { + + public StonecutterMenu(int syncId, Inventory playerInventory, final ContainerLevelAccess context) { + super(MenuType.STONECUTTER, syncId); +- this.selectedRecipeIndex = DataSlot.standalone(); ++ this.selectedRecipeIndex = DataSlot.shared(new int[1], 0); // Paper - Add PlayerStonecutterRecipeSelectEvent + this.recipesForInput = SelectableRecipe.SingleInputSet.empty(); + this.input = ItemStack.EMPTY; + this.slotUpdateListener = () -> { +@@ -153,8 +153,34 @@ public class StonecutterMenu extends AbstractContainerMenu { + return false; + } else { + if (this.isValidRecipeIndex(id)) { +- this.selectedRecipeIndex.set(id); +- this.setupResultSlot(id); ++ // Paper start - Add PlayerStonecutterRecipeSelectEvent ++ int recipeIndex = id; ++ this.selectedRecipeIndex.set(recipeIndex); ++ this.selectedRecipeIndex.checkAndClearUpdateFlag(); // mark as changed ++ paperEventBlock: if (this.isValidRecipeIndex(id)) { ++ final Optional<RecipeHolder<StonecutterRecipe>> recipe = this.recipesForInput.entries().get(id).recipe().recipe(); ++ if (recipe.isEmpty()) break paperEventBlock; // The recipe selected does not have an actual server recipe (presumably its the empty one). Cannot call the event, just break. ++ ++ io.papermc.paper.event.player.PlayerStonecutterRecipeSelectEvent event = new io.papermc.paper.event.player.PlayerStonecutterRecipeSelectEvent((Player) player.getBukkitEntity(), getBukkitView().getTopInventory(), (org.bukkit.inventory.StonecuttingRecipe) recipe.get().toBukkitRecipe()); ++ if (!event.callEvent()) { ++ player.containerMenu.sendAllDataToRemote(); ++ return false; ++ } ++ ++ net.minecraft.resources.ResourceLocation key = org.bukkit.craftbukkit.util.CraftNamespacedKey.toMinecraft(event.getStonecuttingRecipe().getKey()); ++ if (!recipe.get().id().location().equals(key)) { // If the recipe did NOT stay the same ++ for (int newRecipeIndex = 0; newRecipeIndex < this.recipesForInput.entries().size(); newRecipeIndex++) { ++ if (this.recipesForInput.entries().get(newRecipeIndex).recipe().recipe().filter(r -> r.id().location().equals(key)).isPresent()) { ++ recipeIndex = newRecipeIndex; ++ break; ++ } ++ } ++ } ++ } ++ player.containerMenu.sendAllDataToRemote(); ++ this.selectedRecipeIndex.set(recipeIndex); // set new index, so that listeners can read it ++ this.setupResultSlot(recipeIndex); ++ // Paper end - Add PlayerStonecutterRecipeSelectEvent + } + + return true; diff --git a/patches/server/0474-Expand-EntityUnleashEvent.patch b/patches/server/0474-Expand-EntityUnleashEvent.patch new file mode 100644 index 0000000000..a982255f45 --- /dev/null +++ b/patches/server/0474-Expand-EntityUnleashEvent.patch @@ -0,0 +1,174 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Nassim Jahnke <[email protected]> +Date: Fri, 29 Jan 2021 15:13:11 +0100 +Subject: [PATCH] Expand EntityUnleashEvent + + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index e556788a7c5e38c6ed293d566f75ef6501b7464c..a18e716a8c1c6f1fe23dcd3f4da033da95417e22 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -2715,12 +2715,15 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + if (leashable.getLeashHolder() == player) { + if (!this.level().isClientSide()) { + // CraftBukkit start - fire PlayerUnleashEntityEvent +- if (CraftEventFactory.callPlayerUnleashEntityEvent(this, player, hand).isCancelled()) { ++ // Paper start - Expand EntityUnleashEvent ++ org.bukkit.event.player.PlayerUnleashEntityEvent event = CraftEventFactory.callPlayerUnleashEntityEvent(this, player, hand, !player.hasInfiniteMaterials()); ++ if (event.isCancelled()) { ++ // Paper end - Expand EntityUnleashEvent + ((ServerPlayer) player).connection.send(new ClientboundSetEntityLinkPacket(this, leashable.getLeashHolder())); + return InteractionResult.PASS; + } + // CraftBukkit end +- if (player.hasInfiniteMaterials()) { ++ if (!event.isDropLeash()) { // Paper - Expand EntityUnleashEvent + leashable.removeLeash(); + } else { + leashable.dropLeash(); +@@ -3690,9 +3693,16 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + + protected void removeAfterChangingDimensions() { + this.setRemoved(Entity.RemovalReason.CHANGED_DIMENSION, null); // CraftBukkit - add Bukkit remove cause +- if (this instanceof Leashable leashable) { +- this.level().getCraftServer().getPluginManager().callEvent(new EntityUnleashEvent(this.getBukkitEntity(), UnleashReason.UNKNOWN)); // CraftBukkit +- leashable.removeLeash(); ++ if (this instanceof Leashable leashable && leashable.isLeashed()) { // Paper - only call if it is leashed ++ // Paper start - Expand EntityUnleashEvent ++ final EntityUnleashEvent event = new EntityUnleashEvent(this.getBukkitEntity(), UnleashReason.UNKNOWN, false); // CraftBukkit ++ event.callEvent(); ++ if (!event.isDropLeash()) { ++ leashable.removeLeash(); ++ } else { ++ leashable.dropLeash(); ++ } ++ // Paper end - Expand EntityUnleashEvent + } + + } +diff --git a/src/main/java/net/minecraft/world/entity/Leashable.java b/src/main/java/net/minecraft/world/entity/Leashable.java +index 5e67402340a81a965b0b3af88307d36500c2dc6b..68b869c5d76aeb390a05b053eef70486bd4126fd 100644 +--- a/src/main/java/net/minecraft/world/entity/Leashable.java ++++ b/src/main/java/net/minecraft/world/entity/Leashable.java +@@ -184,8 +184,11 @@ public interface Leashable { + + if (leashable_a != null && leashable_a.leashHolder != null) { + if (!entity.isAlive() || !leashable_a.leashHolder.isAlive()) { +- world.getCraftServer().getPluginManager().callEvent(new EntityUnleashEvent(entity.getBukkitEntity(), (!entity.isAlive()) ? UnleashReason.PLAYER_UNLEASH : UnleashReason.HOLDER_GONE)); // CraftBukkit +- if (world.getGameRules().getBoolean(GameRules.RULE_DOENTITYDROPS) && !entity.pluginRemoved) { // CraftBukkit - SPIGOT-7487: Don't drop leash, when the holder was removed by a plugin ++ // Paper start - Expand EntityUnleashEvent ++ final EntityUnleashEvent event = new EntityUnleashEvent(entity.getBukkitEntity(), (!entity.isAlive()) ? UnleashReason.PLAYER_UNLEASH : UnleashReason.HOLDER_GONE, world.getGameRules().getBoolean(GameRules.RULE_DOENTITYDROPS) && !entity.pluginRemoved); ++ event.callEvent(); ++ if (event.isDropLeash()) { // CraftBukkit - SPIGOT-7487: Don't drop leash, when the holder was removed by a plugin ++ // Paper end - Expand EntityUnleashEvent + ((Leashable) entity).dropLeash(); + } else { + ((Leashable) entity).removeLeash(); +@@ -220,11 +223,20 @@ public interface Leashable { + + default void leashTooFarBehaviour() { + // CraftBukkit start ++ boolean dropLeash = true; // Paper + if (this instanceof Entity entity) { +- entity.level().getCraftServer().getPluginManager().callEvent(new EntityUnleashEvent(entity.getBukkitEntity(), EntityUnleashEvent.UnleashReason.DISTANCE)); ++ // Paper start - Expand EntityUnleashEvent ++ final EntityUnleashEvent event = new EntityUnleashEvent(entity.getBukkitEntity(), EntityUnleashEvent.UnleashReason.DISTANCE, true); ++ if (!event.callEvent()) return; ++ dropLeash = event.isDropLeash(); + } + // CraftBukkit end +- this.dropLeash(); ++ if (dropLeash) { ++ this.dropLeash(); ++ } else { ++ this.removeLeash(); ++ } ++ // Paper end - Expand EntityUnleashEvent + } + + default void closeRangeLeashBehaviour(Entity entity) {} +diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java +index b3c27cdf7d336404403fe09553080835f2d3de49..7d3e165bae2c00737019689b7bf1e74ad2517270 100644 +--- a/src/main/java/net/minecraft/world/entity/Mob.java ++++ b/src/main/java/net/minecraft/world/entity/Mob.java +@@ -1612,8 +1612,15 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + boolean flag1 = super.startRiding(entity, force); + + if (flag1 && this.isLeashed()) { +- this.level().getCraftServer().getPluginManager().callEvent(new EntityUnleashEvent(this.getBukkitEntity(), UnleashReason.UNKNOWN)); // CraftBukkit +- this.dropLeash(); ++ // Paper start - Expand EntityUnleashEvent ++ EntityUnleashEvent event = new EntityUnleashEvent(this.getBukkitEntity(), EntityUnleashEvent.UnleashReason.UNKNOWN, true); ++ if (!event.callEvent()) { return flag1; } ++ if (event.isDropLeash()) { ++ this.dropLeash(); ++ } else { ++ this.removeLeash(); ++ } ++ // Paper end - Expand EntityUnleashEvent + } + + return flag1; +diff --git a/src/main/java/net/minecraft/world/entity/TamableAnimal.java b/src/main/java/net/minecraft/world/entity/TamableAnimal.java +index 9c6e2124da7c2358d2d524c52d3e77422f15f6e5..45a340fddcec48dfb796f4515a500452adb30c59 100644 +--- a/src/main/java/net/minecraft/world/entity/TamableAnimal.java ++++ b/src/main/java/net/minecraft/world/entity/TamableAnimal.java +@@ -102,7 +102,15 @@ public abstract class TamableAnimal extends Animal implements OwnableEntity { + public boolean handleLeashAtDistance(Entity leashHolder, float distance) { + if (this.isInSittingPose()) { + if (distance > (float) this.level().paperConfig().misc.maxLeashDistance.or(Leashable.LEASH_TOO_FAR_DIST)) { // Paper - Configurable max leash distance +- this.dropLeash(); ++ // Paper start - Expand EntityUnleashEvent ++ org.bukkit.event.entity.EntityUnleashEvent event = new org.bukkit.event.entity.EntityUnleashEvent(this.getBukkitEntity(), org.bukkit.event.entity.EntityUnleashEvent.UnleashReason.DISTANCE, true); ++ if (!event.callEvent()) return false; ++ if (event.isDropLeash()) { ++ this.dropLeash(); ++ } else { ++ this.removeLeash(); ++ } ++ // Paper end - Expand EntityUnleashEvent + } + + return false; +diff --git a/src/main/java/net/minecraft/world/entity/decoration/LeashFenceKnotEntity.java b/src/main/java/net/minecraft/world/entity/decoration/LeashFenceKnotEntity.java +index d4955d4174e5c2920d5cccaa4494db2c27284660..d06ec03ad16630f2dfd81cf9f32542bd1c2592de 100644 +--- a/src/main/java/net/minecraft/world/entity/decoration/LeashFenceKnotEntity.java ++++ b/src/main/java/net/minecraft/world/entity/decoration/LeashFenceKnotEntity.java +@@ -119,13 +119,18 @@ public class LeashFenceKnotEntity extends BlockAttachedEntity { + + if (leashable1.isLeashed() && leashable1.getLeashHolder() == this) { + // CraftBukkit start ++ boolean dropLeash = !player.hasInfiniteMaterials(); + if (leashable1 instanceof Entity leashed) { +- if (CraftEventFactory.callPlayerUnleashEntityEvent(leashed, player, hand).isCancelled()) { ++ // Paper start - Expand EntityUnleashEvent ++ org.bukkit.event.player.PlayerUnleashEntityEvent event = CraftEventFactory.callPlayerUnleashEntityEvent(leashed, player, hand, dropLeash); ++ dropLeash = event.isDropLeash(); ++ if (event.isCancelled()) { ++ // Paper end - Expand EntityUnleashEvent + die = false; + continue; + } + } +- if (player.getAbilities().instabuild){ ++ if (!dropLeash) { // Paper - Expand EntityUnleashEvent + leashable1.removeLeash(); + } else { + leashable1.dropLeash(); +diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +index 6d1d3451054af05e2381d70d71b99869f37c0901..697c69b60aa45b6a229f3bec77dc728e50a895ce 100644 +--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java ++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +@@ -1596,8 +1596,10 @@ public class CraftEventFactory { + Bukkit.getPluginManager().callEvent(new PlayerRecipeBookSettingsChangeEvent(player.getBukkitEntity(), bukkitType, open, filter)); + } + +- public static PlayerUnleashEntityEvent callPlayerUnleashEntityEvent(Entity entity, net.minecraft.world.entity.player.Player player, InteractionHand enumhand) { +- PlayerUnleashEntityEvent event = new PlayerUnleashEntityEvent(entity.getBukkitEntity(), (Player) player.getBukkitEntity(), CraftEquipmentSlot.getHand(enumhand)); ++ // Paper start - Expand EntityUnleashEvent ++ public static PlayerUnleashEntityEvent callPlayerUnleashEntityEvent(Entity entity, net.minecraft.world.entity.player.Player player, InteractionHand enumhand, boolean dropLeash) { ++ PlayerUnleashEntityEvent event = new PlayerUnleashEntityEvent(entity.getBukkitEntity(), (Player) player.getBukkitEntity(), CraftEquipmentSlot.getHand(enumhand), dropLeash); ++ // Paper end - Expand EntityUnleashEvent + entity.level().getCraftServer().getPluginManager().callEvent(event); + return event; + } diff --git a/patches/server/0475-Reset-shield-blocking-on-dimension-change.patch b/patches/server/0475-Reset-shield-blocking-on-dimension-change.patch new file mode 100644 index 0000000000..3126d1c539 --- /dev/null +++ b/patches/server/0475-Reset-shield-blocking-on-dimension-change.patch @@ -0,0 +1,22 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Yive <[email protected]> +Date: Sun, 24 Jan 2021 08:55:19 -0800 +Subject: [PATCH] Reset shield blocking on dimension change + + +diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java +index 6344947f445e1da19d21de67a51b88de65eceb7a..f046b8e6307079878d94eab615a5463a807e6c6c 100644 +--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java ++++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java +@@ -1604,6 +1604,11 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player { + PlayerChangedWorldEvent changeEvent = new PlayerChangedWorldEvent(this.getBukkitEntity(), worldserver1.getWorld()); + this.level().getCraftServer().getPluginManager().callEvent(changeEvent); + // CraftBukkit end ++ // Paper start - Reset shield blocking on dimension change ++ if (this.isBlocking()) { ++ this.stopUsingItem(); ++ } ++ // Paper end - Reset shield blocking on dimension change + return this; + } + } diff --git a/patches/server/0476-Add-DragonEggFormEvent.patch b/patches/server/0476-Add-DragonEggFormEvent.patch new file mode 100644 index 0000000000..34506b3c79 --- /dev/null +++ b/patches/server/0476-Add-DragonEggFormEvent.patch @@ -0,0 +1,34 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Trigary <[email protected]> +Date: Mon, 25 Jan 2021 14:53:57 +0100 +Subject: [PATCH] Add DragonEggFormEvent + + +diff --git a/src/main/java/net/minecraft/world/level/dimension/end/EndDragonFight.java b/src/main/java/net/minecraft/world/level/dimension/end/EndDragonFight.java +index 0e1eceb2b83aaafccbb5d58cf5098cfbc6f25a54..3d1a49d865e17a61ff74c6fe0efbd908447ee730 100644 +--- a/src/main/java/net/minecraft/world/level/dimension/end/EndDragonFight.java ++++ b/src/main/java/net/minecraft/world/level/dimension/end/EndDragonFight.java +@@ -410,8 +410,22 @@ public class EndDragonFight { + this.dragonEvent.setVisible(false); + this.spawnExitPortal(true); + this.spawnNewGateway(); ++ // Paper start - Add DragonEggFormEvent ++ BlockPos eggPosition = this.level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, EndPodiumFeature.getLocation(this.origin)); ++ org.bukkit.craftbukkit.block.CraftBlockState eggState = org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(this.level, eggPosition); ++ eggState.setData(Blocks.DRAGON_EGG.defaultBlockState()); ++ io.papermc.paper.event.block.DragonEggFormEvent eggEvent = new io.papermc.paper.event.block.DragonEggFormEvent(org.bukkit.craftbukkit.block.CraftBlock.at(this.level, eggPosition), eggState, ++ new org.bukkit.craftbukkit.boss.CraftDragonBattle(this)); ++ // Paper end - Add DragonEggFormEvent + if (this.level.paperConfig().entities.behavior.enderDragonsDeathAlwaysPlacesDragonEgg || !this.previouslyKilled) { // Paper - Add toggle for always placing the dragon egg +- this.level.setBlockAndUpdate(this.level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, EndPodiumFeature.getLocation(this.origin)), Blocks.DRAGON_EGG.defaultBlockState()); ++ // Paper start - Add DragonEggFormEvent ++ // this.level.setBlockAndUpdate(this.level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, EndPodiumFeature.getLocation(this.origin)), Blocks.DRAGON_EGG.defaultBlockState()); ++ } else { ++ eggEvent.setCancelled(true); ++ } ++ if (eggEvent.callEvent()) { ++ eggEvent.getNewState().update(true); ++ // Paper end - Add DragonEggFormEvent + } + + this.previouslyKilled = true; diff --git a/patches/server/0477-Add-EntityMoveEvent.patch b/patches/server/0477-Add-EntityMoveEvent.patch new file mode 100644 index 0000000000..298e088eb9 --- /dev/null +++ b/patches/server/0477-Add-EntityMoveEvent.patch @@ -0,0 +1,55 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath <[email protected]> +Date: Tue, 11 Feb 2020 21:56:48 -0600 +Subject: [PATCH] Add EntityMoveEvent + + +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index d6fe710a4e3784ba38776a0496eac7fa702aed88..b28a2756429270414f8ef89539f91ce0595d13c6 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -1647,6 +1647,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 - BlockPhysicsEvent ++ worldserver.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent + + gameprofilerfiller.push(() -> { + String s = String.valueOf(worldserver); +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index e56f9e823f9460854c6f0f2f05422136c12aabba..de948bb8903c48e0431c1a46f5aac4fc1828b581 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -230,6 +230,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + public final LevelStorageSource.LevelStorageAccess convertable; + public final UUID uuid; + public boolean hasPhysicsEvent = true; // Paper - BlockPhysicsEvent ++ public boolean hasEntityMoveEvent; // Paper - Add EntityMoveEvent + + public LevelChunk getChunkIfLoaded(int x, int z) { + return this.chunkSource.getChunk(x, z, false); +diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java +index d7e5c2d5935a86bab9987c36df8e1b957fe90ba0..2d3f418b5a7a5414ca5be8525146e480add51635 100644 +--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java ++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java +@@ -3638,6 +3638,20 @@ public abstract class LivingEntity extends Entity implements Attackable { + + this.pushEntities(); + gameprofilerfiller.pop(); ++ // Paper start - Add EntityMoveEvent ++ if (((ServerLevel) this.level()).hasEntityMoveEvent && !(this instanceof net.minecraft.world.entity.player.Player)) { ++ if (this.xo != this.getX() || this.yo != this.getY() || this.zo != this.getZ() || this.yRotO != this.getYRot() || this.xRotO != this.getXRot()) { ++ Location from = new Location(this.level().getWorld(), this.xo, this.yo, this.zo, this.yRotO, this.xRotO); ++ Location to = new Location(this.level().getWorld(), this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot()); ++ io.papermc.paper.event.entity.EntityMoveEvent event = new io.papermc.paper.event.entity.EntityMoveEvent(this.getBukkitLivingEntity(), from, to.clone()); ++ if (!event.callEvent()) { ++ this.absMoveTo(from.getX(), from.getY(), from.getZ(), from.getYaw(), from.getPitch()); ++ } else if (!to.equals(event.getTo())) { ++ this.absMoveTo(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ(), event.getTo().getYaw(), event.getTo().getPitch()); ++ } ++ } ++ } ++ // Paper end - Add EntityMoveEvent + world = this.level(); + if (world instanceof ServerLevel worldserver) { + if (this.isSensitiveToWater() && this.isInWaterRainOrBubble()) { diff --git a/patches/server/0478-added-option-to-disable-pathfinding-updates-on-block.patch b/patches/server/0478-added-option-to-disable-pathfinding-updates-on-block.patch new file mode 100644 index 0000000000..dd3704bba6 --- /dev/null +++ b/patches/server/0478-added-option-to-disable-pathfinding-updates-on-block.patch @@ -0,0 +1,26 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: lukas81298 <[email protected]> +Date: Mon, 25 Jan 2021 14:37:57 +0100 +Subject: [PATCH] added option to disable pathfinding updates on block changes + + +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index de948bb8903c48e0431c1a46f5aac4fc1828b581..dc2b8d8e62cf1405191aa4fc8d4fa548c7249032 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -1363,6 +1363,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + + this.getChunkSource().blockChanged(pos); + this.pathTypesByPosCache.invalidate(pos); ++ if (this.paperConfig().misc.updatePathfindingOnBlockUpdate) { // Paper - option to disable pathfinding updates + VoxelShape voxelshape = oldState.getCollisionShape(this, pos); + VoxelShape voxelshape1 = newState.getCollisionShape(this, pos); + +@@ -1404,6 +1405,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + } + + } ++ } // Paper - option to disable pathfinding updates + } + + @Override diff --git a/patches/server/0479-Inline-shift-direction-fields.patch b/patches/server/0479-Inline-shift-direction-fields.patch new file mode 100644 index 0000000000..e47ea8a571 --- /dev/null +++ b/patches/server/0479-Inline-shift-direction-fields.patch @@ -0,0 +1,56 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Andrew Steinborn <[email protected]> +Date: Mon, 18 Jan 2021 20:45:25 -0500 +Subject: [PATCH] Inline shift direction fields + +Removes a layer of indirection for EnumDirection.getAdjacent(X|Y|Z)(), which is in the +critical section for much of the server, including the lighting engine. + +diff --git a/src/main/java/net/minecraft/core/Direction.java b/src/main/java/net/minecraft/core/Direction.java +index acecb0e9df1b157579d2b438abf1568d7672b570..690e1d2394e68356c56a39ac083cc53ee0388d71 100644 +--- a/src/main/java/net/minecraft/core/Direction.java ++++ b/src/main/java/net/minecraft/core/Direction.java +@@ -57,6 +57,12 @@ public enum Direction implements StringRepresentable { + .sorted(Comparator.comparingInt(direction -> direction.data2d)) + .toArray(Direction[]::new); + ++ // Paper start - Perf: Inline shift direction fields ++ private final int adjX; ++ private final int adjY; ++ private final int adjZ; ++ // Paper end - Perf: Inline shift direction fields ++ + private Direction( + final int id, + final int idOpposite, +@@ -74,6 +80,11 @@ public enum Direction implements StringRepresentable { + this.axisDirection = direction; + this.normal = vector; + this.normalVec3 = Vec3.atLowerCornerOf(vector); ++ // Paper start - Perf: Inline shift direction fields ++ this.adjX = vector.getX(); ++ this.adjY = vector.getY(); ++ this.adjZ = vector.getZ(); ++ // Paper end - Perf: Inline shift direction fields + } + + public static Direction[] orderedByNearest(Entity entity) { +@@ -247,15 +258,15 @@ public enum Direction implements StringRepresentable { + } + + public int getStepX() { +- return this.normal.getX(); ++ return this.adjX; // Paper - Perf: Inline shift direction fields + } + + public int getStepY() { +- return this.normal.getY(); ++ return this.adjY; // Paper - Perf: Inline shift direction fields + } + + public int getStepZ() { +- return this.normal.getZ(); ++ return this.adjZ; // Paper - Perf: Inline shift direction fields + } + + public Vector3f step() { diff --git a/patches/server/0480-Allow-adding-items-to-BlockDropItemEvent.patch b/patches/server/0480-Allow-adding-items-to-BlockDropItemEvent.patch new file mode 100644 index 0000000000..7b84a8ac96 --- /dev/null +++ b/patches/server/0480-Allow-adding-items-to-BlockDropItemEvent.patch @@ -0,0 +1,44 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: BillyGalbreath <[email protected]> +Date: Wed, 20 Jan 2021 14:23:37 -0600 +Subject: [PATCH] Allow adding items to BlockDropItemEvent + + +diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +index 697c69b60aa45b6a229f3bec77dc728e50a895ce..49efae40345fcfca8f80fcc541dcfde1b8a8b07b 100644 +--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java ++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +@@ -461,13 +461,30 @@ public class CraftEventFactory { + } + + public static void handleBlockDropItemEvent(Block block, BlockState state, ServerPlayer player, List<ItemEntity> items) { +- BlockDropItemEvent event = new BlockDropItemEvent(block, state, player.getBukkitEntity(), Lists.transform(items, (item) -> (org.bukkit.entity.Item) item.getBukkitEntity())); ++ // Paper start - Allow adding items to BlockDropItemEvent ++ List<Item> list = new ArrayList<>(); ++ for (ItemEntity item : items) { ++ list.add((Item) item.getBukkitEntity()); ++ } ++ BlockDropItemEvent event = new BlockDropItemEvent(block, state, player.getBukkitEntity(), list); ++ // Paper end - Allow adding items to BlockDropItemEvent + Bukkit.getPluginManager().callEvent(event); + + if (!event.isCancelled()) { +- for (ItemEntity item : items) { +- item.level().addFreshEntity(item); ++ // Paper start - Allow adding items to BlockDropItemEvent ++ for (Item bukkit : list) { ++ if (!bukkit.isValid()) { ++ Entity item = ((org.bukkit.craftbukkit.entity.CraftItem) bukkit).getHandle(); ++ item.level().addFreshEntity(item); ++ } ++ } ++ } else { ++ for (Item bukkit : list) { ++ if (bukkit.isValid()) { ++ bukkit.remove(); ++ } + } ++ // Paper end - Allow adding items to BlockDropItemEvent + } + } + diff --git a/patches/server/0481-Add-getMainThreadExecutor-to-BukkitScheduler.patch b/patches/server/0481-Add-getMainThreadExecutor-to-BukkitScheduler.patch new file mode 100644 index 0000000000..2f143cc0b4 --- /dev/null +++ b/patches/server/0481-Add-getMainThreadExecutor-to-BukkitScheduler.patch @@ -0,0 +1,26 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Aleksander Jagiello <[email protected]> +Date: Sun, 24 Jan 2021 22:17:54 +0100 +Subject: [PATCH] Add getMainThreadExecutor to BukkitScheduler + + +diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java +index 20760e08b3d9aba86969b886b46deec5b125bf1f..1354ccfbf525e5e64483ac5f443cc2325ba63850 100644 +--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java ++++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java +@@ -633,4 +633,15 @@ public class CraftScheduler implements BukkitScheduler { + public BukkitTask runTaskTimerAsynchronously(Plugin plugin, BukkitRunnable task, long delay, long period) throws IllegalArgumentException { + throw new UnsupportedOperationException("Use BukkitRunnable#runTaskTimerAsynchronously(Plugin, long, long)"); + } ++ ++ // Paper start - add getMainThreadExecutor ++ @Override ++ public Executor getMainThreadExecutor(Plugin plugin) { ++ Preconditions.checkArgument(plugin != null, "Plugin cannot be null"); ++ return command -> { ++ Preconditions.checkArgument(command != null, "Command cannot be null"); ++ this.runTask(plugin, command); ++ }; ++ } ++ // Paper end + } diff --git a/patches/server/0482-living-entity-allow-attribute-registration.patch b/patches/server/0482-living-entity-allow-attribute-registration.patch new file mode 100644 index 0000000000..ef6df5b4ed --- /dev/null +++ b/patches/server/0482-living-entity-allow-attribute-registration.patch @@ -0,0 +1,57 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: ysl3000 <[email protected]> +Date: Sat, 24 Oct 2020 16:37:44 +0200 +Subject: [PATCH] living entity allow attribute registration + + +diff --git a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java +index b3707ca404245ae048ddba4ce2190c0801b474d4..fb967ac7b3e7828301f08a7fe9b039441cf7da30 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java ++++ b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java +@@ -162,4 +162,12 @@ public class AttributeMap { + } + } + } ++ ++ // Paper - start - living entity allow attribute registration ++ public void registerAttribute(Holder<Attribute> attributeBase) { ++ AttributeInstance attributeModifiable = new AttributeInstance(attributeBase, AttributeInstance::getAttribute); ++ attributes.put(attributeBase, attributeModifiable); ++ } ++ // Paper - end - living entity allow attribute registration ++ + } +diff --git a/src/main/java/org/bukkit/craftbukkit/attribute/CraftAttributeMap.java b/src/main/java/org/bukkit/craftbukkit/attribute/CraftAttributeMap.java +index 5678d2007d5adf45dec0638c5dd848b601801814..0a7ed5a4f1644a70d8f98ad7a6962b814ad6daf4 100644 +--- a/src/main/java/org/bukkit/craftbukkit/attribute/CraftAttributeMap.java ++++ b/src/main/java/org/bukkit/craftbukkit/attribute/CraftAttributeMap.java +@@ -35,4 +35,11 @@ public class CraftAttributeMap implements Attributable { + + return (nms == null) ? null : new CraftAttributeInstance(nms, attribute); + } ++ // Paper start - living entity allow attribute registration ++ @Override ++ public void registerAttribute(Attribute attribute) { ++ Preconditions.checkArgument(attribute != null, "attribute"); ++ handle.registerAttribute(CraftAttribute.bukkitToMinecraftHolder(attribute)); ++ } ++ // Paper end - living entity allow attribute registration + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +index 1ca252f36500322d56c0c12b6ec80c069214c0e8..a4be1eea356ba99358d707381df70032ded42140 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +@@ -775,6 +775,13 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { + return this.getHandle().craftAttributes.getAttribute(attribute); + } + ++ // Paper start - living entity allow attribute registration ++ @Override ++ public void registerAttribute(Attribute attribute) { ++ getHandle().craftAttributes.registerAttribute(attribute); ++ } ++ // Paper end - living entity allow attribute registration ++ + @Override + public void setAI(boolean ai) { + if (this.getHandle() instanceof Mob) { diff --git a/patches/server/0483-fix-dead-slime-setSize-invincibility.patch b/patches/server/0483-fix-dead-slime-setSize-invincibility.patch new file mode 100644 index 0000000000..494e56127e --- /dev/null +++ b/patches/server/0483-fix-dead-slime-setSize-invincibility.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Trigary <[email protected]> +Date: Fri, 5 Feb 2021 22:12:13 +0100 +Subject: [PATCH] fix dead slime setSize invincibility + + +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftSlime.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftSlime.java +index 3d9b7c0e128ea05bec5600c774e9685998b71cac..e48f7d1cbec4a2319745ba48a5d44ab9925214e2 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftSlime.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftSlime.java +@@ -16,7 +16,7 @@ public class CraftSlime extends CraftMob implements Slime, CraftEnemy { + + @Override + public void setSize(int size) { +- this.getHandle().setSize(size, true); ++ this.getHandle().setSize(size, /* true */ getHandle().isAlive()); // Paper - fix dead slime setSize invincibility + } + + @Override diff --git a/patches/server/0484-Merchant-getRecipes-should-return-an-immutable-list.patch b/patches/server/0484-Merchant-getRecipes-should-return-an-immutable-list.patch new file mode 100644 index 0000000000..d40a7cf8d7 --- /dev/null +++ b/patches/server/0484-Merchant-getRecipes-should-return-an-immutable-list.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jason Penilla <[email protected]> +Date: Wed, 10 Feb 2021 14:53:36 -0800 +Subject: [PATCH] Merchant#getRecipes should return an immutable list + + +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchant.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchant.java +index 770686757a87d923ecb0361ba0939b4f7d184d76..72b5f6724278ec78605d7f435ef7ca340f195f5b 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchant.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchant.java +@@ -16,7 +16,7 @@ public interface CraftMerchant extends Merchant { + + @Override + default List<MerchantRecipe> getRecipes() { +- return Collections.unmodifiableList(Lists.transform(this.getMerchant().getOffers(), new Function<net.minecraft.world.item.trading.MerchantOffer, MerchantRecipe>() { ++ return List.copyOf(Lists.transform(this.getMerchant().getOffers(), new Function<net.minecraft.world.item.trading.MerchantOffer, MerchantRecipe>() { // Paper - javadoc says 'an immutable list of trades' - not 'an unmodifiable view of a list of trades'. fixes issue with setRecipes(getRecipes()) + @Override + public MerchantRecipe apply(net.minecraft.world.item.trading.MerchantOffer recipe) { + return recipe.asBukkit(); diff --git a/patches/server/0485-Expose-Tracked-Players.patch b/patches/server/0485-Expose-Tracked-Players.patch new file mode 100644 index 0000000000..5e7f3c585c --- /dev/null +++ b/patches/server/0485-Expose-Tracked-Players.patch @@ -0,0 +1,32 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Tom <[email protected]> +Date: Fri, 26 Feb 2021 16:24:25 -0600 +Subject: [PATCH] Expose Tracked Players + + +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +index dbb1a52b1e5871bbb1ccbd300b8edb9aa0f56e49..78afac72265e3f586c0203951b8237832fb7c6fb 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +@@ -1065,4 +1065,21 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { + return getHandle().isTicking(); + } + // Paper end - isTicking API ++ ++ // Paper start - tracked players API ++ @Override ++ public Set<org.bukkit.entity.Player> getTrackedPlayers() { ++ ServerLevel world = (net.minecraft.server.level.ServerLevel)this.entity.level(); ++ ChunkMap.TrackedEntity tracker = world == null ? null : world.getChunkSource().chunkMap.entityMap.get(this.entity.getId()); ++ if (tracker == null) { ++ return java.util.Collections.emptySet(); ++ } ++ ++ Set<org.bukkit.entity.Player> set = new java.util.HashSet<>(tracker.seenBy.size()); ++ for (net.minecraft.server.network.ServerPlayerConnection connection : tracker.seenBy) { ++ set.add(connection.getPlayer().getBukkitEntity().getPlayer()); ++ } ++ return set; ++ } ++ // Paper end - tracked players API + } diff --git a/patches/server/0486-Improve-ServerGUI.patch b/patches/server/0486-Improve-ServerGUI.patch new file mode 100644 index 0000000000..8dde1d1a9b --- /dev/null +++ b/patches/server/0486-Improve-ServerGUI.patch @@ -0,0 +1,431 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AlexProgrammerDE <[email protected]> +Date: Sat, 3 Oct 2020 08:27:40 +0200 +Subject: [PATCH] Improve ServerGUI + +- Added logo to server frame +- Show tps in the server stats + +diff --git a/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java b/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java +index f93373d28d741e1f8a53e07b4e328ce9c4e1657f..12b327eea95e0de9e9c39b7d039badee8ec46508 100644 +--- a/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java ++++ b/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java +@@ -58,9 +58,22 @@ public class RAMDetails extends JList<String> { + public void update() { + GraphData data = RAMGraph.DATA.peekLast(); + Vector<String> vector = new Vector<>(); ++ ++ // Follows CraftServer#getTPS ++ double[] tps = new double[] { ++ server.tps1.getAverage(), ++ server.tps5.getAverage(), ++ server.tps15.getAverage() ++ }; ++ String[] tpsAvg = new String[tps.length]; ++ ++ for ( int g = 0; g < tps.length; g++) { ++ tpsAvg[g] = format( tps[g] ); ++ } + vector.add("Memory use: " + (data.getUsedMem() / 1024L / 1024L) + " mb (" + (data.getFree() * 100L / data.getMax()) + "% free)"); + vector.add("Heap: " + (data.getTotal() / 1024L / 1024L) + " / " + (data.getMax() / 1024L / 1024L) + " mb"); + vector.add("Avg tick: " + DECIMAL_FORMAT.format((double)this.server.getAverageTickTimeNanos() / (double) TimeUtil.NANOSECONDS_PER_MILLISECOND) + " ms"); ++ vector.add("TPS from last 1m, 5m, 15m: " + String.join(", ", tpsAvg)); + setListData(vector); + } + +@@ -71,4 +84,8 @@ public class RAMDetails extends JList<String> { + } + return ((double) total / (double) tickTimes.length) * 1.0E-6D; + } ++ ++ private static String format(double tps) { ++ return ( ( tps > 21.0 ) ? "*" : "" ) + Math.min( Math.round( tps * 100.0 ) / 100.0, 20.0 ); ++ } + } +diff --git a/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java b/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java +index 8b570b0c3967a22c085f390110cb29cbd9c8feff..4d3fe4f56e0b264fa030409919caf52d5f421d46 100644 +--- a/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java ++++ b/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java +@@ -59,6 +59,15 @@ public class MinecraftServerGui extends JComponent { + jframe.pack(); + jframe.setLocationRelativeTo((Component) null); + jframe.setVisible(true); ++ jframe.setName("Minecraft server"); // Paper - Improve ServerGUI ++ ++ // Paper start - Improve ServerGUI ++ try { ++ jframe.setIconImage(javax.imageio.ImageIO.read(Objects.requireNonNull(MinecraftServerGui.class.getClassLoader().getResourceAsStream("logo.png")))); ++ } catch (java.io.IOException ignore) { ++ } ++ // Paper end - Improve ServerGUI ++ + jframe.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent windowevent) { + if (!servergui.isClosing.getAndSet(true)) { +diff --git a/src/main/java/net/minecraft/server/gui/StatsComponent.java b/src/main/java/net/minecraft/server/gui/StatsComponent.java +index 6e9c6d556ed55325e36d191fc9d1508c00879671..096c89bd01cec2abd151bf6fffc4847d1bcd548f 100644 +--- a/src/main/java/net/minecraft/server/gui/StatsComponent.java ++++ b/src/main/java/net/minecraft/server/gui/StatsComponent.java +@@ -34,10 +34,19 @@ public class StatsComponent extends JComponent { + + private void tick() { + long l = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); ++ // Paper start - Improve ServerGUI ++ double[] tps = org.bukkit.Bukkit.getTPS(); ++ String[] tpsAvg = new String[tps.length]; ++ ++ for ( int g = 0; g < tps.length; g++) { ++ tpsAvg[g] = format( tps[g] ); ++ } + this.msgs[0] = "Memory use: " + l / 1024L / 1024L + " mb (" + Runtime.getRuntime().freeMemory() * 100L / Runtime.getRuntime().maxMemory() + "% free)"; + this.msgs[1] = "Avg tick: " + + DECIMAL_FORMAT.format((double)this.server.getAverageTickTimeNanos() / (double)TimeUtil.NANOSECONDS_PER_MILLISECOND) + + " ms"; ++ this.msgs[2] = "TPS from last 1m, 5m, 15m: " + String.join(", ", tpsAvg); ++ // Paper end - Improve ServerGUI + this.values[this.vp++ & 0xFF] = (int)(l * 100L / Runtime.getRuntime().maxMemory()); + this.repaint(); + } +@@ -66,4 +75,10 @@ public class StatsComponent extends JComponent { + public void close() { + this.timer.stop(); + } ++ ++ // Paper start - Improve ServerGUI ++ private static String format(double tps) { ++ return (( tps > 21.0 ) ? "*" : "") + Math.min(Math.round(tps * 100.0) / 100.0, 20.0); // only print * at 21, we commonly peak to 20.02 as the tick sleep is not accurate enough, stop the noise ++ } ++ // Paper end - Improve ServerGUI + } +diff --git a/src/main/resources/logo.png b/src/main/resources/logo.png +new file mode 100644 +index 0000000000000000000000000000000000000000..8b924977b7886df9ab8790b1e4ff9b1c04a2af45 +GIT binary patch +literal 16900 +zcmaicV|1Ng^k&Q(Hn!T>_Kj`Zc4OO48rwD-G<MS1Mq@Vi-2P_Pf6b?v4@vGy-W=|C +zpS_>w*(Xv_UIGaL4*?7e3`t5-R2lSh^xqd84Cs4}W^FDQn9zijsF13M{zVR~<`0dB +z;hww3Rk_uLO*yyZ^N(arMN#SjFcHEi60E_fZug<B-Z(hc9|bGkSKT99oyHP$K-u|n +zr9jc`PyX#|q=B419>`IjtJ^LVtno=lKj+Jze{_WszRIN1X*HUTCH>C_wc;+D)6YYT +z*RWmTUi`Puu_Uwkj<o$pPrusj%r}Dj%{SY7MbnC{1HmJy;yKgqG6L^Ez8~9WV_rtC +zkBu))Zv;Ih0AviFcp(<NpM4Iud9PHSRtyW*Q9aG$D&A6MQR80UdQHb@G&*iYdL;l6 +z&*F~9IaLf)qv{I13YG3pb-Q9r9@Z>6-qwu_Ue*kO&$%=o%J?6*rej_Ock3znkGIb6 +zWm&yS2Z9LS7slFgUx+?ilDgQBdj7`ruw|IVzJ@wV{&tD)G@SPTMW@9Wl5lcsuU~6` +z7raw|%Or|@P<iuewhar*LG$fv{PV-rumN!^YF+sriw!F(vXddsdC8crID6#HGQV_Y +zQNMQwU{~ee<Y?4p>nlh`7!!rA1H$`p;<B)P>zz}+92Tp2bFmKDAL`nrC>)<{qBHso +zvJ6|o^vMxL?frh4XZ`3WdH7<mM;zSn%>s_NI0p@{EElbnX*!yp;Vtx&K&w$&to`sW +z79>enm;xWhu;ZKKIN}-h!eBK<r?3Weu&JNQ#rPT>ZM6j$9~*Q(SlE*i_bHS0o#tPY +z5-j+ww|x>h9%`RLUixM!e%f<G!vp5vXXFX8kGG@oBs1697je+0Nx}#TpoAWD*|IYb +z-Ssx&KBQO74>0qVAe5GH83X6?!#^_j-M@lO@*-aD%NMF2;Hg^Wgh@}elrPA3o_&(- +zeNyws4es~%;K1o+pfG(Z!G-nFWzl7)ejRNxY?M~uI=I&MYuz@4>GLH*ptjlQJ`LYr +z*KIIVzBhKHIDwe`X2hc@gsdjzXxX%b<_#kc$vIHFi2)-XM1=fs(`g?0)M{lcJXwp< +zBgIdDXM&n-=+_%;1a?sE$oeN{r%w=8tFfAl<Qy{<_vK)o%n;sjWBhI?x8=90Lxkg` +zxkx20^#e}g0<LSzIxbF{Tmco72&OwY!+=|%_J|fBLX?~-X)wN)AGYA$bh|T>QopAk +z%wrVN=r>)oZ0w7^M~Xi~qp6lEaABgF<Z2SwJ{3&bce8KBXsfF_q3EEvBsuR{Xk!fV +z#)f;iYiQ^(Y|-yJ2)^R36EIGnWJP5LR`fjt>(ck7V3Un;@cg|ODuD7@fw~OZ;^TQV +z$&4AiUj}-4;o`6JV$Y4C<S!r5P|PN44CIQ+sDKy+`zUdEn?bMs=B=5}O}j~Et~>2G +z8hVweUdzl78hW<t^KJYb^Pi*-y`5~j=yosxBRqeq6FN})Y2R*>zD|&J_)oRr2JdJP +zA&lca);^P(q@hQb9-kqN<EgkX9B1Lg0x!^RwSp0@(*X;oZOCftvUGI4_lBx=xUhLb +zo3nPmzU-m&+)f%w8l)wW0-YsCBels@kMfp_Lh9??mD=nZ*zvU*Wd{-d!a#GMo*nMA +z=lqHGJ=0_2?8Y-csf@Z852{7hrkD&)J1n#!Wx(38*yZz}JzrCxB@jR9tG6LU{M;p+ +zwQ)kSjO%)r1@%(I`<3J)ARrMR`c8)=f1Lt6$3>XVo9An7Q3NoAtyRQw-@JUDD$o<f +znMRf7r(dF(J|Ki=lKXJy=}$IiE1fIb0^Ym|0TW#X<*B86mU(PHgf6oWN~L>luryjE +z3{zzbZhStP-K;xw@Yxf-B=4h(p=4f`k8p2DH$>qQLPR!szD!2|vJ}J`C6=EoRwG^+ +z;`ZDv1SGVO+?IqSxpxSM^_V~@2E+~dZQdl+oz;TP1MX+XXwugMy?Z5AoZ7#R33Y@T +zM)w4;9L0szO3>6i#4fV3q49@wu&`zcvQ!d8!m*dpn&7pp0Y=;QbiyOzhC7)Ki7tDt +zXaIqysWqx53ZgHlO)|YRDG**$7&F{0a8VEECY`3;yx)F>2;4Xr&gC;Iqiqx;orWkF +z8xk0Ty-mK&z`^~Fbs#S;;Qd@1ZFJh4R`+H>Wx$xgn>^oka;w9~QfR>rS7lYHG?D#o +z6Jo`Qg<Muls%8W(@Bn4~pcP6}Gk0gmCKDnOK3h(_4kg0j6MLXiCxDBi%<rTP>_-DP +zX@kdURs~L5?afF*73QF!=HQ?vIysP;FNCMBfA*}*&%$eDHh5L|y~D=C^v8(wdtcYZ +z)8Q|56BuZ~3~KpF-oKg|5Uf@Ac15Z>sP<9hpm(E>^cgr8dMxGhn7mnWA+JPK+EGR; +zCfK+V1&Xi1M6CUFIA+oJqr(aF3W_=ph7h;IVlqq&xJ=d(CqczQwL>f*A$gJW_|iZw +z&>!^cGyI)UH(_%jFMta0ci8K;?^D#C4_`@%@wP6R4qvs8y@ecdj|*ia7Exg3*BpG4 +z%Dqav(-_hWolzv04-3Ygs)Z~U$`R?hQq2Is2`RWS%z4?!GF2CryzMjCEFg_Y%K+yz +zG8tm;0X{;XG5?BBT|pMZ296(fGUtoF_$Ryrso&s;Cc!g3a;pYOn-tjPvW+1)iAQ)I +zaPyG(wl0MZUqz_Z!4+oEh$t>QIaiZ+J1|fQdfugliOCAg+6D!~3<-k#gA8N#Rk3@5 +z&u3Yevetsi3m`sm2Ntt>FV(PfME~wR=LFu+2@Noy&wr###hgP3mjy&H03re#97OQ% +zsZ;NtktNoC?s@G44<dmW{f5jWvTyiJLH^yQs|kPo;mUvH?YAxCAg|ea`1SCn%9nW+ +zAtbqiTV^rUI1*#_DYehL-JvZU|HMeqE-qAdM%s9}lUT3VidcPhzcs{J{1Pbv9kWhq +zc>Num-@G1zw*?jMf)<DEgHaOi16Tc_GG^ay^ID~7jj9}99V+-ffa!?g&F0&L_z(A~ +zM}JT5(~DuP{Yy+if=%fxqzfhy4MOrHp2A$WEHs?v`3DSZv0AY|>dA`SWJHyI-Lp=m +zyv8V97L8$~?>Sf(&Ee27TQvEf=-_%~EL56_n`*ZRVS`=4Ka4&HGjr9P8e3rf;8BK& +z&0s~H!Z|V-mPt9vUj?5&%Sa@;XK~`TS$ylgW4|1h&I!<9c6_zoDdR2)FLErHw%Sow +zwc_2ZKizcAMchMvZ^6OY8)<qG>uiUt&RwA(`3@dzgihQ1MSrNi;ruq-C+?oVa@U0x +z(>^4ei3Bedg+!LX52G(u@W4P&3sdv45%OawU(*aQat~OuEf?Hi6Zi>__qCd)nw0_j +zvUwA_6WQ5tnFsl_AZNz8L8L*=L4?0A>inj9l&C`<n8!WYp@GJJsV7VD2F>AC71u=H +z?bu{Q_=al@1+|F&El|te2eQB@?#+g(D(LjFx>w=0X;CJ|CQc@tuin_)Rd$KH$Y9P9 +z${MAq+Ns2`>_SLAfKm9~%?U2bK6><zn<Ur6w>hiDEbdUD#NMd$hR*wFx8TxWVY3Za +zM&tRPhR$htT-*KlZT-SGBy4YD;6aZfAz^Jt1`=ABifztn#D_;u)2WTa-Bo^EKL;=o +zDc6Ov2x3y<odK`qKZ@d;Abc>bU1B6gkFjv-UvyFl^(EFkIb4ht2Z<LM*J8D=dG^q^ +zMkYxOjCGDOM{O^I*O5+BWk`rv1mWOE@+P54krJ^tP#<l=ckM4=+6h&OIU&z>(*io4 +zW(6^Rp7OMxVh73mYH?bkbxgXB=+<qd2^f6f<36>TL>U^8OY>=P$oXPkGAmF?6#80T +z+e?24uzuJC8?nCu`7)ef&Nu8x+`0%wOB9wmZ^(+|&$!T80~3uj?NRH)aNhf~#vN9e +zem1VW#bKd$SZ4ufS0-pzoJ%P7UWdT@8yg`1+kpYLV153t;UJy~P8@7sO+#<ky&oE2 +zaTRY)D<ZM)m%mo_4}){09c-QN`>{ePIXcSgw}v2XayA<>Jxh}D)tMOGRgJY0QEJs` +z{>aB;ssVeqKi-6L#(PnBpPuOu<4Rf*GWVk8BdM<!bAw+)5FLpck)j0P;g-XUo>Cd} +zc^_!LU3n2YWBEk1?0<%f@MkB;t#h0%&cixNCZn@Lft$eDVl6z=l@Ga}k<7cF5n!!o +zXet^Q3;AyG!j)+$=3U>7D5c<q^S#x_L*FxCE%0h8`d^(!P9b1&)aUD8>Ef)=<q!3` +zEx>YMZ)jSZ?)!6EoSa3kU!<iV+p|9{t1Yog7AUs!^759kl+_ECQKgKtv<Me0u){xU +zvNK4lG5Lj18W*ZXoYw+7Q{~Io3%NJQ!=lyM&XLM?;1=x9;!=Y@!qr8739mD3ld??W +zk}v8<+^du4Lfdz_gYEgeF&99Myp%KlUPme7^F^!q+sB0B0C>3W2Xn`K`Pq<Ha|d)N +z;q~h1DC+D-+Dz}H`Ee}lrc`+QC0$^;y5!2lQxMKotyYaHh3Dh`k6e%Q%VuYLdoeFJ +zH~ZfHz~PX7I>R|ML`Ju!A)|K2`l1><Ha2;a0S_c>ErJG>o*qIC72B&jHYe36od@P! +zi)qQ9Y7g*>N;Y4;sSLlPxvM;q-Tzw2m;Zx=x>{mk0;Ed5zA?Hb1FrDGc6-;m+iSFU +zc22aC&R^-iyw5vE$D?GWWo7A5o@@>d3_uD92sGM_-tlsdQ?ZbAnF4LsSxDj&0TFgO +zFbB*@;0<;Y0es>tB&~M12_up)gRS(Ce{seFR$9$~MC8~S%gCTV+2AIiH`gndEW2~H +z`z|RK5KuxIccy|<V?aP`0WazqQSmO_S2;aqzDxX>!;Bkm8puw0EcWFE{ij71G*o4( +z0~y!3%z_nq1kdh3x<;XVQS{_v?Q3|H1so1Z#CL|Zm2Z&7-mTO?&1?U-oogOAE4Cm{ +z`d4o(XCnWH-J^hx&?7X^xHns&B`u2*skUy`s~w=0252bVaZy(}U?e5?u>fG!UbYaS +z4Gz$YBX|~|U$??YUR+zxw2g5F_OJB7viI^}qx|ouEswnc0o{D4T~~|912EVr9)4P& +zS=*@uBmgy>GC)sz_8A$Iga2y-R#LKP$zyVe7P=4Vrn@Q)Fp6mG;Nall=^07<{OPT~ +zPDD~5M}Py>^H&ikOMCrXaXjFMyNuyNg$gXaPOE4z3=$o3<OMi7)&qF(4hc$VL&fJj +z1E&LS!gpH;axN~M{o&Ywv1H1dv$~$Wx93|NHp>Jt(guFuvAQbA?*MR;Dx}r~+zsgJ +zzCtQ*$r?UAKNl$E39K|(pdcV17*;zU{VtG7{)QDicnC&XAit07AxkJs2xbNxkEh-l +ztI=-hZ#0{5e0{huHk5pMKFXUdk-_HT=8j~#**>ze%L-Vq--ELbc7OqlEqqgfDL$7| +z^zia3^m~7il#>&4bK{s6W!C%o9eQ_nw_LRXoq&)qk2e`~Carh!_+@C+^?4E@nB?8v +zrP(B~aF_-3_5wx4#3EgX2f|T2iDX6dBot9e+}zxz-+7y;fop?^#LWumnJ%(ER<|F> +z44(0)x_-m7iZI17bV#w5<;|{V>IZ-R+z|XI2d!L0M$z{_<K@~dl`C(tpC1Y)Szw}R +zq&}HG+Lrj-8Y`=$N~2}?8b|l9dGHv+zjnS*Kq`-?mfB^p+k|=E9!9dNC=>~PzI|b} +z_>I9TkwT-USfkDE<T3o2UFH-DLvho7UH5}kxNU*8zhI%MKhH(OJ-i?BY$W~-^|55) +zWa=r&p}q^d3PUbX_6!#<7Dh`;%jaA$@p7{>yuoB7YJe7^SUeW*JCd>d31w)Viag>w +zE)Hcnu_U(A@CEh^w;UM0IVsDf+yNUB)lCpiM=a>2dMS<By}F7IgoNFjDG)Kn@=#YA +zK3S^4|Mhywi<Y3>Vx95URpuHBLGh>h8fgM&77%eeba~6*@>lA8=;7iEw2QP4d^IvP +z8fpiWc?lq5kxp*C)nS|HY^i2ov(x?A!{1u(mk%xyJ_nmAsx{Zt=LV=Ta0-O}2|y4O +z5yIAhMw5|xp<jAS{T1goL&?&tBz5keV*#2g$~m*;O60P60)&DePvpc$(-fJzJYIL@ +zMljJ}1>3lvw|Ps$0W*KZd^Wlj=W@{AaG=^es3_){Y~Jis`IYYiWN~ho|DLil1qRD5 +zN6xAlvXG=U-8`VKVHr!k-;5Bi)EfnJRTtvY$;jR$#e%~lxMV?xboY;JA{IT_^y}D0 +zw1mJ8tVoSO-(}a<iGU($L_s!qHr~lX-p3`LpC;{qQD>bsB6M8b$Zqe)Ok0$OkaA#I +z48@e8TAlv;PmB6dbP|{7<%qt@Ea>I;PRL4)=M`_G!A40Y$Xy1Mum)I0#!3<77H4)u +zI6c{)TUsy&o^*@2H9Bp>QJA#S8$`zN?+@z^IIQL|VxYEQfVw~Oc}Wq!FS`G2T=aDu +z-DMYe(1$x=331oN(i#yV%?Q)lcY`}FpGRp*74@@$fX%pE+dAGOh5QRhJ&mcaXOhk4 +zLi_pirw^Zws;d9n^#IE8T1ypZDX|crNABquU?iL2;Ql%<xZgIthm*+Me<Sv144IQh +z+y#X35h0I(f?Jn7vV3}cbE`r@p<fw1A#t1bZc1YTd?oNP`S~O<1l*vNz*T7GKk&TY +z>4Vg5cNBt}OJdbLKnEi|`g2q%v70%e<MWo{Z54-bRg#0&`qxlWo}1oq$2e7Jg~<kZ +zzW&tUlrY9b8!wMZ!)FG}oq-Cgt7G(fIFBUcbt4fJ%!@avinPuQhDC|WkXbV}j>M&7 +z5gdFef<yi8=NIQs=cn8~Fk<)<8{fRekO|}3F~KUG-K#19**0R4d)JJ>u8Ix3n54MC +zW40SGT11ajrrm5AI24T?-2$|VMsU%VX}AMmt>Pr~B}#An{>%QG>_1FQYV^)CExzx2 +z&7E_9c!fpiCLci|F3H*eM2DQQRtQp4>V2RP=KX3ZVw#OXuFxj$VDmM<G<u~~j< +zr)S^na7COim80bdHj8W`F@;C%2>&HQD{*dc7301976VQyI69%EFvxxn>qC&L<E!j` +zIHGshQ9G04HH3kRXKn^}+y*oVrx5jxdh0M60xK|Sd)@G1l7$%@oa4CL9SGkajQnDZ +zq^Mqq7mh3yOT@pX=+DK)rD+NiZ3~dhN|m0JtBX_NP2nRs2(SK@;dIooJ>o-`%ImvM +zCv>AXKPcD26Z_;m`1pw)uF6Mp=RnShU^yM81!?jbl!v#-kSa#RLhSOG0?yp1YB6Jr +zW=GrO|0zIRSHiH?DYiO+$EpdMkwz#4I6V(J12-W0+dAo4J*?nDQrFI<*}a92Y%1bU +z`RC_4<V2ee4eMNw8s%FkpJh5UxnvQ!odmE*L6UqK9@Z+6xHczFTyw(v>tyg7>R(8{ +zA8*g?PWv##WoF+p0bJe>whg#+(1_+A+<aAEfj?>)9HS$|n?k;(r=Le*vR;57rn)2& +zEkD8KBSZm#3Drt?t!*#s#>0+yUNysIKRg=t`KSOcSHieiUP0z8F_$tZ(ciPnq_o~@ +z%-{zh<J*Veq)ewvHPIm<P)@9do?q;~Z}j;4gSzD8<V-%gspfzg<TvK(YEGM>bs{i7 +zt~8q8%WO|MF(FE_y<dA>e*bl_-@NcA!S9$IMb6x0`e_oNF!hy5a)<B9TQy2D<cFAc +zF?41xX{OMZmefU3+w>H^H)5)t(}ek4a1Nc~FF4@f;5aO%aB&3O%B8NuMWWCzYb`d> +zQ-&3)G|5M|pzcLy>pA(p=?3&XKn+v0^`HNsS?M0eb+60BxF|&Y{?>MI^x``)Vp}1V +z;<0N$BUc(0=p=y>zD3k<q}Y8+AcCcIhZE%FEx<NLcJ~PH4f57sm^|KipT6?7YhrO< +z5n4J2NHz)P@VNF5$KnrBFZ`vUnel)AgDsoGqc%y9n%hT)4PFSW=~V|s-Gnq#m>_I~ +zMC>T|r<IzMPZsUpMmccM`~7q^-gBxE8n1Uoi@@k@#WI2$Y*f#I7`5xI_*1MgU2N2@ +z^eG)oSYCiMe_2*N+|r=0Vu@%7?B{{Xx;a?lDd3cv9kkEP*W;ZaA8JRpl=-jMJ%r^M +zCe<r8%uD7nt!zE<kwG@ud2YkLV(WH~-pw~fnJZrqo`&ZGr=v%-HmRL^lg5w%)?Xdf +z8GyHmjcJ}p(SA=9aPzv&i8wZs^1@?kH(d$pau473%lc-?eyx*})9B>n!T!wN%lqT@ +z&Afsj|04$m&CH2M?F|6yeqb+e`&JWTP^~~z(;c>5;z6RuFKe)%3j|YzeZB9c)5E08 +zvX9?L9%?PT7Vu(RAIXR}s*=I<uRwy_BSL{QL;Eu-Qa(o`mnTNne9Sa30EPPEJt+@< +zp#ohDc&Gd*U!MV!j5B~M)TLn{`N4eLPTO+kv$bEVK;t!H(BsE%ztuJNxvUZm<n?`V +zX;2AC&F&+U382#5nDIK+u;g9D2ceKb>*@Qp<*vA{&7B2uwdBH$_I`33U5di9weG|3 +zx-Iy`1L`R>G-q<+w-{f5qc<7ls}^cT4Y^Qi+meHXFIDgqkt0wpdBZGY?LB+q9&o`T +zd18L5%R+44Ml^UNbEw58BXP#{+I#J1$;VGO`#6Grd<=RWgP+T+ktE6H^>C;%(}szj +zK;wt^oW<tgof(@F90Mq+=n&8JLg&8fAC)T&bMQR|%m&TaFS`11YvCXVKCa{ZL8~Bl +zX!HfBiRXgv4WYI!Z!S^;rJig#<C+@{PjVn61MRZd6~tP@hcr-a@OEY_3Jo#X{yUxB +zCbb<x53jHQ<07TdnY6iL20clAriMFMj02|lPk!CdidDvC{5bvm4$t3wF6Em!UD54g +zDwqgD^Rl(wYb>!yG4Fz=zm4zKw@$Wdo`VJm=879kp$F&$uMP_qiKSB4L@SV)<o?t! +z@b8}I9N>g55F9Rb=3ocrK>iqIRR9n!X0Do*Ldi{9M&^sg&T_TZz~>`tbXc$p%%BI% +z#MahUA?U0t#2ZA4_41*w&52#TXU^_G4)$#uGOnpIb{Gs?Bge_xP|beH;cUSBec^gk +zu;a`And#3j5LZ)LAL<cm7Q+mP2=~Fd!STmi<e5Z8e4wG<pEWU}FV0~dCjlgckVACH +zq9q6%IKSam)`{4|E{#}*z9J$;s9GrM5PCf_#PW!sFXBVO08lMbOJy_uZixMCC|@VD +zV`k3ntJG>L9lQ0{$A?tzx&K6M(;#M))7n&`7KTkT>KvjI7O4?mTa;X`81yn7WAir6 +z^Dv#2{~#3{X=5gyP*2v`3yoLJl)--n2rC2}*3n8(L~4ohHzT6QbyEu{!K3q#&p9Lp +z?3#RrZR0JWoh5V%Au%m2?uSB&R<iQA92+*Y@+cI6j44t_h023EBCpi<I5`60E*hIL +z>O!i99khjDd#7P;NaxJ<_f>mYXQOtXqBZif<x5V;e8$sJ4ucprdS6=76OH3DIx00; +zr@?!2AN?pOs)?RY{8}AkNKVZJa%;%y+M^NF<4tc9%D-iY`=)tTYcBWKE<%Yiw9%%D +zS*EjFv(hfL)a~iYFgm5X_PF5~>oWn1d5WC&hmG;&Gv(>!l)|)selJ-m-pz9Og@*rA +z%Xl~n+gHI_Rjy513U_dEaq-~ZLm%H7RpV<IR0p~J+;&2?kV82msqT8fkP1sSj2%4` +z1)^UjAV%_(0=dQf^t|3Rqv$6qMVAAHX%%m(_6P>bREoW=Zu*D?n%JFyy6(v}{RCOy +z>_wu--o5bv-4rRuWG0oN3a2+(f)C6nR0%>9HdI1mB`d{jE6Q4vSf>>{@~N-bGMc6~ +zn=1MB2?XIjZuOC!s@-pN5{60UUw-L4f1L-3Ohud?4)I$4Y&#w^A*ij(1$$3|Vskv} +z#YKCOBnHKh5QN8fd|k)wI{^HZj_1!`{L&>R(m@P^tYk*J)5>eCrio9{j>kWLDCGrM +z*O<)utCbjQiH>aHzD!~>S<PU3pyI^|2H^|uA8K8K@16lp(bU!op*y#_y`x#B*bbDc +z7LCa{Z6vjY3|g#Hj9@0vV=JdXah1mvnC-C=(k%WxIkMjH1PFK%C1_nf?QEs`jYDCF +zUTUHpRm64A3!+5iuiW+nnU1zIUP;N%T?I<%OK~d}&sT$agrSxf=YC~O3^hi4ze58t +zYrh*M$%Mt*g#V6dL?bm7a==9py)xK`hVB_Ta-nZ_kJFQw=~*NkZ)SVx&6coZl;7FQ +zN4qWzPH870+<`J%9aos>NyzV|B?uyizaR*!v`(g6N5ks=aSqWHk#wzbQOx2Ehc(>s +zfl`oSK+EzLOKDeK?n<u>#pu;5qF1g-8bXyN##%K`x2R14CxOh8w&P-kz4U}>3Q=A& +zwAa>sCXe?|fR^Y+S9_jW;=!_GK`1Bc2HY6Y)*s}A##+#}239~LV&Q~wL&4n_6^@vW +z;nGUYJ$5-C#kJr2EtD&Ty$t-H)#GyT->}39LWB1gdo%LwqR8{YbRBL*-FCEc5iY{; +z#TpZ~y8yolNKuWi&enqz%<*)Y)j#ff)9q1ezkI|N7|zr3<o?*+;JRvZ-Y?YN3nrDc +z<Onp!j9Mf+5A2NRh3|Az8KhKm@KH&niH`ddg;Z;SxUyCP16j;Grz-FV0d?P3g)Le| +zos7y#E&CJ+9vSa&X1`JVNHhrwj&NnqqCPt(M^2wsW(6k!Uf|=Y$zG%w@JT7|R|gxi +zr3+j8jJ3EnSpUKST|4`Vq!l90IE9{SoFqR+GHa1EC1bt2R5F5fF*>b=T|b>+m?)d% +zKJ;1@L~w8ZQn0MxZS*{ew-;Ohn^Jl!+U{m|QvgB~tai**t#d>0E=CMjN*SZ+36QnO +z4NrSN!Cd>9SLf?=!Hjh+ek}c}ND_U`vvi9(MS>7nGZ*l<Hmq_}pg^NoxPAelAVczK +z+9v-jKscGR%3D?J^Xp3qcvM>Pm%4(7(bhfuTHod8y%;N{YO_KMV}N<7D)x5snD;XG +zzCOH#WK2$4mAvQWFCCZW#F8TRInJ+=$6eR`V~dES6+!6-=6lkVCHyCW^Bb-$@=b%3 +zi%hxQwAp^EOp|zR61~UikJsM89qE@P3@X5J>+K)hO6K`Z$80UqhLV&|mVt3wQ#G4H +zi4>T}s*jr9pkN+B@=LbuMW8^kzEFQde*yOdnXiUws9u#OD8dYzm?0F`qCm7pBCNNz +zOJB@PR!5?2&9Zw_Jg~i=TwmStKiYq<aCxk}5?tZbG5<T2QE@w{`v9b{e*GpE>1_@$ +zZKB*^u}y2o({7rV#Nl+8<Rdkl0a@$MpN!_-&_Ccw-kxLT);QIY%C|Au!%Igfx^3nY +zqQW?uNhGyO*g%79wi{Xl<pL%^<L*Ucm}hQ29FcEt&?fH3+ltiY=y5&ppGG-@oEz4J +z7QH5KxK71nNG<)%_=$zL><i?GEBH?(B40WD(*2LZ1LB`N{Ao5PmAglN&FZpl>$2T5 +zthMF3X`+*;4Q-~<qaR}9Th9vMz1AXL>&-*4NzrU=7>#}h=jB}<^tsAch7Ac~Vq;V7 +ziknpCHOP}_P8F&VE%6e`WG~EVa?$ra`knKZrYWbIZ_w@4vO+{B!(Pb&!YhY8pCfe= +zjxF8x>Zh3;#gw`fu})grVJcf=Ohg_<xsdZ&$;Db2&61EKPttRh=b4(sN_y`B$-^iU +zbaR-Yb11Loh#pK7^C%^llk_r#NFww#waCKFozWylT7w{l+sUF-C2bd{Wnaa2cZe^u +zn|G4%4HN4LI(1E&Cy+D;QqbqgF=GjrLR+E06_dwL=4wv4Tj*+|*(R0fY_3G+nX##| +z9LQLMOV`Lu0>Xc9m?(57$!NXQ#N%;Q{V}EjtmA$m<@Ie2(h2j9T2Xq=0<2R#daW&$ +z85=lCIqjn+?h$SF4u|?#DOOKg9>2c{9GSdlh{<(WR;Mb+bxH>u95roevUiqSmcdG* +zEL`{Qv+mA#hjLxuC*l?ROBgDsPYkDNU%;m09$2^ni=SVA=kS_<QrbUz1Y8%cg`w>) +z_h->URCbhQr89T-a-Gg9Dk?P`CT8-=f%@A28AYMmma&Ks#DNDsr^|eI%nHBQ0Nps* +z<{@u^G-9krSD|^{Vm?_nRkW_T!;E*n95To#4sxn;9FH2W%&T043S^Vg_Bk^^&J9*H +z=-^Zd6GYUG(CMkA?hy<&4Tc5fn4$3ys+ZiGw!07qHH1zPDzAJY;{8Oj#B1-LTAZ>D +zKqX)c%j0#o|H%z2zdkxYKaV6<&nEMgP`q%2&v+2dsa++rFeWoOnf$VkCAY6|8|kw{ +zdwe(maC?oeGlx#HVClH?)W&QZ`+=l3PIeQ%9cb~nWxJ9)YD|MPt`v?0-3bMcbZ<2Z +zG7xSnH{QoOr#C@?R{C$168|JMfCxcPAVuEhewgQpYO@AfbP3Fw+|Vi7h~L@$6ydj5 +zyf7_h9Rp$0Gii0mkT9xddqw>hIVCXV203~$D~swIj_)TV=zX)@-tK6Hb66mM;EywH +zsMV;{!i^8fva<OFy6>e3b)iz7_f6$4yU2i-b%Bh|o@eU2$RD^G(AtWlyl0^8dxd<9 +zCi_xU0%&wFugtmc%-uOk=xMY?lR%{7BQRZ~b8}1<=DQI)v2*#3|70VNVV*?SK4O}0 +z-HEICfCoyTwy@{F=Ac>4KISQEgQLDcj|>j}h<?bSz+1B0{-w9kD!eM3*<Z37%?4E* +zkA{ZE<$MVE{8K_UuE}NuEQ7P^4<ITksnw<(11+kf3MpfIy*u6n*}`3yO2>zn(*RSn +zZw&u6!^Z2~7ae&u`+{IHYm_vxJJ@RRZ!LoCjQ2ecK6E;Aqey<dg6j^l0`!YnxYi9$ +zM6LAhrXuv}BqgdM(}PZ8CZas7EFSpef@p;1<$!_e)*`_#yxN-Rs6oNz6|Hvb!y~|q +zh|&aXdTokY2g!RF%s;~-*j|$hW4@1<n{R1pndLxAptQ|@z=;7T$_-oy6r5g`(6WW7 +z0~Lg5P%%i9;@gCpDpoF$H4@@H)CjjK;d~ijGr8!04az5G=lEzh!m;dMSOO20Zv`}Y +zr-iB|ED^!%pcBHh?<gu=GhyRLC1tsuIE(YJXUH?a_pCjE<xhHzrjd&pxx`;jQzh5; +zl9Q4KN4`!eE6v~vYIt=mO!=-hn!UAAu}eYoAW6h3plLh*H$37JSU(h+uGkpx@7+$Q +zFHJlY-*f#a+nGt2y#)horiF~LDlif$em(#7hPWT7k)?Nq{j<MPS$NS8i1>JZxfuAC +zaFBgBIQO4DawgA~vN)BCS%`;S38kn@9kWOTMq)$V$+z&4nDQvH*{(1#N58$C)v2#; +zJW|ch#FaXRBNNj6mX)HNV{_ScADWB7#Jn(Th}B15lvrI|-2<dL5!1=&wWue31zOTq +zw^i}lLoabQhZfQf?iUFP9Z5m3!A3{9j?q)ToPigJcwL-KMw|?59r7;lq=EA1Xyn|3 +zKQFEpiW@9}A<zAO?vr_<V%};_IxKbySSVeCdLh1TCD(W}kZUFmMeb{5>fj-=SL1AY +zQrI&y#`tyxRIyenc$G7)m}|d;5&h;8q8?ap1~7v{vEXIAhojO|^XI$6=K!f+>;5yx +zJJXiq*Z?mW;Ak{?4<=)9$$a@6Q*<UTmpguGcnDIPC0WEYN#Q;#Yxy$|D3``2G%7BN +z0Yu^RQ7okX8CBPqG!lDN%^_d=COePPay&UYI#6#@B{KaL`8fF_auJMF1vvL@@Ng<C +zI<Vd6`Flf-AW}D7j+&*Un2E<)hp>=1_%}Nx&bGA3oqS%{I)k3y{#DALAzrPw)h(FU +zj}8a8Xte($dBp<ijg|@?5L~1^;NP*a?DW~(Zh!0u1DnIboQI)1jmk@=vdiYoethVK +z2VA2EQv@N8+$L;v?}g`7We;lAQ0N7Cs45%8&+P5um4~~FV_#?}YNMf!&GB+#_IG>T +z_ZLeg50aO#<yc2n3)}HjIAy6<VTQX8SM42|2g1dr((CMP{B(Y6qxk|d#EXAUaxXkM +zwUwD<6NhB^T_hSjX`KSqm$ECgHu=6Ocle)oFKYFN8Tma6BWbCWiB;waOh;6`(c*u4 +zqG$he^u#%iy<Uw`Ct;c4{~nZS%#WV4@bfxg(X2g|KN3$5q}$mfwzscUhZSWBB*Pr~ +zM-+k3z<RHH>zhmy?M*+dS#c4NyP>CZSyS+OOi>@2;)lr;&A$)(OEO;kV+bz6O57by +zyW>9>Ij2^Du|A83(r~$46%S7?Ancv<t1a_}DOz@l5HE6yFlo?8Jw?4@@8O%XR>(6R +zJK?TL+k$9p$KMJgY}hdrTzyS}0it==hvU?8YM**7M}l@-<ok|B?D9J>W{&s26~NM6 +z#U8(RCX-=6Lw%{$D&=aKSfE%aJ<__RASP1DaZcJPva<-yi3NH#t$OuNk6wlp&CD~1 +zanJ|7AhF;l{a^)Qhr<C0*mv)OH?=aSzsFD-;L^+K4SEaqsYLqhx9tkX_6ia?J#83$ +z`$z06sIM{&fPSt1-%z9uNqIz!!`X7AZNbDv=pR>_9Bo;2ZG8=}0whx#r7zZ6W`Fs5 +zJEbvhZVJVsORu$w4Y1HyT1E4?Vka&kS*mSpBuKM>OAT~3W;g7KLGzfQWF~QJ1)H6S +zFCOXwP_auqzKSygLBPB}EH;Q1gXb@Wm*lZWfM<8NWGZM_*$8Ze)0+^IpqCyco5T+P +z>!edzc-RMsx%H6~4%a*u{&6!V2Xf)f8oOKEEtBAhvI#TkSv+Ago-TMSQ(2q}=S0FP +zL(1v}1vp6Ya1@zfO!}Dq3ke|~@mmFXu2dHEQWpO$6X$;c8V@V*w>NACSkmSKF-THX +zXc85Wu2(uhx0b@}vaeA-YhO(oJ!8ZlugSxzOn{tnI7h@dCB`UVE~EEY_ww_|qDlb| +zQh0>qvDy{uar91x0J$!N&ch{3*B*?y730`NAZJT0IXU?T1Oo1Zc+QnB&!+ZYLh%_v +zV;)6DQs1sEzvoxu0r{lou-yG%CgwotYzFK>vqr!e>KRehvaz@y)fTge`_wgV2*|2H +zVl|vbxEx$3ymn~uGqN65%FYqJ<_)*Uqs49;KY2h*(Xa?Tk7AFfl-xf>irJoUyL*;0 +z19&1GQV*5Ni~#kTnaq0ymCiLjk_=0q&=&|cG{r57n*6NwV6zJl<AE{?uiy^?^PFEl +zHL69trWdxghat&0+%;d3D%)bwcJp!RtqFvYL{}8g0Q6YuQRDUcg4GskLUHlFezjgb +z%oGcmW{c;iGpDCy?cU95%R+Qk73F1uDmg--Py0a;zrr32XFHuef2iiC4FRw~6D^mv +zgMdY9dT?<uc8v)5UGd`0_-us5eL?}U1d|_P=m;QXl76{#yY>5K*ED&DsZy8iEL_rr +zgsLXr6cN9-S7dCo0TeKI3ByoGNNBIG{4b4m4=LB^FstU0B?!6TBZ1v~zn%e*Xk=B) +z@_rySE6i<YPde}>HcIxSfbe^sRAkjZKFfR!7A5uNa|Q%HSV{);)`X_I$=Rz#g9)RV +zjIuDE+A6IDHt@No<L%X=db;Hw`M7lZ{F)`q!D5Htt7nHrf@-5e-`j-vV+h{^xhA8s +z$s-;kt^*QI)B|UnrciuVNlIMmjGIErd$4j48G;5;lAAA$Ev}+q;LPGdoNB5SegP#K +z{r5Q+H?7HkfTtUE_k9@xH;}4o67QOQ?Hl585(7w&`EOai?w1T9lK$xN+LxkuSRGel +zqy!S_YM9)(Tp?r#S;~dB<|bI?1gcloG<=?UibWT`0kG_<eKrPzOC}*3Hy088)4@Z+ +zv_SccFl?&OC^;g&?yV!ni}*NhUvPXkw)lcuC^*Zf0?cUPiE6Ma9gu1!C^&e?-3+~^ +zXl50oV>y^%sCnU|?kL3tCMU12QN7688MFeYr;%^{CT)BqX<4rY8gFNo(^2<+x6~@> +z0Y;8%xJK3sk3si!JoTyNPRqf>i>%mkw_b{g-~}-aAljQww_S1L53kdn=uMD<c17D# +z?H*+c=osa2w*s-S4Z~_SUsrKZwWYR;6)|&Y4rFt<B;x%HbWu)tqyir54O=xC@8VBz +zgT4^EGyVX(hb0g9pv%V|#R4Op=lH^tCrs~K9#Hm6!z@M3QY`N@z7d4`-v-z^-t=yw +zgL!IF60pMfQRkwZjPjYEk%wzZ^3xZK3Q^@$Emwcl&{&RZg@DVEDLYS0N>ZM5$#ndk +z&22o*u=b&^trc3UMGkzzrL*~$;t?gd{w8WCC+z$)6{fY`v4CL%;?|JZtR3}&oLz8* +zT?G#HsX)xAYvWho@h=pJpzsjcWp0%LD4s08onG)Nb4)MY=8K^XfVvcKVvP||0{idF +zr>Wx=dX&);ID@-|u5Y#BAa0c8rW_t)Xfo<vlc#AAL?V;xK!(VoUOQfwYKLZS&i<-9 +z;;vFoDu&qH04x=`k?XASkyK@GT+_OrFRio<oEjgbKi&<{<pem1Bh|_{rd}3b6M_~k +z#ug2gX33LeGUF6ujY8^-+NBC3N3Fo!SH?DlQ(x%FqF&l0I?|LVAq9|-_ZAzs`MtE0 +zz6|*#9u5wvDGZ0bong!tha)Mz6I;9$ZWQF+WC+Z02hfO#<HL~fql9E=*Ih)@0t$FV +z%W2<l>4c@By|jKCCPsr7DjJ6t;eTIrmF;CpM`~(ysWB=S@seY-cC;IYp7eGp3%$l} +z)oc?3j<N+0i5LM!Lg2p)9=a<RaaY_xwy2ck^!-q?ggIYnFfpFqE!+Gptg``+3UZ&B +z|G7`opGD})?f*>DrN<0qs>+yfj#><OZl7$l^xn+cudeRjEGbCoGdi+?_*DlwButhM +z2c4jyp#csao|(;WM}d0ov1}Oihu(2H(;^$M+d1l4WRg4(PiAdTKH$e65RXehqGcYp +ziS@I=L*)Cyiu;g}I&7*~m`bn>o^%eHp8`K^wUK{qUM_Xl#K;;VHK+>&$DqLQV1~<L +zJDOoVC28%&(mauG>BoxLuBrt&0}DAhEKn_^ER<H!yx{%QhA9tM0kEdHkOyS`=c%lV +zq{%S;|CVPO-A_h<n$FyCVaW`RarknRGNlHhU*Qp*V8JL9k4MsRCTKGmV-lT;?XB>` +zz-29QNvC|8F%an87xNYKcn*LCu89T8nVkc&?~&O83)5GbY)slt*#=)i7s;A<N)hyx +zwh^cOb?iKU)IbRP=ka+qf+JT_=N|faOQrq_JH^K1`TFgfF^F{DYfaT@vyY6ISleTm +zGL!<vL!F>_C=2r7N7+fk`X1KngTDCyUEafq@X5m_z1=DeiD@Q38P{+Ou8AdwgrjC5 +zajlbj!7Ae^jZ~9GGnmvF%|dV*Siz7~1$lG}zFHP5%BV8TD09lQN!w79WRZ;`=PM(z +z0;YT`0PcRb5SM~SQ_OKjwTc~?W_G_IPe||U$;Um2U%fe+7X>%Nvy!xcXUbbT1miw0 +z=$X7_W&m0ay!h~`ae>C68mu@al*ia7R0saqO=sn$tE@ww372nWLhU^>%{WE>Eoln8 +zaeH(5Zly+xlW1Z@B{Z2HqS52V*oh`BC}k&quf19RS}N6$l#0qGWzl9DQkZ@85<PA+ +zldE}FJS4PxXhbzMsRmrwaRz~V3QLN=WqdEEvulNbgjbr&&1P5w4PCwA3{?jrWGCM~ +zX0DmWj<kYm`dP~kIl-=0KWu`Jc;838I{cbhtw0szBJu5{7M^n&!<`QEsTb#X9`$lT +z57Hwj9Ps0kuw}6a#aAGdM8Uyjl-gC)G1hP^b}DQCDLs;KR8(o5(@&tS<A5C%$Hu%& +zajcvfB#m7XUTidGKu@9pZHG2y*-%G$Ih9jXO6!k#h<t!VB52|u)nv~y!>(#UMH4E) +z!&hPrOmR$HRF*}2C{e3A#U3h9d)gN68^|>O9=TO4Ga~u#5kl0}_*QP9IxEl~Ce;Vj +zS3zvyQ+p-TKYiV8z>J$akDBH=i$W7}&)8|aN%_17$7$H|;eKWRKgAtrMwoyE;#kJp +z>iJ{R+d4p$2q2;Y5EBQ7>@E&mk*MzVW>!EDsQ9Pd1Icl|=0d^U2HU!hP6MLe0bwp2 +zA=U!|OQM?{{^8dU?o^&w|I~Y5fw~zw)IT&*mzBRUy1Ljo^-=Z`fvN|N_J<BBAdS~k +z!ALu4Q%-z*R{oO&4hJxm{nHfwfAoO3sOznOQIr1~0QZdfH=zHAn6N<6r7+IV)Vf=N +z-qCbD?!=wp{X>gxG~k*Hc%03VftQZkoi*AD{-11-bt2%}_=-R;7ZY`jOzsFyAEWb! +zVJNLPL#@4|8iv-c@m4Lu!^Uc7?VOsDWty>@T6^QN67|~9P?w&boWVpR2)d)gI@s*$ +zT0uPct)H#x^_Y(_q2El&g2<(pF8niAzCde(;c)XAp3awn@Z)3{qMO$l1?#O_cXL+a +zB+yS96Q;w{xIBw9%-h2xp$%a(D0`Noi$$31BbukCM_lu$4sG_+rWsH9U`eD0eY3t3 +z@`vkyB5OW$_NhyNPE(&_JPvYO1XVd%SiaJPVza|ZguGogD*p`OzJ!Odk4wR7o=G7; +zQFEN*_9WQcO`Vliy5G@VCnZ;Qb~fJ44e1$o^Tw=L_lA;Z-8Dw0CC}X_m5Q_J*xP61 +z2tVQGAnU9PA@k;{9QL{c=-~c_joC`W*8qxTI)7}foE-)SU;g6SD;S1P5oGCta0DrC +zGXz?khB$Fn{Ycwuk%t&RTyJ!Mz8mnC0U+AYu}PkaA-t-gE*25%;RVKNKyWz!scpu6 +zZDKFBX5S4#lCQK!Ip%UxMsP%cC4T!8d`;mo#M{(B)h;Ilk3UVA`-O^+JuQDuUnt-K +z=jEH2NuzvVs7mGT0rJ;Nz54;;pVk-{O`o<8h5~yAG9cx)%sJ+#d0-B8j!9{+{>1@9 +zYiz-m^g@6wE8^*umZD0JhIN!|&Ok-?2XhJ@B|oI&FfS^$rs90JhlZBoJW`e5b9j^- +zWO>uD9oB-o4QKEBn$akVeT1MeUX-s%#m~lP<b&9IWGgbi$*OIzpK_3n^}!I)WVooa +z#6_PWZ8QA$W_OwNhR51iZ{AQ8gp5IC_qMYoKTO<wW7Lxu951+0Q*<Z_wU3?^MX!Qs +zKFIi5(!_vxR?8&ce&YV47mZ8#83_LZ{o>XZR!_h7SU~%Y_rx{QlrO<RbvccdJya54 +zEx3&41--x^p6UqQ4-K0v<iu-hD(5Xrz}`FaganW<;qBVLd$lhu5Wn*9nE3U4nha#d +zqjK-c{nPa2P{JRx8UPnyz`s1eOA2Taz`!T`m}fLvc<|5nl(qw*BvH~+UyPRhKzyrh +zxOKlLj5wBR=EU`4w)nCr{hTY!q<lpX5(s|w3QJ3;mfaaus{2>`$o+{oUb!PIS+x5N +z+{O+YLa6?IE1#&A?RMZ&J}!O!vj>Os^y>J_BMi^Cu8;>FP)!5eagStg`4k8`f<9)s +zLv>uniXJHc5tD}2a*xO+UycHT8lGykAS#<PAVxm$cbPxfCa|;$;o~~iE+XVlgHCw7 +zC#7nl^r~{M*TsmN95D{cIk!IhJ_X-wC?{Y<`4dUpmRY}yVmTSPk&p-vIF$+M=~`b# +z6Xw_$9|qJhncu-46MlAh5ITV>tq7H&?$Q|yXO#aH{77;M;}%#Rn*u_i#Q#=kFoCjB +zxM)O)sW@_wx=K{lJ|iyESH0iv9Nr111eP3eEA!SenTb%U12{RS*7qj0=;%^Kd#QiJ +ziYTEU=jFY{zWsSqmqmw<7L@5T1o7NxWhht`9gu$(b|QZnjVAE)D;lyC=><hR)|0rK +z4G=-Wk9u*;^2!F@ZPDmuT=Fj`zK22q4P|a@naT2k6OIr&5bt7mM+Zi{1dh<!#Q+MV +zNV$%grklh|8>~hv=8piE3T9#-QVKCSaq-q&xr*zuRbfKtru+;Kkp5Si5+<6{tz}rp +zigZWmiiYYR#xdxCbhhJz=wN$k9zPcR8H;AJErv2><3*Bm51h&CEJlpT9yo<pH&s}f +zXzYb2r<w#K%?wiq#0>5`<Zq9U!x1gwEv^qA_gUtKoaUH|F#B+-I$;m;h@imuPKwTl +zn58Fdpf)L2vT#)zU+}?#P)g7lir`yLqN5l4uSh3fGa;S@>1`w{pnaAJ%0k=ISmg0E +zo$J6^H1-w0!^WV5w|yx36dtal`WN}DGpD-gqYjDTfjIaLtR}xxCDSo6v=}KHRM^9@ +z&T;nw5x5ee(K3%Z3QQF%sMId_cIRpr&3g$f><9ZoX7X_c7g4f{y)mf(?;`TLI@jLv +z?N)ryzDJ)LsBZU+VnRH0X1E}KJ!}%#n_-<YL8nvAJG1pi@pQ)DNORI)b_#$>hEY9w +z`8(=7Fd9^wGY;{_ggJK@ZR?yW!1!^^d;F^x%}=DG(7K8XMm$L~K*Np|t>vZmA5%Y| +zINrWxnZFq_J7&ksTGEluekfNRCX$8u^xk+?w8Q1iII^7LA8Wc=uh=>E34C14fN(+~ +zjb&LKSzG|ur8^cG=n*d|U)DK;5`-D7c>o{;1qb8{cYdL5^ll*Y29ag^ZWs(}{Dq?& +z7Vt6fu%BVSoqvD;RYW!I!KS^e-kCz_2@FvAByt<`2mpv<fkZr|^`JQiBI_`lhGk(! +z_N*Sb+Ln<Xn})Qgi4eM-7qPD_UOjLAm9k+61#g=VRLjj+Bq9RB{*-?aNH;_9hvInV +z1PSqXdP+6AuCc1VrYiH#W?-)Rn#1F?YJ)<4bv{8UexqS@(f!Bn_=kD5>xlE{aWp)% +z7->KZs4&!M+Z9|_;(Qr<M|^-9J`VLlA0T%cdqRyI>bPRGNC2zLU&;bq*v@zaDlNR7 +zR!OB(0w7?XvMI3w1tc_A&fY$=RO&K>9q)K{?KeL9#X2nl`k!ouFF)XFC@Tui*%L4~ +zwNvTu3}=K5TH;uDS!^k3d+!l_hx$f?(hkYU(6NBYx@mz*Y6dZ7D@JF^5^p{aiT5zv +z;Xjc--#|sw407DGZz<4^FBXBq5F)zwTQ|65$~FTfyft2wOiY&QG(ydKoz#wa?YKny +z)9C@EX0c#XN}}K5dNFdMNo^+Os>0sS^c;E5Ky4zm)q;>J{J+z3sdUj)7tN@@gZSf7 +zJ|wiD$oI`e{Xe-gDV9P_(x}i7AaPVJn&m~NMi(84-RGbXy6@{lY?h66ze7!6Ee=i! +zInre-6PCHrI9+8v4+)Zge*esLVEy0*)t)o|)801Zf98hgQ=EZH2bpZ=)5NN_2yjw# +zP8Ewr(5WN{8DJpt*e!|G(gvZ5Pxywag$Agdns%%4+I<chK&;6@52mh48>H>|FMw9b +zKb<-v)*Cb*Ao~hb;B*`Ee&trZYBi`{$ru%gmKbuXcPNb3lD3H3Jimki7;BEFp{bxX +zFJ7Rk<~$d5(AGs1%w=$DDrj&3=?C4wX`U{m8^^=Z8R3YTB_A>ZA<nn`Er>OkmldWl +zwo0ZyTNCB`dfUZA+chm*()HWtA2!JQ3>g${<ZEUqmU&IH*$?ZzOs2IhHpYEix|NPQ +zJ-Pi715VXGJVDjN><!w*tid~hXaqgXjMCinP(wu6AN~G*C5MrlRjoO?J1hQ>8%Vr% +zasf==&095e)fG}M%iIsk{PaQ>2|D59ppz^2pExvb9Ou9EI^`kN!0aXr*u3p0ex0b4 +z=AnHH#@v>`#o*LjN-yB0^^l)H2Nm=yD3|>1aNigv$f`s680kxF8B%d>SUG)YF0R~W +z$TI5rvll2~&q4RSwu3})*@1!~z4l}@NsY#MwV(2<h@*@k1>Y=hbLZh-ce*Eq3<#rZ +zxra}au9h@`-JaCDeW|)St?N40z`g~4rjZ?xu=?#W;cJyHNPXCV2DuxD%N1A2hAlFH +zwTJm(6XPn#dA&{dq>&yd{5Lp=pa<%$*em=~TdQ%rn_v#5`><qe0k3yPzhk;_7^Ch6 +z``4jh8^vb#=_?9Hh_)q6T)5{?KdaF@G)h>I!IS>M^uNpl#N|wC@HMBcRTMT#SL;d7 +z<(&BuA6dLkkx|8fWw@PXzCeCBgDx@HJs@)L+j8y~gZ<df6K`wk{>)7)${p-|O7{G? +z&|M6FI|A*^d_U+Of-3`+w(c~-YsQby|NH)g|G7xv|Nek^|Jex)g~z+)I0xPC0460S +LFIp>X81%mY^Bg|U + +literal 0 +HcmV?d00001 + diff --git a/patches/server/0487-fix-converting-txt-to-json-file.patch b/patches/server/0487-fix-converting-txt-to-json-file.patch new file mode 100644 index 0000000000..7f63558c05 --- /dev/null +++ b/patches/server/0487-fix-converting-txt-to-json-file.patch @@ -0,0 +1,61 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Mon, 4 Jan 2021 19:49:15 -0800 +Subject: [PATCH] fix converting txt to json file + + +diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedPlayerList.java b/src/main/java/net/minecraft/server/dedicated/DedicatedPlayerList.java +index 929f59bce01c8e6ed4b0b551744d42e131b8fc80..22c4f8dea99f92a1eb3da2baf0a15bf9d2ca0462 100644 +--- a/src/main/java/net/minecraft/server/dedicated/DedicatedPlayerList.java ++++ b/src/main/java/net/minecraft/server/dedicated/DedicatedPlayerList.java +@@ -18,6 +18,11 @@ public class DedicatedPlayerList extends PlayerList { + this.setViewDistance(dedicatedServerProperties.viewDistance); + this.setSimulationDistance(dedicatedServerProperties.simulationDistance); + super.setUsingWhiteList(dedicatedServerProperties.whiteList.get()); ++ // Paper start - fix converting txt to json file; moved from constructor ++ } ++ @Override ++ public void loadAndSaveFiles() { ++ // Paper end - fix converting txt to json file + this.loadUserBanList(); + this.saveUserBanList(); + this.loadIpBanList(); +diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +index 9a3e73a5c206b78dfcf6f41a47b614342e52acc8..9d05e998d6df1069c2de69478a1f9688ac435e67 100644 +--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java ++++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +@@ -213,6 +213,12 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface + this.paperConfigurations.initializeGlobalConfiguration(this.registryAccess()); + this.paperConfigurations.initializeWorldDefaultsConfiguration(this.registryAccess()); + // Paper end - initialize global and world-defaults configuration ++ // Paper start - fix converting txt to json file; convert old users earlier after PlayerList creation but before file load/save ++ if (this.convertOldUsers()) { ++ this.getProfileCache().save(false); // Paper ++ } ++ this.getPlayerList().loadAndSaveFiles(); // Must be after convertNames ++ // Paper end - fix converting txt to json file + org.spigotmc.WatchdogThread.doStart(org.spigotmc.SpigotConfig.timeoutTime, org.spigotmc.SpigotConfig.restartOnCrash); // Paper - start watchdog thread + io.papermc.paper.command.PaperCommands.registerCommands(this); // Paper - setup /paper command + com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); // Paper - start metrics +@@ -267,9 +273,6 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface + DedicatedServer.LOGGER.warn("To change this, set \"online-mode\" to \"true\" in the server.properties file."); + } + +- if (this.convertOldUsers()) { +- this.getProfileCache().save(false); // Paper - Perf: Async GameProfileCache saving +- } + + if (!OldUsersConverter.serverReadyAfterUserconversion(this)) { + return false; +diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java +index 061bba184c8bc2569ce1d413435ec1360fa14007..e80e6e402ae286358b7f26073329b2e10604153b 100644 +--- a/src/main/java/net/minecraft/server/players/PlayerList.java ++++ b/src/main/java/net/minecraft/server/players/PlayerList.java +@@ -179,6 +179,7 @@ public abstract class PlayerList { + this.maxPlayers = maxPlayers; + this.playerIo = saveHandler; + } ++ abstract public void loadAndSaveFiles(); // Paper - fix converting txt to json file; moved from DedicatedPlayerList constructor + + public void placeNewPlayer(Connection connection, ServerPlayer player, CommonListenerCookie clientData) { + player.isRealPlayer = true; // Paper diff --git a/patches/server/0488-Add-worldborder-events.patch b/patches/server/0488-Add-worldborder-events.patch new file mode 100644 index 0000000000..014dba4c88 --- /dev/null +++ b/patches/server/0488-Add-worldborder-events.patch @@ -0,0 +1,72 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Mon, 4 Jan 2021 22:40:34 -0800 +Subject: [PATCH] Add worldborder events + + +diff --git a/src/main/java/net/minecraft/world/level/border/WorldBorder.java b/src/main/java/net/minecraft/world/level/border/WorldBorder.java +index b50090df116697a12f5498d65dd2e5d6d5297fb5..807a097a7b6399f24ede741f94ce98eb67e55add 100644 +--- a/src/main/java/net/minecraft/world/level/border/WorldBorder.java ++++ b/src/main/java/net/minecraft/world/level/border/WorldBorder.java +@@ -148,6 +148,14 @@ public class WorldBorder { + } + + public void setCenter(double x, double z) { ++ // Paper start - Add worldborder events ++ if (this.world != null) { ++ io.papermc.paper.event.world.border.WorldBorderCenterChangeEvent event = new io.papermc.paper.event.world.border.WorldBorderCenterChangeEvent(world.getWorld(), world.getWorld().getWorldBorder(), new org.bukkit.Location(world.getWorld(), this.getCenterX(), 0, this.getCenterZ()), new org.bukkit.Location(world.getWorld(), x, 0, z)); ++ if (!event.callEvent()) return; ++ x = event.getNewCenter().getX(); ++ z = event.getNewCenter().getZ(); ++ } ++ // Paper end - Add worldborder events + this.centerX = x; + this.centerZ = z; + this.extent.onCenterChange(); +@@ -174,6 +182,17 @@ public class WorldBorder { + } + + public void setSize(double size) { ++ // Paper start - Add worldborder events ++ if (this.world != null) { ++ io.papermc.paper.event.world.border.WorldBorderBoundsChangeEvent event = new io.papermc.paper.event.world.border.WorldBorderBoundsChangeEvent(world.getWorld(), world.getWorld().getWorldBorder(), io.papermc.paper.event.world.border.WorldBorderBoundsChangeEvent.Type.INSTANT_MOVE, getSize(), size, 0); ++ if (!event.callEvent()) return; ++ if (event.getType() == io.papermc.paper.event.world.border.WorldBorderBoundsChangeEvent.Type.STARTED_MOVE && event.getDuration() > 0) { // If changed to a timed transition ++ lerpSizeBetween(event.getOldSize(), event.getNewSize(), event.getDuration()); ++ return; ++ } ++ size = event.getNewSize(); ++ } ++ // Paper end - Add worldborder events + this.extent = new WorldBorder.StaticBorderExtent(size); + Iterator iterator = this.getListeners().iterator(); + +@@ -186,6 +205,20 @@ public class WorldBorder { + } + + public void lerpSizeBetween(double fromSize, double toSize, long time) { ++ // Paper start - Add worldborder events ++ if (this.world != null) { ++ io.papermc.paper.event.world.border.WorldBorderBoundsChangeEvent.Type type; ++ if (fromSize == toSize) { // new size = old size ++ type = io.papermc.paper.event.world.border.WorldBorderBoundsChangeEvent.Type.INSTANT_MOVE; // Use INSTANT_MOVE because below it creates a Static border if they are equal. ++ } else { ++ type = io.papermc.paper.event.world.border.WorldBorderBoundsChangeEvent.Type.STARTED_MOVE; ++ } ++ io.papermc.paper.event.world.border.WorldBorderBoundsChangeEvent event = new io.papermc.paper.event.world.border.WorldBorderBoundsChangeEvent(world.getWorld(), world.getWorld().getWorldBorder(), type, fromSize, toSize, time); ++ if (!event.callEvent()) return; ++ toSize = event.getNewSize(); ++ time = event.getDuration(); ++ } ++ // Paper end - Add worldborder events + this.extent = (WorldBorder.BorderExtent) (fromSize == toSize ? new WorldBorder.StaticBorderExtent(toSize) : new WorldBorder.MovingBorderExtent(fromSize, toSize, time)); + Iterator iterator = this.getListeners().iterator(); + +@@ -497,6 +530,7 @@ public class WorldBorder { + + @Override + public WorldBorder.BorderExtent update() { ++ if (world != null && this.getLerpRemainingTime() <= 0L) new io.papermc.paper.event.world.border.WorldBorderBoundsChangeFinishEvent(world.getWorld(), world.getWorld().getWorldBorder(), this.from, this.to, this.lerpDuration).callEvent(); // Paper - Add worldborder events + return (WorldBorder.BorderExtent) (this.getLerpRemainingTime() <= 0L ? WorldBorder.this.new StaticBorderExtent(this.to) : this); + } + diff --git a/patches/server/0489-Add-PlayerNameEntityEvent.patch b/patches/server/0489-Add-PlayerNameEntityEvent.patch new file mode 100644 index 0000000000..8d982f76db --- /dev/null +++ b/patches/server/0489-Add-PlayerNameEntityEvent.patch @@ -0,0 +1,26 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sun, 5 Jul 2020 00:33:54 -0700 +Subject: [PATCH] Add PlayerNameEntityEvent + + +diff --git a/src/main/java/net/minecraft/world/item/NameTagItem.java b/src/main/java/net/minecraft/world/item/NameTagItem.java +index 244663ff31bdb29bbff8b3ffd9fe99279b173296..df9cdcb9544a171a5a07c65ba0150933fb70d5fc 100644 +--- a/src/main/java/net/minecraft/world/item/NameTagItem.java ++++ b/src/main/java/net/minecraft/world/item/NameTagItem.java +@@ -18,8 +18,13 @@ public class NameTagItem extends Item { + Component component = stack.get(DataComponents.CUSTOM_NAME); + if (component != null && entity.getType().canSerialize() && entity.canBeNameTagged()) { + if (!user.level().isClientSide && entity.isAlive()) { +- entity.setCustomName(component); +- if (entity instanceof Mob mob) { ++ // Paper start - Add PlayerNameEntityEvent ++ io.papermc.paper.event.player.PlayerNameEntityEvent event = new io.papermc.paper.event.player.PlayerNameEntityEvent(((net.minecraft.server.level.ServerPlayer) user).getBukkitEntity(), entity.getBukkitLivingEntity(), io.papermc.paper.adventure.PaperAdventure.asAdventure(stack.getHoverName()), true); ++ if (!event.callEvent()) return InteractionResult.PASS; ++ LivingEntity newEntity = ((org.bukkit.craftbukkit.entity.CraftLivingEntity) event.getEntity()).getHandle(); ++ newEntity.setCustomName(event.getName() != null ? io.papermc.paper.adventure.PaperAdventure.asVanilla(event.getName()) : null); ++ if (event.isPersistent() && newEntity instanceof Mob mob) { ++ // Paper end - Add PlayerNameEntityEvent + mob.setPersistenceRequired(); + } + diff --git a/patches/server/0490-Add-recipe-to-cook-events.patch b/patches/server/0490-Add-recipe-to-cook-events.patch new file mode 100644 index 0000000000..416e598dc6 --- /dev/null +++ b/patches/server/0490-Add-recipe-to-cook-events.patch @@ -0,0 +1,44 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Thonk <[email protected]> +Date: Wed, 6 Jan 2021 12:04:03 -0800 +Subject: [PATCH] Add recipe to cook events + + +diff --git a/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java +index b2c0de191fdc84d4a007373309a5df81cacd5466..4acf487f9a5f3fa828ee76f9708d6a2ae28707e1 100644 +--- a/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java ++++ b/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java +@@ -327,7 +327,7 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit + CraftItemStack source = CraftItemStack.asCraftMirror(itemstack); + org.bukkit.inventory.ItemStack result = CraftItemStack.asBukkitCopy(itemstack1); + +- FurnaceSmeltEvent furnaceSmeltEvent = new FurnaceSmeltEvent(CraftBlock.at(world, blockposition), source, result); ++ FurnaceSmeltEvent furnaceSmeltEvent = new FurnaceSmeltEvent(CraftBlock.at(world, blockposition), source, result, (org.bukkit.inventory.CookingRecipe<?>) recipeholder.toBukkitRecipe()); // Paper - Add recipe to cook events + world.getCraftServer().getPluginManager().callEvent(furnaceSmeltEvent); + + if (furnaceSmeltEvent.isCancelled()) { +diff --git a/src/main/java/net/minecraft/world/level/block/entity/CampfireBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/CampfireBlockEntity.java +index 30035d534e144bf31f94073c57b0195be7e62772..7fa1aea7942a1bc4d9779a9f8ab020ccd5566923 100644 +--- a/src/main/java/net/minecraft/world/level/block/entity/CampfireBlockEntity.java ++++ b/src/main/java/net/minecraft/world/level/block/entity/CampfireBlockEntity.java +@@ -66,7 +66,10 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable { + + if (blockEntity.cookingProgress[i] >= blockEntity.cookingTime[i]) { + SingleRecipeInput singlerecipeinput = new SingleRecipeInput(itemstack); +- ItemStack itemstack1 = (ItemStack) recipeMatchGetter.getRecipeFor(singlerecipeinput, world).map((recipeholder) -> { ++ // Paper start - add recipe to cook events ++ final Optional<RecipeHolder<CampfireCookingRecipe>> recipeHolderOptional = recipeMatchGetter.getRecipeFor(singlerecipeinput, world); ++ ItemStack itemstack1 = (ItemStack) recipeHolderOptional.map((recipeholder) -> { ++ // Paper end - add recipe to cook events + return ((CampfireCookingRecipe) recipeholder.value()).assemble(singlerecipeinput, world.registryAccess()); + }).orElse(itemstack); + +@@ -75,7 +78,7 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable { + CraftItemStack source = CraftItemStack.asCraftMirror(itemstack); + org.bukkit.inventory.ItemStack result = CraftItemStack.asBukkitCopy(itemstack1); + +- BlockCookEvent blockCookEvent = new BlockCookEvent(CraftBlock.at(world, pos), source, result); ++ BlockCookEvent blockCookEvent = new BlockCookEvent(CraftBlock.at(world, pos), source, result, (org.bukkit.inventory.CookingRecipe<?>) recipeHolderOptional.map(RecipeHolder::toBukkitRecipe).orElse(null)); // Paper - Add recipe to cook events + world.getCraftServer().getPluginManager().callEvent(blockCookEvent); + + if (blockCookEvent.isCancelled()) { diff --git a/patches/server/0491-Add-Block-isValidTool.patch b/patches/server/0491-Add-Block-isValidTool.patch new file mode 100644 index 0000000000..e0c42e8e37 --- /dev/null +++ b/patches/server/0491-Add-Block-isValidTool.patch @@ -0,0 +1,20 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Mon, 6 Jul 2020 12:44:31 -0700 +Subject: [PATCH] Add Block#isValidTool + + +diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java +index 1595e877b02b447b36f5c316ae70d4023b78a862..54fb380a6896731a18c0100722d12099e590cbc9 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java +@@ -693,5 +693,9 @@ public class CraftBlock implements Block { + public String translationKey() { + return this.getNMS().getBlock().getDescriptionId(); + } ++ ++ public boolean isValidTool(ItemStack itemStack) { ++ return getDrops(itemStack).size() != 0; ++ } + // Paper end + } diff --git a/patches/server/0492-Allow-using-signs-inside-spawn-protection.patch b/patches/server/0492-Allow-using-signs-inside-spawn-protection.patch new file mode 100644 index 0000000000..3953df000d --- /dev/null +++ b/patches/server/0492-Allow-using-signs-inside-spawn-protection.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Anton Lindroth <[email protected]> +Date: Wed, 15 Apr 2020 01:54:02 +0200 +Subject: [PATCH] Allow using signs inside spawn protection + + +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index 3d4ab22077eb358168b2959c3d1beb10b7104ab7..e23b1f98c4ea066e1c35e15454ed524a20f65b4d 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -1827,7 +1827,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + int i = this.player.level().getMaxY(); + + if (blockposition.getY() <= i) { +- if (this.awaitingPositionFromClient == null && worldserver.mayInteract(this.player, blockposition)) { ++ if (this.awaitingPositionFromClient == null && (worldserver.mayInteract(this.player, blockposition) || (worldserver.paperConfig().spawn.allowUsingSignsInsideSpawnProtection && worldserver.getBlockState(blockposition).getBlock() instanceof net.minecraft.world.level.block.SignBlock))) { // Paper - Allow using signs inside spawn protection + this.player.stopUsingItem(); // CraftBukkit - SPIGOT-4706 + InteractionResult enuminteractionresult = this.player.gameMode.useItemOn(this.player, worldserver, itemstack, enumhand, movingobjectpositionblock); + diff --git a/patches/server/0493-Expand-world-key-API.patch b/patches/server/0493-Expand-world-key-API.patch new file mode 100644 index 0000000000..7e7d90c838 --- /dev/null +++ b/patches/server/0493-Expand-world-key-API.patch @@ -0,0 +1,84 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Wed, 6 Jan 2021 00:34:04 -0800 +Subject: [PATCH] Expand world key API + + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java b/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java +index 15da29058f80a2d7cf2be26c48421c1746815a10..a070b2a83edaa702b13bc6d3026914126c211576 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java +@@ -521,5 +521,10 @@ public abstract class CraftRegionAccessor implements RegionAccessor { + public io.papermc.paper.world.MoonPhase getMoonPhase() { + return io.papermc.paper.world.MoonPhase.getPhase(this.getHandle().dayTime() / 24000L); + } ++ ++ @Override ++ public org.bukkit.NamespacedKey getKey() { ++ return org.bukkit.craftbukkit.util.CraftNamespacedKey.fromMinecraft(this.getHandle().getLevel().dimension().location()); ++ } + // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index 5ae8f646083fb580ac8d28fcbfe8ed2208b45d09..3cfacacd1d8fd17ec4b54936afd8124823b1a00b 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -1185,9 +1185,15 @@ public final class CraftServer implements Server { + File folder = new File(this.getWorldContainer(), name); + World world = this.getWorld(name); + +- if (world != null) { +- return world; ++ // Paper start ++ World worldByKey = this.getWorld(creator.key()); ++ if (world != null || worldByKey != null) { ++ if (world == worldByKey) { ++ return world; ++ } ++ throw new IllegalArgumentException("Cannot create a world with key " + creator.key() + " and name " + name + " one (or both) already match a world that exists"); + } ++ // Paper end + + if (folder.exists()) { + Preconditions.checkArgument(folder.isDirectory(), "File (%s) exists and isn't a folder", name); +@@ -1313,7 +1319,7 @@ public final class CraftServer implements Server { + } else if (name.equals(levelName + "_the_end")) { + worldKey = net.minecraft.world.level.Level.END; + } else { +- worldKey = ResourceKey.create(Registries.DIMENSION, ResourceLocation.withDefaultNamespace(name.toLowerCase(Locale.ROOT))); ++ worldKey = ResourceKey.create(Registries.DIMENSION, ResourceLocation.fromNamespaceAndPath(creator.key().namespace(), creator.key().value())); + } + + // If set to not keep spawn in memory (changed from default) then adjust rule accordingly +@@ -1409,6 +1415,15 @@ public final class CraftServer implements Server { + return null; + } + ++ // Paper start ++ @Override ++ public World getWorld(net.kyori.adventure.key.Key worldKey) { ++ ServerLevel worldServer = console.getLevel(ResourceKey.create(net.minecraft.core.registries.Registries.DIMENSION, io.papermc.paper.adventure.PaperAdventure.asVanilla(worldKey))); ++ if (worldServer == null) return null; ++ return worldServer.getWorld(); ++ } ++ // Paper end ++ + public void addWorld(World world) { + // Check if a World already exists with the UID. + if (this.getWorld(world.getUID()) != null) { +diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +index 5f0fd1d4fe419435ac7293106c0ebb5382d64074..8d9380b50424546bcebdfa868635f94c8efa5899 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +@@ -538,6 +538,11 @@ public final class CraftMagicNumbers implements UnsafeValues { + public int nextEntityId() { + return net.minecraft.world.entity.Entity.nextEntityId(); + } ++ ++ @Override ++ public String getMainLevelName() { ++ return ((net.minecraft.server.dedicated.DedicatedServer) net.minecraft.server.MinecraftServer.getServer()).getProperties().levelName; ++ } + // Paper end + + /** diff --git a/patches/server/0494-Add-fast-alternative-constructor-for-Rotations.patch b/patches/server/0494-Add-fast-alternative-constructor-for-Rotations.patch new file mode 100644 index 0000000000..e28775fc9a --- /dev/null +++ b/patches/server/0494-Add-fast-alternative-constructor-for-Rotations.patch @@ -0,0 +1,29 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Irmo van den Berge <[email protected]> +Date: Wed, 10 Mar 2021 21:26:31 +0100 +Subject: [PATCH] Add fast alternative constructor for Rotations + + +diff --git a/src/main/java/net/minecraft/core/Rotations.java b/src/main/java/net/minecraft/core/Rotations.java +index e6c97efb3aa89646149d11bb0ae4420b3977d214..27007280dba9e6d19a50dc2a4b160e96b20c67f7 100644 +--- a/src/main/java/net/minecraft/core/Rotations.java ++++ b/src/main/java/net/minecraft/core/Rotations.java +@@ -34,6 +34,18 @@ public class Rotations { + this(serialized.getFloat(0), serialized.getFloat(1), serialized.getFloat(2)); + } + ++ // Paper start - faster alternative constructor ++ private Rotations(float x, float y, float z, Void dummy_var) { ++ this.x = x; ++ this.y = y; ++ this.z = z; ++ } ++ ++ public static Rotations createWithoutValidityChecks(float x, float y, float z) { ++ return new Rotations(x, y, z, null); ++ } ++ // Paper end - faster alternative constructor ++ + public ListTag save() { + ListTag listTag = new ListTag(); + listTag.add(FloatTag.valueOf(this.x)); diff --git a/patches/server/0495-Drop-carried-item-when-player-has-disconnected.patch b/patches/server/0495-Drop-carried-item-when-player-has-disconnected.patch new file mode 100644 index 0000000000..42055a9f10 --- /dev/null +++ b/patches/server/0495-Drop-carried-item-when-player-has-disconnected.patch @@ -0,0 +1,27 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Dmitry Sidorov <[email protected]> +Date: Thu, 4 Feb 2021 20:32:01 +0300 +Subject: [PATCH] Drop carried item when player has disconnected + +Fixes disappearance of held items, when a player gets disconnected and PlayerDropItemEvent is cancelled. +Closes #5036 + +diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java +index e80e6e402ae286358b7f26073329b2e10604153b..6ddcc928a49630e3a0f7f40cca496642419efa2c 100644 +--- a/src/main/java/net/minecraft/server/players/PlayerList.java ++++ b/src/main/java/net/minecraft/server/players/PlayerList.java +@@ -537,6 +537,14 @@ public abstract class PlayerList { + } + // Paper end - Configurable player collision + ++ // Paper - Drop carried item when player has disconnected ++ if (!entityplayer.containerMenu.getCarried().isEmpty()) { ++ net.minecraft.world.item.ItemStack carried = entityplayer.containerMenu.getCarried(); ++ entityplayer.containerMenu.setCarried(net.minecraft.world.item.ItemStack.EMPTY); ++ entityplayer.drop(carried, false); ++ } ++ // Paper end - Drop carried item when player has disconnected ++ + this.save(entityplayer); + if (entityplayer.isPassenger()) { + Entity entity = entityplayer.getRootVehicle(); diff --git a/patches/server/0496-forced-whitelist-use-configurable-kick-message.patch b/patches/server/0496-forced-whitelist-use-configurable-kick-message.patch new file mode 100644 index 0000000000..f9aab54d8f --- /dev/null +++ b/patches/server/0496-forced-whitelist-use-configurable-kick-message.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Trigary <[email protected]> +Date: Sat, 27 Mar 2021 09:24:23 +0100 +Subject: [PATCH] forced whitelist: use configurable kick message + + +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index b28a2756429270414f8ef89539f91ce0595d13c6..0ab92fb362729108beca6bc0230fb6fb06ca6280 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -2325,7 +2325,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa + ServerPlayer entityplayer = (ServerPlayer) iterator.next(); + + if (!whitelist.isWhiteListed(entityplayer.getGameProfile()) && !this.getPlayerList().isOp(entityplayer.getGameProfile())) { // Paper - Fix kicking ops when whitelist is reloaded (MC-171420) +- entityplayer.connection.disconnect((Component) Component.translatable("multiplayer.disconnect.not_whitelisted")); ++ entityplayer.connection.disconnect(net.kyori.adventure.text.Component.text(org.spigotmc.SpigotConfig.whitelistMessage)); + } + } + diff --git a/patches/server/0497-Don-t-ignore-result-of-PlayerEditBookEvent.patch b/patches/server/0497-Don-t-ignore-result-of-PlayerEditBookEvent.patch new file mode 100644 index 0000000000..890d12fe22 --- /dev/null +++ b/patches/server/0497-Don-t-ignore-result-of-PlayerEditBookEvent.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jason Penilla <[email protected]> +Date: Mon, 5 Apr 2021 18:35:15 -0700 +Subject: [PATCH] Don't ignore result of PlayerEditBookEvent + + +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index e23b1f98c4ea066e1c35e15454ed524a20f65b4d..ddf2b9c6de079f55c3ac8836345c1e681f23fcac 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -1219,7 +1219,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + List<Filterable<String>> list1 = pages.stream().map(this::filterableFromOutgoing).toList(); + + itemstack.set(DataComponents.WRITABLE_BOOK_CONTENT, new WritableBookContent(list1)); +- CraftEventFactory.handleEditBookEvent(this.player, slotId, handItem, itemstack); // CraftBukkit ++ this.player.getInventory().setItem(slotId, CraftEventFactory.handleEditBookEvent(this.player, slotId, handItem, itemstack)); // CraftBukkit // Paper - Don't ignore result (see other callsite for handleEditBookEvent) + } + } + diff --git a/patches/server/0498-Expose-protocol-version.patch b/patches/server/0498-Expose-protocol-version.patch new file mode 100644 index 0000000000..cd6a7ab2b0 --- /dev/null +++ b/patches/server/0498-Expose-protocol-version.patch @@ -0,0 +1,22 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Nassim Jahnke <[email protected]> +Date: Fri, 26 Mar 2021 11:23:17 +0100 +Subject: [PATCH] Expose protocol version + + +diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +index 8d9380b50424546bcebdfa868635f94c8efa5899..30f2ddb66385718304b4b0302f7efcafd54bc42a 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +@@ -543,6 +543,11 @@ public final class CraftMagicNumbers implements UnsafeValues { + public String getMainLevelName() { + return ((net.minecraft.server.dedicated.DedicatedServer) net.minecraft.server.MinecraftServer.getServer()).getProperties().levelName; + } ++ ++ @Override ++ public int getProtocolVersion() { ++ return net.minecraft.SharedConstants.getCurrentVersion().getProtocolVersion(); ++ } + // Paper end + + /** diff --git a/patches/server/0499-Enhance-console-tab-completions-for-brigadier-comman.patch b/patches/server/0499-Enhance-console-tab-completions-for-brigadier-comman.patch new file mode 100644 index 0000000000..3d1a0fb945 --- /dev/null +++ b/patches/server/0499-Enhance-console-tab-completions-for-brigadier-comman.patch @@ -0,0 +1,445 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jason Penilla <[email protected]> +Date: Tue, 30 Mar 2021 16:06:08 -0700 +Subject: [PATCH] Enhance console tab completions for brigadier commands + +Co-authored-by: Jake Potrebic <[email protected]> + +diff --git a/src/main/java/com/destroystokyo/paper/console/PaperConsole.java b/src/main/java/com/destroystokyo/paper/console/PaperConsole.java +index a4070b59e261f0f1ac4beec47b11492f4724bf27..6ee39b534b8d992655bc0cef3c299d12cbae0034 100644 +--- a/src/main/java/com/destroystokyo/paper/console/PaperConsole.java ++++ b/src/main/java/com/destroystokyo/paper/console/PaperConsole.java +@@ -1,5 +1,8 @@ + package com.destroystokyo.paper.console; + ++import io.papermc.paper.configuration.GlobalConfiguration; ++import io.papermc.paper.console.BrigadierCompletionMatcher; ++import io.papermc.paper.console.BrigadierConsoleParser; + import net.minecraft.server.dedicated.DedicatedServer; + import net.minecrell.terminalconsole.SimpleTerminalConsole; + import org.bukkit.craftbukkit.command.ConsoleCommandCompleter; +@@ -16,11 +19,20 @@ public final class PaperConsole extends SimpleTerminalConsole { + + @Override + protected LineReader buildReader(LineReaderBuilder builder) { +- return super.buildReader(builder ++ builder + .appName("Paper") + .variable(LineReader.HISTORY_FILE, java.nio.file.Paths.get(".console_history")) + .completer(new ConsoleCommandCompleter(this.server)) +- ); ++ .option(LineReader.Option.COMPLETE_IN_WORD, true); ++ if (io.papermc.paper.configuration.GlobalConfiguration.get().console.enableBrigadierHighlighting) { ++ builder.highlighter(new io.papermc.paper.console.BrigadierCommandHighlighter(this.server)); ++ } ++ if (GlobalConfiguration.get().console.enableBrigadierCompletions) { ++ System.setProperty("org.jline.reader.support.parsedline", "true"); // to hide a warning message about the parser not supporting ++ builder.parser(new BrigadierConsoleParser(this.server)); ++ builder.completionMatcher(new BrigadierCompletionMatcher()); ++ } ++ return super.buildReader(builder); + } + + @Override +diff --git a/src/main/java/io/papermc/paper/console/BrigadierCommandCompleter.java b/src/main/java/io/papermc/paper/console/BrigadierCommandCompleter.java +new file mode 100644 +index 0000000000000000000000000000000000000000..bf7b9518c05ff8a6d4b7d7cd36187ca22257e3dc +--- /dev/null ++++ b/src/main/java/io/papermc/paper/console/BrigadierCommandCompleter.java +@@ -0,0 +1,119 @@ ++package io.papermc.paper.console; ++ ++import com.destroystokyo.paper.event.server.AsyncTabCompleteEvent; ++import com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion; ++import com.google.common.base.Suppliers; ++import com.mojang.brigadier.CommandDispatcher; ++import com.mojang.brigadier.ParseResults; ++import com.mojang.brigadier.StringReader; ++import com.mojang.brigadier.suggestion.Suggestion; ++import io.papermc.paper.adventure.PaperAdventure; ++import java.util.ArrayList; ++import java.util.Collections; ++import java.util.List; ++import java.util.function.Supplier; ++import net.kyori.adventure.text.Component; ++import net.minecraft.commands.CommandSourceStack; ++import net.minecraft.network.chat.ComponentUtils; ++import net.minecraft.server.dedicated.DedicatedServer; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.checker.nullness.qual.Nullable; ++import org.jline.reader.Candidate; ++import org.jline.reader.LineReader; ++import org.jline.reader.ParsedLine; ++ ++import static com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion.completion; ++ ++public final class BrigadierCommandCompleter { ++ private final Supplier<CommandSourceStack> commandSourceStack; ++ private final DedicatedServer server; ++ ++ public BrigadierCommandCompleter(final @NonNull DedicatedServer server) { ++ this.server = server; ++ this.commandSourceStack = Suppliers.memoize(this.server::createCommandSourceStack); ++ } ++ ++ public void complete(final @NonNull LineReader reader, final @NonNull ParsedLine line, final @NonNull List<Candidate> candidates, final @NonNull List<Completion> existing) { ++ //noinspection ConstantConditions ++ if (this.server.overworld() == null) { // check if overworld is null, as worlds haven't been loaded yet ++ return; ++ } else if (!io.papermc.paper.configuration.GlobalConfiguration.get().console.enableBrigadierCompletions) { ++ this.addCandidates(candidates, Collections.emptyList(), existing, new ParseContext(line.line(), 0)); ++ return; ++ } ++ final CommandDispatcher<CommandSourceStack> dispatcher = this.server.getCommands().getDispatcher(); ++ final ParseResults<CommandSourceStack> results = dispatcher.parse(new StringReader(line.line()), this.commandSourceStack.get()); ++ this.addCandidates( ++ candidates, ++ dispatcher.getCompletionSuggestions(results, line.cursor()).join().getList(), ++ existing, ++ new ParseContext(line.line(), results.getContext().findSuggestionContext(line.cursor()).startPos) ++ ); ++ } ++ ++ private void addCandidates( ++ final @NonNull List<Candidate> candidates, ++ final @NonNull List<Suggestion> brigSuggestions, ++ final @NonNull List<Completion> existing, ++ final @NonNull ParseContext context ++ ) { ++ brigSuggestions.forEach(it -> { ++ if (it.getText().isEmpty()) return; ++ candidates.add(toCandidate(it, context)); ++ }); ++ for (final AsyncTabCompleteEvent.Completion completion : existing) { ++ if (completion.suggestion().isEmpty() || brigSuggestions.stream().anyMatch(it -> it.getText().equals(completion.suggestion()))) { ++ continue; ++ } ++ candidates.add(toCandidate(completion)); ++ } ++ } ++ ++ private static Candidate toCandidate(final Suggestion suggestion, final @NonNull ParseContext context) { ++ Component tooltip = null; ++ if (suggestion.getTooltip() != null) { ++ tooltip = PaperAdventure.asAdventure(ComponentUtils.fromMessage(suggestion.getTooltip())); ++ } ++ return toCandidate(context.line.substring(context.suggestionStart, suggestion.getRange().getStart()) + suggestion.getText(), tooltip); ++ } ++ ++ private static @NonNull Candidate toCandidate(final @NonNull Completion completion) { ++ return toCandidate(completion.suggestion(), completion.tooltip()); ++ } ++ ++ private static @NonNull Candidate toCandidate(final @NonNull String suggestionText, final @Nullable Component tooltip) { ++ final String suggestionTooltip = PaperAdventure.ANSI_SERIALIZER.serializeOr(tooltip, null); ++ //noinspection SpellCheckingInspection ++ return new PaperCandidate( ++ suggestionText, ++ suggestionText, ++ null, ++ suggestionTooltip, ++ null, ++ null, ++ /* ++ in an ideal world, this would sometimes be true if the suggestion represented the final possible value for a word. ++ Like for `/execute alig`, pressing enter on align would add a trailing space if this value was true. But not all ++ suggestions should add spaces after, like `/execute as @`, accepting any suggestion here would be valid, but its also ++ valid to have a `[` following the selector ++ */ ++ false ++ ); ++ } ++ ++ private static @NonNull Completion toCompletion(final @NonNull Suggestion suggestion) { ++ if (suggestion.getTooltip() == null) { ++ return completion(suggestion.getText()); ++ } ++ return completion(suggestion.getText(), PaperAdventure.asAdventure(ComponentUtils.fromMessage(suggestion.getTooltip()))); ++ } ++ ++ private record ParseContext(String line, int suggestionStart) { ++ } ++ ++ public static final class PaperCandidate extends Candidate { ++ public PaperCandidate(final String value, final String display, final String group, final String descr, final String suffix, final String key, final boolean complete) { ++ super(value, display, group, descr, suffix, key, complete); ++ } ++ } ++} +diff --git a/src/main/java/io/papermc/paper/console/BrigadierCommandHighlighter.java b/src/main/java/io/papermc/paper/console/BrigadierCommandHighlighter.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0b21dac4473e3ea8022ef5c17f5f7d4d49d3ac0a +--- /dev/null ++++ b/src/main/java/io/papermc/paper/console/BrigadierCommandHighlighter.java +@@ -0,0 +1,67 @@ ++package io.papermc.paper.console; ++ ++import com.google.common.base.Suppliers; ++import com.mojang.brigadier.ParseResults; ++import com.mojang.brigadier.StringReader; ++import com.mojang.brigadier.context.ParsedCommandNode; ++import com.mojang.brigadier.tree.LiteralCommandNode; ++import java.util.function.Supplier; ++import java.util.regex.Pattern; ++import net.minecraft.commands.CommandSourceStack; ++import net.minecraft.server.dedicated.DedicatedServer; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.jline.reader.Highlighter; ++import org.jline.reader.LineReader; ++import org.jline.utils.AttributedString; ++import org.jline.utils.AttributedStringBuilder; ++import org.jline.utils.AttributedStyle; ++ ++public final class BrigadierCommandHighlighter implements Highlighter { ++ private static final int[] COLORS = {AttributedStyle.CYAN, AttributedStyle.YELLOW, AttributedStyle.GREEN, AttributedStyle.MAGENTA, /* Client uses GOLD here, not BLUE, however there is no GOLD AttributedStyle. */ AttributedStyle.BLUE}; ++ private final Supplier<CommandSourceStack> commandSourceStack; ++ private final DedicatedServer server; ++ ++ public BrigadierCommandHighlighter(final @NonNull DedicatedServer server) { ++ this.server = server; ++ this.commandSourceStack = Suppliers.memoize(this.server::createCommandSourceStack); ++ } ++ ++ @Override ++ public AttributedString highlight(final @NonNull LineReader reader, final @NonNull String buffer) { ++ //noinspection ConstantConditions ++ if (this.server.overworld() == null) { // check if overworld is null, as worlds haven't been loaded yet ++ return new AttributedString(buffer, AttributedStyle.DEFAULT.foreground(AttributedStyle.RED)); ++ } ++ final AttributedStringBuilder builder = new AttributedStringBuilder(); ++ final ParseResults<CommandSourceStack> results = this.server.getCommands().getDispatcher().parse(new StringReader(buffer), this.commandSourceStack.get()); ++ int pos = 0; ++ int component = -1; ++ for (final ParsedCommandNode<CommandSourceStack> node : results.getContext().getLastChild().getNodes()) { ++ if (node.getRange().getStart() >= buffer.length()) { ++ break; ++ } ++ final int start = node.getRange().getStart(); ++ final int end = Math.min(node.getRange().getEnd(), buffer.length()); ++ builder.append(buffer.substring(pos, start), AttributedStyle.DEFAULT); ++ if (node.getNode() instanceof LiteralCommandNode) { ++ builder.append(buffer.substring(start, end), AttributedStyle.DEFAULT); ++ } else { ++ if (++component >= COLORS.length) { ++ component = 0; ++ } ++ builder.append(buffer.substring(start, end), AttributedStyle.DEFAULT.foreground(COLORS[component])); ++ } ++ pos = end; ++ } ++ if (pos < buffer.length()) { ++ builder.append((buffer.substring(pos)), AttributedStyle.DEFAULT.foreground(AttributedStyle.RED)); ++ } ++ return builder.toAttributedString(); ++ } ++ ++ @Override ++ public void setErrorPattern(final Pattern errorPattern) {} ++ ++ @Override ++ public void setErrorIndex(final int errorIndex) {} ++} +diff --git a/src/main/java/io/papermc/paper/console/BrigadierCompletionMatcher.java b/src/main/java/io/papermc/paper/console/BrigadierCompletionMatcher.java +new file mode 100644 +index 0000000000000000000000000000000000000000..1e8028a43db0ff1d5b22d06ef12c1c32d992c09c +--- /dev/null ++++ b/src/main/java/io/papermc/paper/console/BrigadierCompletionMatcher.java +@@ -0,0 +1,27 @@ ++package io.papermc.paper.console; ++ ++import com.google.common.collect.Iterables; ++import java.util.HashMap; ++import java.util.List; ++import java.util.Map; ++import org.jline.reader.Candidate; ++import org.jline.reader.CompletingParsedLine; ++import org.jline.reader.LineReader; ++import org.jline.reader.impl.CompletionMatcherImpl; ++ ++public class BrigadierCompletionMatcher extends CompletionMatcherImpl { ++ ++ @Override ++ protected void defaultMatchers(final Map<LineReader.Option, Boolean> options, final boolean prefix, final CompletingParsedLine line, final boolean caseInsensitive, final int errors, final String originalGroupName) { ++ super.defaultMatchers(options, prefix, line, caseInsensitive, errors, originalGroupName); ++ this.matchers.addFirst(m -> { ++ final Map<String, List<Candidate>> candidates = new HashMap<>(); ++ for (final Map.Entry<String, List<Candidate>> entry : m.entrySet()) { ++ if (Iterables.all(entry.getValue(), BrigadierCommandCompleter.PaperCandidate.class::isInstance)) { ++ candidates.put(entry.getKey(), entry.getValue()); ++ } ++ } ++ return candidates; ++ }); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/console/BrigadierConsoleParser.java b/src/main/java/io/papermc/paper/console/BrigadierConsoleParser.java +new file mode 100644 +index 0000000000000000000000000000000000000000..8239a8ba57f856cbbee237a601b3cabfce20ba26 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/console/BrigadierConsoleParser.java +@@ -0,0 +1,79 @@ ++package io.papermc.paper.console; ++ ++import com.mojang.brigadier.ImmutableStringReader; ++import com.mojang.brigadier.ParseResults; ++import com.mojang.brigadier.StringReader; ++import com.mojang.brigadier.context.CommandContextBuilder; ++import com.mojang.brigadier.context.ParsedCommandNode; ++import com.mojang.brigadier.context.StringRange; ++import java.util.ArrayList; ++import java.util.List; ++import net.minecraft.commands.CommandSourceStack; ++import net.minecraft.server.dedicated.DedicatedServer; ++import org.jline.reader.ParsedLine; ++import org.jline.reader.Parser; ++import org.jline.reader.SyntaxError; ++ ++public class BrigadierConsoleParser implements Parser { ++ ++ private final DedicatedServer server; ++ ++ public BrigadierConsoleParser(DedicatedServer server) { ++ this.server = server; ++ } ++ ++ @Override ++ public ParsedLine parse(final String line, final int cursor, final ParseContext context) throws SyntaxError { ++ final ParseResults<CommandSourceStack> results = this.server.getCommands().getDispatcher().parse(new StringReader(line), this.server.createCommandSourceStack()); ++ final ImmutableStringReader reader = results.getReader(); ++ final List<String> words = new ArrayList<>(); ++ CommandContextBuilder<CommandSourceStack> currentContext = results.getContext(); ++ int currentWordIdx = -1; ++ int wordIdx = -1; ++ int inWordCursor = -1; ++ if (currentContext.getRange().getLength() > 0) { ++ do { ++ for (final ParsedCommandNode<CommandSourceStack> node : currentContext.getNodes()) { ++ final StringRange nodeRange = node.getRange(); ++ String current = nodeRange.get(reader); ++ words.add(current); ++ currentWordIdx++; ++ if (wordIdx == -1 && nodeRange.getStart() <= cursor && nodeRange.getEnd() >= cursor) { ++ // if cursor is in the middle of a parsed word/node ++ wordIdx = currentWordIdx; ++ inWordCursor = cursor - nodeRange.getStart(); ++ } ++ } ++ currentContext = currentContext.getChild(); ++ } while (currentContext != null); ++ } ++ final String leftovers = reader.getRemaining(); ++ if (!leftovers.isEmpty() && leftovers.isBlank()) { ++ // if brig didn't consume the whole line, and everything else is blank, add a new empty word ++ currentWordIdx++; ++ words.add(""); ++ if (wordIdx == -1) { ++ wordIdx = currentWordIdx; ++ inWordCursor = 0; ++ } ++ } else if (!leftovers.isEmpty()) { ++ // if there are unparsed leftovers, add a new word with the remaining input ++ currentWordIdx++; ++ words.add(leftovers); ++ if (wordIdx == -1) { ++ wordIdx = currentWordIdx; ++ inWordCursor = cursor - reader.getCursor(); ++ } ++ } ++ if (wordIdx == -1) { ++ currentWordIdx++; ++ words.add(""); ++ wordIdx = currentWordIdx; ++ inWordCursor = 0; ++ } ++ return new BrigadierParsedLine(words.get(wordIdx), inWordCursor, wordIdx, words, line, cursor); ++ } ++ ++ record BrigadierParsedLine(String word, int wordCursor, int wordIndex, List<String> words, String line, int cursor) implements ParsedLine { ++ } ++} +diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +index 9d05e998d6df1069c2de69478a1f9688ac435e67..7c92b2f0a59fe222ad13a998476e312bf571a1bf 100644 +--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java ++++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +@@ -187,7 +187,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface + + thread.setDaemon(true); + thread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(DedicatedServer.LOGGER)); +- thread.start(); ++ // thread.start(); // Paper - Enhance console tab completions for brigadier commands; moved down + DedicatedServer.LOGGER.info("Starting minecraft server version {}", SharedConstants.getCurrentVersion().getName()); + if (Runtime.getRuntime().maxMemory() / 1024L / 1024L < 512L) { + DedicatedServer.LOGGER.warn("To start the server with more ram, launch it as \"java -Xmx1024M -Xms1024M -jar minecraft_server.jar\""); +@@ -220,6 +220,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface + this.getPlayerList().loadAndSaveFiles(); // Must be after convertNames + // Paper end - fix converting txt to json file + org.spigotmc.WatchdogThread.doStart(org.spigotmc.SpigotConfig.timeoutTime, org.spigotmc.SpigotConfig.restartOnCrash); // Paper - start watchdog thread ++ thread.start(); // Paper - Enhance console tab completions for brigadier commands; start console thread after MinecraftServer.console & PaperConfig are initialized + io.papermc.paper.command.PaperCommands.registerCommands(this); // Paper - setup /paper command + com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); // Paper - start metrics + com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // Paper - load version history now +diff --git a/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java b/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java +index 15bc85f4799a4b23edd2f1e93f1794de5ca3e8e3..a45e658996e483e9a21cfd8178153ddb7b87ae69 100644 +--- a/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java ++++ b/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java +@@ -18,9 +18,11 @@ import org.bukkit.event.server.TabCompleteEvent; + + public class ConsoleCommandCompleter implements Completer { + private final DedicatedServer server; // Paper - CraftServer -> DedicatedServer ++ private final io.papermc.paper.console.BrigadierCommandCompleter brigadierCompleter; // Paper - Enhance console tab completions for brigadier commands + + public ConsoleCommandCompleter(DedicatedServer server) { // Paper - CraftServer -> DedicatedServer + this.server = server; ++ this.brigadierCompleter = new io.papermc.paper.console.BrigadierCommandCompleter(this.server); // Paper - Enhance console tab completions for brigadier commands + } + + // Paper start - Change method signature for JLine update +@@ -64,7 +66,7 @@ public class ConsoleCommandCompleter implements Completer { + } + } + +- if (!completions.isEmpty()) { ++ if (false && !completions.isEmpty()) { + for (final com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion completion : completions) { + if (completion.suggestion().isEmpty()) { + continue; +@@ -80,6 +82,7 @@ public class ConsoleCommandCompleter implements Completer { + )); + } + } ++ this.addCompletions(reader, line, candidates, completions); + return; + } + +@@ -99,10 +102,12 @@ public class ConsoleCommandCompleter implements Completer { + try { + List<String> offers = waitable.get(); + if (offers == null) { ++ this.addCompletions(reader, line, candidates, Collections.emptyList()); // Paper - Enhance console tab completions for brigadier commands + return; // Paper - Method returns void + } + + // Paper start - JLine update ++ /* + for (String completion : offers) { + if (completion.isEmpty()) { + continue; +@@ -110,6 +115,8 @@ public class ConsoleCommandCompleter implements Completer { + + candidates.add(new Candidate(completion)); + } ++ */ ++ this.addCompletions(reader, line, candidates, offers.stream().map(com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion::completion).collect(java.util.stream.Collectors.toList())); + // Paper end + + // Paper start - JLine handles cursor now +@@ -138,5 +145,9 @@ public class ConsoleCommandCompleter implements Completer { + } + return false; + } ++ ++ private void addCompletions(final LineReader reader, final ParsedLine line, final List<Candidate> candidates, final List<com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion> existing) { ++ this.brigadierCompleter.complete(reader, line, candidates, existing); ++ } + // Paper end + } diff --git a/patches/server/0500-Fix-PlayerItemConsumeEvent-cancelling-properly.patch b/patches/server/0500-Fix-PlayerItemConsumeEvent-cancelling-properly.patch new file mode 100644 index 0000000000..7ab0446f86 --- /dev/null +++ b/patches/server/0500-Fix-PlayerItemConsumeEvent-cancelling-properly.patch @@ -0,0 +1,22 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: chickeneer <[email protected]> +Date: Fri, 19 Mar 2021 00:33:15 -0500 +Subject: [PATCH] Fix PlayerItemConsumeEvent cancelling properly + +When the active item is not cleared, the item is still readied +for use and will repeatedly trigger the PlayerItemConsumeEvent +till their item is switched. +This patch clears the active item when the event is cancelled + +diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java +index 2d3f418b5a7a5414ca5be8525146e480add51635..509139ab9a3070c8c96d14fdc4d07bc229b41bd6 100644 +--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java ++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java +@@ -4149,6 +4149,7 @@ public abstract class LivingEntity extends Entity implements Attackable { + } + entityPlayer.getBukkitEntity().updateInventory(); + entityPlayer.getBukkitEntity().updateScaledHealth(); ++ this.stopUsingItem(); // Paper - event is using an item, clear active item to reset its use + return; + } + diff --git a/patches/server/0501-Add-bypass-host-check.patch b/patches/server/0501-Add-bypass-host-check.patch new file mode 100644 index 0000000000..53fea6e14d --- /dev/null +++ b/patches/server/0501-Add-bypass-host-check.patch @@ -0,0 +1,30 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Shane Freeder <[email protected]> +Date: Sun, 18 Apr 2021 21:27:01 +0100 +Subject: [PATCH] Add bypass host check + +Paper.bypassHostCheck + +Seriously, fix your firewalls. -.- + +diff --git a/src/main/java/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java +index 0c1bdf2329936ce479a2cc53e8a46bd2ad685ec1..a5bbea6a073e00c10c3c5facd997eb8473fd9a5f 100644 +--- a/src/main/java/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java +@@ -32,6 +32,7 @@ public class ServerHandshakePacketListenerImpl implements ServerHandshakePacketL + private static final Component IGNORE_STATUS_REASON = Component.translatable("disconnect.ignoring_status_request"); + private final MinecraftServer server; + private final Connection connection; ++ private static final boolean BYPASS_HOSTCHECK = Boolean.getBoolean("Paper.bypassHostCheck"); // Paper + + public ServerHandshakePacketListenerImpl(MinecraftServer server, Connection connection) { + this.server = server; +@@ -164,7 +165,7 @@ public class ServerHandshakePacketListenerImpl implements ServerHandshakePacketL + String[] split = packet.hostName().split("\00"); + if (!handledByEvent && proxyLogicEnabled) { // Paper + // if (org.spigotmc.SpigotConfig.bungee) { // Paper - comment out, we check above! +- if ( ( split.length == 3 || split.length == 4 ) && ( ServerHandshakePacketListenerImpl.HOST_PATTERN.matcher( split[1] ).matches() ) ) { ++ if ( ( split.length == 3 || split.length == 4 ) && ( ServerHandshakePacketListenerImpl.BYPASS_HOSTCHECK || ServerHandshakePacketListenerImpl.HOST_PATTERN.matcher( split[1] ).matches() ) ) { // Paper - Add bypass host check + this.connection.hostname = split[0]; + this.connection.address = new java.net.InetSocketAddress(split[1], ((java.net.InetSocketAddress) this.connection.getRemoteAddress()).getPort()); + this.connection.spoofedUUID = com.mojang.util.UndashedUuid.fromStringLenient( split[2] ); diff --git a/patches/server/0502-Set-area-affect-cloud-rotation.patch b/patches/server/0502-Set-area-affect-cloud-rotation.patch new file mode 100644 index 0000000000..92bb3dc84a --- /dev/null +++ b/patches/server/0502-Set-area-affect-cloud-rotation.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Owen1212055 <[email protected]> +Date: Mon, 5 Apr 2021 16:58:20 -0400 +Subject: [PATCH] Set area affect cloud rotation + + +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java +index 0ebcf7ed1cb9e896de5fbac60afdb937ba86d15c..95c2ef53e6c5574e69614d8058eb3ed94ab6b0d6 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java +@@ -426,7 +426,7 @@ public final class CraftEntityTypes { + register(new EntityTypeData<>(EntityType.EXPERIENCE_ORB, ExperienceOrb.class, CraftExperienceOrb::new, + spawnData -> new net.minecraft.world.entity.ExperienceOrb(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z(), 0, org.bukkit.entity.ExperienceOrb.SpawnReason.CUSTOM, null, null) // Paper + )); +- register(new EntityTypeData<>(EntityType.AREA_EFFECT_CLOUD, AreaEffectCloud.class, CraftAreaEffectCloud::new, spawnData -> new net.minecraft.world.entity.AreaEffectCloud(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z()))); ++ register(new EntityTypeData<>(EntityType.AREA_EFFECT_CLOUD, AreaEffectCloud.class, CraftAreaEffectCloud::new, createAndMove(net.minecraft.world.entity.EntityType.AREA_EFFECT_CLOUD))); // Paper - set area effect cloud rotation + register(new EntityTypeData<>(EntityType.EGG, Egg.class, CraftEgg::new, spawnData -> new ThrownEgg(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z(), new net.minecraft.world.item.ItemStack(Items.EGG)))); + register(new EntityTypeData<>(EntityType.LEASH_KNOT, LeashHitch.class, CraftLeash::new, spawnData -> new LeashFenceKnotEntity(spawnData.minecraftWorld(), BlockPos.containing(spawnData.x(), spawnData.y(), spawnData.z())))); // SPIGOT-5732: LeashHitch has no direction and is always centered at a block + register(new EntityTypeData<>(EntityType.SNOWBALL, Snowball.class, CraftSnowball::new, spawnData -> new net.minecraft.world.entity.projectile.Snowball(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z(), new net.minecraft.world.item.ItemStack(Items.SNOWBALL)))); diff --git a/patches/server/0503-add-isDeeplySleeping-to-HumanEntity.patch b/patches/server/0503-add-isDeeplySleeping-to-HumanEntity.patch new file mode 100644 index 0000000000..645f1201ea --- /dev/null +++ b/patches/server/0503-add-isDeeplySleeping-to-HumanEntity.patch @@ -0,0 +1,24 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Thu, 8 Apr 2021 17:36:10 -0700 +Subject: [PATCH] add isDeeplySleeping to HumanEntity + + +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java +index 42c41cd4b280839a35f77e04d0dc6a06d262d8f3..d5c4184e0d93eabc699661829ff4bf9b46d27142 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java +@@ -132,6 +132,13 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity { + } + // Paper end + ++ // Paper start ++ @Override ++ public boolean isDeeplySleeping() { ++ return getHandle().isSleepingLongEnough(); ++ } ++ // Paper end ++ + @Override + public int getSleepTicks() { + return this.getHandle().sleepCounter; diff --git a/patches/server/0504-add-consumeFuel-to-FurnaceBurnEvent.patch b/patches/server/0504-add-consumeFuel-to-FurnaceBurnEvent.patch new file mode 100644 index 0000000000..150b4fe7c0 --- /dev/null +++ b/patches/server/0504-add-consumeFuel-to-FurnaceBurnEvent.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Thu, 22 Apr 2021 16:45:28 -0700 +Subject: [PATCH] add consumeFuel to FurnaceBurnEvent + + +diff --git a/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java +index 4acf487f9a5f3fa828ee76f9708d6a2ae28707e1..7b63e2fd004ae452c7350f0b2d8d7c57a42891ea 100644 +--- a/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java ++++ b/src/main/java/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java +@@ -250,7 +250,7 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit + if (blockEntity.isLit() && furnaceBurnEvent.isBurning()) { + // CraftBukkit end + flag1 = true; +- if (flag3) { ++ if (flag3 && furnaceBurnEvent.willConsumeFuel()) { // Paper - add consumeFuel to FurnaceBurnEvent + Item item = itemstack.getItem(); + + itemstack.shrink(1); diff --git a/patches/server/0505-add-get-set-drop-chance-to-EntityEquipment.patch b/patches/server/0505-add-get-set-drop-chance-to-EntityEquipment.patch new file mode 100644 index 0000000000..e794470410 --- /dev/null +++ b/patches/server/0505-add-get-set-drop-chance-to-EntityEquipment.patch @@ -0,0 +1,72 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Thu, 22 Apr 2021 00:28:11 -0700 +Subject: [PATCH] add get-set drop chance to EntityEquipment + +== AT == +public net.minecraft.world.entity.Mob getEquipmentDropChance(Lnet/minecraft/world/entity/EquipmentSlot;)F + +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftEntityEquipment.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftEntityEquipment.java +index cb704cef3845727c465fe3ea7210a11545da56c8..fdcc414f4fa246082ad0732133c870d915ae3084 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftEntityEquipment.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftEntityEquipment.java +@@ -244,15 +244,22 @@ public class CraftEntityEquipment implements EntityEquipment { + public void setBootsDropChance(float chance) { + this.setDropChance(net.minecraft.world.entity.EquipmentSlot.FEET, chance); + } ++ // Paper start ++ @Override ++ public float getDropChance(EquipmentSlot slot) { ++ return getDropChance(CraftEquipmentSlot.getNMS(slot)); ++ } ++ ++ @Override ++ public void setDropChance(EquipmentSlot slot, float chance) { ++ setDropChance(CraftEquipmentSlot.getNMS(slot), chance); ++ } ++ // Paper end + + private void setDropChance(net.minecraft.world.entity.EquipmentSlot slot, float chance) { + Preconditions.checkArgument(this.entity.getHandle() instanceof Mob, "Cannot set drop chance for non-Mob entity"); + +- if (slot == net.minecraft.world.entity.EquipmentSlot.MAINHAND || slot == net.minecraft.world.entity.EquipmentSlot.OFFHAND) { +- ((Mob) this.entity.getHandle()).handDropChances[slot.getIndex()] = chance; +- } else { +- ((Mob) this.entity.getHandle()).armorDropChances[slot.getIndex()] = chance; +- } ++ ((Mob) this.entity.getHandle()).setDropChance(slot, chance); // Paper - use setter on Mob + } + + private float getDropChance(net.minecraft.world.entity.EquipmentSlot slot) { +@@ -260,10 +267,6 @@ public class CraftEntityEquipment implements EntityEquipment { + return 1; + } + +- if (slot == net.minecraft.world.entity.EquipmentSlot.MAINHAND || slot == net.minecraft.world.entity.EquipmentSlot.OFFHAND) { +- return ((Mob) this.entity.getHandle()).handDropChances[slot.getIndex()]; +- } else { +- return ((Mob) this.entity.getHandle()).armorDropChances[slot.getIndex()]; +- } ++ return ((Mob) this.entity.getHandle()).getEquipmentDropChance(slot); // Paper - use getter on Mob + } + } +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java +index f039c5041e783ef55e19194cb7db7610429b1d96..107fa1d4bd977d17dc062da280dda46eb3c5f81c 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java +@@ -353,4 +353,15 @@ public class CraftInventoryPlayer extends CraftInventory implements org.bukkit.i + public void setBootsDropChance(float chance) { + throw new UnsupportedOperationException("Cannot set drop chance for PlayerInventory"); + } ++ // Paper start ++ @Override ++ public float getDropChance(EquipmentSlot slot) { ++ return 1; ++ } ++ ++ @Override ++ public void setDropChance(EquipmentSlot slot, float chance) { ++ throw new UnsupportedOperationException("Cannot set drop chance for PlayerInventory"); ++ } ++ // Paper end + } diff --git a/patches/server/0506-fix-PigZombieAngerEvent-cancellation.patch b/patches/server/0506-fix-PigZombieAngerEvent-cancellation.patch new file mode 100644 index 0000000000..040d076f37 --- /dev/null +++ b/patches/server/0506-fix-PigZombieAngerEvent-cancellation.patch @@ -0,0 +1,35 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Trigary <[email protected]> +Date: Thu, 18 Mar 2021 21:38:01 +0100 +Subject: [PATCH] fix PigZombieAngerEvent cancellation + + +diff --git a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java +index faaaee79e14adb62e810641ccc8f51428d39883d..03e3cbe73119ca76417d4dd192e1560bdfc373ec 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java ++++ b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java +@@ -56,6 +56,7 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob { + private static final int ALERT_RANGE_Y = 10; + private static final UniformInt ALERT_INTERVAL = TimeUtil.rangeOfSeconds(4, 6); + private int ticksUntilNextAlert; ++ private HurtByTargetGoal pathfinderGoalHurtByTarget; // Paper - fix PigZombieAngerEvent cancellation + + public ZombifiedPiglin(EntityType<? extends ZombifiedPiglin> type, Level world) { + super(type, world); +@@ -71,7 +72,7 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob { + protected void addBehaviourGoals() { + this.goalSelector.addGoal(2, new ZombieAttackGoal(this, 1.0D, false)); + this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0D)); +- this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[0])).setAlertOthers()); ++ this.targetSelector.addGoal(1, pathfinderGoalHurtByTarget = (new HurtByTargetGoal(this, new Class[0])).setAlertOthers()); // Paper - fix PigZombieAngerEvent cancellation + this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::isAngryAt)); + this.targetSelector.addGoal(3, new ResetUniversalAngerTargetGoal<>(this, true)); + } +@@ -179,6 +180,7 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob { + this.level().getCraftServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + this.setPersistentAngerTarget(null); ++ pathfinderGoalHurtByTarget.stop(); // Paper - fix PigZombieAngerEvent cancellation + return; + } + this.setRemainingPersistentAngerTime(event.getNewAnger()); diff --git a/patches/server/0507-fix-PlayerItemHeldEvent-firing-twice.patch b/patches/server/0507-fix-PlayerItemHeldEvent-firing-twice.patch new file mode 100644 index 0000000000..631dbbe515 --- /dev/null +++ b/patches/server/0507-fix-PlayerItemHeldEvent-firing-twice.patch @@ -0,0 +1,18 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: chickeneer <[email protected]> +Date: Thu, 22 Apr 2021 19:02:07 -0700 +Subject: [PATCH] fix PlayerItemHeldEvent firing twice + + +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index ddf2b9c6de079f55c3ac8836345c1e681f23fcac..47f925707d8756796ce169b7cbd8f2410b0b7104 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -2014,6 +2014,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); + if (this.player.isImmobile()) return; // CraftBukkit + if (packet.getSlot() >= 0 && packet.getSlot() < Inventory.getSelectionSize()) { ++ if (packet.getSlot() == this.player.getInventory().selected) { return; } // Paper - don't fire itemheldevent when there wasn't a slot change + PlayerItemHeldEvent event = new PlayerItemHeldEvent(this.getCraftPlayer(), this.player.getInventory().selected, packet.getSlot()); + this.cserver.getPluginManager().callEvent(event); + if (event.isCancelled()) { diff --git a/patches/server/0508-Add-PlayerDeepSleepEvent.patch b/patches/server/0508-Add-PlayerDeepSleepEvent.patch new file mode 100644 index 0000000000..1750b3bc37 --- /dev/null +++ b/patches/server/0508-Add-PlayerDeepSleepEvent.patch @@ -0,0 +1,24 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Wed, 21 Apr 2021 15:58:19 -0700 +Subject: [PATCH] Add PlayerDeepSleepEvent + + +diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java +index 136a6b8e2065c7bada396e0f54f91c841a85c3d8..13f21822d0eb1d167b9b71addb46c4f508301c8d 100644 +--- a/src/main/java/net/minecraft/world/entity/player/Player.java ++++ b/src/main/java/net/minecraft/world/entity/player/Player.java +@@ -265,6 +265,13 @@ public abstract class Player extends LivingEntity { + + if (this.isSleeping()) { + ++this.sleepCounter; ++ // Paper start - Add PlayerDeepSleepEvent ++ if (this.sleepCounter == SLEEP_DURATION) { ++ if (!new io.papermc.paper.event.player.PlayerDeepSleepEvent((org.bukkit.entity.Player) getBukkitEntity()).callEvent()) { ++ this.sleepCounter = Integer.MIN_VALUE; ++ } ++ } ++ // Paper end - Add PlayerDeepSleepEvent + if (this.sleepCounter > 100) { + this.sleepCounter = 100; + } diff --git a/patches/server/0509-More-World-API.patch b/patches/server/0509-More-World-API.patch new file mode 100644 index 0000000000..2b853eb7b0 --- /dev/null +++ b/patches/server/0509-More-World-API.patch @@ -0,0 +1,57 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Tue, 7 Jul 2020 10:52:34 -0700 +Subject: [PATCH] More World API + + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index 69464a4fe467128121fe1a9ba08c9c7154e22c7f..f20717ee4505d28fa44f0f9ced5c71ae75c8a6d9 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -2130,6 +2130,28 @@ public class CraftWorld extends CraftRegionAccessor implements World { + return new CraftStructureSearchResult(CraftStructure.minecraftToBukkit(found.getSecond().value()), CraftLocation.toBukkit(found.getFirst(), this)); + } + ++ // Paper start ++ @Override ++ public double getCoordinateScale() { ++ return getHandle().dimensionType().coordinateScale(); ++ } ++ ++ @Override ++ public boolean isFixedTime() { ++ return getHandle().dimensionType().hasFixedTime(); ++ } ++ ++ @Override ++ public Collection<org.bukkit.Material> getInfiniburn() { ++ return com.google.common.collect.Sets.newHashSet(com.google.common.collect.Iterators.transform(net.minecraft.core.registries.BuiltInRegistries.BLOCK.getTagOrEmpty(this.getHandle().dimensionType().infiniburn()).iterator(), blockHolder -> CraftBlockType.minecraftToBukkit(blockHolder.value()))); ++ } ++ ++ @Override ++ public void sendGameEvent(Entity sourceEntity, org.bukkit.GameEvent gameEvent, Vector position) { ++ getHandle().gameEvent(sourceEntity != null ? ((CraftEntity) sourceEntity).getHandle(): null, net.minecraft.core.registries.BuiltInRegistries.GAME_EVENT.get(org.bukkit.craftbukkit.util.CraftNamespacedKey.toMinecraft(gameEvent.getKey())).orElseThrow(), org.bukkit.craftbukkit.util.CraftVector.toBlockPos(position)); ++ } ++ // Paper end ++ + @Override + public BiomeSearchResult locateNearestBiome(Location origin, int radius, Biome... biomes) { + return this.locateNearestBiome(origin, radius, 32, 64, biomes); +diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftVector.java b/src/main/java/org/bukkit/craftbukkit/util/CraftVector.java +index 3071ac1ac0e733d73dade49597a39f7d156bbc04..967445b2eb158454100a27369a1f463d69f54f27 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/CraftVector.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/CraftVector.java +@@ -12,4 +12,13 @@ public final class CraftVector { + public static net.minecraft.world.phys.Vec3 toNMS(org.bukkit.util.Vector bukkit) { + return new net.minecraft.world.phys.Vec3(bukkit.getX(), bukkit.getY(), bukkit.getZ()); + } ++ // Paper start ++ public static org.bukkit.util.Vector toBukkit(net.minecraft.core.BlockPos blockPosition) { ++ return new org.bukkit.util.Vector(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()); ++ } ++ ++ public static net.minecraft.core.BlockPos toBlockPos(org.bukkit.util.Vector bukkit) { ++ return net.minecraft.core.BlockPos.containing(bukkit.getX(), bukkit.getY(), bukkit.getZ()); ++ } ++ // Paper end + } diff --git a/patches/server/0510-Add-PlayerBedFailEnterEvent.patch b/patches/server/0510-Add-PlayerBedFailEnterEvent.patch new file mode 100644 index 0000000000..375fec28c9 --- /dev/null +++ b/patches/server/0510-Add-PlayerBedFailEnterEvent.patch @@ -0,0 +1,36 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Thu, 24 Dec 2020 12:27:41 -0800 +Subject: [PATCH] Add PlayerBedFailEnterEvent + + +diff --git a/src/main/java/net/minecraft/world/level/block/BedBlock.java b/src/main/java/net/minecraft/world/level/block/BedBlock.java +index 1e5f1b4c746fe9a105c2771c785976aae70a9a3e..30db6ae29e2fe03866dfa656330f7341e0a3f9ab 100644 +--- a/src/main/java/net/minecraft/world/level/block/BedBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/BedBlock.java +@@ -120,14 +120,23 @@ public class BedBlock extends HorizontalDirectionalBlock implements EntityBlock + BlockPos finalblockposition = pos; + // CraftBukkit end + player.startSleepInBed(pos).ifLeft((entityhuman_enumbedresult) -> { ++ // Paper start - PlayerBedFailEnterEvent ++ if (entityhuman_enumbedresult != null) { ++ io.papermc.paper.event.player.PlayerBedFailEnterEvent event = new io.papermc.paper.event.player.PlayerBedFailEnterEvent((org.bukkit.entity.Player) player.getBukkitEntity(), io.papermc.paper.event.player.PlayerBedFailEnterEvent.FailReason.values()[entityhuman_enumbedresult.ordinal()], org.bukkit.craftbukkit.block.CraftBlock.at(world, finalblockposition), !world.dimensionType().bedWorks(), io.papermc.paper.adventure.PaperAdventure.asAdventure(entityhuman_enumbedresult.getMessage())); ++ if (!event.callEvent()) { ++ return; ++ } ++ // Paper end - PlayerBedFailEnterEvent + // CraftBukkit start - handling bed explosion from below here +- if (!world.dimensionType().bedWorks()) { ++ if (event.getWillExplode()) { // Paper - PlayerBedFailEnterEvent + this.explodeBed(finaliblockdata, world, finalblockposition); + } else + // CraftBukkit end + if (entityhuman_enumbedresult.getMessage() != null) { +- player.displayClientMessage(entityhuman_enumbedresult.getMessage(), true); ++ final net.kyori.adventure.text.Component message = event.getMessage(); // Paper - PlayerBedFailEnterEvent ++ if (message != null) player.displayClientMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(message), true); // Paper - PlayerBedFailEnterEvent + } ++ } // Paper - PlayerBedFailEnterEvent + + }); + return InteractionResult.SUCCESS_SERVER; diff --git a/patches/server/0511-Implement-methods-to-convert-between-Component-and-B.patch b/patches/server/0511-Implement-methods-to-convert-between-Component-and-B.patch new file mode 100644 index 0000000000..c5765e9de7 --- /dev/null +++ b/patches/server/0511-Implement-methods-to-convert-between-Component-and-B.patch @@ -0,0 +1,55 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jason Penilla <[email protected]> +Date: Sat, 24 Apr 2021 02:09:32 -0700 +Subject: [PATCH] Implement methods to convert between Component and + Brigadier's Message + + +diff --git a/src/main/java/io/papermc/paper/brigadier/PaperBrigadierProviderImpl.java b/src/main/java/io/papermc/paper/brigadier/PaperBrigadierProviderImpl.java +new file mode 100644 +index 0000000000000000000000000000000000000000..dd6012b6a097575b2d1471be5069eccee4537c0a +--- /dev/null ++++ b/src/main/java/io/papermc/paper/brigadier/PaperBrigadierProviderImpl.java +@@ -0,0 +1,30 @@ ++package io.papermc.paper.brigadier; ++ ++import com.mojang.brigadier.Message; ++import io.papermc.paper.adventure.PaperAdventure; ++import net.kyori.adventure.text.Component; ++import net.kyori.adventure.text.ComponentLike; ++import net.minecraft.network.chat.ComponentUtils; ++import org.checkerframework.checker.nullness.qual.NonNull; ++ ++import static java.util.Objects.requireNonNull; ++ ++public enum PaperBrigadierProviderImpl implements PaperBrigadierProvider { ++ INSTANCE; ++ ++ PaperBrigadierProviderImpl() { ++ PaperBrigadierProvider.initialize(this); ++ } ++ ++ @Override ++ public @NonNull Message message(final @NonNull ComponentLike componentLike) { ++ requireNonNull(componentLike, "componentLike"); ++ return PaperAdventure.asVanilla(componentLike.asComponent()); ++ } ++ ++ @Override ++ public @NonNull Component componentFromMessage(final @NonNull Message message) { ++ requireNonNull(message, "message"); ++ return PaperAdventure.asAdventure(ComponentUtils.fromMessage(message)); ++ } ++} +diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +index 7c92b2f0a59fe222ad13a998476e312bf571a1bf..761663514b98a1c0a0a905150411aff450a0cc50 100644 +--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java ++++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +@@ -224,6 +224,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface + io.papermc.paper.command.PaperCommands.registerCommands(this); // Paper - setup /paper command + com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); // Paper - start metrics + com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // Paper - load version history now ++ io.papermc.paper.brigadier.PaperBrigadierProviderImpl.INSTANCE.getClass(); // Paper - init PaperBrigadierProvider + + this.setPvpAllowed(dedicatedserverproperties.pvp); + this.setFlightAllowed(dedicatedserverproperties.allowFlight); diff --git a/patches/server/0512-Expand-PlayerRespawnEvent-fix-passed-parameter-issue.patch b/patches/server/0512-Expand-PlayerRespawnEvent-fix-passed-parameter-issue.patch new file mode 100644 index 0000000000..017cc4e93e --- /dev/null +++ b/patches/server/0512-Expand-PlayerRespawnEvent-fix-passed-parameter-issue.patch @@ -0,0 +1,26 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: HexedHero <[email protected]> +Date: Fri, 23 Apr 2021 22:42:42 +0100 +Subject: [PATCH] Expand PlayerRespawnEvent, fix passed parameter issues + +Co-authored-by: Jake Potrebic <[email protected]> + +diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java +index f046b8e6307079878d94eab615a5463a807e6c6c..c22dce8b30c8af3e37b497b0d97c6ceef0295883 100644 +--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java ++++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java +@@ -1447,7 +1447,13 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player { + Player respawnPlayer = this.getBukkitEntity(); + Location location = CraftLocation.toBukkit(teleportTransition.position(), teleportTransition.newLevel().getWorld(), teleportTransition.yRot(), teleportTransition.xRot()); + +- PlayerRespawnEvent respawnEvent = new PlayerRespawnEvent(respawnPlayer, location, isBedSpawn, isAnchorSpawn, reason); ++ // Paper start - respawn flags ++ com.google.common.collect.ImmutableSet.Builder<org.bukkit.event.player.PlayerRespawnEvent.RespawnFlag> builder = com.google.common.collect.ImmutableSet.builder(); ++ if (reason == org.bukkit.event.player.PlayerRespawnEvent.RespawnReason.END_PORTAL) { ++ builder.add(org.bukkit.event.player.PlayerRespawnEvent.RespawnFlag.END_PORTAL); ++ } ++ PlayerRespawnEvent respawnEvent = new PlayerRespawnEvent(respawnPlayer, location, isBedSpawn, isAnchorSpawn, reason, builder); ++ // Paper end - respawn flags + this.level().getCraftServer().getPluginManager().callEvent(respawnEvent); + // Spigot Start + if (this.connection.isDisconnected()) { diff --git a/patches/server/0513-Introduce-beacon-activation-deactivation-events.patch b/patches/server/0513-Introduce-beacon-activation-deactivation-events.patch new file mode 100644 index 0000000000..336acbe811 --- /dev/null +++ b/patches/server/0513-Introduce-beacon-activation-deactivation-events.patch @@ -0,0 +1,37 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spyridon Pagkalos <[email protected]> +Date: Thu, 25 Mar 2021 20:28:04 +0200 +Subject: [PATCH] Introduce beacon activation/deactivation events + + +diff --git a/src/main/java/net/minecraft/world/level/block/entity/BeaconBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BeaconBlockEntity.java +index dc3171b1493d7c4c8ddf1c79587c4e27bd819c17..8aab6f68f576fb022eb59798585e264f5aafbc69 100644 +--- a/src/main/java/net/minecraft/world/level/block/entity/BeaconBlockEntity.java ++++ b/src/main/java/net/minecraft/world/level/block/entity/BeaconBlockEntity.java +@@ -225,6 +225,15 @@ public class BeaconBlockEntity extends BlockEntity implements MenuProvider, Name + BeaconBlockEntity.playSound(world, pos, SoundEvents.BEACON_AMBIENT); + } + } ++ // Paper start - beacon activation/deactivation events ++ if (i1 <= 0 && blockEntity.levels > 0) { ++ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(world, pos); ++ new io.papermc.paper.event.block.BeaconActivatedEvent(block).callEvent(); ++ } else if (i1 > 0 && blockEntity.levels <= 0) { ++ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(world, pos); ++ new io.papermc.paper.event.block.BeaconDeactivatedEvent(block).callEvent(); ++ } ++ // Paper end - beacon activation/deactivation events + + if (blockEntity.lastCheckY >= l) { + blockEntity.lastCheckY = world.getMinY() - 1; +@@ -282,6 +291,10 @@ public class BeaconBlockEntity extends BlockEntity implements MenuProvider, Name + + @Override + public void setRemoved() { ++ // Paper start - beacon activation/deactivation events ++ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, worldPosition); ++ new io.papermc.paper.event.block.BeaconDeactivatedEvent(block).callEvent(); ++ // Paper end - beacon activation/deactivation events + BeaconBlockEntity.playSound(this.level, this.worldPosition, SoundEvents.BEACON_DEACTIVATE); + super.setRemoved(); + } diff --git a/patches/server/0514-Add-Channel-initialization-listeners.patch b/patches/server/0514-Add-Channel-initialization-listeners.patch new file mode 100644 index 0000000000..430324c8f2 --- /dev/null +++ b/patches/server/0514-Add-Channel-initialization-listeners.patch @@ -0,0 +1,155 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Nassim Jahnke <[email protected]> +Date: Thu, 29 Apr 2021 21:19:33 +0200 +Subject: [PATCH] Add Channel initialization listeners + + +diff --git a/src/main/java/io/papermc/paper/network/ChannelInitializeListener.java b/src/main/java/io/papermc/paper/network/ChannelInitializeListener.java +new file mode 100644 +index 0000000000000000000000000000000000000000..88099df34c2d74daba9645aadf65b446ca795a91 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/network/ChannelInitializeListener.java +@@ -0,0 +1,15 @@ ++package io.papermc.paper.network; ++ ++import io.netty.channel.Channel; ++import org.checkerframework.checker.nullness.qual.NonNull; ++ ++/** ++ * Internal API to register channel initialization listeners. ++ * <p> ++ * This is not officially supported API and we make no guarantees to the existence or state of this interface. ++ */ ++@FunctionalInterface ++public interface ChannelInitializeListener { ++ ++ void afterInitChannel(@NonNull Channel channel); ++} +diff --git a/src/main/java/io/papermc/paper/network/ChannelInitializeListenerHolder.java b/src/main/java/io/papermc/paper/network/ChannelInitializeListenerHolder.java +new file mode 100644 +index 0000000000000000000000000000000000000000..30e62719e0a83525daa33cf41cb61df360c0e046 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/network/ChannelInitializeListenerHolder.java +@@ -0,0 +1,74 @@ ++package io.papermc.paper.network; ++ ++import io.netty.channel.Channel; ++import net.kyori.adventure.key.Key; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.checker.nullness.qual.Nullable; ++ ++import java.util.Collections; ++import java.util.HashMap; ++import java.util.Map; ++ ++/** ++ * Internal API to register channel initialization listeners. ++ * <p> ++ * This is not officially supported API and we make no guarantees to the existence or state of this class. ++ */ ++public final class ChannelInitializeListenerHolder { ++ ++ private static final Map<Key, ChannelInitializeListener> LISTENERS = new HashMap<>(); ++ private static final Map<Key, ChannelInitializeListener> IMMUTABLE_VIEW = Collections.unmodifiableMap(LISTENERS); ++ ++ private ChannelInitializeListenerHolder() { ++ } ++ ++ /** ++ * Registers whether an initialization listener is registered under the given key. ++ * ++ * @param key key ++ * @return whether an initialization listener is registered under the given key ++ */ ++ public static boolean hasListener(@NonNull Key key) { ++ return LISTENERS.containsKey(key); ++ } ++ ++ /** ++ * Registers a channel initialization listener called after ServerConnection is initialized. ++ * ++ * @param key key ++ * @param listener initialization listeners ++ */ ++ public static void addListener(@NonNull Key key, @NonNull ChannelInitializeListener listener) { ++ LISTENERS.put(key, listener); ++ } ++ ++ /** ++ * Removes and returns an initialization listener registered by the given key if present. ++ * ++ * @param key key ++ * @return removed initialization listener if present ++ */ ++ public static @Nullable ChannelInitializeListener removeListener(@NonNull Key key) { ++ return LISTENERS.remove(key); ++ } ++ ++ /** ++ * Returns an immutable map of registered initialization listeners. ++ * ++ * @return immutable map of registered initialization listeners ++ */ ++ public static @NonNull Map<Key, ChannelInitializeListener> getListeners() { ++ return IMMUTABLE_VIEW; ++ } ++ ++ /** ++ * Calls the registered listeners with the given channel. ++ * ++ * @param channel channel ++ */ ++ public static void callListeners(@NonNull Channel channel) { ++ for (ChannelInitializeListener listener : LISTENERS.values()) { ++ listener.afterInitChannel(channel); ++ } ++ } ++} +diff --git a/src/main/java/io/papermc/paper/network/ConnectionEvent.java b/src/main/java/io/papermc/paper/network/ConnectionEvent.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0d7e7db9e37ef0183c32b217bd944fb4f41ab83a +--- /dev/null ++++ b/src/main/java/io/papermc/paper/network/ConnectionEvent.java +@@ -0,0 +1,10 @@ ++package io.papermc.paper.network; ++ ++/** ++ * Internal connection pipeline events. ++ */ ++public enum ConnectionEvent { ++ ++ COMPRESSION_THRESHOLD_SET, ++ COMPRESSION_DISABLED ++} +diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java +index 448bde861b4190a678a8ccb63cade43c4a3d6ad9..f5fe87103ce53ba07b43c6c10fc9dafcc5ea4958 100644 +--- a/src/main/java/net/minecraft/network/Connection.java ++++ b/src/main/java/net/minecraft/network/Connection.java +@@ -680,6 +680,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> { + } else { + this.channel.pipeline().addAfter("prepender", "compress", new CompressionEncoder(compressionThreshold)); + } ++ this.channel.pipeline().fireUserEventTriggered(io.papermc.paper.network.ConnectionEvent.COMPRESSION_THRESHOLD_SET); // Paper - Add Channel initialization listeners + } else { + if (this.channel.pipeline().get("decompress") instanceof CompressionDecoder) { + this.channel.pipeline().remove("decompress"); +@@ -688,6 +689,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> { + if (this.channel.pipeline().get("compress") instanceof CompressionEncoder) { + this.channel.pipeline().remove("compress"); + } ++ this.channel.pipeline().fireUserEventTriggered(io.papermc.paper.network.ConnectionEvent.COMPRESSION_DISABLED); // Paper - Add Channel initialization listeners + } + + } +diff --git a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java +index 64119f2188f8958b8a5913fec82ac5bba1b74b2a..d6d7f1c446ba5507f67038ff27775ba75156f4a7 100644 +--- a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java ++++ b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java +@@ -115,6 +115,7 @@ public class ServerConnectionListener { + pending.add(object); // Paper - prevent blocking on adding a new connection while the server is ticking + ((Connection) object).configurePacketHandler(channelpipeline); + ((Connection) object).setListenerForServerboundHandshake(new ServerHandshakePacketListenerImpl(ServerConnectionListener.this.server, (Connection) object)); ++ io.papermc.paper.network.ChannelInitializeListenerHolder.callListeners(channel); // Paper - Add Channel initialization listeners + } + }).group(eventloopgroup).localAddress(address, port)).option(ChannelOption.AUTO_READ, false).bind().syncUninterruptibly()); // CraftBukkit + } diff --git a/patches/server/0515-Send-empty-commands-if-tab-completion-is-disabled.patch b/patches/server/0515-Send-empty-commands-if-tab-completion-is-disabled.patch new file mode 100644 index 0000000000..35cf408aa7 --- /dev/null +++ b/patches/server/0515-Send-empty-commands-if-tab-completion-is-disabled.patch @@ -0,0 +1,24 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Shane Freeder <[email protected]> +Date: Mon, 26 Apr 2021 01:27:08 +0100 +Subject: [PATCH] Send empty commands if tab completion is disabled + + +diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java +index 2dcb1a1bdba1d1cad3caa117d85a8619ed9e67b4..d602c713696e23ba6a2d542b2e9e2cce46d79a66 100644 +--- a/src/main/java/net/minecraft/commands/Commands.java ++++ b/src/main/java/net/minecraft/commands/Commands.java +@@ -453,7 +453,12 @@ public class Commands { + } + + public void sendCommands(ServerPlayer player) { +- if ( org.spigotmc.SpigotConfig.tabComplete < 0 ) return; // Spigot ++ // Paper start - Send empty commands if tab completion is disabled ++ if (org.spigotmc.SpigotConfig.tabComplete < 0) { ++ player.connection.send(new ClientboundCommandsPacket(new RootCommandNode<>())); ++ return; ++ } ++ // Paper end - Send empty commands if tab completion is disabled + // CraftBukkit start + // Register Vanilla commands into builtRoot as before + // Paper start - Perf: Async command map building diff --git a/patches/server/0516-Add-more-WanderingTrader-API.patch b/patches/server/0516-Add-more-WanderingTrader-API.patch new file mode 100644 index 0000000000..4221f28015 --- /dev/null +++ b/patches/server/0516-Add-more-WanderingTrader-API.patch @@ -0,0 +1,65 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: HexedHero <[email protected]> +Date: Thu, 6 May 2021 14:56:43 +0100 +Subject: [PATCH] Add more WanderingTrader API + + +diff --git a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java +index 8034588a9a87b907c35e28e220280d463f34554e..a65fba5621c067c453858efb7fee64cbee1e7916 100644 +--- a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java ++++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java +@@ -62,6 +62,10 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill + @Nullable + private BlockPos wanderTarget; + private int despawnDelay; ++ // Paper start - Add more WanderingTrader API ++ public boolean canDrinkPotion = true; ++ public boolean canDrinkMilk = true; ++ // Paper end - Add more WanderingTrader API + + public WanderingTrader(EntityType<? extends WanderingTrader> type, Level world) { + super(type, world); +@@ -72,10 +76,10 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill + protected void registerGoals() { + this.goalSelector.addGoal(0, new FloatGoal(this)); + this.goalSelector.addGoal(0, new UseItemGoal<>(this, PotionContents.createItemStack(Items.POTION, Potions.INVISIBILITY), SoundEvents.WANDERING_TRADER_DISAPPEARED, (entityvillagertrader) -> { +- return this.level().isNight() && !entityvillagertrader.isInvisible(); ++ return this.canDrinkPotion && this.level().isNight() && !entityvillagertrader.isInvisible(); // Paper - Add more WanderingTrader API + })); + this.goalSelector.addGoal(0, new UseItemGoal<>(this, new ItemStack(Items.MILK_BUCKET), SoundEvents.WANDERING_TRADER_REAPPEARED, (entityvillagertrader) -> { +- return this.level().isDay() && entityvillagertrader.isInvisible(); ++ return this.canDrinkMilk && this.level().isDay() && entityvillagertrader.isInvisible(); // Paper - Add more WanderingTrader API + })); + this.goalSelector.addGoal(1, new TradeWithPlayerGoal(this)); + this.goalSelector.addGoal(1, new AvoidEntityGoal<>(this, Zombie.class, 8.0F, 0.5D, 0.5D)); +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftWanderingTrader.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftWanderingTrader.java +index 08194a78c2170e971ee8ff440b276ed3590e8c4a..0e597394a3dd08f022614fc9777302fea581eb55 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftWanderingTrader.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWanderingTrader.java +@@ -28,4 +28,26 @@ public class CraftWanderingTrader extends CraftAbstractVillager implements Wande + public void setDespawnDelay(int despawnDelay) { + this.getHandle().setDespawnDelay(despawnDelay); + } ++ ++ // Paper start - Add more WanderingTrader API ++ @Override ++ public void setCanDrinkPotion(boolean bool) { ++ getHandle().canDrinkPotion = bool; ++ } ++ ++ @Override ++ public boolean canDrinkPotion() { ++ return getHandle().canDrinkPotion; ++ } ++ ++ @Override ++ public void setCanDrinkMilk(boolean bool) { ++ getHandle().canDrinkMilk = bool; ++ } ++ ++ @Override ++ public boolean canDrinkMilk() { ++ return getHandle().canDrinkMilk; ++ } ++ // Paper end + } diff --git a/patches/server/0517-Add-EntityBlockStorage-clearEntities.patch b/patches/server/0517-Add-EntityBlockStorage-clearEntities.patch new file mode 100644 index 0000000000..51ffd96ff7 --- /dev/null +++ b/patches/server/0517-Add-EntityBlockStorage-clearEntities.patch @@ -0,0 +1,38 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Owen1212055 <[email protected]> +Date: Mon, 5 Apr 2021 18:12:29 -0400 +Subject: [PATCH] Add EntityBlockStorage#clearEntities() + + +diff --git a/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java +index 333189cf01ce571993e8152f5851b8c362ba4b70..801528022c902714138c264bc4d05ef0df85912e 100644 +--- a/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java ++++ b/src/main/java/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java +@@ -152,6 +152,11 @@ public class BeehiveBlockEntity extends BlockEntity { + return this.stored.size(); + } + ++ // Paper start - Add EntityBlockStorage clearEntities ++ public void clearBees() { ++ this.stored.clear(); ++ } ++ // Paper end - Add EntityBlockStorage clearEntities + public static int getHoneyLevel(BlockState state) { + return (Integer) state.getValue(BeehiveBlock.HONEY_LEVEL); + } +diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java +index fbacbef0dd51037fa0af56fedeea50a5c7bed8c7..1a2a05160ba51d9c75f1ae6ae61d944d81428722 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBeehive.java +@@ -95,4 +95,11 @@ public class CraftBeehive extends CraftBlockEntityState<BeehiveBlockEntity> impl + public CraftBeehive copy(Location location) { + return new CraftBeehive(this, location); + } ++ ++ // Paper start - Add EntityBlockStorage clearEntities ++ @Override ++ public void clearEntities() { ++ getSnapshot().clearBees(); ++ } ++ // Paper end + } diff --git a/patches/server/0518-Add-Adventure-message-to-PlayerAdvancementDoneEvent.patch b/patches/server/0518-Add-Adventure-message-to-PlayerAdvancementDoneEvent.patch new file mode 100644 index 0000000000..9df7276feb --- /dev/null +++ b/patches/server/0518-Add-Adventure-message-to-PlayerAdvancementDoneEvent.patch @@ -0,0 +1,35 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Alvinn8 <[email protected]> +Date: Fri, 8 Jan 2021 20:31:13 +0100 +Subject: [PATCH] Add Adventure message to PlayerAdvancementDoneEvent + + +diff --git a/src/main/java/net/minecraft/server/PlayerAdvancements.java b/src/main/java/net/minecraft/server/PlayerAdvancements.java +index 0fe941b0802f2966ad55509baac124f56ecef999..1dcb8a287be7df2a59b5b4c1345be80637a7f679 100644 +--- a/src/main/java/net/minecraft/server/PlayerAdvancements.java ++++ b/src/main/java/net/minecraft/server/PlayerAdvancements.java +@@ -236,11 +236,21 @@ public class PlayerAdvancements { + this.progressChanged.add(advancement); + flag = true; + if (!flag1 && advancementprogress.isDone()) { +- this.player.level().getCraftServer().getPluginManager().callEvent(new org.bukkit.event.player.PlayerAdvancementDoneEvent(this.player.getBukkitEntity(), advancement.toBukkit())); // CraftBukkit ++ // Paper start - Add Adventure message to PlayerAdvancementDoneEvent ++ final net.kyori.adventure.text.Component message = advancement.value().display().flatMap(info -> { ++ return java.util.Optional.ofNullable( ++ info.shouldAnnounceChat() ? io.papermc.paper.adventure.PaperAdventure.asAdventure(info.getType().createAnnouncement(advancement, this.player)) : null ++ ); ++ }).orElse(null); ++ final org.bukkit.event.player.PlayerAdvancementDoneEvent event = new org.bukkit.event.player.PlayerAdvancementDoneEvent(this.player.getBukkitEntity(), advancement.toBukkit(), message); ++ this.player.level().getCraftServer().getPluginManager().callEvent(event); // CraftBukkit ++ // Paper end + advancement.value().rewards().grant(this.player); + advancement.value().display().ifPresent((advancementdisplay) -> { +- if (advancementdisplay.shouldAnnounceChat() && this.player.serverLevel().getGameRules().getBoolean(GameRules.RULE_ANNOUNCE_ADVANCEMENTS)) { +- this.playerList.broadcastSystemMessage(advancementdisplay.getType().createAnnouncement(advancement, this.player), false); ++ // Paper start - Add Adventure message to PlayerAdvancementDoneEvent ++ if (event.message() != null && this.player.serverLevel().getGameRules().getBoolean(GameRules.RULE_ANNOUNCE_ADVANCEMENTS)) { ++ this.playerList.broadcastSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(event.message()), false); ++ // Paper end + } + + }); diff --git a/patches/server/0519-Add-HiddenPotionEffect-API.patch b/patches/server/0519-Add-HiddenPotionEffect-API.patch new file mode 100644 index 0000000000..2d0b0d5b03 --- /dev/null +++ b/patches/server/0519-Add-HiddenPotionEffect-API.patch @@ -0,0 +1,29 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Tamion <[email protected]> +Date: Sun, 5 Nov 2023 09:51:28 +0100 +Subject: [PATCH] Add HiddenPotionEffect API + +== AT == +public net.minecraft.world.effect.MobEffectInstance hiddenEffect + +diff --git a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionUtil.java b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionUtil.java +index 01af4db5d0f17ea2943e5c464d4122a358503bc1..cb11f0624e4e65aa06bfaaec90729ee536cd53a0 100644 +--- a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionUtil.java ++++ b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionUtil.java +@@ -78,6 +78,7 @@ public class CraftPotionUtil { + + public static MobEffectInstance fromBukkit(PotionEffect effect) { + Holder<MobEffect> type = CraftPotionEffectType.bukkitToMinecraftHolder(effect.getType()); ++ // Paper - Note: do not copy over the hidden effect, as this method is only used for applying to entities which we do not want to convert over. + return new MobEffectInstance(type, effect.getDuration(), effect.getAmplifier(), effect.isAmbient(), effect.hasParticles(), effect.hasIcon()); // Paper + } + +@@ -87,7 +88,7 @@ public class CraftPotionUtil { + int duration = effect.getDuration(); + boolean ambient = effect.isAmbient(); + boolean particles = effect.isVisible(); +- return new PotionEffect(type, duration, amp, ambient, particles, effect.showIcon()); // Paper ++ return new PotionEffect(type, duration, amp, ambient, particles, effect.showIcon(), effect.hiddenEffect == null ? null : toBukkit(effect.hiddenEffect)); // Paper + } + + public static boolean equals(Holder<MobEffect> mobEffect, PotionEffectType type) { diff --git a/patches/server/0520-Inventory-close.patch b/patches/server/0520-Inventory-close.patch new file mode 100644 index 0000000000..9dcad3d0b1 --- /dev/null +++ b/patches/server/0520-Inventory-close.patch @@ -0,0 +1,25 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Tue, 11 May 2021 14:54:56 -0700 +Subject: [PATCH] Inventory#close + + +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java +index f3a1859a1c2adb0448186c322793585dafefe7e0..bc5ec42a54401a2275c05f0f506ba89f00c19ec9 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java +@@ -439,6 +439,14 @@ public class CraftInventory implements Inventory { + this.clear(i); + } + } ++ // Paper start ++ @Override ++ public int close() { ++ int count = this.inventory.getViewers().size(); ++ com.google.common.collect.Lists.newArrayList(this.inventory.getViewers()).forEach(HumanEntity::closeInventory); ++ return count; ++ } ++ // Paper end + + @Override + public ListIterator<ItemStack> iterator() { diff --git a/patches/server/0521-Add-a-should-burn-in-sunlight-API-for-Phantoms-and-S.patch b/patches/server/0521-Add-a-should-burn-in-sunlight-API-for-Phantoms-and-S.patch new file mode 100644 index 0000000000..cf84510891 --- /dev/null +++ b/patches/server/0521-Add-a-should-burn-in-sunlight-API-for-Phantoms-and-S.patch @@ -0,0 +1,130 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: MeFisto94 <[email protected]> +Date: Tue, 11 May 2021 00:48:33 +0200 +Subject: [PATCH] Add a "should burn in sunlight" API for Phantoms and + Skeletons + + +diff --git a/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java b/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java +index 7c80c79a87ec438d0891c5c977e162f272d80039..cb89a95e6ff9db73c912bba04d27657683135153 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java ++++ b/src/main/java/net/minecraft/world/entity/monster/AbstractSkeleton.java +@@ -97,9 +97,15 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo + + abstract SoundEvent getStepSound(); + ++ // Paper start - shouldBurnInDay API ++ private boolean shouldBurnInDay = true; ++ public boolean shouldBurnInDay() { return shouldBurnInDay; } ++ public void setShouldBurnInDay(boolean shouldBurnInDay) { this.shouldBurnInDay = shouldBurnInDay; } ++ // Paper end - shouldBurnInDay API ++ + @Override + public void aiStep() { +- boolean flag = this.isSunBurnTick(); ++ boolean flag = shouldBurnInDay && this.isSunBurnTick(); // Paper - shouldBurnInDay API + + if (flag) { + ItemStack itemstack = this.getItemBySlot(EquipmentSlot.HEAD); +@@ -243,7 +249,20 @@ public abstract class AbstractSkeleton extends Monster implements RangedAttackMo + public void readAdditionalSaveData(CompoundTag nbt) { + super.readAdditionalSaveData(nbt); + this.reassessWeaponGoal(); ++ // Paper start - shouldBurnInDay API ++ if (nbt.contains("Paper.ShouldBurnInDay")) { ++ this.shouldBurnInDay = nbt.getBoolean("Paper.ShouldBurnInDay"); ++ } ++ // Paper end - shouldBurnInDay API ++ } ++ ++ // Paper start - shouldBurnInDay API ++ @Override ++ public void addAdditionalSaveData(CompoundTag nbt) { ++ super.addAdditionalSaveData(nbt); ++ nbt.putBoolean("Paper.ShouldBurnInDay", this.shouldBurnInDay); + } ++ // Paper end - shouldBurnInDay API + + @Override + public void setItemSlot(EquipmentSlot slot, ItemStack stack) { +diff --git a/src/main/java/net/minecraft/world/entity/monster/Phantom.java b/src/main/java/net/minecraft/world/entity/monster/Phantom.java +index 1171a4e45bed0455b29b2cf012fbc2883b16d061..4ff75412452649ebf106ef591cb97dc7ac8175e7 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/Phantom.java ++++ b/src/main/java/net/minecraft/world/entity/monster/Phantom.java +@@ -139,7 +139,7 @@ public class Phantom extends FlyingMob implements Enemy { + + @Override + public void aiStep() { +- if (this.isAlive() && this.isSunBurnTick()) { ++ if (this.isAlive() && this.shouldBurnInDay && this.isSunBurnTick()) { // Paper - shouldBurnInDay API + this.igniteForSeconds(8.0F); + } + +@@ -165,6 +165,9 @@ public class Phantom extends FlyingMob implements Enemy { + if (nbt.hasUUID("Paper.SpawningEntity")) { + this.spawningEntity = nbt.getUUID("Paper.SpawningEntity"); + } ++ if (nbt.contains("Paper.ShouldBurnInDay")) { ++ this.shouldBurnInDay = nbt.getBoolean("Paper.ShouldBurnInDay"); ++ } + // Paper end + } + +@@ -179,6 +182,7 @@ public class Phantom extends FlyingMob implements Enemy { + if (this.spawningEntity != null) { + nbt.putUUID("Paper.SpawningEntity", this.spawningEntity); + } ++ nbt.putBoolean("Paper.ShouldBurnInDay", shouldBurnInDay); + // Paper end + } + +@@ -238,6 +242,9 @@ public class Phantom extends FlyingMob implements Enemy { + return this.spawningEntity; + } + public void setSpawningEntity(java.util.UUID entity) { this.spawningEntity = entity; } ++ private boolean shouldBurnInDay = true; ++ public boolean shouldBurnInDay() { return shouldBurnInDay; } ++ public void setShouldBurnInDay(boolean shouldBurnInDay) { this.shouldBurnInDay = shouldBurnInDay; } + // Paper end + + private static enum AttackPhase { +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractSkeleton.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractSkeleton.java +index 5ff566186431440c25a26900aba14e4adb642031..5beaa2bb0d58fe477ce8d2de8b77600d3b416d8c 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractSkeleton.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractSkeleton.java +@@ -20,4 +20,15 @@ public abstract class CraftAbstractSkeleton extends CraftMonster implements Abst + return (net.minecraft.world.entity.monster.AbstractSkeleton) super.getHandle(); + } + // Paper end ++ // Paper start ++ @Override ++ public boolean shouldBurnInDay() { ++ return getHandle().shouldBurnInDay(); ++ } ++ ++ @Override ++ public void setShouldBurnInDay(boolean shouldBurnInDay) { ++ getHandle().setShouldBurnInDay(shouldBurnInDay); ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java +index 305a635b049741ac5e2670060c6818cb2c07e5ab..9304e201db1ec96d0916aa8ea781f3e4bc7991e6 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java +@@ -34,5 +34,15 @@ public class CraftPhantom extends CraftFlying implements Phantom, CraftEnemy { + public java.util.UUID getSpawningEntity() { + return getHandle().getSpawningEntity(); + } ++ ++ @Override ++ public boolean shouldBurnInDay() { ++ return getHandle().shouldBurnInDay(); ++ } ++ ++ @Override ++ public void setShouldBurnInDay(boolean shouldBurnInDay) { ++ getHandle().setShouldBurnInDay(shouldBurnInDay); ++ } + // Paper end + } diff --git a/patches/server/0522-Add-basic-Datapack-API.patch b/patches/server/0522-Add-basic-Datapack-API.patch new file mode 100644 index 0000000000..990697b9ea --- /dev/null +++ b/patches/server/0522-Add-basic-Datapack-API.patch @@ -0,0 +1,209 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Connor Linfoot <[email protected]> +Date: Sun, 16 May 2021 15:07:34 +0100 +Subject: [PATCH] Add basic Datapack API + +Co-authored-by: Jake Potrebic <[email protected]> + +diff --git a/src/main/java/io/papermc/paper/datapack/PaperDatapack.java b/src/main/java/io/papermc/paper/datapack/PaperDatapack.java +new file mode 100644 +index 0000000000000000000000000000000000000000..8bd8263b51fb2bb364353565b1ba26b3b0d1d55e +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datapack/PaperDatapack.java +@@ -0,0 +1,103 @@ ++package io.papermc.paper.datapack; ++ ++import io.papermc.paper.adventure.PaperAdventure; ++import io.papermc.paper.event.server.ServerResourcesReloadedEvent; ++import io.papermc.paper.world.flag.PaperFeatureFlagProviderImpl; ++import java.util.ArrayList; ++import java.util.List; ++import java.util.Map; ++import java.util.Set; ++import java.util.concurrent.ConcurrentHashMap; ++import net.kyori.adventure.text.Component; ++import net.minecraft.server.MinecraftServer; ++import net.minecraft.server.packs.repository.Pack; ++import net.minecraft.server.packs.repository.PackSource; ++import org.bukkit.FeatureFlag; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.checker.nullness.qual.Nullable; ++import org.checkerframework.framework.qual.DefaultQualifier; ++ ++@DefaultQualifier(NonNull.class) ++public class PaperDatapack implements Datapack { ++ ++ private static final Map<PackSource, DatapackSource> PACK_SOURCES = new ConcurrentHashMap<>(); ++ static { ++ PACK_SOURCES.put(PackSource.DEFAULT, DatapackSource.DEFAULT); ++ PACK_SOURCES.put(PackSource.BUILT_IN, DatapackSource.BUILT_IN); ++ PACK_SOURCES.put(PackSource.FEATURE, DatapackSource.FEATURE); ++ PACK_SOURCES.put(PackSource.WORLD, DatapackSource.WORLD); ++ PACK_SOURCES.put(PackSource.SERVER, DatapackSource.SERVER); ++ } ++ ++ private final Pack pack; ++ private final boolean enabled; ++ ++ PaperDatapack(final Pack pack, final boolean enabled) { ++ this.pack = pack; ++ this.enabled = enabled; ++ } ++ ++ @Override ++ public String getName() { ++ return this.pack.getId(); ++ } ++ ++ @Override ++ public Component getTitle() { ++ return PaperAdventure.asAdventure(this.pack.getTitle()); ++ } ++ ++ @Override ++ public Component getDescription() { ++ return PaperAdventure.asAdventure(this.pack.getDescription()); ++ } ++ ++ @Override ++ public boolean isRequired() { ++ return this.pack.isRequired(); ++ } ++ ++ @Override ++ public Compatibility getCompatibility() { ++ return Datapack.Compatibility.valueOf(this.pack.getCompatibility().name()); ++ } ++ ++ @Override ++ public Set<FeatureFlag> getRequiredFeatures() { ++ return PaperFeatureFlagProviderImpl.fromNms(this.pack.getRequestedFeatures()); ++ } ++ ++ @Override ++ public boolean isEnabled() { ++ return this.enabled; ++ } ++ ++ @Override ++ public void setEnabled(final boolean enabled) { ++ final MinecraftServer server = MinecraftServer.getServer(); ++ final List<Pack> enabledPacks = new ArrayList<>(server.getPackRepository().getSelectedPacks()); ++ final @Nullable Pack packToChange = server.getPackRepository().getPack(this.getName()); ++ if (packToChange == null) { ++ throw new IllegalStateException("Cannot toggle state of pack that doesn't exist: " + this.getName()); ++ } ++ if (enabled == enabledPacks.contains(packToChange)) { ++ return; ++ } ++ if (enabled) { ++ packToChange.getDefaultPosition().insert(enabledPacks, packToChange, Pack::selectionConfig, false); // modeled off the default /datapack enable logic ++ } else { ++ enabledPacks.remove(packToChange); ++ } ++ server.reloadResources(enabledPacks.stream().map(Pack::getId).toList(), ServerResourcesReloadedEvent.Cause.PLUGIN); ++ } ++ ++ @Override ++ public DatapackSource getSource() { ++ return PACK_SOURCES.computeIfAbsent(this.pack.location().source(), source -> new DatapackSourceImpl(source.toString())); ++ } ++ ++ @Override ++ public Component computeDisplayName() { ++ return PaperAdventure.asAdventure(this.pack.getChatLink(this.enabled)); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/datapack/PaperDatapackManager.java b/src/main/java/io/papermc/paper/datapack/PaperDatapackManager.java +new file mode 100644 +index 0000000000000000000000000000000000000000..caa41c525d2b36b5a9f9942380f06c97d5781a93 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/datapack/PaperDatapackManager.java +@@ -0,0 +1,55 @@ ++package io.papermc.paper.datapack; ++ ++import com.google.common.collect.Collections2; ++import java.util.Collection; ++import java.util.Collections; ++import java.util.function.Predicate; ++import net.minecraft.server.packs.repository.Pack; ++import net.minecraft.server.packs.repository.PackRepository; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.checker.nullness.qual.Nullable; ++import org.checkerframework.framework.qual.DefaultQualifier; ++ ++@DefaultQualifier(NonNull.class) ++public class PaperDatapackManager implements DatapackManager { ++ ++ private final PackRepository repository; ++ ++ public PaperDatapackManager(final PackRepository repository) { ++ this.repository = repository; ++ } ++ ++ @Override ++ public void refreshPacks() { ++ this.repository.reload(); ++ } ++ ++ @Override ++ public @Nullable Datapack getPack(final @NonNull String name) { ++ final @Nullable Pack pack = this.repository.getPack(name); ++ if (pack == null) { ++ return null; ++ } ++ return new PaperDatapack(pack, this.repository.getSelectedPacks().contains(pack)); ++ } ++ ++ @Override ++ public Collection<Datapack> getPacks() { ++ final Collection<Pack> enabledPacks = this.repository.getSelectedPacks(); ++ return this.transformPacks(this.repository.getAvailablePacks(), enabledPacks::contains); ++ } ++ ++ @Override ++ public Collection<Datapack> getEnabledPacks() { ++ return this.transformPacks(this.repository.getSelectedPacks(), pack -> true); ++ } ++ ++ private Collection<Datapack> transformPacks(final Collection<Pack> packs, final Predicate<Pack> enabled) { ++ return Collections.unmodifiableCollection( ++ Collections2.transform( ++ packs, ++ pack -> new PaperDatapack(pack, enabled.test(pack)) ++ ) ++ ); ++ } ++} +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index 3cfacacd1d8fd17ec4b54936afd8124823b1a00b..b4af066a21e4893b5ec146d109b5146b6996a0cf 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -308,6 +308,7 @@ public final class CraftServer implements Server { + private final List<CraftPlayer> playerView; + public int reloadCount; + public Set<String> activeCompatibilities = Collections.emptySet(); ++ private final io.papermc.paper.datapack.PaperDatapackManager datapackManager; // Paper + public static Exception excessiveVelEx; // Paper - Velocity warnings + + static { +@@ -392,6 +393,7 @@ public final class CraftServer implements Server { + if (this.configuration.getBoolean("settings.use-map-color-cache")) { + MapPalette.setMapColorCache(new CraftMapColorCache(this.logger)); + } ++ datapackManager = new io.papermc.paper.datapack.PaperDatapackManager(console.getPackRepository()); // Paper + } + + public boolean getCommandBlockOverride(String command) { +@@ -3036,5 +3038,11 @@ public final class CraftServer implements Server { + public com.destroystokyo.paper.entity.ai.MobGoals getMobGoals() { + return mobGoals; + } ++ ++ @Override ++ public io.papermc.paper.datapack.PaperDatapackManager getDatapackManager() { ++ return datapackManager; ++ } ++ + // Paper end + } diff --git a/patches/server/0523-Add-environment-variable-to-disable-server-gui.patch b/patches/server/0523-Add-environment-variable-to-disable-server-gui.patch new file mode 100644 index 0000000000..186cfa260f --- /dev/null +++ b/patches/server/0523-Add-environment-variable-to-disable-server-gui.patch @@ -0,0 +1,18 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Riley Park <[email protected]> +Date: Mon, 17 May 2021 00:34:55 -0700 +Subject: [PATCH] Add environment variable to disable server gui + + +diff --git a/src/main/java/net/minecraft/server/Main.java b/src/main/java/net/minecraft/server/Main.java +index da61f3a4ec9bb9726d9d45552b8eada3f87d9e58..c551f6827476b6432ebe1d48e7ca5d168df305c5 100644 +--- a/src/main/java/net/minecraft/server/Main.java ++++ b/src/main/java/net/minecraft/server/Main.java +@@ -320,6 +320,7 @@ public class Main { + */ + boolean flag2 = !optionset.has("nogui") && !optionset.nonOptionArguments().contains("nogui"); + ++ if(!Boolean.parseBoolean(System.getenv().getOrDefault("PAPER_DISABLE_SERVER_GUI", String.valueOf(false)))) // Paper - Add environment variable to disable server gui + if (flag2 && !GraphicsEnvironment.isHeadless()) { + dedicatedserver1.showGui(); + } diff --git a/patches/server/0524-Expand-PlayerGameModeChangeEvent.patch b/patches/server/0524-Expand-PlayerGameModeChangeEvent.patch new file mode 100644 index 0000000000..065d67fdf4 --- /dev/null +++ b/patches/server/0524-Expand-PlayerGameModeChangeEvent.patch @@ -0,0 +1,161 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sat, 15 May 2021 10:04:43 -0700 +Subject: [PATCH] Expand PlayerGameModeChangeEvent + + +diff --git a/src/main/java/net/minecraft/server/commands/DefaultGameModeCommands.java b/src/main/java/net/minecraft/server/commands/DefaultGameModeCommands.java +index b95a979f8f58f43e0becedcd843ff8bb992eb455..a046a0b1519806ff3d987e6402f152b60a3a6f4c 100644 +--- a/src/main/java/net/minecraft/server/commands/DefaultGameModeCommands.java ++++ b/src/main/java/net/minecraft/server/commands/DefaultGameModeCommands.java +@@ -28,9 +28,13 @@ public class DefaultGameModeCommands { + GameType gameType = minecraftServer.getForcedGameType(); + if (gameType != null) { + for (ServerPlayer serverPlayer : minecraftServer.getPlayerList().getPlayers()) { +- if (serverPlayer.setGameMode(gameType)) { +- i++; ++ // Paper start - Expand PlayerGameModeChangeEvent ++ org.bukkit.event.player.PlayerGameModeChangeEvent event = serverPlayer.setGameMode(gameType, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.DEFAULT_GAMEMODE, net.kyori.adventure.text.Component.empty()); ++ if (event != null && event.isCancelled()) { ++ source.sendSuccess(() -> io.papermc.paper.adventure.PaperAdventure.asVanilla(event.cancelMessage()), false); + } ++ // Paper end - Expand PlayerGameModeChangeEvent ++ i++; + } + } + +diff --git a/src/main/java/net/minecraft/server/commands/GameModeCommand.java b/src/main/java/net/minecraft/server/commands/GameModeCommand.java +index 7f09119bc7d661e08a960dd2bd46006efe752d3e..d1da3600dc07107309b20ebe6e7c0c4da0e8de76 100644 +--- a/src/main/java/net/minecraft/server/commands/GameModeCommand.java ++++ b/src/main/java/net/minecraft/server/commands/GameModeCommand.java +@@ -60,9 +60,14 @@ public class GameModeCommand { + int i = 0; + + for (ServerPlayer serverPlayer : targets) { +- if (serverPlayer.setGameMode(gameMode)) { ++ // Paper start - Expand PlayerGameModeChangeEvent ++ org.bukkit.event.player.PlayerGameModeChangeEvent event = serverPlayer.setGameMode(gameMode, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.COMMAND, net.kyori.adventure.text.Component.empty()); ++ if (event != null && !event.isCancelled()) { + logGamemodeChange(context.getSource(), serverPlayer, gameMode); + i++; ++ } else if (event != null && event.cancelMessage() != null) { ++ context.getSource().sendSuccess(() -> io.papermc.paper.adventure.PaperAdventure.asVanilla(event.cancelMessage()), true); ++ // Paper end - Expand PlayerGameModeChangeEvent + } + } + +diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java +index c22dce8b30c8af3e37b497b0d97c6ceef0295883..2125f9dc87dbf31a66004dc859788f584e099e73 100644 +--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java ++++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java +@@ -2315,10 +2315,18 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player { + } + + public boolean setGameMode(GameType gameMode) { ++ // Paper start - Expand PlayerGameModeChangeEvent ++ org.bukkit.event.player.PlayerGameModeChangeEvent event = this.setGameMode(gameMode, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.UNKNOWN, null); ++ return event == null ? false : event.isCancelled(); ++ } ++ @Nullable ++ public org.bukkit.event.player.PlayerGameModeChangeEvent setGameMode(GameType gameMode, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause cause, @Nullable net.kyori.adventure.text.Component message) { + boolean flag = this.isSpectator(); + +- if (!this.gameMode.changeGameModeForPlayer(gameMode)) { +- return false; ++ org.bukkit.event.player.PlayerGameModeChangeEvent event = this.gameMode.changeGameModeForPlayer(gameMode, cause, message); ++ if (event == null || event.isCancelled()) { ++ return null; ++ // Paper end - Expand PlayerGameModeChangeEvent + } else { + this.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.CHANGE_GAME_MODE, (float) gameMode.getId())); + if (gameMode == GameType.SPECTATOR) { +@@ -2334,7 +2342,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player { + + this.onUpdateAbilities(); + this.updateEffectVisibility(); +- return true; ++ return event; // Paper - Expand PlayerGameModeChangeEvent + } + } + +@@ -2783,6 +2791,16 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player { + } + + public void loadGameTypes(@Nullable CompoundTag nbt) { ++ // Paper start - Expand PlayerGameModeChangeEvent ++ if (this.server.getForcedGameType() != null && this.server.getForcedGameType() != ServerPlayer.readPlayerMode(nbt, "playerGameType")) { ++ if (new org.bukkit.event.player.PlayerGameModeChangeEvent(this.getBukkitEntity(), org.bukkit.GameMode.getByValue(this.server.getDefaultGameType().getId()), org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.DEFAULT_GAMEMODE, null).callEvent()) { ++ this.gameMode.setGameModeForPlayer(this.server.getForcedGameType(), GameType.DEFAULT_MODE); ++ } else { ++ this.gameMode.setGameModeForPlayer(ServerPlayer.readPlayerMode(nbt,"playerGameType"), ServerPlayer.readPlayerMode(nbt, "previousPlayerGameType")); ++ } ++ return; ++ } ++ // Paper end - Expand PlayerGameModeChangeEvent + this.gameMode.setGameModeForPlayer(this.calculateGameModeForNewPlayer(ServerPlayer.readPlayerMode(nbt, "playerGameType")), ServerPlayer.readPlayerMode(nbt, "previousPlayerGameType")); + } + +diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java +index 546be40a8e4470fb5a6686072cdd342cdaa6fe15..e000a918230187f6841b03b7b0dd73687f3cc15e 100644 +--- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java ++++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java +@@ -72,14 +72,21 @@ public class ServerPlayerGameMode { + } + + public boolean changeGameModeForPlayer(GameType gameMode) { ++ // Paper start - Expand PlayerGameModeChangeEvent ++ PlayerGameModeChangeEvent event = this.changeGameModeForPlayer(gameMode, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.UNKNOWN, null); ++ return event != null && event.isCancelled(); ++ } ++ @Nullable ++ public PlayerGameModeChangeEvent changeGameModeForPlayer(GameType gameMode, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause cause, @Nullable net.kyori.adventure.text.Component cancelMessage) { ++ // Paper end - Expand PlayerGameModeChangeEvent + if (gameMode == this.gameModeForPlayer) { +- return false; ++ return null; // Paper - Expand PlayerGameModeChangeEvent + } else { + // CraftBukkit start +- PlayerGameModeChangeEvent event = new PlayerGameModeChangeEvent(this.player.getBukkitEntity(), GameMode.getByValue(gameMode.getId())); ++ PlayerGameModeChangeEvent event = new PlayerGameModeChangeEvent(this.player.getBukkitEntity(), GameMode.getByValue(gameMode.getId()), cause, cancelMessage); // Paper + this.level.getCraftServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { +- return false; ++ return event; // Paper - Expand PlayerGameModeChangeEvent + } + // CraftBukkit end + this.setGameModeForPlayer(gameMode, this.previousGameModeForPlayer); +@@ -90,7 +97,7 @@ public class ServerPlayerGameMode { + this.player.resetCurrentImpulseContext(); + } + +- return true; ++ return event; // Paper - Expand PlayerGameModeChangeEvent + } + } + +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index 47f925707d8756796ce169b7cbd8f2410b0b7104..f97bbed2b0098fb9b9b0a9e7c2a124e1370608cc 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -2818,7 +2818,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + this.player = this.server.getPlayerList().respawn(this.player, false, Entity.RemovalReason.KILLED, RespawnReason.DEATH); // CraftBukkit + this.resetPosition(); + if (this.server.isHardcore()) { +- this.player.setGameMode(GameType.SPECTATOR); ++ this.player.setGameMode(GameType.SPECTATOR, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.HARDCORE_DEATH, null); // Paper - Expand PlayerGameModeChangeEvent + ((GameRules.BooleanValue) this.player.serverLevel().getGameRules().getRule(GameRules.RULE_SPECTATORSGENERATECHUNKS)).set(false, this.player.serverLevel()); // CraftBukkit - per-world + } + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +index c93ec7e97c9af44ed75e7ea4fb1c6c58c2b6bc1a..0b151a66d7419653088526bd72119ebd2d6dd18e 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -1671,7 +1671,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + Preconditions.checkArgument(mode != null, "GameMode cannot be null"); + if (this.getHandle().connection == null) return; + +- this.getHandle().setGameMode(GameType.byId(mode.getValue())); ++ this.getHandle().setGameMode(GameType.byId(mode.getValue()), org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.PLUGIN, null); // Paper - Expand PlayerGameModeChangeEvent + } + + @Override diff --git a/patches/server/0525-ItemStack-repair-check-API.patch b/patches/server/0525-ItemStack-repair-check-API.patch new file mode 100644 index 0000000000..33c7e11836 --- /dev/null +++ b/patches/server/0525-ItemStack-repair-check-API.patch @@ -0,0 +1,72 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sat, 15 May 2021 22:11:11 -0700 +Subject: [PATCH] ItemStack repair check API + + +diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +index 30f2ddb66385718304b4b0302f7efcafd54bc42a..fa4e8cb2d53e0ac4e15a8188454ceed0afafe503 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +@@ -548,6 +548,14 @@ public final class CraftMagicNumbers implements UnsafeValues { + public int getProtocolVersion() { + return net.minecraft.SharedConstants.getCurrentVersion().getProtocolVersion(); + } ++ ++ @Override ++ public boolean isValidRepairItemStack(org.bukkit.inventory.ItemStack itemToBeRepaired, org.bukkit.inventory.ItemStack repairMaterial) { ++ if (!itemToBeRepaired.getType().isItem() || !repairMaterial.getType().isItem()) { ++ return false; ++ } ++ return CraftItemStack.unwrap(itemToBeRepaired).isValidRepairItem(CraftItemStack.unwrap(repairMaterial)); ++ } + // Paper end + + /** +diff --git a/src/test/java/io/papermc/paper/util/ItemStackRepairCheckTest.java b/src/test/java/io/papermc/paper/util/ItemStackRepairCheckTest.java +new file mode 100644 +index 0000000000000000000000000000000000000000..04e2568816f1fbe090b40e5a55d8d4effc045740 +--- /dev/null ++++ b/src/test/java/io/papermc/paper/util/ItemStackRepairCheckTest.java +@@ -0,0 +1,41 @@ ++package io.papermc.paper.util; ++ ++import org.bukkit.Material; ++import org.bukkit.inventory.ItemStack; ++import org.bukkit.support.environment.VanillaFeature; ++import org.junit.jupiter.api.Test; ++ ++import static org.junit.jupiter.api.Assertions.assertFalse; ++import static org.junit.jupiter.api.Assertions.assertTrue; ++ ++@VanillaFeature ++public class ItemStackRepairCheckTest { ++ ++ @Test ++ public void testIsRepariableBy() { ++ ItemStack diamondPick = new ItemStack(Material.DIAMOND_PICKAXE); ++ ++ assertTrue(diamondPick.isRepairableBy(new ItemStack(Material.DIAMOND)), "diamond pick isn't repairable by a diamond"); ++ } ++ ++ @Test ++ public void testCanRepair() { ++ ItemStack diamond = new ItemStack(Material.DIAMOND); ++ ++ assertTrue(diamond.canRepair(new ItemStack(Material.DIAMOND_AXE)), "diamond can't repair a diamond axe"); ++ } ++ ++ @Test ++ public void testIsNotRepairableBy() { ++ ItemStack notDiamondPick = new ItemStack(Material.ACACIA_SAPLING); ++ ++ assertFalse(notDiamondPick.isRepairableBy(new ItemStack(Material.DIAMOND)), "acacia sapling is repairable by a diamond"); ++ } ++ ++ @Test ++ public void testCanNotRepair() { ++ ItemStack diamond = new ItemStack(Material.DIAMOND); ++ ++ assertFalse(diamond.canRepair(new ItemStack(Material.OAK_BUTTON)), "diamond can repair oak button"); ++ } ++} diff --git a/patches/server/0526-More-Enchantment-API.patch b/patches/server/0526-More-Enchantment-API.patch new file mode 100644 index 0000000000..a3bf8f8e7e --- /dev/null +++ b/patches/server/0526-More-Enchantment-API.patch @@ -0,0 +1,105 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Thu, 6 May 2021 19:57:58 -0700 +Subject: [PATCH] More Enchantment API + +== AT == +public net.minecraft.world.item.enchantment.Enchantment definition + +Co-authored-by: Luis <[email protected]> +Co-authored-by: Janet Blackquill <[email protected]> + +diff --git a/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java b/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java +index 59c9c970b83f62245d860994c4ac0c21dcc15398..4221a1e9cba35c8dc58e51e162e7fcbd0e8b31af 100644 +--- a/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java ++++ b/src/main/java/org/bukkit/craftbukkit/enchantments/CraftEnchantment.java +@@ -5,6 +5,7 @@ import java.util.Locale; + import net.minecraft.Util; + import net.minecraft.core.Holder; + import net.minecraft.core.registries.Registries; ++import net.minecraft.network.chat.contents.TranslatableContents; + import net.minecraft.tags.EnchantmentTags; + import org.bukkit.NamespacedKey; + import org.bukkit.Registry; +@@ -90,7 +91,7 @@ public class CraftEnchantment extends Enchantment implements Handleable<net.mine + + @Override + public boolean isTreasure() { +- return !this.handle.is(EnchantmentTags.IN_ENCHANTING_TABLE); ++ return this.handle.is(EnchantmentTags.TREASURE); // Paper - use treasure tag + } + + @Override +@@ -148,7 +149,7 @@ public class CraftEnchantment extends Enchantment implements Handleable<net.mine + // Paper start + @Override + public net.kyori.adventure.text.Component displayName(int level) { +- return io.papermc.paper.adventure.PaperAdventure.asAdventure(getHandle().getFullname(level)); ++ return io.papermc.paper.adventure.PaperAdventure.asAdventure(net.minecraft.world.item.enchantment.Enchantment.getFullname(this.handle, level)); + } + // Paper end + +@@ -159,10 +160,62 @@ public class CraftEnchantment extends Enchantment implements Handleable<net.mine + throw new UnsupportedOperationException("Description isn't translatable!"); // Paper + } + return translatableContents.getKey(); +- + } + // Paper end - add translationKey methods + ++ // Paper start - more Enchantment API ++ @Override ++ public boolean isTradeable() { ++ return this.handle.is(EnchantmentTags.TRADEABLE); ++ } ++ ++ @Override ++ public boolean isDiscoverable() { ++ return this.handle.is(EnchantmentTags.IN_ENCHANTING_TABLE) ++ || this.handle.is(EnchantmentTags.ON_RANDOM_LOOT) ++ || this.handle.is(EnchantmentTags.ON_MOB_SPAWN_EQUIPMENT) ++ || this.handle.is(EnchantmentTags.TRADEABLE) ++ || this.handle.is(EnchantmentTags.ON_TRADED_EQUIPMENT); ++ } ++ ++ @Override ++ public int getMinModifiedCost(int level) { ++ return this.getHandle().definition().minCost().calculate(level); ++ } ++ ++ @Override ++ public int getMaxModifiedCost(int level) { ++ return this.getHandle().definition().maxCost().calculate(level); ++ } ++ ++ @Override ++ public int getAnvilCost() { ++ return this.getHandle().definition().anvilCost(); ++ } ++ ++ @Override ++ public io.papermc.paper.enchantments.EnchantmentRarity getRarity() { ++ throw new UnsupportedOperationException("Enchantments don't have a rarity anymore in 1.20.5+."); ++ } ++ ++ @Override ++ public float getDamageIncrease(int level, org.bukkit.entity.EntityCategory entityCategory) { ++ throw new UnsupportedOperationException("Enchantments are based on complex effect maps since 1.21, cannot compute a simple damage increase"); ++ } ++ ++ @Override ++ public float getDamageIncrease(int level, org.bukkit.entity.EntityType entityType) { ++ throw new UnsupportedOperationException("Enchantments are based on complex effect maps since 1.21, cannot compute a simple damage increase"); ++ } ++ ++ @Override ++ public java.util.Set<org.bukkit.inventory.EquipmentSlotGroup> getActiveSlotGroups() { ++ return this.getHandle().definition().slots().stream() ++ .map(org.bukkit.craftbukkit.CraftEquipmentSlot::getSlot) ++ .collect(java.util.stream.Collectors.toSet()); ++ } ++ // Paper end - more Enchantment API ++ + @Override + public String getTranslationKey() { + return Util.makeDescriptionId("enchantment", this.handle.unwrapKey().get().location()); diff --git a/patches/server/0527-Move-range-check-for-block-placing-up.patch b/patches/server/0527-Move-range-check-for-block-placing-up.patch new file mode 100644 index 0000000000..fb8c43feb0 --- /dev/null +++ b/patches/server/0527-Move-range-check-for-block-placing-up.patch @@ -0,0 +1,22 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Nassim Jahnke <[email protected]> +Date: Wed, 8 Jun 2022 10:52:18 +0200 +Subject: [PATCH] Move range check for block placing up + + +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index f97bbed2b0098fb9b9b0a9e7c2a124e1370608cc..10376b24e3a09c023a73a21a4499415589795423 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -1814,6 +1814,11 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + if (itemstack.isItemEnabled(worldserver.enabledFeatures())) { + BlockHitResult movingobjectpositionblock = packet.getHitResult(); + Vec3 vec3d = movingobjectpositionblock.getLocation(); ++ // Paper start - improve distance check ++ if (!Double.isFinite(vec3d.x) || !Double.isFinite(vec3d.y) || !Double.isFinite(vec3d.z)) { ++ return; ++ } ++ // Paper end - improve distance check + BlockPos blockposition = movingobjectpositionblock.getBlockPos(); + + if (this.player.canInteractWithBlock(blockposition, 1.0D)) { diff --git a/patches/server/0528-Add-Mob-lookAt-API.patch b/patches/server/0528-Add-Mob-lookAt-API.patch new file mode 100644 index 0000000000..8a8ba9fdcd --- /dev/null +++ b/patches/server/0528-Add-Mob-lookAt-API.patch @@ -0,0 +1,64 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: BillyGalbreath <[email protected]> +Date: Fri, 14 May 2021 13:42:17 -0500 +Subject: [PATCH] Add Mob#lookAt API + + +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java +index c67673772c876dab7c015dcdfb75b297d3c4fbad..bd739428a7e5e35ebcdb70cd187379b3d222339b 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java +@@ -97,5 +97,53 @@ public abstract class CraftMob extends CraftLivingEntity implements Mob { + public boolean isInDaylight() { + return getHandle().isSunBurnTick(); + } ++ ++ @Override ++ public void lookAt(@org.jetbrains.annotations.NotNull org.bukkit.Location location) { ++ com.google.common.base.Preconditions.checkNotNull(location, "location cannot be null"); ++ com.google.common.base.Preconditions.checkArgument(location.getWorld().equals(getWorld()), "location in a different world"); ++ getHandle().getLookControl().setLookAt(location.getX(), location.getY(), location.getZ()); ++ } ++ ++ @Override ++ public void lookAt(@org.jetbrains.annotations.NotNull org.bukkit.Location location, float headRotationSpeed, float maxHeadPitch) { ++ com.google.common.base.Preconditions.checkNotNull(location, "location cannot be null"); ++ com.google.common.base.Preconditions.checkArgument(location.getWorld().equals(getWorld()), "location in a different world"); ++ getHandle().getLookControl().setLookAt(location.getX(), location.getY(), location.getZ(), headRotationSpeed, maxHeadPitch); ++ } ++ ++ @Override ++ public void lookAt(@org.jetbrains.annotations.NotNull org.bukkit.entity.Entity entity) { ++ com.google.common.base.Preconditions.checkNotNull(entity, "entity cannot be null"); ++ com.google.common.base.Preconditions.checkArgument(entity.getWorld().equals(getWorld()), "entity in a different world"); ++ getHandle().getLookControl().setLookAt(((CraftEntity) entity).getHandle()); ++ } ++ ++ @Override ++ public void lookAt(@org.jetbrains.annotations.NotNull org.bukkit.entity.Entity entity, float headRotationSpeed, float maxHeadPitch) { ++ com.google.common.base.Preconditions.checkNotNull(entity, "entity cannot be null"); ++ com.google.common.base.Preconditions.checkArgument(entity.getWorld().equals(getWorld()), "entity in a different world"); ++ getHandle().getLookControl().setLookAt(((CraftEntity) entity).getHandle(), headRotationSpeed, maxHeadPitch); ++ } ++ ++ @Override ++ public void lookAt(double x, double y, double z) { ++ getHandle().getLookControl().setLookAt(x, y, z); ++ } ++ ++ @Override ++ public void lookAt(double x, double y, double z, float headRotationSpeed, float maxHeadPitch) { ++ getHandle().getLookControl().setLookAt(x, y, z, headRotationSpeed, maxHeadPitch); ++ } ++ ++ @Override ++ public int getHeadRotationSpeed() { ++ return getHandle().getHeadRotSpeed(); ++ } ++ ++ @Override ++ public int getMaxHeadPitch() { ++ return getHandle().getMaxHeadXRot(); ++ } + // Paper end + } diff --git a/patches/server/0529-Correctly-check-if-bucket-dispenses-will-succeed-for.patch b/patches/server/0529-Correctly-check-if-bucket-dispenses-will-succeed-for.patch new file mode 100644 index 0000000000..cc05cc8b4e --- /dev/null +++ b/patches/server/0529-Correctly-check-if-bucket-dispenses-will-succeed-for.patch @@ -0,0 +1,28 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Mon, 1 Jan 2024 12:57:19 -0800 +Subject: [PATCH] Correctly check if bucket dispenses will succeed for event + +Upstream incorrectly checks if the bucket place will succeed +in order to fire the BlockDispenseEvent. This patch corrects +that. + +diff --git a/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java +index ab002c2a5cfb029bd61b7d8e0548fd135b95cdcc..780f83d50aac70c819608f4c79c08ef34664d7b0 100644 +--- a/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java ++++ b/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java +@@ -330,7 +330,13 @@ public interface DispenseItemBehavior { + int y = blockposition.getY(); + int z = blockposition.getZ(); + BlockState iblockdata = worldserver.getBlockState(blockposition); +- if (iblockdata.isAir() || iblockdata.canBeReplaced() || (dispensiblecontaineritem instanceof BucketItem && iblockdata.getBlock() instanceof LiquidBlockContainer && ((LiquidBlockContainer) iblockdata.getBlock()).canPlaceLiquid((Player) null, worldserver, blockposition, iblockdata, ((BucketItem) dispensiblecontaineritem).content))) { ++ // Paper start - correctly check if the bucket place will succeed ++ /* Taken from SolidBucketItem#emptyContents */ ++ boolean willEmptyContentsSolidBucketItem = dispensiblecontaineritem instanceof net.minecraft.world.item.SolidBucketItem && worldserver.isInWorldBounds(blockposition) && iblockdata.isAir(); ++ /* Taken from BucketItem#emptyContents */ ++ boolean willEmptyBucketItem = dispensiblecontaineritem instanceof final BucketItem bucketItem && bucketItem.content instanceof net.minecraft.world.level.material.FlowingFluid && (iblockdata.isAir() || iblockdata.canBeReplaced(bucketItem.content) || (iblockdata.getBlock() instanceof LiquidBlockContainer liquidBlockContainer && liquidBlockContainer.canPlaceLiquid(null, worldserver, blockposition, iblockdata, bucketItem.content))); ++ if (willEmptyContentsSolidBucketItem || willEmptyBucketItem) { ++ // Paper end - correctly check if the bucket place will succeed + org.bukkit.block.Block block = CraftBlock.at(worldserver, pointer.pos()); + CraftItemStack craftItem = CraftItemStack.asCraftMirror(stack); + diff --git a/patches/server/0530-Add-Unix-domain-socket-support.patch b/patches/server/0530-Add-Unix-domain-socket-support.patch new file mode 100644 index 0000000000..08bcf8abe1 --- /dev/null +++ b/patches/server/0530-Add-Unix-domain-socket-support.patch @@ -0,0 +1,137 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Andrew Steinborn <[email protected]> +Date: Tue, 11 May 2021 17:39:22 -0400 +Subject: [PATCH] Add Unix domain socket support + + +diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +index 761663514b98a1c0a0a905150411aff450a0cc50..21d6f728d6ecd35a05933e9406a386c36135a456 100644 +--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java ++++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +@@ -233,6 +233,20 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface + this.setEnforceWhitelist(dedicatedserverproperties.enforceWhitelist); + // this.worldData.setGameType(dedicatedserverproperties.gamemode); // CraftBukkit - moved to world loading + DedicatedServer.LOGGER.info("Default game type: {}", dedicatedserverproperties.gamemode); ++ // Paper start - Unix domain socket support ++ java.net.SocketAddress bindAddress; ++ if (this.getLocalIp().startsWith("unix:")) { ++ if (!io.netty.channel.epoll.Epoll.isAvailable()) { ++ DedicatedServer.LOGGER.error("**** INVALID CONFIGURATION!"); ++ DedicatedServer.LOGGER.error("You are trying to use a Unix domain socket but you're not on a supported OS."); ++ return false; ++ } else if (!io.papermc.paper.configuration.GlobalConfiguration.get().proxies.velocity.enabled && !org.spigotmc.SpigotConfig.bungee) { ++ DedicatedServer.LOGGER.error("**** INVALID CONFIGURATION!"); ++ DedicatedServer.LOGGER.error("Unix domain sockets require IPs to be forwarded from a proxy."); ++ return false; ++ } ++ bindAddress = new io.netty.channel.unix.DomainSocketAddress(this.getLocalIp().substring("unix:".length())); ++ } else { + InetAddress inetaddress = null; + + if (!this.getLocalIp().isEmpty()) { +@@ -242,12 +256,15 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface + if (this.getPort() < 0) { + this.setPort(dedicatedserverproperties.serverPort); + } ++ bindAddress = new java.net.InetSocketAddress(inetaddress, this.getPort()); ++ } ++ // Paper end - Unix domain socket support + + this.initializeKeyPair(); + DedicatedServer.LOGGER.info("Starting Minecraft server on {}:{}", this.getLocalIp().isEmpty() ? "*" : this.getLocalIp(), this.getPort()); + + try { +- this.getConnection().startTcpServerListener(inetaddress, this.getPort()); ++ this.getConnection().bind(bindAddress); // Paper - Unix domain socket support + } catch (IOException ioexception) { + DedicatedServer.LOGGER.warn("**** FAILED TO BIND TO PORT!"); + DedicatedServer.LOGGER.warn("The exception was: {}", ioexception.toString()); +diff --git a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java +index d6d7f1c446ba5507f67038ff27775ba75156f4a7..c63c194c44646e6bc1a59426552787011fc2ced5 100644 +--- a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java ++++ b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java +@@ -76,7 +76,12 @@ public class ServerConnectionListener { + this.running = true; + } + ++ // Paper start - Unix domain socket support + public void startTcpServerListener(@Nullable InetAddress address, int port) throws IOException { ++ bind(new java.net.InetSocketAddress(address, port)); ++ } ++ public void bind(java.net.SocketAddress address) throws IOException { ++ // Paper end - Unix domain socket support + List list = this.channels; + + synchronized (this.channels) { +@@ -84,7 +89,13 @@ public class ServerConnectionListener { + EventLoopGroup eventloopgroup; + + if (Epoll.isAvailable() && this.server.isEpollEnabled()) { ++ // Paper start - Unix domain socket support ++ if (address instanceof io.netty.channel.unix.DomainSocketAddress) { ++ oclass = io.netty.channel.epoll.EpollServerDomainSocketChannel.class; ++ } else { + oclass = EpollServerSocketChannel.class; ++ } ++ // Paper end - Unix domain socket support + eventloopgroup = (EventLoopGroup) ServerConnectionListener.SERVER_EPOLL_EVENT_GROUP.get(); + ServerConnectionListener.LOGGER.info("Using epoll channel type"); + } else { +@@ -117,7 +128,7 @@ public class ServerConnectionListener { + ((Connection) object).setListenerForServerboundHandshake(new ServerHandshakePacketListenerImpl(ServerConnectionListener.this.server, (Connection) object)); + io.papermc.paper.network.ChannelInitializeListenerHolder.callListeners(channel); // Paper - Add Channel initialization listeners + } +- }).group(eventloopgroup).localAddress(address, port)).option(ChannelOption.AUTO_READ, false).bind().syncUninterruptibly()); // CraftBukkit ++ }).group(eventloopgroup).localAddress(address)).option(ChannelOption.AUTO_READ, false).bind().syncUninterruptibly()); // CraftBukkit // Paper - Unix domain socket support + } + } + +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index 10376b24e3a09c023a73a21a4499415589795423..979f2aa4716cd5ddc35e2d81a3de82af4b4c296e 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -2631,6 +2631,11 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + // Spigot Start + public SocketAddress getRawAddress() + { ++ // Paper start - Unix domain socket support; this can be nullable in the case of a Unix domain socket, so if it is, fake something ++ if (connection.channel.remoteAddress() == null) { ++ return new java.net.InetSocketAddress(java.net.InetAddress.getLoopbackAddress(), 0); ++ } ++ // Paper end - Unix domain socket support + return this.connection.channel.remoteAddress(); + } + // Spigot End +diff --git a/src/main/java/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java +index a5bbea6a073e00c10c3c5facd997eb8473fd9a5f..ddf42645402afefc0f5caebc684b191eef9d6ec2 100644 +--- a/src/main/java/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java +@@ -81,6 +81,7 @@ public class ServerHandshakePacketListenerImpl implements ServerHandshakePacketL + this.connection.setupOutboundProtocol(LoginProtocols.CLIENTBOUND); + // CraftBukkit start - Connection throttle + try { ++ if (!(this.connection.channel.localAddress() instanceof io.netty.channel.unix.DomainSocketAddress)) { // Paper - Unix domain socket support; the connection throttle is useless when you have a Unix domain socket + long currentTime = System.currentTimeMillis(); + long connectionThrottle = this.server.server.getConnectionThrottle(); + InetAddress address = ((java.net.InetSocketAddress) this.connection.getRemoteAddress()).getAddress(); +@@ -109,6 +110,7 @@ public class ServerHandshakePacketListenerImpl implements ServerHandshakePacketL + } + } + } ++ } // Paper - Unix domain socket support + } catch (Throwable t) { + org.apache.logging.log4j.LogManager.getLogger().debug("Failed to check connection throttle", t); + } +@@ -166,8 +168,11 @@ public class ServerHandshakePacketListenerImpl implements ServerHandshakePacketL + if (!handledByEvent && proxyLogicEnabled) { // Paper + // if (org.spigotmc.SpigotConfig.bungee) { // Paper - comment out, we check above! + if ( ( split.length == 3 || split.length == 4 ) && ( ServerHandshakePacketListenerImpl.BYPASS_HOSTCHECK || ServerHandshakePacketListenerImpl.HOST_PATTERN.matcher( split[1] ).matches() ) ) { // Paper - Add bypass host check ++ // Paper start - Unix domain socket support ++ java.net.SocketAddress socketAddress = this.connection.getRemoteAddress(); + this.connection.hostname = split[0]; +- this.connection.address = new java.net.InetSocketAddress(split[1], ((java.net.InetSocketAddress) this.connection.getRemoteAddress()).getPort()); ++ this.connection.address = new java.net.InetSocketAddress(split[1], socketAddress instanceof java.net.InetSocketAddress ? ((java.net.InetSocketAddress) socketAddress).getPort() : 0); ++ // Paper end - Unix domain socket support + this.connection.spoofedUUID = com.mojang.util.UndashedUuid.fromStringLenient( split[2] ); + } else + { diff --git a/patches/server/0531-Add-EntityInsideBlockEvent.patch b/patches/server/0531-Add-EntityInsideBlockEvent.patch new file mode 100644 index 0000000000..cf9fee7e14 --- /dev/null +++ b/patches/server/0531-Add-EntityInsideBlockEvent.patch @@ -0,0 +1,294 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sat, 8 May 2021 18:02:36 -0700 +Subject: [PATCH] Add EntityInsideBlockEvent + + +diff --git a/src/main/java/net/minecraft/world/level/block/BaseFireBlock.java b/src/main/java/net/minecraft/world/level/block/BaseFireBlock.java +index 7f0811bc22d78cdc0aca4c6869c90252d8a59eed..c8ca41cd81a72f9bff40f5c1b3bfc1189bf51f98 100644 +--- a/src/main/java/net/minecraft/world/level/block/BaseFireBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/BaseFireBlock.java +@@ -128,6 +128,7 @@ public abstract class BaseFireBlock extends Block { + + @Override + protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { ++ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + if (!entity.fireImmune()) { + if (entity.getRemainingFireTicks() < 0) { + entity.setRemainingFireTicks(entity.getRemainingFireTicks() + 1); +diff --git a/src/main/java/net/minecraft/world/level/block/BasePressurePlateBlock.java b/src/main/java/net/minecraft/world/level/block/BasePressurePlateBlock.java +index cd3d9e69653afba51a55a3dfc6764b712b8df859..9afa811579ac2e556b5c5c23b3b49587439dfadc 100644 +--- a/src/main/java/net/minecraft/world/level/block/BasePressurePlateBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/BasePressurePlateBlock.java +@@ -77,6 +77,7 @@ public abstract class BasePressurePlateBlock extends Block { + + @Override + protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { ++ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + if (!world.isClientSide) { + int i = this.getSignalForState(state); + +diff --git a/src/main/java/net/minecraft/world/level/block/BigDripleafBlock.java b/src/main/java/net/minecraft/world/level/block/BigDripleafBlock.java +index 87083f7de53e32713b54315803ccd414db2debc8..9e3f1441d62128535112621bf259c24f1a90595b 100644 +--- a/src/main/java/net/minecraft/world/level/block/BigDripleafBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/BigDripleafBlock.java +@@ -180,6 +180,7 @@ public class BigDripleafBlock extends HorizontalDirectionalBlock implements Bone + + @Override + protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { ++ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + if (!world.isClientSide) { + if (state.getValue(BigDripleafBlock.TILT) == Tilt.NONE && BigDripleafBlock.canEntityTilt(pos, entity) && !world.hasNeighborSignal(pos)) { + // CraftBukkit start - tilt dripleaf +diff --git a/src/main/java/net/minecraft/world/level/block/BubbleColumnBlock.java b/src/main/java/net/minecraft/world/level/block/BubbleColumnBlock.java +index c2d8caee4acb878aaa43c0cdc6f6a37555b69a12..385da0585f409ee453f10d45f5837cdc09adc21b 100644 +--- a/src/main/java/net/minecraft/world/level/block/BubbleColumnBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/BubbleColumnBlock.java +@@ -48,6 +48,7 @@ public class BubbleColumnBlock extends Block implements BucketPickup { + + @Override + protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { ++ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + BlockState blockState = world.getBlockState(pos.above()); + if (blockState.isAir()) { + entity.onAboveBubbleCol(state.getValue(DRAG_DOWN)); +diff --git a/src/main/java/net/minecraft/world/level/block/ButtonBlock.java b/src/main/java/net/minecraft/world/level/block/ButtonBlock.java +index e210c210b733fc968b9c3816bb1f9755d76660ef..061a8f8b58d9fa7959333e2f59d3b7ee03cbf92d 100644 +--- a/src/main/java/net/minecraft/world/level/block/ButtonBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/ButtonBlock.java +@@ -208,6 +208,7 @@ public class ButtonBlock extends FaceAttachedHorizontalDirectionalBlock { + + @Override + protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { ++ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + if (!world.isClientSide && this.type.canButtonBeActivatedByArrows() && !(Boolean) state.getValue(ButtonBlock.POWERED)) { + this.checkPressed(state, world, pos); + } +diff --git a/src/main/java/net/minecraft/world/level/block/CactusBlock.java b/src/main/java/net/minecraft/world/level/block/CactusBlock.java +index de1b64e0cbe7f2de63f04262428c9e6ec340916e..c045b1cccf0047dbef8c04d5a28d31d53389054f 100644 +--- a/src/main/java/net/minecraft/world/level/block/CactusBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/CactusBlock.java +@@ -122,6 +122,7 @@ public class CactusBlock extends Block { + + @Override + protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { ++ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + entity.hurt(world.damageSources().cactus().directBlock(world, pos), 1.0F); // CraftBukkit + } + +diff --git a/src/main/java/net/minecraft/world/level/block/CampfireBlock.java b/src/main/java/net/minecraft/world/level/block/CampfireBlock.java +index e03e2f1d84b4f12d78974b90eb4b741818cdac17..1b94f26e78db062f80d806b82f714a815b4710ff 100644 +--- a/src/main/java/net/minecraft/world/level/block/CampfireBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/CampfireBlock.java +@@ -112,6 +112,7 @@ public class CampfireBlock extends BaseEntityBlock implements SimpleWaterloggedB + + @Override + protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { ++ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + if ((Boolean) state.getValue(CampfireBlock.LIT) && entity instanceof LivingEntity) { + entity.hurt(world.damageSources().campfire().directBlock(world, pos), (float) this.fireDamage); // CraftBukkit + } +diff --git a/src/main/java/net/minecraft/world/level/block/CropBlock.java b/src/main/java/net/minecraft/world/level/block/CropBlock.java +index 91dd550de860fd16585218814eae0b9dc877c77d..6fe896079c0ae622976c2055f8d13cda5ed3ea3d 100644 +--- a/src/main/java/net/minecraft/world/level/block/CropBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/CropBlock.java +@@ -174,6 +174,7 @@ public class CropBlock extends BushBlock implements BonemealableBlock { + + @Override + protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { ++ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + if (world instanceof ServerLevel worldserver) { + if (entity instanceof Ravager && CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState(), !worldserver.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit + worldserver.destroyBlock(pos, true, entity); +diff --git a/src/main/java/net/minecraft/world/level/block/DetectorRailBlock.java b/src/main/java/net/minecraft/world/level/block/DetectorRailBlock.java +index 1e2f56b5c40c3dc72bc38354160f8e7de1f4f5cf..fa1c4defd0d4e4cd888eb26eed131539d0ed573f 100644 +--- a/src/main/java/net/minecraft/world/level/block/DetectorRailBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/DetectorRailBlock.java +@@ -52,6 +52,7 @@ public class DetectorRailBlock extends BaseRailBlock { + + @Override + protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { ++ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + if (!world.isClientSide) { + if (!(Boolean) state.getValue(DetectorRailBlock.POWERED)) { + this.checkPressed(world, pos, state); +diff --git a/src/main/java/net/minecraft/world/level/block/EndGatewayBlock.java b/src/main/java/net/minecraft/world/level/block/EndGatewayBlock.java +index b27b09fcb895b72c51335bdcb095f0f3bd3a190b..58b2454ac5bd3400559aba3c64f228f41f6218ae 100644 +--- a/src/main/java/net/minecraft/world/level/block/EndGatewayBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/EndGatewayBlock.java +@@ -92,6 +92,7 @@ public class EndGatewayBlock extends BaseEntityBlock implements Portal { + + @Override + protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { ++ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + if (entity.canUsePortal(false)) { + BlockEntity tileentity = world.getBlockEntity(pos); + +diff --git a/src/main/java/net/minecraft/world/level/block/EndPortalBlock.java b/src/main/java/net/minecraft/world/level/block/EndPortalBlock.java +index 8887d35d188510cf10da3dc46b0b56373ac346bd..8ca226641588a88c8b068a7acac9d7e92b077144 100644 +--- a/src/main/java/net/minecraft/world/level/block/EndPortalBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/EndPortalBlock.java +@@ -68,6 +68,7 @@ public class EndPortalBlock extends BaseEntityBlock implements Portal { + + @Override + protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { ++ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + if (entity.canUsePortal(false)) { + // CraftBukkit start - Entity in portal + EntityPortalEnterEvent event = new EntityPortalEnterEvent(entity.getBukkitEntity(), new org.bukkit.Location(world.getWorld(), pos.getX(), pos.getY(), pos.getZ())); +diff --git a/src/main/java/net/minecraft/world/level/block/FrogspawnBlock.java b/src/main/java/net/minecraft/world/level/block/FrogspawnBlock.java +index aee71779f31def5f1ef7438cf06219d1de7092ec..34be6b349722240e99f91d28067578aa0b4bd1fe 100644 +--- a/src/main/java/net/minecraft/world/level/block/FrogspawnBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/FrogspawnBlock.java +@@ -89,6 +89,7 @@ public class FrogspawnBlock extends Block { + + @Override + protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { ++ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + if (entity.getType().equals(EntityType.FALLING_BLOCK)) { + this.destroyBlock(world, pos); + } +diff --git a/src/main/java/net/minecraft/world/level/block/HoneyBlock.java b/src/main/java/net/minecraft/world/level/block/HoneyBlock.java +index 3ddf43a8afe8f00ca910d7838356dfc6d007a4f9..5c360c6768582c1a35431739613e9b406875cc21 100644 +--- a/src/main/java/net/minecraft/world/level/block/HoneyBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/HoneyBlock.java +@@ -60,6 +60,7 @@ public class HoneyBlock extends HalfTransparentBlock { + + @Override + protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { ++ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + if (this.isSlidingDown(pos, entity)) { + this.maybeDoSlideAchievement(entity, pos); + this.doSlideMovement(entity); +diff --git a/src/main/java/net/minecraft/world/level/block/HopperBlock.java b/src/main/java/net/minecraft/world/level/block/HopperBlock.java +index 882e7e6c7fa77eef109f8203f22edfa9536a96f8..9da02e643948aaea91307e28eb83177aa5d0ecec 100644 +--- a/src/main/java/net/minecraft/world/level/block/HopperBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/HopperBlock.java +@@ -178,6 +178,7 @@ public class HopperBlock extends BaseEntityBlock { + + @Override + protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { ++ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + BlockEntity blockEntity = world.getBlockEntity(pos); + if (blockEntity instanceof HopperBlockEntity) { + HopperBlockEntity.entityInside(world, pos, state, entity, (HopperBlockEntity)blockEntity); +diff --git a/src/main/java/net/minecraft/world/level/block/LavaCauldronBlock.java b/src/main/java/net/minecraft/world/level/block/LavaCauldronBlock.java +index 7bf2c33a194517d4e52511fe32a8434cbed0361f..d29a62775913922ffb8e3c58ae0db7e37f77226e 100644 +--- a/src/main/java/net/minecraft/world/level/block/LavaCauldronBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/LavaCauldronBlock.java +@@ -32,6 +32,7 @@ public class LavaCauldronBlock extends AbstractCauldronBlock { + + @Override + protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { ++ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + if (this.isEntityInsideContent(state, pos, entity)) { + entity.lavaHurt(); + } +diff --git a/src/main/java/net/minecraft/world/level/block/LayeredCauldronBlock.java b/src/main/java/net/minecraft/world/level/block/LayeredCauldronBlock.java +index c088d713e80f16ead333ea5283f7f95a2de08523..4ab73a083eba2ad3e12526af0a0dbcfba5cf6c14 100644 +--- a/src/main/java/net/minecraft/world/level/block/LayeredCauldronBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/LayeredCauldronBlock.java +@@ -67,6 +67,7 @@ public class LayeredCauldronBlock extends AbstractCauldronBlock { + + @Override + protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { ++ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + if (world instanceof ServerLevel worldserver) { + if (entity.isOnFire() && this.isEntityInsideContent(state, pos, entity)) { + // CraftBukkit start - moved down +diff --git a/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java b/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java +index e404722c119631e31c0519111ccd5f1682c2638d..151025a407a07271bc205955f0ce06f84231563b 100644 +--- a/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java +@@ -115,6 +115,7 @@ public class NetherPortalBlock extends Block implements Portal { + + @Override + protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { ++ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + if (entity.canUsePortal(false)) { + // CraftBukkit start - Entity in portal + EntityPortalEnterEvent event = new EntityPortalEnterEvent(entity.getBukkitEntity(), new org.bukkit.Location(world.getWorld(), pos.getX(), pos.getY(), pos.getZ())); +diff --git a/src/main/java/net/minecraft/world/level/block/PitcherCropBlock.java b/src/main/java/net/minecraft/world/level/block/PitcherCropBlock.java +index 3825b25c7417e4c2e5a25154879199b155a4921f..972d8833127090c01d620cab10b3eca3d3601710 100644 +--- a/src/main/java/net/minecraft/world/level/block/PitcherCropBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/PitcherCropBlock.java +@@ -107,6 +107,7 @@ public class PitcherCropBlock extends DoublePlantBlock implements BonemealableBl + + @Override + public void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { ++ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + if (world instanceof ServerLevel serverLevel && entity instanceof Ravager && serverLevel.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { + serverLevel.destroyBlock(pos, true, entity); + } +diff --git a/src/main/java/net/minecraft/world/level/block/PowderSnowBlock.java b/src/main/java/net/minecraft/world/level/block/PowderSnowBlock.java +index aaf9350a096022c87ccb788d657c1ae6a7b53a47..53f1a7ed6b4bd6e2d8460531226aabf249994c02 100644 +--- a/src/main/java/net/minecraft/world/level/block/PowderSnowBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/PowderSnowBlock.java +@@ -59,6 +59,7 @@ public class PowderSnowBlock extends Block implements BucketPickup { + + @Override + protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { ++ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + if (!(entity instanceof LivingEntity) || entity.getInBlockState().is((Block) this)) { + entity.makeStuckInBlock(state, new Vec3(0.8999999761581421D, 1.5D, 0.8999999761581421D)); + if (world.isClientSide) { +diff --git a/src/main/java/net/minecraft/world/level/block/SweetBerryBushBlock.java b/src/main/java/net/minecraft/world/level/block/SweetBerryBushBlock.java +index a044a2d7e5cb7027394c95c495c59530bc5e18ce..265c85413da04dbc7292a0af934a8b665b2fced5 100644 +--- a/src/main/java/net/minecraft/world/level/block/SweetBerryBushBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/SweetBerryBushBlock.java +@@ -84,6 +84,7 @@ public class SweetBerryBushBlock extends BushBlock implements BonemealableBlock + + @Override + protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { ++ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + if (entity instanceof LivingEntity && entity.getType() != EntityType.FOX && entity.getType() != EntityType.BEE) { + entity.makeStuckInBlock(state, new Vec3(0.800000011920929D, 0.75D, 0.800000011920929D)); + if (world instanceof ServerLevel) { +diff --git a/src/main/java/net/minecraft/world/level/block/TripWireBlock.java b/src/main/java/net/minecraft/world/level/block/TripWireBlock.java +index 93777f83cdd2de30ccf597fadd8418853954d1ce..f079e5a9aa098225acf09ed9b4aa7ddbc2381270 100644 +--- a/src/main/java/net/minecraft/world/level/block/TripWireBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/TripWireBlock.java +@@ -141,6 +141,7 @@ public class TripWireBlock extends Block { + + @Override + protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { ++ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + if (!world.isClientSide) { + if (!(Boolean) state.getValue(TripWireBlock.POWERED)) { + this.checkPressed(world, pos, List.of(entity)); +diff --git a/src/main/java/net/minecraft/world/level/block/WaterlilyBlock.java b/src/main/java/net/minecraft/world/level/block/WaterlilyBlock.java +index 34599b88011aff718f5a6373229489ea3d947ff7..674d710ff88db5eced9e017284d1b7ec7a4fe7cd 100644 +--- a/src/main/java/net/minecraft/world/level/block/WaterlilyBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/WaterlilyBlock.java +@@ -33,6 +33,7 @@ public class WaterlilyBlock extends BushBlock { + + @Override + protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { ++ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + super.entityInside(state, world, pos, entity); + if (world instanceof ServerLevel && entity instanceof AbstractBoat) { + // CraftBukkit start +diff --git a/src/main/java/net/minecraft/world/level/block/WebBlock.java b/src/main/java/net/minecraft/world/level/block/WebBlock.java +index 4b621793da3d6fbc44f90df863b099ba992930fb..fc209fab3ed1ccb35706a5529ec23ad8b902e491 100644 +--- a/src/main/java/net/minecraft/world/level/block/WebBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/WebBlock.java +@@ -24,6 +24,7 @@ public class WebBlock extends Block { + + @Override + protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { ++ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + Vec3 vec3 = new Vec3(0.25, 0.05F, 0.25); + if (entity instanceof LivingEntity livingEntity && livingEntity.hasEffect(MobEffects.WEAVING)) { + vec3 = new Vec3(0.5, 0.25, 0.5); +diff --git a/src/main/java/net/minecraft/world/level/block/WitherRoseBlock.java b/src/main/java/net/minecraft/world/level/block/WitherRoseBlock.java +index b42924ebdd0f5369151b8a37f0070dec7f402073..8b79b2b7766d873b7e689309d936982fc1724686 100644 +--- a/src/main/java/net/minecraft/world/level/block/WitherRoseBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/WitherRoseBlock.java +@@ -63,6 +63,7 @@ public class WitherRoseBlock extends FlowerBlock { + + @Override + protected void entityInside(BlockState state, Level world, BlockPos pos, Entity entity) { ++ if (!new io.papermc.paper.event.entity.EntityInsideBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(world, pos)).callEvent()) { return; } // Paper - Add EntityInsideBlockEvent + if (world instanceof ServerLevel worldserver) { + if (world.getDifficulty() != Difficulty.PEACEFUL && entity instanceof LivingEntity entityliving) { + if (!entityliving.isInvulnerableTo(worldserver, world.damageSources().wither())) { diff --git a/patches/server/0532-Improve-item-default-attribute-API.patch b/patches/server/0532-Improve-item-default-attribute-API.patch new file mode 100644 index 0000000000..4ded5398f8 --- /dev/null +++ b/patches/server/0532-Improve-item-default-attribute-API.patch @@ -0,0 +1,80 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sat, 8 May 2021 15:01:54 -0700 +Subject: [PATCH] Improve item default attribute API + + +diff --git a/src/main/java/org/bukkit/craftbukkit/attribute/CraftAttributeInstance.java b/src/main/java/org/bukkit/craftbukkit/attribute/CraftAttributeInstance.java +index de0eba19c0c963adb4f17cea22333240021fd801..3b171a08bd0bedfe224905feb5838d2540199bce 100644 +--- a/src/main/java/org/bukkit/craftbukkit/attribute/CraftAttributeInstance.java ++++ b/src/main/java/org/bukkit/craftbukkit/attribute/CraftAttributeInstance.java +@@ -75,7 +75,7 @@ public class CraftAttributeInstance implements AttributeInstance { + return new AttributeModifier(CraftNamespacedKey.fromMinecraft(nms.id()), nms.amount(), AttributeModifier.Operation.values()[nms.operation().ordinal()], org.bukkit.inventory.EquipmentSlotGroup.ANY); + } + +- public static AttributeModifier convert(net.minecraft.world.entity.ai.attributes.AttributeModifier nms, EquipmentSlot slot) { +- return new AttributeModifier(CraftNamespacedKey.fromMinecraft(nms.id()), nms.amount(), AttributeModifier.Operation.values()[nms.operation().ordinal()], slot.getGroup()); ++ public static AttributeModifier convert(net.minecraft.world.entity.ai.attributes.AttributeModifier nms, net.minecraft.world.entity.EquipmentSlotGroup slot) { // Paper ++ return new AttributeModifier(CraftNamespacedKey.fromMinecraft(nms.id()), nms.amount(), AttributeModifier.Operation.values()[nms.operation().ordinal()], org.bukkit.craftbukkit.CraftEquipmentSlot.getSlot(slot)); // Paper + } + } +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java +index 68756419ac6ee292db9569eab380a5c14d748002..6d76cc1db3ac3f1ae74c13511937fb86082a0b3d 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java +@@ -197,16 +197,33 @@ public class CraftItemType<M extends ItemMeta> implements ItemType.Typed<M>, Han + // return CraftEquipmentSlot.getSlot(EntityInsentient.getEquipmentSlotForItem(CraftItemStack.asNMSCopy(ItemStack.of(this)))); + // } + ++ // Paper start - improve default attribute API ++ @Override ++ public @NotNull Multimap<Attribute, AttributeModifier> getDefaultAttributeModifiers() { ++ return this.getDefaultAttributeModifiers(sg -> true); ++ } ++ // Paper end - improve default attribute API ++ + @Override + public Multimap<Attribute, AttributeModifier> getDefaultAttributeModifiers(EquipmentSlot slot) { ++ // Paper start - improve/fix item default attribute API ++ final net.minecraft.world.entity.EquipmentSlot nmsSlot = CraftEquipmentSlot.getNMS(slot); ++ return this.getDefaultAttributeModifiers(sg -> sg.test(nmsSlot)); ++ } ++ ++ private Multimap<Attribute, AttributeModifier> getDefaultAttributeModifiers(final java.util.function.Predicate<net.minecraft.world.entity.EquipmentSlotGroup> slotPredicate) { ++ // Paper end - improve/fix item default attribute API + ImmutableMultimap.Builder<Attribute, AttributeModifier> defaultAttributes = ImmutableMultimap.builder(); + + ItemAttributeModifiers nmsDefaultAttributes = this.item.components().getOrDefault(DataComponents.ATTRIBUTE_MODIFIERS, ItemAttributeModifiers.EMPTY); +- +- nmsDefaultAttributes.forEach(CraftEquipmentSlot.getNMS(slot), (key, value) -> { +- Attribute attribute = CraftAttribute.minecraftToBukkit(key.value()); +- defaultAttributes.put(attribute, CraftAttributeInstance.convert(value, slot)); +- }); ++ // Paper start - improve/fix item default attribute API ++ for (final net.minecraft.world.item.component.ItemAttributeModifiers.Entry entry : nmsDefaultAttributes.modifiers()) { ++ if (!slotPredicate.test(entry.slot())) continue; ++ final Attribute attribute = CraftAttribute.minecraftHolderToBukkit(entry.attribute()); ++ final AttributeModifier modifier = CraftAttributeInstance.convert(entry.modifier(), entry.slot()); ++ defaultAttributes.put(attribute, modifier); ++ } ++ // Paper end - improve/fix item default attribute API + + return defaultAttributes.build(); + } +diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +index fa4e8cb2d53e0ac4e15a8188454ceed0afafe503..e613ea0eba9a1d162b8f7dfe32c9c31d82f17dd2 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +@@ -386,7 +386,11 @@ public final class CraftMagicNumbers implements UnsafeValues { + + @Override + public Multimap<Attribute, AttributeModifier> getDefaultAttributeModifiers(Material material, EquipmentSlot slot) { +- return material.getDefaultAttributeModifiers(slot); ++ // Paper start - delegate to method on ItemType ++ final org.bukkit.inventory.ItemType item = material.asItemType(); ++ Preconditions.checkArgument(item != null, material + " is not an item and does not have default attributes"); ++ return item.getDefaultAttributeModifiers(slot); ++ // Paper end - delegate to method on ItemType + } + + @Override diff --git a/patches/server/0533-Add-cause-to-Weather-ThunderChangeEvents.patch b/patches/server/0533-Add-cause-to-Weather-ThunderChangeEvents.patch new file mode 100644 index 0000000000..6293b65e40 --- /dev/null +++ b/patches/server/0533-Add-cause-to-Weather-ThunderChangeEvents.patch @@ -0,0 +1,118 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Wed, 2 Dec 2020 18:23:26 -0800 +Subject: [PATCH] Add cause to Weather/ThunderChangeEvents + + +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index dc2b8d8e62cf1405191aa4fc8d4fa548c7249032..c05ccab70bd81d536f93352f30aab9b07b90876a 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -435,8 +435,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + this.serverLevelData.setClearWeatherTime(clearDuration); + this.serverLevelData.setRainTime(rainDuration); + this.serverLevelData.setThunderTime(rainDuration); +- this.serverLevelData.setRaining(raining); +- this.serverLevelData.setThundering(thundering); ++ this.serverLevelData.setRaining(raining, org.bukkit.event.weather.WeatherChangeEvent.Cause.COMMAND); // Paper - Add cause to Weather/ThunderChangeEvents ++ this.serverLevelData.setThundering(thundering, org.bukkit.event.weather.ThunderChangeEvent.Cause.COMMAND); // Paper - Add cause to Weather/ThunderChangeEvents + } + + @Override +@@ -852,8 +852,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + this.serverLevelData.setThunderTime(j); + this.serverLevelData.setRainTime(k); + this.serverLevelData.setClearWeatherTime(i); +- this.serverLevelData.setThundering(flag1); +- this.serverLevelData.setRaining(flag2); ++ this.serverLevelData.setThundering(flag1, org.bukkit.event.weather.ThunderChangeEvent.Cause.NATURAL); // Paper - Add cause to Weather/ThunderChangeEvents ++ this.serverLevelData.setRaining(flag2, org.bukkit.event.weather.WeatherChangeEvent.Cause.NATURAL); // Paper - Add cause to Weather/ThunderChangeEvents + } + + this.oThunderLevel = this.thunderLevel; +@@ -920,14 +920,14 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + @VisibleForTesting + public void resetWeatherCycle() { + // CraftBukkit start +- this.serverLevelData.setRaining(false); ++ this.serverLevelData.setRaining(false, org.bukkit.event.weather.WeatherChangeEvent.Cause.SLEEP); // Paper - Add cause to Weather/ThunderChangeEvents + // If we stop due to everyone sleeping we should reset the weather duration to some other random value. + // Not that everyone ever manages to get the whole server to sleep at the same time.... + if (!this.serverLevelData.isRaining()) { + this.serverLevelData.setRainTime(0); + } + // CraftBukkit end +- this.serverLevelData.setThundering(false); ++ this.serverLevelData.setThundering(false, org.bukkit.event.weather.ThunderChangeEvent.Cause.SLEEP); // Paper - Add cause to Weather/ThunderChangeEvents + // CraftBukkit start + // If we stop due to everyone sleeping we should reset the weather duration to some other random value. + // Not that everyone ever manages to get the whole server to sleep at the same time.... +diff --git a/src/main/java/net/minecraft/world/level/storage/PrimaryLevelData.java b/src/main/java/net/minecraft/world/level/storage/PrimaryLevelData.java +index 6a3959095e57f76b3a092b32d26ff91cf1c5e068..0fa16ff37f09ecfda104b751e48bf246820afc98 100644 +--- a/src/main/java/net/minecraft/world/level/storage/PrimaryLevelData.java ++++ b/src/main/java/net/minecraft/world/level/storage/PrimaryLevelData.java +@@ -337,6 +337,11 @@ public class PrimaryLevelData implements ServerLevelData, WorldData { + + @Override + public void setThundering(boolean thundering) { ++ // Paper start - Add cause to Weather/ThunderChangeEvents ++ this.setThundering(thundering, org.bukkit.event.weather.ThunderChangeEvent.Cause.UNKNOWN); ++ } ++ public void setThundering(boolean thundering, org.bukkit.event.weather.ThunderChangeEvent.Cause cause) { ++ // Paper end - Add cause to Weather/ThunderChangeEvents + // CraftBukkit start + if (this.thundering == thundering) { + return; +@@ -344,7 +349,7 @@ public class PrimaryLevelData implements ServerLevelData, WorldData { + + org.bukkit.World world = Bukkit.getWorld(this.getLevelName()); + if (world != null) { +- ThunderChangeEvent thunder = new ThunderChangeEvent(world, thundering); ++ ThunderChangeEvent thunder = new ThunderChangeEvent(world, thundering, cause); // Paper - Add cause to Weather/ThunderChangeEvents + Bukkit.getServer().getPluginManager().callEvent(thunder); + if (thunder.isCancelled()) { + return; +@@ -371,6 +376,12 @@ public class PrimaryLevelData implements ServerLevelData, WorldData { + + @Override + public void setRaining(boolean raining) { ++ // Paper start - Add cause to Weather/ThunderChangeEvents ++ this.setRaining(raining, org.bukkit.event.weather.WeatherChangeEvent.Cause.UNKNOWN); ++ } ++ ++ public void setRaining(boolean raining, org.bukkit.event.weather.WeatherChangeEvent.Cause cause) { ++ // Paper end - Add cause to Weather/ThunderChangeEvents + // CraftBukkit start + if (this.raining == raining) { + return; +@@ -378,7 +389,7 @@ public class PrimaryLevelData implements ServerLevelData, WorldData { + + org.bukkit.World world = Bukkit.getWorld(this.getLevelName()); + if (world != null) { +- WeatherChangeEvent weather = new WeatherChangeEvent(world, raining); ++ WeatherChangeEvent weather = new WeatherChangeEvent(world, raining, cause); // Paper - Add cause to Weather/ThunderChangeEvents + Bukkit.getServer().getPluginManager().callEvent(weather); + if (weather.isCancelled()) { + return; +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index f20717ee4505d28fa44f0f9ced5c71ae75c8a6d9..691d65bca79d503a43f696d92a0a32226cddaad5 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -1201,7 +1201,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { + + @Override + public void setStorm(boolean hasStorm) { +- this.world.levelData.setRaining(hasStorm); ++ this.world.serverLevelData.setRaining(hasStorm, org.bukkit.event.weather.WeatherChangeEvent.Cause.PLUGIN); // Paper - Add cause to Weather/ThunderChangeEvents + this.setWeatherDuration(0); // Reset weather duration (legacy behaviour) + this.setClearWeatherDuration(0); // Reset clear weather duration (reset "/weather clear" commands) + } +@@ -1223,7 +1223,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { + + @Override + public void setThundering(boolean thundering) { +- this.world.serverLevelData.setThundering(thundering); ++ this.world.serverLevelData.setThundering(thundering, org.bukkit.event.weather.ThunderChangeEvent.Cause.PLUGIN); // Paper - Add cause to Weather/ThunderChangeEvents + this.setThunderDuration(0); // Reset weather duration (legacy behaviour) + this.setClearWeatherDuration(0); // Reset clear weather duration (reset "/weather clear" commands) + } diff --git a/patches/server/0534-More-Lidded-Block-API.patch b/patches/server/0534-More-Lidded-Block-API.patch new file mode 100644 index 0000000000..437434c525 --- /dev/null +++ b/patches/server/0534-More-Lidded-Block-API.patch @@ -0,0 +1,79 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: LemonCaramel <[email protected]> +Date: Sun, 23 May 2021 17:49:51 +0900 +Subject: [PATCH] More Lidded Block API + + +diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBarrel.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBarrel.java +index f4b480e3041fc79060c5fa6ce517047104b280d5..6063f0e1fdc232d063105971359ae688168a2bc4 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBarrel.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBarrel.java +@@ -73,4 +73,11 @@ public class CraftBarrel extends CraftLootable<BarrelBlockEntity> implements Bar + public CraftBarrel copy(Location location) { + return new CraftBarrel(this, location); + } ++ ++ // Paper start - More Lidded Block API ++ @Override ++ public boolean isOpen() { ++ return getTileEntity().openersCounter.opened; ++ } ++ // Paper end - More Lidded Block API + } +diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java b/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java +index 2b6a93a944b27290745278957a3577772b7b8212..6e98a00d526b734992ce39b15768c5820dce4ca8 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java +@@ -92,4 +92,11 @@ public class CraftChest extends CraftLootable<ChestBlockEntity> implements Chest + public CraftChest copy(Location location) { + return new CraftChest(this, location); + } ++ ++ // Paper start - More Lidded Block API ++ @Override ++ public boolean isOpen() { ++ return getTileEntity().openersCounter.opened; ++ } ++ // Paper end - More Lidded Block API + } +diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftEnderChest.java b/src/main/java/org/bukkit/craftbukkit/block/CraftEnderChest.java +index 07b63ce5f5e152f6a644134989ffa03af8a12cdf..b64adbba3e52d32d439e64a243cb74f3fbca2ce3 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/CraftEnderChest.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/CraftEnderChest.java +@@ -51,4 +51,11 @@ public class CraftEnderChest extends CraftBlockEntityState<EnderChestBlockEntity + public CraftEnderChest copy(Location location) { + return new CraftEnderChest(this, location); + } ++ ++ // Paper start - More Lidded Block API ++ @Override ++ public boolean isOpen() { ++ return getTileEntity().openersCounter.opened; ++ } ++ // Paper end - More Lidded Block API + } +diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftShulkerBox.java b/src/main/java/org/bukkit/craftbukkit/block/CraftShulkerBox.java +index 7676313493bcb6327c2a9c026645fe060733c6ac..f7b199fbc7a740de3ee6952ce12ef2c35f057d7a 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/CraftShulkerBox.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/CraftShulkerBox.java +@@ -59,7 +59,7 @@ public class CraftShulkerBox extends CraftLootable<ShulkerBoxBlockEntity> implem + if (this.getTileEntity().opened && this.getWorldHandle() instanceof net.minecraft.world.level.Level) { + net.minecraft.world.level.Level world = this.getTileEntity().getLevel(); + world.blockEvent(this.getPosition(), this.getTileEntity().getBlockState().getBlock(), 1, 0); +- world.playSound(null, this.getPosition(), SoundEvents.SHULKER_BOX_OPEN, SoundSource.BLOCKS, 0.5F, world.random.nextFloat() * 0.1F + 0.9F); ++ world.playSound(null, this.getPosition(), SoundEvents.SHULKER_BOX_CLOSE, SoundSource.BLOCKS, 0.5F, world.random.nextFloat() * 0.1F + 0.9F); // Paper - More Lidded Block API (Wrong sound) + } + this.getTileEntity().opened = false; + } +@@ -73,4 +73,11 @@ public class CraftShulkerBox extends CraftLootable<ShulkerBoxBlockEntity> implem + public CraftShulkerBox copy(Location location) { + return new CraftShulkerBox(this, location); + } ++ ++ // Paper start - More Lidded Block API ++ @Override ++ public boolean isOpen() { ++ return getTileEntity().opened; ++ } ++ // Paper end - More Lidded Block API + } diff --git a/patches/server/0535-Limit-item-frame-cursors-on-maps.patch b/patches/server/0535-Limit-item-frame-cursors-on-maps.patch new file mode 100644 index 0000000000..a55a4490c0 --- /dev/null +++ b/patches/server/0535-Limit-item-frame-cursors-on-maps.patch @@ -0,0 +1,30 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Yive <[email protected]> +Date: Wed, 26 May 2021 15:09:33 -0700 +Subject: [PATCH] Limit item frame cursors on maps + + +diff --git a/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java b/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java +index a89f0b652c515efbc24ffc9af47fa65082946e54..2d5e7380e8a14cbc01ba48cd05deccc0c7f53430 100644 +--- a/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java ++++ b/src/main/java/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java +@@ -326,8 +326,10 @@ public class MapItemSavedData extends SavedData { + + MapFrame worldmapframe1 = new MapFrame(blockposition, entityitemframe.getDirection().get2DDataValue() * 90, entityitemframe.getId()); + ++ if (this.decorations.size() < player.level().paperConfig().maps.itemFrameCursorLimit) { // Paper - Limit item frame cursors on maps + this.addDecoration(MapDecorationTypes.FRAME, player.level(), MapItemSavedData.getFrameKey(entityitemframe.getId()), (double) blockposition.getX(), (double) blockposition.getZ(), (double) (entityitemframe.getDirection().get2DDataValue() * 90), (Component) null); + this.frameMarkers.put(worldmapframe1.getId(), worldmapframe1); ++ } // Paper - Limit item frame cursors on maps + } + + MapDecorations mapdecorations = (MapDecorations) stack.getOrDefault(DataComponents.MAP_DECORATIONS, MapDecorations.EMPTY); +@@ -520,7 +522,7 @@ public class MapItemSavedData extends SavedData { + return true; + } + +- if (!this.isTrackedCountOverLimit(256)) { ++ if (!this.isTrackedCountOverLimit(((Level) world).paperConfig().maps.itemFrameCursorLimit)) { // Paper - Limit item frame cursors on maps + this.bannerMarkers.put(mapiconbanner.getId(), mapiconbanner); + this.addDecoration(mapiconbanner.getDecoration(), world, mapiconbanner.getId(), d0, d1, 180.0D, (Component) mapiconbanner.name().orElse(null)); // CraftBukkit - decompile error + return true; diff --git a/patches/server/0536-Add-PlayerKickEvent-causes.patch b/patches/server/0536-Add-PlayerKickEvent-causes.patch new file mode 100644 index 0000000000..25c4735557 --- /dev/null +++ b/patches/server/0536-Add-PlayerKickEvent-causes.patch @@ -0,0 +1,548 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sat, 15 May 2021 20:30:45 -0700 +Subject: [PATCH] Add PlayerKickEvent causes + + +diff --git a/src/main/java/net/minecraft/network/chat/SignedMessageChain.java b/src/main/java/net/minecraft/network/chat/SignedMessageChain.java +index dbcf183483766f39334d7f7e8336033906625f3f..300929a406905f5ff1ede664d5b99fb0938d4d2e 100644 +--- a/src/main/java/net/minecraft/network/chat/SignedMessageChain.java ++++ b/src/main/java/net/minecraft/network/chat/SignedMessageChain.java +@@ -40,14 +40,14 @@ public class SignedMessageChain { + if (signature == null) { + throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.MISSING_PROFILE_KEY); + } else if (playerPublicKey.data().hasExpired()) { +- throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.EXPIRED_PROFILE_KEY); ++ throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.EXPIRED_PROFILE_KEY, org.bukkit.event.player.PlayerKickEvent.Cause.EXPIRED_PROFILE_PUBLIC_KEY); // Paper - kick event causes + } else { + SignedMessageLink signedMessageLink = SignedMessageChain.this.nextLink; + if (signedMessageLink == null) { + throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.CHAIN_BROKEN); + } else if (body.timeStamp().isBefore(SignedMessageChain.this.lastTimeStamp)) { + this.setChainBroken(); +- throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.OUT_OF_ORDER_CHAT); ++ throw new SignedMessageChain.DecodeException(SignedMessageChain.DecodeException.OUT_OF_ORDER_CHAT, org.bukkit.event.player.PlayerKickEvent.Cause.OUT_OF_ORDER_CHAT); // Paper - kick event causes + } else { + SignedMessageChain.this.lastTimeStamp = body.timeStamp(); + PlayerChatMessage playerChatMessage = new PlayerChatMessage(signedMessageLink, signature, body, null, FilterMask.PASS_THROUGH); +@@ -80,8 +80,15 @@ public class SignedMessageChain { + static final Component INVALID_SIGNATURE = Component.translatable("chat.disabled.invalid_signature"); + static final Component OUT_OF_ORDER_CHAT = Component.translatable("chat.disabled.out_of_order_chat"); + +- public DecodeException(Component message) { ++ // Paper start ++ public final org.bukkit.event.player.PlayerKickEvent.Cause kickCause; ++ public DecodeException(Component message, org.bukkit.event.player.PlayerKickEvent.Cause event) { + super(message); ++ this.kickCause = event; ++ } ++ // Paper end ++ public DecodeException(Component message) { ++ this(message, org.bukkit.event.player.PlayerKickEvent.Cause.UNKNOWN); // Paper + } + } + +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 0ab92fb362729108beca6bc0230fb6fb06ca6280..edd2e83df282b0e24d4c7e3a34776a5b039c2c6b 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -2325,7 +2325,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa + ServerPlayer entityplayer = (ServerPlayer) iterator.next(); + + if (!whitelist.isWhiteListed(entityplayer.getGameProfile()) && !this.getPlayerList().isOp(entityplayer.getGameProfile())) { // Paper - Fix kicking ops when whitelist is reloaded (MC-171420) +- entityplayer.connection.disconnect(net.kyori.adventure.text.Component.text(org.spigotmc.SpigotConfig.whitelistMessage)); ++ entityplayer.connection.disconnect(net.kyori.adventure.text.Component.text(org.spigotmc.SpigotConfig.whitelistMessage), org.bukkit.event.player.PlayerKickEvent.Cause.WHITELIST); // Paper - use configurable message & kick event cause + } + } + +diff --git a/src/main/java/net/minecraft/server/commands/BanIpCommands.java b/src/main/java/net/minecraft/server/commands/BanIpCommands.java +index b451f98aaa5b694cfd9fadebcb5a4441951e9e87..b23dca02fe85a299d13353706915db2aec3467a6 100644 +--- a/src/main/java/net/minecraft/server/commands/BanIpCommands.java ++++ b/src/main/java/net/minecraft/server/commands/BanIpCommands.java +@@ -66,7 +66,7 @@ public class BanIpCommands { + } + + for (ServerPlayer serverPlayer : list) { +- serverPlayer.connection.disconnect(Component.translatable("multiplayer.disconnect.ip_banned")); ++ serverPlayer.connection.disconnect(Component.translatable("multiplayer.disconnect.ip_banned"), org.bukkit.event.player.PlayerKickEvent.Cause.IP_BANNED); // Paper - kick event cause + } + + return list.size(); +diff --git a/src/main/java/net/minecraft/server/commands/BanPlayerCommands.java b/src/main/java/net/minecraft/server/commands/BanPlayerCommands.java +index e63a03a419061edc6a1305f6469d2282d960d6d1..be436480873ac914d67dac36061ac087b7389ab1 100644 +--- a/src/main/java/net/minecraft/server/commands/BanPlayerCommands.java ++++ b/src/main/java/net/minecraft/server/commands/BanPlayerCommands.java +@@ -55,7 +55,7 @@ public class BanPlayerCommands { + ); + ServerPlayer serverPlayer = source.getServer().getPlayerList().getPlayer(gameProfile.getId()); + if (serverPlayer != null) { +- serverPlayer.connection.disconnect(Component.translatable("multiplayer.disconnect.banned")); ++ serverPlayer.connection.disconnect(Component.translatable("multiplayer.disconnect.banned"), org.bukkit.event.player.PlayerKickEvent.Cause.BANNED); // Paper - kick event cause + } + } + } +diff --git a/src/main/java/net/minecraft/server/commands/KickCommand.java b/src/main/java/net/minecraft/server/commands/KickCommand.java +index d1caaecfdfba1cdeba032f0bc38c06541fa61633..6468b3a25c7527a2fde6899e4812b5cb79ce4b1d 100644 +--- a/src/main/java/net/minecraft/server/commands/KickCommand.java ++++ b/src/main/java/net/minecraft/server/commands/KickCommand.java +@@ -48,7 +48,7 @@ public class KickCommand { + + for (ServerPlayer serverPlayer : targets) { + if (!source.getServer().isSingleplayerOwner(serverPlayer.getGameProfile())) { +- serverPlayer.connection.disconnect(reason); ++ serverPlayer.connection.disconnect(reason, org.bukkit.event.player.PlayerKickEvent.Cause.KICK_COMMAND); // Paper - kick event cause + source.sendSuccess(() -> Component.translatable("commands.kick.success", serverPlayer.getDisplayName(), reason), true); + i++; + } +diff --git a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java +index f8ae8c8eff73e4e87eb34d0f2635517f1688a6f1..59d20fd62e850a38380d877cef95ed69cb46ecbd 100644 +--- a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java +@@ -63,8 +63,8 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack + } + + @Override +- public void kickPlayer(Component reason) { +- this.disconnect(reason); ++ public void kickPlayer(Component reason, org.bukkit.event.player.PlayerKickEvent.Cause cause) { // Paper - kick event causes ++ this.disconnect(reason, cause); // Paper - kick event causes + } + // CraftBukkit end + private static final Logger LOGGER = LogUtils.getLogger(); +@@ -140,7 +140,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack + } else if (!this.isSingleplayerOwner()) { + // Paper start - This needs to be handled on the main thread for plugins + server.submit(() -> { +- this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE); ++ this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE, PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause + }); + // Paper end - This needs to be handled on the main thread for plugins + } +@@ -176,7 +176,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack + } + } catch (Exception ex) { + ServerGamePacketListenerImpl.LOGGER.error("Couldn\'t register custom payload", ex); +- this.disconnect(Component.literal("Invalid payload REGISTER!")); ++ this.disconnect(Component.literal("Invalid payload REGISTER!"), PlayerKickEvent.Cause.INVALID_PAYLOAD); // Paper - kick event cause + } + } else if (identifier.equals(ServerCommonPacketListenerImpl.CUSTOM_UNREGISTER)) { + try { +@@ -186,7 +186,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack + } + } catch (Exception ex) { + ServerGamePacketListenerImpl.LOGGER.error("Couldn\'t unregister custom payload", ex); +- this.disconnect(Component.literal("Invalid payload UNREGISTER!")); ++ this.disconnect(Component.literal("Invalid payload UNREGISTER!"), PlayerKickEvent.Cause.INVALID_PAYLOAD); // Paper - kick event cause + } + } else { + try { +@@ -204,7 +204,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack + this.cserver.getMessenger().dispatchIncomingMessage(this.player.getBukkitEntity(), identifier.toString(), data); + } catch (Exception ex) { + ServerGamePacketListenerImpl.LOGGER.error("Couldn\'t dispatch custom payload", ex); +- this.disconnect(Component.literal("Invalid custom payload!")); ++ this.disconnect(Component.literal("Invalid custom payload!"), PlayerKickEvent.Cause.INVALID_PAYLOAD); // Paper - kick event cause + } + } + +@@ -220,7 +220,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack + PacketUtils.ensureRunningOnSameThread(packet, this, (BlockableEventLoop) this.server); + if (packet.action() == ServerboundResourcePackPacket.Action.DECLINED && this.server.isResourcePackRequired()) { + ServerCommonPacketListenerImpl.LOGGER.info("Disconnecting {} due to resource pack {} rejection", this.playerProfile().getName(), packet.id()); +- this.disconnect((Component) Component.translatable("multiplayer.requiredTexturePrompt.disconnect")); ++ this.disconnect((Component) Component.translatable("multiplayer.requiredTexturePrompt.disconnect"), PlayerKickEvent.Cause.RESOURCE_PACK_REJECTION); // Paper - kick event cause + } + // Paper start - adventure pack callbacks + // call the callbacks before the previously-existing event so the event has final say +@@ -250,7 +250,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack + return; + } + // CraftBukkit end +- this.disconnect(ServerCommonPacketListenerImpl.DISCONNECT_UNEXPECTED_QUERY); ++ this.disconnect(ServerCommonPacketListenerImpl.DISCONNECT_UNEXPECTED_QUERY, PlayerKickEvent.Cause.INVALID_COOKIE); // Paper - kick event cause + } + + protected void keepConnectionAlive() { +@@ -262,7 +262,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack + + if (!this.isSingleplayerOwner() && elapsedTime >= 15000L) { // Paper - use vanilla's 15000L between keep alive packets + if (this.keepAlivePending && !this.processedDisconnect && elapsedTime >= KEEPALIVE_LIMIT) { // Paper - check keepalive limit, don't fire if already disconnected +- this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE); ++ this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE, PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause + } else if (this.checkIfClosed(currentTime)) { // Paper + this.keepAlivePending = true; + this.keepAliveTime = currentTime; +@@ -278,7 +278,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack + private boolean checkIfClosed(long time) { + if (this.closed) { + if (time - this.closedListenerTime >= 15000L) { +- this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE); ++ this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE, PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause + } + + return false; +@@ -330,15 +330,25 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack + + // Paper start - adventure + public void disconnect(final net.kyori.adventure.text.Component reason) { +- this.disconnect(io.papermc.paper.adventure.PaperAdventure.asVanilla(reason)); ++ this.disconnect(reason, PlayerKickEvent.Cause.UNKNOWN); ++ } ++ public void disconnect(final net.kyori.adventure.text.Component reason, PlayerKickEvent.Cause cause) { ++ this.disconnect(io.papermc.paper.adventure.PaperAdventure.asVanilla(reason), cause); ++ // Paper end - kick event causes + } + // Paper end - adventure + ++ @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - kick event causes + public void disconnect(Component reason) { +- this.disconnect(new DisconnectionDetails(reason)); ++ // Paper start - kick event causes ++ this.disconnect(reason, PlayerKickEvent.Cause.UNKNOWN); ++ } ++ public void disconnect(final Component reason, PlayerKickEvent.Cause cause) { ++ this.disconnect(new DisconnectionDetails(reason), cause); ++ // Paper end - kick event causes + } + +- public void disconnect(DisconnectionDetails disconnectionInfo) { ++ public void disconnect(DisconnectionDetails disconnectionInfo, PlayerKickEvent.Cause cause) { // Paper - kick event cause + // CraftBukkit start - fire PlayerKickEvent + if (this.processedDisconnect) { + return; +@@ -347,7 +357,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack + Waitable waitable = new Waitable() { + @Override + protected Object evaluate() { +- ServerCommonPacketListenerImpl.this.disconnect(disconnectionInfo); ++ ServerCommonPacketListenerImpl.this.disconnect(disconnectionInfo, cause); // Paper - kick event causes + return null; + } + }; +@@ -366,7 +376,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack + + net.kyori.adventure.text.Component leaveMessage = net.kyori.adventure.text.Component.translatable("multiplayer.player.left", net.kyori.adventure.text.format.NamedTextColor.YELLOW, io.papermc.paper.configuration.GlobalConfiguration.get().messages.useDisplayNameInQuitMessage ? this.player.getBukkitEntity().displayName() : net.kyori.adventure.text.Component.text(this.player.getScoreboardName())); // Paper - Adventure + +- PlayerKickEvent event = new PlayerKickEvent(this.player.getBukkitEntity(), io.papermc.paper.adventure.PaperAdventure.asAdventure(disconnectionInfo.reason()), leaveMessage); // Paper - adventure ++ PlayerKickEvent event = new PlayerKickEvent(this.player.getBukkitEntity(), io.papermc.paper.adventure.PaperAdventure.asAdventure(disconnectionInfo.reason()), leaveMessage, cause); // Paper - adventure & kick event causes + + if (this.cserver.getServer().isRunning()) { + this.cserver.getPluginManager().callEvent(event); +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index 979f2aa4716cd5ddc35e2d81a3de82af4b4c296e..fffefbed068a339f0db607d6734be611b273bbcf 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -361,7 +361,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + if (this.clientIsFloating && !this.player.isSleeping() && !this.player.isPassenger() && !this.player.isDeadOrDying()) { + if (++this.aboveGroundTickCount > this.getMaximumFlyingTicks(this.player)) { + ServerGamePacketListenerImpl.LOGGER.warn("{} was kicked for floating too long!", this.player.getName().getString()); +- this.disconnect(io.papermc.paper.configuration.GlobalConfiguration.get().messages.kick.flyingPlayer); // Paper - use configurable kick message ++ this.disconnect(io.papermc.paper.configuration.GlobalConfiguration.get().messages.kick.flyingPlayer, org.bukkit.event.player.PlayerKickEvent.Cause.FLYING_PLAYER); // Paper - use configurable kick message & kick event cause + return; + } + } else { +@@ -380,7 +380,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + if (this.clientVehicleIsFloating && this.lastVehicle.getControllingPassenger() == this.player) { + if (++this.aboveGroundVehicleTickCount > this.getMaximumFlyingTicks(this.lastVehicle)) { + ServerGamePacketListenerImpl.LOGGER.warn("{} was kicked for floating a vehicle too long!", this.player.getName().getString()); +- this.disconnect(io.papermc.paper.configuration.GlobalConfiguration.get().messages.kick.flyingVehicle); // Paper - use configurable kick message ++ this.disconnect(io.papermc.paper.configuration.GlobalConfiguration.get().messages.kick.flyingVehicle, org.bukkit.event.player.PlayerKickEvent.Cause.FLYING_VEHICLE); // Paper - use configurable kick message & kick event cause + return; + } + } else { +@@ -400,7 +400,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + this.dropSpamThrottler.tick(); + if (this.player.getLastActionTime() > 0L && this.server.getPlayerIdleTimeout() > 0 && Util.getMillis() - this.player.getLastActionTime() > (long) this.server.getPlayerIdleTimeout() * 1000L * 60L) { + this.player.resetLastActionTime(); // CraftBukkit - SPIGOT-854 +- this.disconnect((Component) Component.translatable("multiplayer.disconnect.idling")); ++ this.disconnect((Component) Component.translatable("multiplayer.disconnect.idling"), org.bukkit.event.player.PlayerKickEvent.Cause.IDLING); // Paper - kick event cause + } + + } +@@ -488,7 +488,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + public void handleMoveVehicle(ServerboundMoveVehiclePacket packet) { + PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); + if (ServerGamePacketListenerImpl.containsInvalidValues(packet.position().x(), packet.position().y(), packet.position().z(), packet.yRot(), packet.xRot())) { +- this.disconnect((Component) Component.translatable("multiplayer.disconnect.invalid_vehicle_movement")); ++ this.disconnect((Component) Component.translatable("multiplayer.disconnect.invalid_vehicle_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_VEHICLE_MOVEMENT); // Paper - kick event cause + } else if (!this.updateAwaitingTeleport() && this.player.hasClientLoaded()) { + Entity entity = this.player.getRootVehicle(); + +@@ -687,7 +687,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); + if (packet.getId() == this.awaitingTeleport) { + if (this.awaitingPositionFromClient == null) { +- this.disconnect((Component) Component.translatable("multiplayer.disconnect.invalid_player_movement")); ++ this.disconnect((Component) Component.translatable("multiplayer.disconnect.invalid_player_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PLAYER_MOVEMENT); // Paper - kick event cause + return; + } + +@@ -754,7 +754,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + // PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); // Paper - AsyncTabCompleteEvent; run this async + // CraftBukkit start + if (!this.tabSpamThrottler.isIncrementAndUnderThreshold() && !this.server.getPlayerList().isOp(this.player.getGameProfile()) && !this.server.isSingleplayerOwner(this.player.getGameProfile())) { // Paper - configurable tab spam limits +- this.disconnect(Component.translatable("disconnect.spam")); ++ this.disconnect(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - Kick event cause + return; + } + // CraftBukkit end +@@ -1178,14 +1178,14 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + + if (byteTotal > byteAllowed) { + ServerGamePacketListenerImpl.LOGGER.warn("{} tried to send a book too large. Book size: {} - Allowed: {} - Pages: {}", this.player.getScoreboardName(), byteTotal, byteAllowed, pageList.size()); +- this.disconnect(Component.literal("Book too large!")); ++ this.disconnect(Component.literal("Book too large!"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - kick event cause + return; + } + } + // Paper end - Book size limits + // CraftBukkit start + if (this.lastBookTick + 20 > MinecraftServer.currentTick) { +- this.disconnect(Component.literal("Book edited too quickly!")); ++ this.disconnect(Component.literal("Book edited too quickly!"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - kick event cause + return; + } + this.lastBookTick = MinecraftServer.currentTick; +@@ -1294,7 +1294,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + public void handleMovePlayer(ServerboundMovePlayerPacket packet) { + PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); + if (ServerGamePacketListenerImpl.containsInvalidValues(packet.getX(0.0D), packet.getY(0.0D), packet.getZ(0.0D), packet.getYRot(0.0F), packet.getXRot(0.0F))) { +- this.disconnect((Component) Component.translatable("multiplayer.disconnect.invalid_player_movement")); ++ this.disconnect((Component) Component.translatable("multiplayer.disconnect.invalid_player_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PLAYER_MOVEMENT); // Paper - kick event cause + } else { + ServerLevel worldserver = this.player.serverLevel(); + +@@ -1734,7 +1734,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + this.dropCount++; + if (this.dropCount >= 20) { + ServerGamePacketListenerImpl.LOGGER.warn(this.player.getScoreboardName() + " dropped their items too quickly!"); +- this.disconnect(Component.literal("You dropped your items too quickly (Hacking?)")); ++ this.disconnect(Component.literal("You dropped your items too quickly (Hacking?)"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - kick event cause + return; + } + } +@@ -2036,7 +2036,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + this.player.resetLastActionTime(); + } else { + ServerGamePacketListenerImpl.LOGGER.warn("{} tried to set an invalid carried item", this.player.getName().getString()); +- this.disconnect(Component.literal("Invalid hotbar selection (Hacking?)")); // CraftBukkit ++ this.disconnect(Component.literal("Invalid hotbar selection (Hacking?)"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // CraftBukkit // Paper - kick event cause + } + } + +@@ -2234,7 +2234,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + + private void tryHandleChat(String s, Runnable runnable, boolean sync) { // CraftBukkit + if (ServerGamePacketListenerImpl.isChatMessageIllegal(s)) { +- this.disconnect((Component) Component.translatable("multiplayer.disconnect.illegal_characters")); ++ this.disconnect((Component) Component.translatable("multiplayer.disconnect.illegal_characters"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_CHARACTERS); // Paper + } else if (this.player.isRemoved() || this.player.getChatVisibility() == ChatVisiblity.HIDDEN) { // CraftBukkit - dead men tell no tales + this.send(new ClientboundSystemChatPacket(Component.translatable("chat.disabled.options").withStyle(ChatFormatting.RED), false)); + } else { +@@ -2257,7 +2257,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + + if (optional.isEmpty()) { + ServerGamePacketListenerImpl.LOGGER.warn("Failed to validate message acknowledgements from {}", this.player.getName().getString()); +- this.disconnect(ServerGamePacketListenerImpl.CHAT_VALIDATION_FAILED); ++ this.disconnect(ServerGamePacketListenerImpl.CHAT_VALIDATION_FAILED, org.bukkit.event.player.PlayerKickEvent.Cause.CHAT_VALIDATION_FAILED); // Paper - kick event causes + } + + return optional; +@@ -2438,7 +2438,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + // this.chatSpamThrottler.increment(); + if (!this.chatSpamThrottler.isIncrementAndUnderThreshold() && !this.server.getPlayerList().isOp(this.player.getGameProfile()) && !this.server.isSingleplayerOwner(this.player.getGameProfile())) { + // CraftBukkit end +- this.disconnect((Component) Component.translatable("disconnect.spam")); ++ this.disconnect((Component) Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - kick event cause + } + + } +@@ -2450,7 +2450,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + synchronized (this.lastSeenMessages) { + if (!this.lastSeenMessages.applyOffset(packet.offset())) { + ServerGamePacketListenerImpl.LOGGER.warn("Failed to validate message acknowledgements from {}", this.player.getName().getString()); +- this.disconnect(ServerGamePacketListenerImpl.CHAT_VALIDATION_FAILED); ++ this.disconnect(ServerGamePacketListenerImpl.CHAT_VALIDATION_FAILED, org.bukkit.event.player.PlayerKickEvent.Cause.CHAT_VALIDATION_FAILED); // Paper - kick event causes + } + + } +@@ -2603,7 +2603,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + } + + if (i > 4096) { +- this.disconnect((Component) Component.translatable("multiplayer.disconnect.too_many_pending_chats")); ++ this.disconnect((Component) Component.translatable("multiplayer.disconnect.too_many_pending_chats"), org.bukkit.event.player.PlayerKickEvent.Cause.TOO_MANY_PENDING_CHATS); // Paper - kick event cause + } + + } +@@ -2662,7 +2662,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + // Spigot Start + if ( entity == this.player && !this.player.isSpectator() ) + { +- this.disconnect( Component.literal( "Cannot interact with self!" ) ); ++ this.disconnect( Component.literal( "Cannot interact with self!" ), org.bukkit.event.player.PlayerKickEvent.Cause.SELF_INTERACTION ); // Paper - kick event cause + return; + } + // Spigot End +@@ -2778,7 +2778,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + } + } + +- ServerGamePacketListenerImpl.this.disconnect((Component) Component.translatable("multiplayer.disconnect.invalid_entity_attacked")); ++ ServerGamePacketListenerImpl.this.disconnect((Component) Component.translatable("multiplayer.disconnect.invalid_entity_attacked"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_ENTITY_ATTACKED); // Paper - add cause + ServerGamePacketListenerImpl.LOGGER.warn("Player {} tried to attack an invalid entity", ServerGamePacketListenerImpl.this.player.getName().getString()); + } + }); +@@ -3178,7 +3178,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + // Paper start - auto recipe limit + if (!org.bukkit.Bukkit.isPrimaryThread()) { + if (!this.recipeSpamPackets.isIncrementAndUnderThreshold()) { +- this.disconnect(net.minecraft.network.chat.Component.translatable("disconnect.spam")); ++ this.disconnect(net.minecraft.network.chat.Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - kick event cause + return; + } + } +@@ -3440,7 +3440,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + + if (!Objects.equals(profilepublickey_a, profilepublickey_a1)) { + if (profilepublickey_a != null && profilepublickey_a1.expiresAt().isBefore(profilepublickey_a.expiresAt())) { +- this.disconnect(ProfilePublicKey.EXPIRED_PROFILE_PUBLIC_KEY); ++ this.disconnect(ProfilePublicKey.EXPIRED_PROFILE_PUBLIC_KEY, org.bukkit.event.player.PlayerKickEvent.Cause.EXPIRED_PROFILE_PUBLIC_KEY); // Paper - kick event causes + } else { + try { + SignatureValidator signaturevalidator = this.server.getProfileKeySignatureValidator(); +@@ -3453,7 +3453,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + this.resetPlayerChatState(remotechatsession_a.validate(this.player.getGameProfile(), signaturevalidator)); + } catch (ProfilePublicKey.ValidationException profilepublickey_b) { + ServerGamePacketListenerImpl.LOGGER.error("Failed to validate profile key: {}", profilepublickey_b.getMessage()); +- this.disconnect(profilepublickey_b.getComponent()); ++ this.disconnect(profilepublickey_b.getComponent(), profilepublickey_b.kickCause); // Paper - kick event causes + } + + } +diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +index 9d5723cdfdbf6257a71e57842aea9ba317fc049a..1e4b288f20153ce0c91fabf164c5c8320c90ba7d 100644 +--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +@@ -70,7 +70,7 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, + } + + @Override +- public void kickPlayer(Component reason) { ++ public void kickPlayer(Component reason, org.bukkit.event.player.PlayerKickEvent.Cause cause) { // Paper - kick event causes - during login, no event can be called. + this.disconnect(reason); + } + // CraftBukkit end +diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java +index 6ddcc928a49630e3a0f7f40cca496642419efa2c..26ba0cec3a8492d91df894a69cc1fc8076eeda0d 100644 +--- a/src/main/java/net/minecraft/server/players/PlayerList.java ++++ b/src/main/java/net/minecraft/server/players/PlayerList.java +@@ -632,7 +632,7 @@ public abstract class PlayerList { + while (iterator.hasNext()) { + entityplayer = (ServerPlayer) iterator.next(); + this.save(entityplayer); // CraftBukkit - Force the player's inventory to be saved +- entityplayer.connection.disconnect(Component.translatable("multiplayer.disconnect.duplicate_login")); ++ entityplayer.connection.disconnect(Component.translatable("multiplayer.disconnect.duplicate_login"), org.bukkit.event.player.PlayerKickEvent.Cause.DUPLICATE_LOGIN); // Paper - kick event cause + } + + // Instead of kicking then returning, we need to store the kick reason +@@ -1236,7 +1236,7 @@ public abstract class PlayerList { + // Paper end + // CraftBukkit start - disconnect safely + for (ServerPlayer player : this.players) { +- if (isRestarting) player.connection.disconnect(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(org.spigotmc.SpigotConfig.restartMessage)); else // Paper ++ if (isRestarting) player.connection.disconnect(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(org.spigotmc.SpigotConfig.restartMessage), org.bukkit.event.player.PlayerKickEvent.Cause.UNKNOWN); else // Paper - kick event cause (cause is never used here) + player.connection.disconnect(java.util.Objects.requireNonNullElseGet(this.server.server.shutdownMessage(), net.kyori.adventure.text.Component::empty)); // CraftBukkit - add custom shutdown message // Paper - Adventure + } + // CraftBukkit end +diff --git a/src/main/java/net/minecraft/world/entity/player/ProfilePublicKey.java b/src/main/java/net/minecraft/world/entity/player/ProfilePublicKey.java +index 9e2ad78b12cadbf0e2bda1e12fe844120529c347..6a7d7fad990fc44fdda6849d43dad141e61f7f37 100644 +--- a/src/main/java/net/minecraft/world/entity/player/ProfilePublicKey.java ++++ b/src/main/java/net/minecraft/world/entity/player/ProfilePublicKey.java +@@ -24,7 +24,7 @@ public record ProfilePublicKey(ProfilePublicKey.Data data) { + + public static ProfilePublicKey createValidated(SignatureValidator servicesSignatureVerifier, UUID playerUuid, ProfilePublicKey.Data publicKeyData) throws ProfilePublicKey.ValidationException { + if (!publicKeyData.validateSignature(servicesSignatureVerifier, playerUuid)) { +- throw new ProfilePublicKey.ValidationException(INVALID_SIGNATURE); ++ throw new ProfilePublicKey.ValidationException(INVALID_SIGNATURE, org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_PUBLIC_KEY_SIGNATURE); // Paper - kick event causes + } else { + return new ProfilePublicKey(publicKeyData); + } +@@ -88,8 +88,16 @@ public record ProfilePublicKey(ProfilePublicKey.Data data) { + } + + public static class ValidationException extends ThrowingComponent { ++ public final org.bukkit.event.player.PlayerKickEvent.Cause kickCause; // Paper ++ @io.papermc.paper.annotation.DoNotUse @Deprecated // Paper + public ValidationException(Component messageText) { ++ // Paper start ++ this(messageText, org.bukkit.event.player.PlayerKickEvent.Cause.UNKNOWN); ++ } ++ public ValidationException(Component messageText, org.bukkit.event.player.PlayerKickEvent.Cause kickCause) { ++ // Paper end + super(messageText); ++ this.kickCause = kickCause; // Paper + } + } + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +index 0b151a66d7419653088526bd72119ebd2d6dd18e..ff8d6a0ceb41258541c0049464dc0923ed4872bd 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -280,7 +280,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + + void sendPacket(Packet<?> packet); + +- void kickPlayer(Component reason); ++ void kickPlayer(Component reason, org.bukkit.event.player.PlayerKickEvent.Cause cause); // Paper - kick event causes + } + + public record CookieFuture(ResourceLocation key, CompletableFuture<byte[]> future) { +@@ -640,7 +640,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + @Override + public void kickPlayer(String message) { + org.spigotmc.AsyncCatcher.catchOp("player kick"); // Spigot +- this.getHandle().transferCookieConnection.kickPlayer(CraftChatMessage.fromStringOrEmpty(message, true)); ++ this.getHandle().transferCookieConnection.kickPlayer(CraftChatMessage.fromStringOrEmpty(message, true), org.bukkit.event.player.PlayerKickEvent.Cause.PLUGIN); // Paper - kick event cause + } + + // Paper start +@@ -652,10 +652,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + + @Override + public void kick(final net.kyori.adventure.text.Component message) { ++ kick(message, org.bukkit.event.player.PlayerKickEvent.Cause.PLUGIN); ++ } ++ ++ @Override ++ public void kick(net.kyori.adventure.text.Component message, org.bukkit.event.player.PlayerKickEvent.Cause cause) { + org.spigotmc.AsyncCatcher.catchOp("player kick"); + final ServerGamePacketListenerImpl connection = this.getHandle().connection; + if (connection != null) { +- connection.disconnect(message == null ? net.kyori.adventure.text.Component.empty() : message); ++ connection.disconnect(message == null ? net.kyori.adventure.text.Component.empty() : message, cause); + } + } + +@@ -716,7 +721,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + + // Paper start - Improve chat handling + if (ServerGamePacketListenerImpl.isChatMessageIllegal(msg)) { +- this.getHandle().connection.disconnect(Component.translatable("multiplayer.disconnect.illegal_characters")); ++ this.getHandle().connection.disconnect(Component.translatable("multiplayer.disconnect.illegal_characters"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_CHARACTERS); // Paper - kick event causes + } else { + if (msg.startsWith("/")) { + this.getHandle().connection.handleCommand(msg); +diff --git a/src/main/java/org/spigotmc/RestartCommand.java b/src/main/java/org/spigotmc/RestartCommand.java +index 824c4ad135ea5177f416687c7042639ed126b70b..39e56b95aaafbcd8ebe68fdefaace83702e9510d 100644 +--- a/src/main/java/org/spigotmc/RestartCommand.java ++++ b/src/main/java/org/spigotmc/RestartCommand.java +@@ -74,7 +74,7 @@ public class RestartCommand extends Command + // Kick all players + for ( ServerPlayer p : com.google.common.collect.ImmutableList.copyOf( MinecraftServer.getServer().getPlayerList().players ) ) + { +- p.connection.disconnect( CraftChatMessage.fromStringOrEmpty( SpigotConfig.restartMessage, true ) ); ++ p.connection.disconnect( CraftChatMessage.fromStringOrEmpty( SpigotConfig.restartMessage, true ), org.bukkit.event.player.PlayerKickEvent.Cause.RESTART_COMMAND); // Paper - kick event reason (cause is never used)) + } + // Give the socket a chance to send the packets + try diff --git a/patches/server/0537-Add-PufferFishStateChangeEvent.patch b/patches/server/0537-Add-PufferFishStateChangeEvent.patch new file mode 100644 index 0000000000..0574c7ed8c --- /dev/null +++ b/patches/server/0537-Add-PufferFishStateChangeEvent.patch @@ -0,0 +1,50 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: HexedHero <[email protected]> +Date: Mon, 10 May 2021 16:59:05 +0100 +Subject: [PATCH] Add PufferFishStateChangeEvent + + +diff --git a/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java b/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java +index 6c5fa151867eb2b15b9aaf94eb4f5309c415a92b..cdb74f86ee92ee143af29962a85d45ca585cee44 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java ++++ b/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java +@@ -102,25 +102,39 @@ public class Pufferfish extends AbstractFish { + public void tick() { + if (!this.level().isClientSide && this.isAlive() && this.isEffectiveAi()) { + if (this.inflateCounter > 0) { ++ boolean increase = true; // Paper - Add PufferFishStateChangeEvent + if (this.getPuffState() == 0) { ++ if (new io.papermc.paper.event.entity.PufferFishStateChangeEvent((org.bukkit.entity.PufferFish) getBukkitEntity(), 1).callEvent()) { // Paper - Add PufferFishStateChangeEvent + this.makeSound(SoundEvents.PUFFER_FISH_BLOW_UP); + this.setPuffState(1); ++ } else { increase = false; } // Paper - Add PufferFishStateChangeEvent + } else if (this.inflateCounter > 40 && this.getPuffState() == 1) { ++ if (new io.papermc.paper.event.entity.PufferFishStateChangeEvent((org.bukkit.entity.PufferFish) getBukkitEntity(), 2).callEvent()) { // Paper - Add PufferFishStateChangeEvent + this.makeSound(SoundEvents.PUFFER_FISH_BLOW_UP); + this.setPuffState(2); ++ } else { increase = false; } // Paper - Add PufferFishStateChangeEvent + } + ++ if (increase) { // Paper - Add PufferFishStateChangeEvent + ++this.inflateCounter; ++ } // Paper - Add PufferFishStateChangeEvent + } else if (this.getPuffState() != 0) { ++ boolean increase = true; // Paper - Add PufferFishStateChangeEvent + if (this.deflateTimer > 60 && this.getPuffState() == 2) { ++ if (new io.papermc.paper.event.entity.PufferFishStateChangeEvent((org.bukkit.entity.PufferFish) getBukkitEntity(), 1).callEvent()) { // Paper - Add PufferFishStateChangeEvent + this.makeSound(SoundEvents.PUFFER_FISH_BLOW_OUT); + this.setPuffState(1); ++ } else { increase = false; } // Paper - Add PufferFishStateChangeEvent + } else if (this.deflateTimer > 100 && this.getPuffState() == 1) { ++ if (new io.papermc.paper.event.entity.PufferFishStateChangeEvent((org.bukkit.entity.PufferFish) getBukkitEntity(), 0).callEvent()) { // Paper - Add PufferFishStateChangeEvent + this.makeSound(SoundEvents.PUFFER_FISH_BLOW_OUT); + this.setPuffState(0); ++ } else { increase = false; } // Paper - Add PufferFishStateChangeEvent + } + ++ if (increase) { // Paper - Add PufferFishStateChangeEvent + ++this.deflateTimer; ++ } // Paper - Add PufferFishStateChangeEvent + } + } + diff --git a/patches/server/0538-Fix-PlayerBucketEmptyEvent-result-itemstack.patch b/patches/server/0538-Fix-PlayerBucketEmptyEvent-result-itemstack.patch new file mode 100644 index 0000000000..646e52a326 --- /dev/null +++ b/patches/server/0538-Fix-PlayerBucketEmptyEvent-result-itemstack.patch @@ -0,0 +1,42 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Thu, 20 May 2021 22:16:37 -0700 +Subject: [PATCH] Fix PlayerBucketEmptyEvent result itemstack + +Fixes SPIGOT-2560: https://hub.spigotmc.org/jira/projects/SPIGOT/issues/SPIGOT-2560 + +diff --git a/src/main/java/net/minecraft/world/item/BucketItem.java b/src/main/java/net/minecraft/world/item/BucketItem.java +index a6aaef9de23bf8084ab13c8f704e9f59de3acdcf..002e2f8e956b2631529e2189be225385dfb501df 100644 +--- a/src/main/java/net/minecraft/world/item/BucketItem.java ++++ b/src/main/java/net/minecraft/world/item/BucketItem.java +@@ -40,6 +40,8 @@ import org.bukkit.event.player.PlayerBucketFillEvent; + + public class BucketItem extends Item implements DispensibleContainerItem { + ++ private static @Nullable ItemStack itemLeftInHandAfterPlayerBucketEmptyEvent = null; // Paper - Fix PlayerBucketEmptyEvent result itemstack ++ + public final Fluid content; + + public BucketItem(Fluid fluid, Item.Properties settings) { +@@ -125,6 +127,13 @@ public class BucketItem extends Item implements DispensibleContainerItem { + } + + public static ItemStack getEmptySuccessItem(ItemStack stack, Player player) { ++ // Paper start - Fix PlayerBucketEmptyEvent result itemstack ++ if (itemLeftInHandAfterPlayerBucketEmptyEvent != null) { ++ ItemStack itemInHand = itemLeftInHandAfterPlayerBucketEmptyEvent; ++ itemLeftInHandAfterPlayerBucketEmptyEvent = null; ++ return itemInHand; ++ } ++ // Paper end - Fix PlayerBucketEmptyEvent result itemstack + return !player.hasInfiniteMaterials() ? new ItemStack(Items.BUCKET) : stack; + } + +@@ -182,6 +191,7 @@ public class BucketItem extends Item implements DispensibleContainerItem { + ((ServerPlayer) entityhuman).getBukkitEntity().updateInventory(); // SPIGOT-4541 + return false; + } ++ itemLeftInHandAfterPlayerBucketEmptyEvent = event.getItemStack() != null ? event.getItemStack().equals(CraftItemStack.asNewCraftStack(net.minecraft.world.item.Items.BUCKET)) ? null : CraftItemStack.asNMSCopy(event.getItemStack()) : ItemStack.EMPTY; // Paper - Fix PlayerBucketEmptyEvent result itemstack + } + // CraftBukkit end + if (!flag2) { diff --git a/patches/server/0539-Synchronize-PalettedContainer-instead-of-ThreadingDe.patch b/patches/server/0539-Synchronize-PalettedContainer-instead-of-ThreadingDe.patch new file mode 100644 index 0000000000..ee4d412a1c --- /dev/null +++ b/patches/server/0539-Synchronize-PalettedContainer-instead-of-ThreadingDe.patch @@ -0,0 +1,91 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Aikar <[email protected]> +Date: Fri, 29 May 2020 20:29:02 -0400 +Subject: [PATCH] Synchronize PalettedContainer instead of + ThreadingDetector/Semaphore + +Mojang has flaws in their logic about chunks being concurrently +wrote to. So we constantly see crashes around multiple threads writing. + +Additionally, java has optimized synchronization so well that its +in many times faster than trying to manage read write locks for low +contention situations. + +And this is extremely a low contention situation. + +diff --git a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java +index 8c318c5f9eaaae1e961ff24247283c232fa84c20..112d1259dd37743076ff6c67ffd711d084ba8698 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java ++++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java +@@ -30,14 +30,14 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer + public final IdMap<T> registry; + private volatile PalettedContainer.Data<T> data; + private final PalettedContainer.Strategy strategy; +- private final ThreadingDetector threadingDetector = new ThreadingDetector("PalettedContainer"); ++ // private final ThreadingDetector threadingDetector = new ThreadingDetector("PalettedContainer"); // Paper - unused + + public void acquire() { +- this.threadingDetector.checkAndLock(); ++ // this.threadingDetector.checkAndLock(); // Paper - disable this - use proper synchronization + } + + public void release() { +- this.threadingDetector.checkAndUnlock(); ++ // this.threadingDetector.checkAndUnlock(); // Paper - disable this + } + + public static <T> Codec<PalettedContainer<T>> codecRW(IdMap<T> idList, Codec<T> entryCodec, PalettedContainer.Strategy paletteProvider, T defaultValue) { +@@ -110,7 +110,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer + } + + @Override +- public int onResize(int newBits, T object) { ++ public synchronized int onResize(int newBits, T object) { // Paper - synchronize + PalettedContainer.Data<T> data = this.data; + PalettedContainer.Data<T> data2 = this.createOrReuseData(data, newBits); + data2.copyFrom(data.palette, data.storage); +@@ -135,7 +135,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer + return this.getAndSet(this.strategy.getIndex(x, y, z), value); + } + +- private T getAndSet(int index, T value) { ++ private synchronized T getAndSet(int index, T value) { // Paper - synchronize + int i = this.data.palette.idFor(value); + int j = this.data.storage.getAndSet(index, i); + return this.data.palette.valueFor(j); +@@ -151,7 +151,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer + } + } + +- private void set(int index, T value) { ++ private synchronized void set(int index, T value) { // Paper - synchronize + int i = this.data.palette.idFor(value); + this.data.storage.set(index, i); + } +@@ -174,7 +174,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer + intSet.forEach(id -> action.accept(palette.valueFor(id))); + } + +- public void read(FriendlyByteBuf buf) { ++ public synchronized void read(FriendlyByteBuf buf) { // Paper - synchronize + this.acquire(); + + try { +@@ -189,7 +189,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer + } + + @Override +- public void write(FriendlyByteBuf buf) { ++ public synchronized void write(FriendlyByteBuf buf) { // Paper - synchronize + this.acquire(); + + try { +@@ -237,7 +237,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer + } + + @Override +- public PalettedContainerRO.PackedData<T> pack(IdMap<T> idList, PalettedContainer.Strategy paletteProvider) { ++ public synchronized PalettedContainerRO.PackedData<T> pack(IdMap<T> idList, PalettedContainer.Strategy paletteProvider) { // Paper - synchronize + this.acquire(); + + PalettedContainerRO.PackedData var12; diff --git a/patches/server/0540-Add-option-to-fix-items-merging-through-walls.patch b/patches/server/0540-Add-option-to-fix-items-merging-through-walls.patch new file mode 100644 index 0000000000..b4e4e39ae1 --- /dev/null +++ b/patches/server/0540-Add-option-to-fix-items-merging-through-walls.patch @@ -0,0 +1,25 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: GioSDA <[email protected]> +Date: Wed, 10 Mar 2021 10:06:45 -0800 +Subject: [PATCH] Add option to fix items merging through walls + + +diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java +index 9974aec00935a1c3068eceee6d7042f14f15ac56..586257fe5c9f5cddd0ed164254f46777c6e71d66 100644 +--- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java ++++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java +@@ -285,6 +285,14 @@ public class ItemEntity extends Entity implements TraceableEntity { + ItemEntity entityitem = (ItemEntity) iterator.next(); + + if (entityitem.isMergable()) { ++ // Paper start - Fix items merging through walls ++ if (this.level().paperConfig().fixes.fixItemsMergingThroughWalls) { ++ if (this.level().clipDirect(this.position(), entityitem.position(), ++ net.minecraft.world.phys.shapes.CollisionContext.of(this)) == net.minecraft.world.phys.HitResult.Type.BLOCK) { ++ continue; ++ } ++ } ++ // Paper end - Fix items merging through walls + this.tryToMerge(entityitem); + if (this.isRemoved()) { + break; diff --git a/patches/server/0541-Add-BellRevealRaiderEvent.patch b/patches/server/0541-Add-BellRevealRaiderEvent.patch new file mode 100644 index 0000000000..871aa2a198 --- /dev/null +++ b/patches/server/0541-Add-BellRevealRaiderEvent.patch @@ -0,0 +1,33 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Owen1212055 <[email protected]> +Date: Wed, 26 May 2021 17:09:07 -0400 +Subject: [PATCH] Add BellRevealRaiderEvent + + +diff --git a/src/main/java/net/minecraft/world/level/block/entity/BellBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BellBlockEntity.java +index 86dac3f82da065bf79d94da9df192f51ce4665e2..946c9dbfabf154db53d811906fd98d17992167d1 100644 +--- a/src/main/java/net/minecraft/world/level/block/entity/BellBlockEntity.java ++++ b/src/main/java/net/minecraft/world/level/block/entity/BellBlockEntity.java +@@ -156,7 +156,7 @@ public class BellBlockEntity extends BlockEntity { + return BellBlockEntity.isRaiderWithinRange(pos, entityliving); + }).map((entity) -> (org.bukkit.entity.LivingEntity) entity.getBukkitEntity()).collect(java.util.stream.Collectors.toCollection(java.util.ArrayList::new)); // CraftBukkit + +- org.bukkit.craftbukkit.event.CraftEventFactory.handleBellResonateEvent(world, pos, entities).forEach(BellBlockEntity::glow); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBellResonateEvent(world, pos, entities).forEach(entity -> glow(entity, pos)); // Paper - Add BellRevealRaiderEvent + // CraftBukkit end + } + +@@ -189,6 +189,13 @@ public class BellBlockEntity extends BlockEntity { + } + + private static void glow(LivingEntity entity) { ++ // Paper start - Add BellRevealRaiderEvent ++ glow(entity, null); ++ } ++ ++ private static void glow(LivingEntity entity, @javax.annotation.Nullable BlockPos pos) { ++ if (pos != null && !new io.papermc.paper.event.block.BellRevealRaiderEvent(org.bukkit.craftbukkit.block.CraftBlock.at(entity.level(), pos), (org.bukkit.entity.Raider) entity.getBukkitEntity()).callEvent()) return; ++ // Paper end - Add BellRevealRaiderEvent + entity.addEffect(new MobEffectInstance(MobEffects.GLOWING, 60)); + } + diff --git a/patches/server/0542-Fix-invulnerable-end-crystals.patch b/patches/server/0542-Fix-invulnerable-end-crystals.patch new file mode 100644 index 0000000000..4349e5a8ad --- /dev/null +++ b/patches/server/0542-Fix-invulnerable-end-crystals.patch @@ -0,0 +1,65 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Max Lee <[email protected]> +Date: Thu, 27 May 2021 14:52:30 -0700 +Subject: [PATCH] Fix invulnerable end crystals + +MC-108513 + +diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java +index 671e8aa7ecc2b3fcc98af62356ff690a2604bcb0..7cb3d69a69e0e3ef4b7f9f9c8b1eb67edb5d116d 100644 +--- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java ++++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EndCrystal.java +@@ -30,6 +30,7 @@ public class EndCrystal extends Entity { + private static final EntityDataAccessor<Optional<BlockPos>> DATA_BEAM_TARGET = SynchedEntityData.defineId(EndCrystal.class, EntityDataSerializers.OPTIONAL_BLOCK_POS); + private static final EntityDataAccessor<Boolean> DATA_SHOW_BOTTOM = SynchedEntityData.defineId(EndCrystal.class, EntityDataSerializers.BOOLEAN); + public int time; ++ public boolean generatedByDragonFight = false; // Paper - Fix invulnerable end crystals + + public EndCrystal(EntityType<? extends EndCrystal> type, Level world) { + super(type, world); +@@ -68,6 +69,17 @@ public class EndCrystal extends Entity { + } + // CraftBukkit end + } ++ // Paper start - Fix invulnerable end crystals ++ if (this.level().paperConfig().unsupportedSettings.fixInvulnerableEndCrystalExploit && this.generatedByDragonFight && this.isInvulnerable()) { ++ if (!java.util.Objects.equals(((ServerLevel) this.level()).uuid, this.getOriginWorld()) ++ || ((ServerLevel) this.level()).getDragonFight() == null ++ || ((ServerLevel) this.level()).getDragonFight().respawnStage == null ++ || ((ServerLevel) this.level()).getDragonFight().respawnStage.ordinal() > net.minecraft.world.level.dimension.end.DragonRespawnAnimation.SUMMONING_DRAGON.ordinal()) { ++ this.setInvulnerable(false); ++ this.setBeamTarget(null); ++ } ++ } ++ // Paper end - Fix invulnerable end crystals + } + + } +@@ -79,6 +91,7 @@ public class EndCrystal extends Entity { + } + + nbt.putBoolean("ShowBottom", this.showsBottom()); ++ if (this.generatedByDragonFight) nbt.putBoolean("Paper.GeneratedByDragonFight", this.generatedByDragonFight); // Paper - Fix invulnerable end crystals + } + + @Override +@@ -87,6 +100,7 @@ public class EndCrystal extends Entity { + if (nbt.contains("ShowBottom", 1)) { + this.setShowBottom(nbt.getBoolean("ShowBottom")); + } ++ if (nbt.contains("Paper.GeneratedByDragonFight", 1)) this.generatedByDragonFight = nbt.getBoolean("Paper.GeneratedByDragonFight"); // Paper - Fix invulnerable end crystals + + } + +diff --git a/src/main/java/net/minecraft/world/level/levelgen/feature/SpikeFeature.java b/src/main/java/net/minecraft/world/level/levelgen/feature/SpikeFeature.java +index 6f0cd7121bf191d8fd01baf4c9b87df7f4f44564..fe5b2bcfaa243c3089f3df83ec1ae0948a63d1eb 100644 +--- a/src/main/java/net/minecraft/world/level/levelgen/feature/SpikeFeature.java ++++ b/src/main/java/net/minecraft/world/level/levelgen/feature/SpikeFeature.java +@@ -115,6 +115,7 @@ public class SpikeFeature extends Feature<SpikeConfiguration> { + endCrystal.moveTo( + (double)spike.getCenterX() + 0.5, (double)(spike.getHeight() + 1), (double)spike.getCenterZ() + 0.5, random.nextFloat() * 360.0F, 0.0F + ); ++ endCrystal.generatedByDragonFight = true; // Paper - Fix invulnerable end crystals + world.addFreshEntity(endCrystal); + BlockPos blockPos2 = endCrystal.blockPosition(); + this.setBlock(world, blockPos2.below(), Blocks.BEDROCK.defaultBlockState()); diff --git a/patches/server/0543-Add-ElderGuardianAppearanceEvent.patch b/patches/server/0543-Add-ElderGuardianAppearanceEvent.patch new file mode 100644 index 0000000000..e28248ff5d --- /dev/null +++ b/patches/server/0543-Add-ElderGuardianAppearanceEvent.patch @@ -0,0 +1,48 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Owen1212055 <[email protected]> +Date: Fri, 19 Mar 2021 23:39:09 -0400 +Subject: [PATCH] Add ElderGuardianAppearanceEvent + + +diff --git a/src/main/java/net/minecraft/world/effect/MobEffectUtil.java b/src/main/java/net/minecraft/world/effect/MobEffectUtil.java +index f8026eb1d9b10e468d28ee2e1296653e873c87db..23977c2074b920b646a639bef79c0f625b284bea 100644 +--- a/src/main/java/net/minecraft/world/effect/MobEffectUtil.java ++++ b/src/main/java/net/minecraft/world/effect/MobEffectUtil.java +@@ -55,10 +55,23 @@ public final class MobEffectUtil { + } + + public static List<ServerPlayer> addEffectToPlayersAround(ServerLevel worldserver, @Nullable Entity entity, Vec3 vec3d, double d0, MobEffectInstance mobeffect, int i, org.bukkit.event.entity.EntityPotionEffectEvent.Cause cause) { ++ // Paper start - Add ElderGuardianAppearanceEvent ++ return addEffectToPlayersAround(worldserver, entity, vec3d, d0, mobeffect, i, cause, null); ++ } ++ ++ public static List<ServerPlayer> addEffectToPlayersAround(ServerLevel worldserver, @Nullable Entity entity, Vec3 vec3d, double d0, MobEffectInstance mobeffect, int i, org.bukkit.event.entity.EntityPotionEffectEvent.Cause cause, @Nullable java.util.function.Predicate<ServerPlayer> playerPredicate) { ++ // Paper end - Add ElderGuardianAppearanceEvent + // CraftBukkit end + Holder<MobEffect> holder = mobeffect.getEffect(); + List<ServerPlayer> list = worldserver.getPlayers((entityplayer) -> { +- return entityplayer.gameMode.isSurvival() && (entity == null || !entity.isAlliedTo((Entity) entityplayer)) && vec3d.closerThan(entityplayer.position(), d0) && (!entityplayer.hasEffect(holder) || entityplayer.getEffect(holder).getAmplifier() < mobeffect.getAmplifier() || entityplayer.getEffect(holder).endsWithin(i - 1)); ++ // Paper start - Add ElderGuardianAppearanceEvent ++ boolean condition = entityplayer.gameMode.isSurvival() && (entity == null || !entity.isAlliedTo((Entity) entityplayer)) && vec3d.closerThan(entityplayer.position(), d0) && (!entityplayer.hasEffect(holder) || entityplayer.getEffect(holder).getAmplifier() < mobeffect.getAmplifier() || entityplayer.getEffect(holder).endsWithin(i - 1)); ++ if (condition) { ++ return playerPredicate == null || playerPredicate.test(entityplayer); // Only test the player AFTER it is true ++ } else { ++ return false; ++ } ++ // Paper ned - Add ElderGuardianAppearanceEvent + }); + + list.forEach((entityplayer) -> { +diff --git a/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java b/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java +index 9290f43b4b37a7fa2afae81f8351ea76b7ee7de0..378694a38115c012978e1fea59d049d1ebd04110 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java ++++ b/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java +@@ -67,7 +67,7 @@ public class ElderGuardian extends Guardian { + super.customServerAiStep(world); + if ((this.tickCount + this.getId()) % 1200 == 0) { + MobEffectInstance mobeffect = new MobEffectInstance(MobEffects.DIG_SLOWDOWN, 6000, 2); +- List<ServerPlayer> list = MobEffectUtil.addEffectToPlayersAround(world, this, this.position(), 50.0D, mobeffect, 1200, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit ++ List<ServerPlayer> list = MobEffectUtil.addEffectToPlayersAround(world, this, this.position(), 50.0D, mobeffect, 1200, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK, (player) -> new io.papermc.paper.event.entity.ElderGuardianAppearanceEvent((org.bukkit.entity.ElderGuardian) this.getBukkitEntity(), player.getBukkitEntity()).callEvent()); // CraftBukkit // Paper - Add ElderGuardianAppearanceEvent + + list.forEach((entityplayer) -> { + entityplayer.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.GUARDIAN_ELDER_EFFECT, this.isSilent() ? 0.0F : 1.0F)); diff --git a/patches/server/0544-Optimize-Biome-Mob-Lookups-for-Mob-Spawning.patch b/patches/server/0544-Optimize-Biome-Mob-Lookups-for-Mob-Spawning.patch new file mode 100644 index 0000000000..3d03f4c4d2 --- /dev/null +++ b/patches/server/0544-Optimize-Biome-Mob-Lookups-for-Mob-Spawning.patch @@ -0,0 +1,53 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Aikar <[email protected]> +Date: Wed, 12 Sep 2018 21:47:01 -0400 +Subject: [PATCH] Optimize Biome Mob Lookups for Mob Spawning + +Uses an EnumMap as well as a Set paired List for O(1) contains calls. + +diff --git a/src/main/java/net/minecraft/world/level/biome/MobSpawnSettings.java b/src/main/java/net/minecraft/world/level/biome/MobSpawnSettings.java +index d6ebd2ebc3a4a184f1237b00c6c4e709c61d2eec..cb7465ed9bdebe1b31f02d11725e75ff8b44ca66 100644 +--- a/src/main/java/net/minecraft/world/level/biome/MobSpawnSettings.java ++++ b/src/main/java/net/minecraft/world/level/biome/MobSpawnSettings.java +@@ -75,8 +75,40 @@ public class MobSpawnSettings { + } + + public static class Builder { ++ // Paper start - Perf: keep track of data in a pair set to give O(1) contains calls - we have to hook removals incase plugins mess with it ++ public static class MobList extends java.util.ArrayList<MobSpawnSettings.SpawnerData> { ++ java.util.Set<MobSpawnSettings.SpawnerData> biomes = new java.util.HashSet<>(); ++ ++ @Override ++ public boolean contains(Object o) { ++ return biomes.contains(o); ++ } ++ ++ @Override ++ public boolean add(MobSpawnSettings.SpawnerData BiomeSettingsMobs) { ++ biomes.add(BiomeSettingsMobs); ++ return super.add(BiomeSettingsMobs); ++ } ++ ++ @Override ++ public MobSpawnSettings.SpawnerData remove(int index) { ++ MobSpawnSettings.SpawnerData removed = super.remove(index); ++ if (removed != null) { ++ biomes.remove(removed); ++ } ++ return removed; ++ } ++ ++ @Override ++ public void clear() { ++ biomes.clear(); ++ super.clear(); ++ } ++ } ++ // use toImmutableEnumMap collector + private final Map<MobCategory, List<MobSpawnSettings.SpawnerData>> spawners = Stream.of(MobCategory.values()) +- .collect(ImmutableMap.toImmutableMap(mobCategory -> (MobCategory)mobCategory, mobCategory -> Lists.newArrayList())); ++ .collect(Maps.toImmutableEnumMap(mobCategory -> (MobCategory)mobCategory, mobCategory -> new MobList())); // Use MobList instead of ArrayList ++ // Paper end - Perf: keep track of data in a pair set to give O(1) contains calls + private final Map<EntityType<?>, MobSpawnSettings.MobSpawnCost> mobSpawnCosts = Maps.newLinkedHashMap(); + private float creatureGenerationProbability = 0.1F; + diff --git a/patches/server/0545-Line-Of-Sight-Changes.patch b/patches/server/0545-Line-Of-Sight-Changes.patch new file mode 100644 index 0000000000..a2ee93fb8f --- /dev/null +++ b/patches/server/0545-Line-Of-Sight-Changes.patch @@ -0,0 +1,74 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: TwoLeggedCat <[email protected]> +Date: Sat, 29 May 2021 14:33:25 -0500 +Subject: [PATCH] Line Of Sight Changes + + +diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java +index 509139ab9a3070c8c96d14fdc4d07bc229b41bd6..b7c854614643cc6fcb78ce7ac378f5dbbb8eb305 100644 +--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java ++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java +@@ -3913,7 +3913,8 @@ public abstract class LivingEntity extends Entity implements Attackable { + Vec3 vec3d = new Vec3(this.getX(), this.getEyeY(), this.getZ()); + Vec3 vec3d1 = new Vec3(entity.getX(), entityY, entity.getZ()); + +- return vec3d1.distanceTo(vec3d) > 128.0D ? false : this.level().clip(new ClipContext(vec3d, vec3d1, shapeType, fluidHandling, this)).getType() == HitResult.Type.MISS; ++ // Paper - diff on change - used in CraftLivingEntity#hasLineOfSight(Location) and CraftWorld#lineOfSightExists ++ return vec3d1.distanceToSqr(vec3d) > 128.0D * 128.0D ? false : this.level().clip(new ClipContext(vec3d, vec3d1, shapeType, fluidHandling, this)).getType() == HitResult.Type.MISS; // Paper - Perf: Use distance squared + } + } + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java b/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java +index a070b2a83edaa702b13bc6d3026914126c211576..ca35e93239eea09b3d0dc6ef18f58743e633996b 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java +@@ -526,5 +526,21 @@ public abstract class CraftRegionAccessor implements RegionAccessor { + public org.bukkit.NamespacedKey getKey() { + return org.bukkit.craftbukkit.util.CraftNamespacedKey.fromMinecraft(this.getHandle().getLevel().dimension().location()); + } ++ ++ public boolean lineOfSightExists(Location from, Location to) { ++ Preconditions.checkArgument(from != null, "from parameter in lineOfSightExists cannot be null"); ++ Preconditions.checkArgument(to != null, "to parameter in lineOfSightExists cannot be null"); ++ if (from.getWorld() != to.getWorld()) { ++ return false; ++ } ++ ++ net.minecraft.world.phys.Vec3 start = new net.minecraft.world.phys.Vec3(from.getX(), from.getY(), from.getZ()); ++ net.minecraft.world.phys.Vec3 end = new net.minecraft.world.phys.Vec3(to.getX(), to.getY(), to.getZ()); ++ if (end.distanceToSqr(start) > 128D * 128D) { ++ return false; // Return early if the distance is greater than 128 blocks ++ } ++ ++ return this.getHandle().clip(new net.minecraft.world.level.ClipContext(start, end, net.minecraft.world.level.ClipContext.Block.COLLIDER, net.minecraft.world.level.ClipContext.Fluid.NONE, net.minecraft.world.phys.shapes.CollisionContext.empty())).getType() == net.minecraft.world.phys.HitResult.Type.MISS; ++ } + // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +index a4be1eea356ba99358d707381df70032ded42140..a4a30f52f45bf91dc88d53f5d6a376ed739d09eb 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +@@ -643,6 +643,23 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { + return this.getHandle().hasLineOfSight(((CraftEntity) other).getHandle()); + } + ++ // Paper start ++ @Override ++ public boolean hasLineOfSight(Location loc) { ++ if (this.getHandle().level() != ((CraftWorld) loc.getWorld()).getHandle()) { ++ return false; ++ } ++ ++ net.minecraft.world.phys.Vec3 start = new net.minecraft.world.phys.Vec3(this.getHandle().getX(), this.getHandle().getEyeY(), this.getHandle().getZ()); ++ net.minecraft.world.phys.Vec3 end = new net.minecraft.world.phys.Vec3(loc.getX(), loc.getY(), loc.getZ()); ++ if (end.distanceToSqr(start) > 128D * 128D) { ++ return false; // Return early if the distance is greater than 128 blocks ++ } ++ ++ return this.getHandle().level().clipDirect(start, end, net.minecraft.world.phys.shapes.CollisionContext.of(this.getHandle())) == net.minecraft.world.phys.HitResult.Type.MISS; ++ } ++ // Paper end ++ + @Override + public boolean getRemoveWhenFarAway() { + return this.getHandle() instanceof Mob && !((Mob) this.getHandle()).isPersistenceRequired(); diff --git a/patches/server/0546-add-per-world-spawn-limits.patch b/patches/server/0546-add-per-world-spawn-limits.patch new file mode 100644 index 0000000000..6ed4ae68af --- /dev/null +++ b/patches/server/0546-add-per-world-spawn-limits.patch @@ -0,0 +1,24 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: chase <[email protected]> +Date: Wed, 2 Dec 2020 22:43:39 -0800 +Subject: [PATCH] add per world spawn limits + + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index 691d65bca79d503a43f696d92a0a32226cddaad5..d72ceb866f8632a8daa7dc19acdc57b6b78fd906 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -219,6 +219,13 @@ public class CraftWorld extends CraftRegionAccessor implements World { + this.biomeProvider = biomeProvider; + + this.environment = env; ++ // Paper start - per world spawn limits ++ for (SpawnCategory spawnCategory : SpawnCategory.values()) { ++ if (CraftSpawnCategory.isValidForLimits(spawnCategory)) { ++ setSpawnLimit(spawnCategory, this.world.paperConfig().entities.spawning.spawnLimits.getInt(CraftSpawnCategory.toNMS(spawnCategory))); ++ } ++ } ++ // Paper end - per world spawn limits + } + + @Override diff --git a/patches/server/0547-Fix-potions-splash-events.patch b/patches/server/0547-Fix-potions-splash-events.patch new file mode 100644 index 0000000000..68350d52f2 --- /dev/null +++ b/patches/server/0547-Fix-potions-splash-events.patch @@ -0,0 +1,181 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Thu, 20 May 2021 20:40:53 -0700 +Subject: [PATCH] Fix potions splash events + +Fix PotionSplashEvent for water splash potions +Fixes SPIGOT-6221: https://hub.spigotmc.org/jira/projects/SPIGOT/issues/SPIGOT-6221 +Fix splash events cancellation that still show particles/sound + +diff --git a/src/main/java/net/minecraft/world/entity/projectile/ThrownPotion.java b/src/main/java/net/minecraft/world/entity/projectile/ThrownPotion.java +index 224e768963d6969f25608065d37ad72d82acda68..d6ac07d9d5ee0430a1d91b7084b378aac1d047e5 100644 +--- a/src/main/java/net/minecraft/world/entity/projectile/ThrownPotion.java ++++ b/src/main/java/net/minecraft/world/entity/projectile/ThrownPotion.java +@@ -109,55 +109,76 @@ public class ThrownPotion extends ThrowableItemProjectile { + ItemStack itemstack = this.getItem(); + PotionContents potioncontents = (PotionContents) itemstack.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY); + ++ boolean showParticles = true; // Paper - Fix potions splash events + if (potioncontents.is(Potions.WATER)) { +- this.applyWater(worldserver); ++ showParticles = this.applyWater(worldserver, hitResult); // Paper - Fix potions splash events + } else if (true || potioncontents.hasEffects()) { // CraftBukkit - Call event even if no effects to apply + if (this.isLingering()) { +- this.makeAreaOfEffectCloud(potioncontents, hitResult); // CraftBukkit - Pass MovingObjectPosition ++ showParticles = this.makeAreaOfEffectCloud(potioncontents, hitResult); // CraftBukkit - Pass MovingObjectPosition // Paper + } else { +- this.applySplash(worldserver, potioncontents.getAllEffects(), hitResult.getType() == HitResult.Type.ENTITY ? ((EntityHitResult) hitResult).getEntity() : null, hitResult); // CraftBukkit - Pass MovingObjectPosition ++ showParticles = this.applySplash(worldserver, potioncontents.getAllEffects(), hitResult.getType() == HitResult.Type.ENTITY ? ((EntityHitResult) hitResult).getEntity() : null, hitResult); // CraftBukkit - Pass MovingObjectPosition // Paper + } + } + ++ if (showParticles) { // Paper - Fix potions splash events + int i = potioncontents.potion().isPresent() && ((Potion) ((Holder) potioncontents.potion().get()).value()).hasInstantEffects() ? 2007 : 2002; + + worldserver.levelEvent(i, this.blockPosition(), potioncontents.getColor()); ++ } // Paper - Fix potions splash events + this.discard(EntityRemoveEvent.Cause.HIT); // CraftBukkit - add Bukkit remove cause + } + } + +- private void applyWater(ServerLevel world) { ++ private static final Predicate<net.minecraft.world.entity.LivingEntity> APPLY_WATER_GET_ENTITIES_PREDICATE = ThrownPotion.WATER_SENSITIVE_OR_ON_FIRE.or(Axolotl.class::isInstance); // Paper - Fix potions splash events ++ private boolean applyWater(ServerLevel world, @Nullable HitResult hitResult) { // Paper - Fix potions splash events + AABB axisalignedbb = this.getBoundingBox().inflate(4.0D, 2.0D, 4.0D); +- List<net.minecraft.world.entity.LivingEntity> list = this.level().getEntitiesOfClass(net.minecraft.world.entity.LivingEntity.class, axisalignedbb, ThrownPotion.WATER_SENSITIVE_OR_ON_FIRE); ++ // Paper start - Fix potions splash events ++ List<net.minecraft.world.entity.LivingEntity> list = this.level().getEntitiesOfClass(net.minecraft.world.entity.LivingEntity.class, axisalignedbb, ThrownPotion.APPLY_WATER_GET_ENTITIES_PREDICATE); ++ Map<LivingEntity, Double> affected = new HashMap<>(); ++ java.util.Set<LivingEntity> rehydrate = new java.util.HashSet<>(); ++ java.util.Set<LivingEntity> extinguish = new java.util.HashSet<>(); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + net.minecraft.world.entity.LivingEntity entityliving = (net.minecraft.world.entity.LivingEntity) iterator.next(); ++ if (entityliving instanceof Axolotl axolotl) { ++ rehydrate.add(((org.bukkit.entity.Axolotl) axolotl.getBukkitEntity())); ++ } + double d0 = this.distanceToSqr((Entity) entityliving); + + if (d0 < 16.0D) { + if (entityliving.isSensitiveToWater()) { +- entityliving.hurtServer(world, this.damageSources().indirectMagic(this, this.getOwner()), 1.0F); ++ affected.put(entityliving.getBukkitLivingEntity(), 1.0); + } + + if (entityliving.isOnFire() && entityliving.isAlive()) { +- entityliving.extinguishFire(); ++ extinguish.add(entityliving.getBukkitLivingEntity()); + } + } + } + +- List<Axolotl> list1 = this.level().getEntitiesOfClass(Axolotl.class, axisalignedbb); +- Iterator iterator1 = list1.iterator(); +- +- while (iterator1.hasNext()) { +- Axolotl axolotl = (Axolotl) iterator1.next(); +- +- axolotl.rehydrate(); ++ io.papermc.paper.event.entity.WaterBottleSplashEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callWaterBottleSplashEvent( ++ this, hitResult, affected, rehydrate, extinguish ++ ); ++ if (!event.isCancelled()) { ++ for (LivingEntity affectedEntity : event.getToDamage()) { ++ ((CraftLivingEntity) affectedEntity).getHandle().hurtServer(world, this.damageSources().indirectMagic(this, this.getOwner()), 1.0F); ++ } ++ for (LivingEntity toExtinguish : event.getToExtinguish()) { ++ ((CraftLivingEntity) toExtinguish).getHandle().extinguishFire(); ++ } ++ for (LivingEntity toRehydrate : event.getToRehydrate()) { ++ if (((CraftLivingEntity) toRehydrate).getHandle() instanceof Axolotl axolotl) { ++ axolotl.rehydrate(); ++ } ++ } ++ // Paper end - Fix potions splash events + } ++ return !event.isCancelled(); // Paper - Fix potions splash events + + } + +- private void applySplash(ServerLevel worldserver, Iterable<MobEffectInstance> iterable, @Nullable Entity entity, HitResult position) { // CraftBukkit - Pass MovingObjectPosition ++ private boolean applySplash(ServerLevel worldserver, Iterable<MobEffectInstance> iterable, @Nullable Entity entity, HitResult position) { // CraftBukkit - Pass MovingObjectPosition // Paper - Fix potions splash events + AABB axisalignedbb = this.getBoundingBox().inflate(4.0D, 2.0D, 4.0D); + List<net.minecraft.world.entity.LivingEntity> list = worldserver.getEntitiesOfClass(net.minecraft.world.entity.LivingEntity.class, axisalignedbb); + Map<LivingEntity, Double> affected = new HashMap<LivingEntity, Double>(); // CraftBukkit +@@ -175,6 +196,7 @@ public class ThrownPotion extends ThrowableItemProjectile { + if (d0 < 16.0D) { + double d1; + ++ // Paper - diff on change, used when calling the splash event for water splash potions + if (entityliving == entity) { + d1 = 1.0D; + } else { +@@ -230,10 +252,11 @@ public class ThrownPotion extends ThrowableItemProjectile { + } + } + } ++ return !event.isCancelled(); // Paper - Fix potions splash events + + } + +- private void makeAreaOfEffectCloud(PotionContents potioncontents, HitResult position) { // CraftBukkit - Pass MovingObjectPosition ++ private boolean makeAreaOfEffectCloud(PotionContents potioncontents, HitResult position) { // CraftBukkit - Pass MovingObjectPosition + AreaEffectCloud entityareaeffectcloud = new AreaEffectCloud(this.level(), this.getX(), this.getY(), this.getZ()); + Entity entity = this.getOwner(); + +@@ -246,14 +269,16 @@ public class ThrownPotion extends ThrowableItemProjectile { + entityareaeffectcloud.setWaitTime(10); + entityareaeffectcloud.setRadiusPerTick(-entityareaeffectcloud.getRadius() / (float) entityareaeffectcloud.getDuration()); + entityareaeffectcloud.setPotionContents(potioncontents); ++ boolean noEffects = potioncontents.hasEffects(); // Paper - Fix potions splash events + // CraftBukkit start + org.bukkit.event.entity.LingeringPotionSplashEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callLingeringPotionSplashEvent(this, position, entityareaeffectcloud); +- if (!(event.isCancelled() || entityareaeffectcloud.isRemoved())) { ++ if (!(event.isCancelled() || entityareaeffectcloud.isRemoved() || (noEffects && !entityareaeffectcloud.potionContents.hasEffects()))) { // Paper - don't spawn area effect cloud if the effects were empty and not changed during the event handling + this.level().addFreshEntity(entityareaeffectcloud); + } else { + entityareaeffectcloud.discard(null); // CraftBukkit - add Bukkit remove cause + } + // CraftBukkit end ++ return !event.isCancelled(); // Paper - Fix potions splash events + } + + public boolean isLingering() { +diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +index 49efae40345fcfca8f80fcc541dcfde1b8a8b07b..24f86724012bb8bcd6d24683a1c78ce66b42ca30 100644 +--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java ++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +@@ -884,6 +884,32 @@ public class CraftEventFactory { + return event; + } + ++ // Paper start - Fix potions splash events ++ public static io.papermc.paper.event.entity.WaterBottleSplashEvent callWaterBottleSplashEvent(net.minecraft.world.entity.projectile.ThrownPotion potion, @Nullable HitResult hitResult, Map<LivingEntity, Double> affectedEntities, java.util.Set<LivingEntity> rehydrate, java.util.Set<LivingEntity> extinguish) { ++ ThrownPotion thrownPotion = (ThrownPotion) potion.getBukkitEntity(); ++ ++ Block hitBlock = null; ++ BlockFace hitFace = null; ++ org.bukkit.entity.Entity hitEntity = null; ++ ++ if (hitResult != null) { ++ if (hitResult.getType() == HitResult.Type.BLOCK) { ++ BlockHitResult blockHitResult = (BlockHitResult) hitResult; ++ hitBlock = CraftBlock.at(potion.level(), blockHitResult.getBlockPos()); ++ hitFace = CraftBlock.notchToBlockFace(blockHitResult.getDirection()); ++ } else if (hitResult.getType() == HitResult.Type.ENTITY) { ++ hitEntity = ((EntityHitResult) hitResult).getEntity().getBukkitEntity(); ++ } ++ } ++ ++ io.papermc.paper.event.entity.WaterBottleSplashEvent event = new io.papermc.paper.event.entity.WaterBottleSplashEvent( ++ thrownPotion, hitEntity, hitBlock, hitFace, affectedEntities, rehydrate, extinguish ++ ); ++ event.callEvent(); ++ return event; ++ } ++ // Paper end - Fix potions splash events ++ + /** + * BlockFadeEvent + */ diff --git a/patches/server/0548-Add-more-LimitedRegion-API.patch b/patches/server/0548-Add-more-LimitedRegion-API.patch new file mode 100644 index 0000000000..f46a2383c8 --- /dev/null +++ b/patches/server/0548-Add-more-LimitedRegion-API.patch @@ -0,0 +1,56 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: dfsek <[email protected]> +Date: Sat, 19 Jun 2021 20:15:59 -0700 +Subject: [PATCH] Add more LimitedRegion API + + +diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java b/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java +index ca250dba422f8092b99f72b322147061a7541d14..2c7b64bd7071cb803a042152d497982d753e0b5d 100644 +--- a/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java ++++ b/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java +@@ -255,4 +255,45 @@ public class CraftLimitedRegion extends CraftRegionAccessor implements LimitedRe + public void addEntityWithPassengers(net.minecraft.world.entity.Entity entity, CreatureSpawnEvent.SpawnReason reason) { + this.entities.add(entity); + } ++ ++ // Paper start - Add more LimitedRegion API ++ @Override ++ public void setBlockState(int x, int y, int z, BlockState state) { ++ BlockPos pos = new BlockPos(x, y, z); ++ if (!state.getBlockData().matches(getHandle().getBlockState(pos).createCraftBlockData())) { ++ throw new IllegalArgumentException("BlockData does not match! Expected " + state.getBlockData().getAsString(false) + ", got " + getHandle().getBlockState(pos).createCraftBlockData().getAsString(false)); ++ } ++ getHandle().getBlockEntity(pos).loadWithComponents(((org.bukkit.craftbukkit.block.CraftBlockEntityState<?>) state).getSnapshotNBT(), this.getHandle().registryAccess()); ++ } ++ ++ @Override ++ public void scheduleBlockUpdate(int x, int y, int z) { ++ BlockPos position = new BlockPos(x, y, z); ++ getHandle().scheduleTick(position, getHandle().getBlockState(position).getBlock(), 0); ++ } ++ ++ @Override ++ public void scheduleFluidUpdate(int x, int y, int z) { ++ BlockPos position = new BlockPos(x, y, z); ++ getHandle().scheduleTick(position, getHandle().getFluidState(position).getType(), 0); ++ } ++ ++ @Override ++ public World getWorld() { ++ // reading/writing the returned Minecraft world causes a deadlock. ++ // By implementing this, and covering it in warnings, we're assuming people won't be stupid, and ++ // if they are stupid, they'll figure it out pretty fast. ++ return getHandle().getMinecraftWorld().getWorld(); ++ } ++ ++ @Override ++ public int getCenterChunkX() { ++ return centerChunkX; ++ } ++ ++ @Override ++ public int getCenterChunkZ() { ++ return centerChunkZ; ++ } ++ // Paper end - Add more LimitedRegion API + } diff --git a/patches/server/0549-Fix-PlayerDropItemEvent-using-wrong-item.patch b/patches/server/0549-Fix-PlayerDropItemEvent-using-wrong-item.patch new file mode 100644 index 0000000000..6fbcb401ee --- /dev/null +++ b/patches/server/0549-Fix-PlayerDropItemEvent-using-wrong-item.patch @@ -0,0 +1,57 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf <[email protected]> +Date: Sun, 20 Jun 2021 21:55:59 -0700 +Subject: [PATCH] Fix PlayerDropItemEvent using wrong item + + +diff --git a/src/main/java/net/minecraft/server/commands/GiveCommand.java b/src/main/java/net/minecraft/server/commands/GiveCommand.java +index c81fd3e1108fb0a02f9240263404af2b968c8494..0d9de4c61c7b26a6ff37c12fde629161fd0c3d5a 100644 +--- a/src/main/java/net/minecraft/server/commands/GiveCommand.java ++++ b/src/main/java/net/minecraft/server/commands/GiveCommand.java +@@ -38,6 +38,7 @@ public class GiveCommand { + + private static int giveItem(CommandSourceStack source, ItemInput item, Collection<ServerPlayer> targets, int count) throws CommandSyntaxException { + ItemStack itemstack = item.createItemStack(1, false); ++ final Component displayName = itemstack.getDisplayName(); // Paper - get display name early + int j = itemstack.getMaxStackSize(); + int k = j * 100; + +@@ -79,11 +80,11 @@ public class GiveCommand { + + if (targets.size() == 1) { + source.sendSuccess(() -> { +- return Component.translatable("commands.give.success.single", count, itemstack.getDisplayName(), ((ServerPlayer) targets.iterator().next()).getDisplayName()); ++ return Component.translatable("commands.give.success.single", count, displayName, ((ServerPlayer) targets.iterator().next()).getDisplayName()); // Paper - use cached display name + }, true); + } else { + source.sendSuccess(() -> { +- return Component.translatable("commands.give.success.single", count, itemstack.getDisplayName(), targets.size()); ++ return Component.translatable("commands.give.success.single", count, displayName, targets.size()); // Paper - use cached display name + }, true); + } + +diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java +index 2125f9dc87dbf31a66004dc859788f584e099e73..b1224b0ef12a746477047073f5bb94405871ce85 100644 +--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java ++++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java +@@ -2724,7 +2724,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player { + + if (flag1) { + if (!itemstack1.isEmpty()) { +- this.awardStat(Stats.ITEM_DROPPED.get(itemstack1.getItem()), itemstack.getCount()); ++ this.awardStat(Stats.ITEM_DROPPED.get(itemstack1.getItem()), itemstack1.getCount()); // Paper - Fix PlayerDropItemEvent using wrong item + } + + this.awardStat(Stats.DROP); +@@ -2740,6 +2740,11 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player { + return null; + } else { + double d0 = this.getEyeY() - 0.30000001192092896D; ++ // Paper start ++ ItemStack tmp = stack.copy(); ++ stack.setCount(0); ++ stack = tmp; ++ // Paper end + ItemEntity entityitem = new ItemEntity(this.level(), this.getX(), d0, this.getZ(), stack); + + entityitem.setPickUpDelay(40); diff --git a/patches/server/0550-Missing-Entity-API.patch b/patches/server/0550-Missing-Entity-API.patch new file mode 100644 index 0000000000..3f4ceb1648 --- /dev/null +++ b/patches/server/0550-Missing-Entity-API.patch @@ -0,0 +1,1402 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Owen1212055 <[email protected]> +Date: Mon, 21 Jun 2021 23:56:07 -0400 +Subject: [PATCH] Missing Entity API + +== AT == +public net.minecraft.world.entity.animal.Fox isDefending()Z +public net.minecraft.world.entity.animal.Fox setDefending(Z)V +public net.minecraft.world.entity.animal.Fox setFaceplanted(Z)V +public net.minecraft.world.entity.animal.Panda getEatCounter()I +public net.minecraft.world.entity.animal.Panda setEatCounter(I)V +public net.minecraft.world.entity.animal.Bee isRolling()Z +public net.minecraft.world.entity.animal.Bee setRolling(Z)V +public net.minecraft.world.entity.animal.Bee numCropsGrownSincePollination +public net.minecraft.world.entity.animal.Bee ticksWithoutNectarSinceExitingHive +public net.minecraft.world.entity.monster.piglin.Piglin isChargingCrossbow()Z +public net.minecraft.world.entity.ambient.Bat targetPosition +public net.minecraft.world.entity.monster.Ravager attackTick +public net.minecraft.world.entity.monster.Ravager stunnedTick +public net.minecraft.world.entity.monster.Ravager roarTick +public net.minecraft.world.entity.vehicle.MinecartTNT explode(D)V +public net.minecraft.world.entity.vehicle.MinecartTNT fuse +public net.minecraft.world.entity.monster.Endermite life +public net.minecraft.world.entity.projectile.AbstractArrow soundEvent +public net.minecraft.world.entity.monster.Phantom anchorPoint +public net.minecraft.world.entity.npc.WanderingTrader getWanderTarget()Lnet/minecraft/core/BlockPos; +public net.minecraft.world.entity.animal.AbstractSchoolingFish leader +public net.minecraft.world.entity.animal.AbstractSchoolingFish schoolSize +public net.minecraft.world.entity.animal.Rabbit moreCarrotTicks +public net.minecraft.world.entity.AreaEffectCloud ownerUUID +public net.minecraft.world.entity.animal.MushroomCow stewEffects +public net.minecraft.world.entity.Entity FLAG_INVISIBLE +public net.minecraft.world.entity.animal.Cat setRelaxStateOne(Z)V +public net.minecraft.world.entity.animal.Cat isRelaxStateOne()Z + +Co-authored-by: Nassim Jahnke <[email protected]> +Co-authored-by: Jake Potrebic <[email protected]> +Co-authored-by: William Blake Galbreath <[email protected]> +Co-authored-by: SoSeDiK <[email protected]> +Co-authored-by: booky10 <[email protected]> +Co-authored-by: Amin <[email protected]> +Co-authored-by: TrollyLoki <[email protected]> +Co-authored-by: FireInstall <[email protected]> +Co-authored-by: maxcom1 <[email protected]> +Co-authored-by: TotalledZebra <[email protected]> + +diff --git a/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java b/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java +index 05e793e722bbb367ca64cd7f26156fa3dabb8474..3470720466fc81f977c18e3a97bb918926025a22 100644 +--- a/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java ++++ b/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java +@@ -164,7 +164,7 @@ public class MobGoalHelper { + bukkitMap.put(net.minecraft.world.entity.monster.Endermite.class, Endermite.class); + bukkitMap.put(net.minecraft.world.entity.monster.Evoker.class, Evoker.class); + bukkitMap.put(AbstractFish.class, Fish.class); +- bukkitMap.put(AbstractSchoolingFish.class, Fish.class); // close enough ++ bukkitMap.put(AbstractSchoolingFish.class, io.papermc.paper.entity.SchoolableFish.class); + bukkitMap.put(FlyingMob.class, Flying.class); + bukkitMap.put(net.minecraft.world.entity.animal.Fox.class, Fox.class); + bukkitMap.put(net.minecraft.world.entity.monster.Ghast.class, Ghast.class); +diff --git a/src/main/java/io/papermc/paper/entity/PaperSchoolableFish.java b/src/main/java/io/papermc/paper/entity/PaperSchoolableFish.java +new file mode 100644 +index 0000000000000000000000000000000000000000..41bf71d116ffc5431586ce54abba7f8def6c1dcf +--- /dev/null ++++ b/src/main/java/io/papermc/paper/entity/PaperSchoolableFish.java +@@ -0,0 +1,52 @@ ++package io.papermc.paper.entity; ++ ++import net.minecraft.world.entity.animal.AbstractSchoolingFish; ++import org.bukkit.craftbukkit.CraftServer; ++import org.bukkit.craftbukkit.entity.CraftFish; ++import org.jetbrains.annotations.NotNull; ++ ++public class PaperSchoolableFish extends CraftFish implements SchoolableFish { ++ ++ public PaperSchoolableFish(CraftServer server, AbstractSchoolingFish entity) { ++ super(server, entity); ++ } ++ ++ @Override ++ public AbstractSchoolingFish getHandle() { ++ return (AbstractSchoolingFish) super.getHandle(); ++ } ++ ++ @Override ++ public void startFollowing(@NotNull SchoolableFish fish) { ++ if (this.getHandle().isFollower()) { // If following a fish already, properly remove the old one ++ this.stopFollowing(); ++ } ++ ++ this.getHandle().startFollowing(((PaperSchoolableFish) fish).getHandle()); ++ } ++ ++ @Override ++ public void stopFollowing() { ++ this.getHandle().stopFollowing(); ++ } ++ ++ @Override ++ public int getSchoolSize() { ++ return this.getHandle().schoolSize; ++ } ++ ++ @Override ++ public int getMaxSchoolSize() { ++ return this.getHandle().getMaxSchoolSize(); ++ } ++ ++ @Override ++ public SchoolableFish getSchoolLeader() { ++ AbstractSchoolingFish leader = this.getHandle().leader; ++ if (leader == null) { ++ return null; ++ } ++ ++ return (SchoolableFish) leader.getBukkitEntity(); ++ } ++} +diff --git a/src/main/java/net/minecraft/world/entity/animal/AbstractSchoolingFish.java b/src/main/java/net/minecraft/world/entity/animal/AbstractSchoolingFish.java +index 30095df7b64cfda4931dbfa22549ff5abefd53e0..c8ae49f58c254119c0e64a4e1501ebc5a70f9a46 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/AbstractSchoolingFish.java ++++ b/src/main/java/net/minecraft/world/entity/animal/AbstractSchoolingFish.java +@@ -51,6 +51,7 @@ public abstract class AbstractSchoolingFish extends AbstractFish { + } + + public void stopFollowing() { ++ if (this.leader == null) return; // Avoid NPE, plugins can now set the leader and certain fish goals might cause this method to be called + this.leader.removeFollower(); + this.leader = null; + } +diff --git a/src/main/java/net/minecraft/world/entity/animal/Bee.java b/src/main/java/net/minecraft/world/entity/animal/Bee.java +index a48a29b5b1963db679b053f3530f64d2b9560290..a5eab5cdb761e3e9d37ea66287f26a2c3345182d 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/Bee.java ++++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java +@@ -565,11 +565,13 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { + this.setFlag(4, hasStung); + } + ++ public net.kyori.adventure.util.TriState rollingOverride = net.kyori.adventure.util.TriState.NOT_SET; // Paper - Rolling override + public boolean isRolling() { + return this.getFlag(2); + } + + public void setRolling(boolean nearTarget) { ++ nearTarget = rollingOverride.toBooleanOrElse(nearTarget); // Paper - Rolling override + this.setFlag(2, nearTarget); + } + +diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java +index b9d7aa4aa0fc3472a2a0700e526778daf62bdc6f..48ac8c3f6e00c3c2dc67b6c994be7c0ac6dfcf81 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java ++++ b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java +@@ -50,6 +50,7 @@ public class Tadpole extends AbstractFish { + public int age; + protected static final ImmutableList<SensorType<? extends Sensor<? super Tadpole>>> SENSOR_TYPES = ImmutableList.of(SensorType.NEAREST_LIVING_ENTITIES, SensorType.NEAREST_PLAYERS, SensorType.HURT_BY, SensorType.FROG_TEMPTATIONS); + protected static final ImmutableList<MemoryModuleType<?>> MEMORY_TYPES = ImmutableList.of(MemoryModuleType.LOOK_TARGET, MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, MemoryModuleType.WALK_TARGET, MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE, MemoryModuleType.PATH, MemoryModuleType.NEAREST_VISIBLE_ADULT, MemoryModuleType.TEMPTATION_COOLDOWN_TICKS, MemoryModuleType.IS_TEMPTED, MemoryModuleType.TEMPTING_PLAYER, MemoryModuleType.BREED_TARGET, MemoryModuleType.IS_PANICKING); ++ public boolean ageLocked; // Paper + + public Tadpole(EntityType<? extends AbstractFish> type, Level world) { + super(type, world); +@@ -102,7 +103,7 @@ public class Tadpole extends AbstractFish { + @Override + public void aiStep() { + super.aiStep(); +- if (!this.level().isClientSide) { ++ if (!this.level().isClientSide && !this.ageLocked) { // Paper + this.setAge(this.age + 1); + } + +@@ -112,12 +113,14 @@ public class Tadpole extends AbstractFish { + public void addAdditionalSaveData(CompoundTag nbt) { + super.addAdditionalSaveData(nbt); + nbt.putInt("Age", this.age); ++ nbt.putBoolean("AgeLocked", this.ageLocked); // Paper + } + + @Override + public void readAdditionalSaveData(CompoundTag nbt) { + super.readAdditionalSaveData(nbt); + this.setAge(nbt.getInt("Age")); ++ this.ageLocked = nbt.getBoolean("AgeLocked"); // Paper + } + + @Nullable +@@ -169,6 +172,7 @@ public class Tadpole extends AbstractFish { + Bucketable.saveDefaultDataToBucketTag(this, stack); + CustomData.update(DataComponents.BUCKET_ENTITY_DATA, stack, (nbttagcompound) -> { + nbttagcompound.putInt("Age", this.getAge()); ++ nbttagcompound.putBoolean("AgeLocked", this.ageLocked); // Paper + }); + } + +@@ -179,6 +183,7 @@ public class Tadpole extends AbstractFish { + this.setAge(nbt.getInt("Age")); + } + ++ this.ageLocked = nbt.getBoolean("AgeLocked"); // Paper + } + + @Override +@@ -210,6 +215,7 @@ public class Tadpole extends AbstractFish { + } + + private void ageUp(int seconds) { ++ if (this.ageLocked) return; // Paper + this.setAge(this.age + seconds * 20); + } + +diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java b/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java +index 709237639ef0ec1cb623f270302a27b0072e8685..74151d69380e4adede40c7d7fc20834553706730 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java ++++ b/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java +@@ -779,6 +779,15 @@ public abstract class AbstractHorse extends Animal implements ContainerListener, + + } + ++ // Paper start - Horse API ++ public void setMouthOpen(boolean open) { ++ this.setFlag(FLAG_OPEN_MOUTH, open); ++ } ++ public boolean isMouthOpen() { ++ return this.getFlag(FLAG_OPEN_MOUTH); ++ } ++ // Paper end - Horse API ++ + @Override + public InteractionResult mobInteract(Player player, InteractionHand hand) { + if (!this.isVehicle() && !this.isBaby()) { +@@ -821,6 +830,11 @@ public abstract class AbstractHorse extends Animal implements ContainerListener, + this.setFlag(16, eatingGrass); + } + ++ // Paper start - Horse API ++ public void setForceStanding(boolean standing) { ++ this.setFlag(FLAG_STANDING, standing); ++ } ++ // Paper end - Horse API + public void setStanding(boolean angry) { + if (angry) { + this.setEating(false); +diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java b/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java +index 04842dd7b9beabcecbd492d0b98faaebeea1a5d9..d5808d0c190877554a4a8191f68e8b4a36f2ff46 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java ++++ b/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java +@@ -71,11 +71,12 @@ public class Llama extends AbstractChestedHorse implements VariantHolder<Llama.V + @Nullable + private Llama caravanHead; + @Nullable +- private Llama caravanTail; ++ public Llama caravanTail; // Paper + + public Llama(EntityType<? extends Llama> type, Level world) { + super(type, world); + this.getNavigation().setRequiredPathLength(40.0F); ++ this.maxDomestication = 30; // Paper - Missing entity API; configure max temper instead of a hardcoded value + } + + public boolean isTraderLlama() { +@@ -294,7 +295,7 @@ public class Llama extends AbstractChestedHorse implements VariantHolder<Llama.V + + @Override + public int getMaxTemper() { +- return 30; ++ return super.getMaxTemper(); // Paper - Missing entity API; delegate to parent + } + + @Override +diff --git a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java +index af721305a3b31f4aa9a36dfbc1cbe0cd278fa6ad..8415f7e1f8c391961dc5b0669da1ab4f8ae4950d 100644 +--- a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java ++++ b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java +@@ -86,6 +86,11 @@ public class WitherBoss extends Monster implements RangedAttackMob { + return !entityliving.getType().is(EntityTypeTags.WITHER_FRIENDS) && entityliving.attackable(); + }; + private static final TargetingConditions TARGETING_CONDITIONS = TargetingConditions.forCombat().range(20.0D).selector(WitherBoss.LIVING_ENTITY_SELECTOR); ++ // Paper start ++ private boolean canPortal = false; ++ ++ public void setCanTravelThroughPortals(boolean canPortal) { this.canPortal = canPortal; } ++ // Paper end + + public WitherBoss(EntityType<? extends WitherBoss> type, Level world) { + super(type, world); +@@ -591,7 +596,7 @@ public class WitherBoss extends Monster implements RangedAttackMob { + + @Override + public boolean canUsePortal(boolean allowVehicles) { +- return false; ++ return this.canPortal; // Paper + } + + @Override +diff --git a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java +index 0ddbebbe6eea00c379f2a250f7a44ba9313c1de1..4b798695af365dc97cbbbd09f370b8fc425f9ed6 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java ++++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java +@@ -428,6 +428,16 @@ public class EnderMan extends Monster implements NeutralMob { + this.entityData.set(EnderMan.DATA_STARED_AT, true); + } + ++ // Paper start ++ public void setCreepy(boolean creepy) { ++ this.entityData.set(EnderMan.DATA_CREEPY, creepy); ++ } ++ ++ public void setHasBeenStaredAt(boolean hasBeenStaredAt) { ++ this.entityData.set(EnderMan.DATA_STARED_AT, hasBeenStaredAt); ++ } ++ // Paper end ++ + @Override + public boolean requiresCustomPersistence() { + return super.requiresCustomPersistence() || this.getCarriedBlock() != null; +diff --git a/src/main/java/net/minecraft/world/entity/monster/Ghast.java b/src/main/java/net/minecraft/world/entity/monster/Ghast.java +index 71259b92a01a4feca270a250b1964f25f6da2d33..a8c8c03e972aa6352843cf4c3e4aebfb8f493125 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/Ghast.java ++++ b/src/main/java/net/minecraft/world/entity/monster/Ghast.java +@@ -66,6 +66,12 @@ public class Ghast extends FlyingMob implements Enemy { + return this.explosionPower; + } + ++ // Paper start ++ public void setExplosionPower(int explosionPower) { ++ this.explosionPower = explosionPower; ++ } ++ // Paper end ++ + @Override + protected boolean shouldDespawnInPeaceful() { + return true; +diff --git a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java +index 7f4c186a757e51ac788ec664a2c75dc7d7ce0eb3..a5d13b8bf7d0b6423ef428042e1134f5999cc24b 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java ++++ b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java +@@ -207,6 +207,12 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder { + } + + public void startConverting(@Nullable UUID uuid, int delay) { ++ // Paper start - missing entity behaviour api - converting without entity event ++ this.startConverting(uuid, delay, true); ++ } ++ ++ public void startConverting(@Nullable UUID uuid, int delay, boolean broadcastEntityEvent) { ++ // Paper end - missing entity behaviour api - converting without entity event + this.conversionStarter = uuid; + this.villagerConversionTime = delay; + this.getEntityData().set(ZombieVillager.DATA_CONVERTING_ID, true); +@@ -214,7 +220,7 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder { + this.removeEffect(MobEffects.WEAKNESS, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.CONVERSION); + this.addEffect(new MobEffectInstance(MobEffects.DAMAGE_BOOST, delay, Math.min(this.level().getDifficulty().getId() - 1, 0)), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.CONVERSION); + // CraftBukkit end +- this.level().broadcastEntityEvent(this, (byte) 16); ++ if (broadcastEntityEvent) this.level().broadcastEntityEvent(this, (byte) 16); // Paper - missing entity behaviour api - converting without entity event + } + + @Override +diff --git a/src/main/java/net/minecraft/world/entity/projectile/ThrownTrident.java b/src/main/java/net/minecraft/world/entity/projectile/ThrownTrident.java +index 20a9dae93ee17bdef16b67a2db8e5e78b94d2c53..3888f13a49af26b0ece8813b607c20fc380cecd5 100644 +--- a/src/main/java/net/minecraft/world/entity/projectile/ThrownTrident.java ++++ b/src/main/java/net/minecraft/world/entity/projectile/ThrownTrident.java +@@ -114,6 +114,20 @@ public class ThrownTrident extends AbstractArrow { + return (Boolean) this.entityData.get(ThrownTrident.ID_FOIL); + } + ++ // Paper start ++ public void setFoil(boolean foil) { ++ this.entityData.set(ThrownTrident.ID_FOIL, foil); ++ } ++ ++ public int getLoyalty() { ++ return this.entityData.get(ThrownTrident.ID_LOYALTY); ++ } ++ ++ public void setLoyalty(byte loyalty) { ++ this.entityData.set(ThrownTrident.ID_LOYALTY, loyalty); ++ } ++ // Paper end ++ + @Nullable + @Override + protected EntityHitResult findHitEntity(Vec3 currentPosition, Vec3 nextPosition) { +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractHorse.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractHorse.java +index 3952e52b94c1cc97e1d2d3885f59d7690efb74ad..9bcc0931510607b8fbd01233e2b3c346369b214d 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractHorse.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractHorse.java +@@ -114,4 +114,36 @@ public abstract class CraftAbstractHorse extends CraftAnimals implements Abstrac + public AbstractHorseInventory getInventory() { + return new CraftSaddledInventory(getHandle().inventory); + } ++ ++ // Paper start - Horse API ++ @Override ++ public boolean isEatingGrass() { ++ return this.getHandle().isEating(); ++ } ++ ++ @Override ++ public void setEatingGrass(boolean eating) { ++ this.getHandle().setEating(eating); ++ } ++ ++ @Override ++ public boolean isRearing() { ++ return this.getHandle().isStanding(); ++ } ++ ++ @Override ++ public void setRearing(boolean rearing) { ++ this.getHandle().setForceStanding(rearing); ++ } ++ ++ @Override ++ public boolean isEating() { ++ return this.getHandle().isMouthOpen(); ++ } ++ ++ @Override ++ public void setEating(boolean eating) { ++ this.getHandle().setMouthOpen(eating); ++ } ++ // Paper end - Horse API + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftAreaEffectCloud.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftAreaEffectCloud.java +index 17bffb45453f15328ca91794e26f6be1defef700..6591513bb62226b6f85fd2ef9f6ebe376f0f7362 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftAreaEffectCloud.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftAreaEffectCloud.java +@@ -229,4 +229,17 @@ public class CraftAreaEffectCloud extends CraftEntity implements AreaEffectCloud + this.getHandle().setOwner(null); + } + } ++ ++ // Paper start - owner API ++ @Override ++ public java.util.UUID getOwnerUniqueId() { ++ return this.getHandle().ownerUUID; ++ } ++ ++ @Override ++ public void setOwnerUniqueId(final java.util.UUID ownerUuid) { ++ this.getHandle().setOwner(null); ++ this.getHandle().ownerUUID = ownerUuid; ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftBat.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftBat.java +index b0a3531476f5a05ae846b68d825eddc35ebddea9..1bb72f28085f3885bec068b586ec222111044884 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftBat.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftBat.java +@@ -27,4 +27,25 @@ public class CraftBat extends CraftAmbient implements Bat { + public void setAwake(boolean state) { + this.getHandle().setResting(!state); + } ++ // Paper start ++ @Override ++ public org.bukkit.Location getTargetLocation() { ++ net.minecraft.core.BlockPos pos = this.getHandle().targetPosition; ++ if (pos == null) { ++ return null; ++ } ++ ++ return io.papermc.paper.util.MCUtil.toLocation(this.getHandle().level(), pos); ++ } ++ ++ @Override ++ public void setTargetLocation(org.bukkit.Location location) { ++ net.minecraft.core.BlockPos pos = null; ++ if (location != null) { ++ pos = io.papermc.paper.util.MCUtil.toBlockPosition(location); ++ } ++ ++ this.getHandle().targetPosition = pos; ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftBee.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftBee.java +index cfff1be6a4a4936a2dadb2590abc3d33c123d048..3dac93b0ab5d5acf5b33dc4b0efed60319eb657b 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftBee.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftBee.java +@@ -86,4 +86,42 @@ public class CraftBee extends CraftAnimals implements Bee { + public void setCannotEnterHiveTicks(int ticks) { + this.getHandle().setStayOutOfHiveCountdown(ticks); + } ++ // Paper start ++ @Override ++ public void setRollingOverride(net.kyori.adventure.util.TriState rolling) { ++ this.getHandle().rollingOverride = rolling; ++ ++ this.getHandle().setRolling(this.getHandle().isRolling()); // Refresh rolling state ++ } ++ ++ @Override ++ public boolean isRolling() { ++ return this.getRollingOverride().toBooleanOrElse(this.getHandle().isRolling()); ++ } ++ ++ @Override ++ public net.kyori.adventure.util.TriState getRollingOverride() { ++ return this.getHandle().rollingOverride; ++ } ++ ++ @Override ++ public void setCropsGrownSincePollination(int crops) { ++ this.getHandle().numCropsGrownSincePollination = crops; ++ } ++ ++ @Override ++ public int getCropsGrownSincePollination() { ++ return this.getHandle().numCropsGrownSincePollination; ++ } ++ ++ @Override ++ public void setTicksSincePollination(int ticks) { ++ this.getHandle().ticksWithoutNectarSinceExitingHive = ticks; ++ } ++ ++ @Override ++ public int getTicksSincePollination() { ++ return this.getHandle().ticksWithoutNectarSinceExitingHive; ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftCat.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftCat.java +index 57664124968e6268ad6699c6bd932981cc2fe6ba..88e876da7df64b68a5b71fd1deab75b59c5a64e3 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftCat.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftCat.java +@@ -139,4 +139,26 @@ public class CraftCat extends CraftTameableAnimal implements Cat { + return this.getKey().hashCode(); + } + } ++ ++ // Paper start - More cat api ++ @Override ++ public void setLyingDown(boolean lyingDown) { ++ this.getHandle().setLying(lyingDown); ++ } ++ ++ @Override ++ public boolean isLyingDown() { ++ return this.getHandle().isLying(); ++ } ++ ++ @Override ++ public void setHeadUp(boolean headUp) { ++ this.getHandle().setRelaxStateOne(headUp); ++ } ++ ++ @Override ++ public boolean isHeadUp() { ++ return this.getHandle().isRelaxStateOne(); ++ } ++ // Paper end - More cat api + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftChicken.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftChicken.java +index 64b75682a936e071353707f7615d6ff512fd617d..96f6e2fd9c6b20d34122abfe5c7fba732502d5a0 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftChicken.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftChicken.java +@@ -18,4 +18,26 @@ public class CraftChicken extends CraftAnimals implements Chicken { + public String toString() { + return "CraftChicken"; + } ++ ++ // Paper start ++ @Override ++ public boolean isChickenJockey() { ++ return this.getHandle().isChickenJockey(); ++ } ++ ++ @Override ++ public void setIsChickenJockey(boolean isChickenJockey) { ++ this.getHandle().setChickenJockey(isChickenJockey); ++ } ++ ++ @Override ++ public int getEggLayTime() { ++ return this.getHandle().eggTime; ++ } ++ ++ @Override ++ public void setEggLayTime(int eggLayTime) { ++ this.getHandle().eggTime = eggLayTime; ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftCod.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftCod.java +index fa0bf7db880063427ba12df1df1c72240fff93e9..63e6b07e3b159c74d9ef17be20b5ab43d07f0f5f 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftCod.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftCod.java +@@ -3,7 +3,7 @@ package org.bukkit.craftbukkit.entity; + import org.bukkit.craftbukkit.CraftServer; + import org.bukkit.entity.Cod; + +-public class CraftCod extends CraftFish implements Cod { ++public class CraftCod extends io.papermc.paper.entity.PaperSchoolableFish implements Cod { // Paper - School Fish API + + public CraftCod(CraftServer server, net.minecraft.world.entity.animal.Cod entity) { + super(server, entity); +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftDolphin.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftDolphin.java +index 5bae70ad160d0d0912aa9ef054c5515812957289..83867b9c5497e6e793b21c482646cc419587e182 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftDolphin.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftDolphin.java +@@ -18,4 +18,36 @@ public class CraftDolphin extends CraftAgeable implements Dolphin { + public String toString() { + return "CraftDolphin"; + } ++ ++ // Paper start - Missing Dolphin API ++ @Override ++ public int getMoistness() { ++ return this.getHandle().getMoistnessLevel(); ++ } ++ ++ @Override ++ public void setMoistness(int moistness) { ++ this.getHandle().setMoisntessLevel(moistness); ++ } ++ ++ @Override ++ public void setHasFish(boolean hasFish) { ++ this.getHandle().setGotFish(hasFish); ++ } ++ ++ @Override ++ public boolean hasFish() { ++ return this.getHandle().gotFish(); ++ } ++ ++ @Override ++ public org.bukkit.Location getTreasureLocation() { ++ return io.papermc.paper.util.MCUtil.toLocation(this.getHandle().level(), this.getHandle().getTreasurePos()); ++ } ++ ++ @Override ++ public void setTreasureLocation(org.bukkit.Location location) { ++ this.getHandle().setTreasurePos(io.papermc.paper.util.MCUtil.toBlockPosition(location)); ++ } ++ // Paper end - Missing Dolphin API + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragonPart.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragonPart.java +index cc4194ac9d7501b5d15655674dade14d59cb6733..33ae03b78b01c005a291a343b42507fb539e81a6 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragonPart.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragonPart.java +@@ -51,6 +51,13 @@ public class CraftEnderDragonPart extends CraftComplexPart implements EnderDrago + this.getParent().setHealth(health); + } + ++ // Paper start - entity heal API ++ @Override ++ public void heal(final double amount, final org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason reason) { ++ this.getParent().heal(amount, reason); ++ } ++ // Paper end - entity heal API ++ + @Override + public double getAbsorptionAmount() { + return this.getParent().getAbsorptionAmount(); +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderman.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderman.java +index 21dc209e6f98b6306833b41e2763e746047d5a94..983b9d6ddb58eff297e96e5c8b28ec427efa267d 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderman.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderman.java +@@ -40,6 +40,28 @@ public class CraftEnderman extends CraftMonster implements Enderman { + this.getHandle().setCarriedBlock(blockData == null ? null : ((CraftBlockData) blockData).getState()); + } + ++ // Paper start ++ @Override ++ public boolean isScreaming() { ++ return this.getHandle().isCreepy(); ++ } ++ ++ @Override ++ public void setScreaming(boolean screaming) { ++ this.getHandle().setCreepy(screaming); ++ } ++ ++ @Override ++ public boolean hasBeenStaredAt() { ++ return this.getHandle().hasBeenStaredAt(); ++ } ++ ++ @Override ++ public void setHasBeenStaredAt(boolean hasBeenStaredAt) { ++ this.getHandle().setHasBeenStaredAt(hasBeenStaredAt); ++ } ++ // Paper end ++ + @Override + public EnderMan getHandle() { + return (EnderMan) this.entity; +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java +index fc0f0e841dc974d080e1abb9bbafb5165801131f..d657fd2c507a5b215aeab0a5f3e9c2ee892a27c8 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEndermite.java +@@ -28,4 +28,15 @@ public class CraftEndermite extends CraftMonster implements Endermite { + public void setPlayerSpawned(boolean playerSpawned) { + // Nop + } ++ // Paper start ++ @Override ++ public void setLifetimeTicks(int ticks) { ++ this.getHandle().life = ticks; ++ } ++ ++ @Override ++ public int getLifetimeTicks() { ++ return this.getHandle().life; ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +index 78afac72265e3f586c0203951b8237832fb7c6fb..888a75423ac90ca85308eeb6d67bac5348bf31e0 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +@@ -1082,4 +1082,27 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { + return set; + } + // Paper end - tracked players API ++ ++ // Paper start - missing entity api ++ @Override ++ public boolean isInvisible() { // Paper - moved up from LivingEntity ++ return this.getHandle().isInvisible(); ++ } ++ ++ @Override ++ public void setInvisible(boolean invisible) { // Paper - moved up from LivingEntity ++ this.getHandle().persistentInvisibility = invisible; ++ this.getHandle().setSharedFlag(Entity.FLAG_INVISIBLE, invisible); ++ } ++ ++ @Override ++ public void setNoPhysics(boolean noPhysics) { ++ this.getHandle().noPhysics = noPhysics; ++ } ++ ++ @Override ++ public boolean hasNoPhysics() { ++ return this.getHandle().noPhysics; ++ } ++ // Paper end - missing entity api + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java +index 142f3e3257afebb2e831fd0970678123d99a1717..1b084d63bdbb24dad45d28eed1693eb6e26e24dc 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java +@@ -84,6 +84,18 @@ public class CraftFireball extends AbstractProjectile implements Fireball { + return new Vector(delta.x, delta.y, delta.z); + } + ++ // Paper start - Expose power on fireball projectiles ++ @Override ++ public void setPower(final Vector power) { ++ this.setAcceleration(power); ++ } ++ ++ @Override ++ public Vector getPower() { ++ return this.getAcceleration(); ++ } ++ // Paper end - Expose power on fireball projectiles ++ + @Override + public AbstractHurtingProjectile getHandle() { + return (AbstractHurtingProjectile) this.entity; +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftFox.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftFox.java +index dd912be2933864236cd4fb35631d505972082d77..bb2b59ce9775a0d1dd9828885e57c14cf40d9f04 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftFox.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftFox.java +@@ -113,4 +113,41 @@ public class CraftFox extends CraftAnimals implements Fox { + public boolean isFaceplanted() { + return this.getHandle().isFaceplanted(); + } ++ ++ // Paper start - Add more fox behavior API ++ @Override ++ public void setInterested(boolean interested) { ++ this.getHandle().setIsInterested(interested); ++ } ++ ++ @Override ++ public boolean isInterested() { ++ return this.getHandle().isInterested(); ++ } ++ ++ @Override ++ public void setLeaping(boolean leaping) { ++ this.getHandle().setIsPouncing(leaping); ++ } ++ ++ @Override ++ public boolean isLeaping() { ++ return this.getHandle().isPouncing(); ++ } ++ ++ @Override ++ public void setDefending(boolean defending) { ++ this.getHandle().setDefending(defending); ++ } ++ ++ @Override ++ public boolean isDefending() { ++ return this.getHandle().isDefending(); ++ } ++ ++ @Override ++ public void setFaceplanted(boolean faceplanted) { ++ this.getHandle().setFaceplanted(faceplanted); ++ } ++ // Paper end - Add more fox behavior API + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftGhast.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftGhast.java +index 2cec61a1bb050c1ef81c5fc3d0afafe9ff29d459..97fa4e1e70203194bd939618b2fad92665af6d59 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftGhast.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftGhast.java +@@ -28,4 +28,17 @@ public class CraftGhast extends CraftFlying implements Ghast, CraftEnemy { + public void setCharging(boolean flag) { + this.getHandle().setCharging(flag); + } ++ ++ // Paper start ++ @Override ++ public int getExplosionPower() { ++ return this.getHandle().getExplosionPower(); ++ } ++ ++ @Override ++ public void setExplosionPower(int explosionPower) { ++ com.google.common.base.Preconditions.checkArgument(explosionPower >= 0 && explosionPower <= 127, "The explosion power has to be between 0 and 127"); ++ this.getHandle().setExplosionPower(explosionPower); ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +index a4a30f52f45bf91dc88d53f5d6a376ed739d09eb..6020c0c164595db4e2001edf0c1fbe99ed87682d 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +@@ -128,6 +128,13 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { + } + } + ++ // Paper start - entity heal API ++ @Override ++ public void heal(final double amount, final org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason reason) { ++ this.getHandle().heal((float) amount, reason); ++ } ++ // Paper end - entity heal API ++ + @Override + public double getAbsorptionAmount() { + return this.getHandle().getAbsorptionAmount(); +@@ -939,14 +946,29 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { + + @Override + public boolean isInvisible() { +- return this.getHandle().isInvisible(); ++ return super.isInvisible(); // Paper - move invisibility up to Entity - diff on change + } + + @Override + public void setInvisible(boolean invisible) { +- this.getHandle().persistentInvisibility = invisible; +- this.getHandle().setSharedFlag(5, invisible); ++ super.setInvisible(invisible); // Paper - move invisibility up to Entity + } ++ // Paper start ++ @Override ++ public float getSidewaysMovement() { ++ return this.getHandle().xxa; ++ } ++ ++ @Override ++ public float getForwardsMovement() { ++ return this.getHandle().zza; ++ } ++ ++ @Override ++ public float getUpwardsMovement() { ++ return this.getHandle().yya; ++ } ++ // Paper end + + // Paper start + @Override +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java +index bf297388c75521266c93580a9caafe6bad70ab45..351f42842b780d053cd2e5bad9ae299449141b10 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLlama.java +@@ -58,4 +58,36 @@ public class CraftLlama extends CraftChestedHorse implements Llama, com.destroys + public String toString() { + return "CraftLlama"; + } ++ ++ // Paper start ++ @Override ++ public boolean inCaravan() { ++ return this.getHandle().inCaravan(); ++ } ++ ++ @Override ++ public void joinCaravan(@org.jetbrains.annotations.NotNull Llama llama) { ++ this.getHandle().joinCaravan(((CraftLlama) llama).getHandle()); ++ } ++ ++ @Override ++ public void leaveCaravan() { ++ this.getHandle().leaveCaravan(); ++ } ++ ++ @Override ++ public boolean hasCaravanTail() { ++ return this.getHandle().hasCaravanTail(); ++ } ++ ++ @Override ++ public Llama getCaravanHead() { ++ return this.getHandle().getCaravanHead() == null ? null : (Llama) this.getHandle().getCaravanHead().getBukkitEntity(); ++ } ++ ++ @Override ++ public Llama getCaravanTail() { ++ return this.getHandle().caravanTail == null ? null : (Llama) this.getHandle().caravanTail.getBukkitEntity(); ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartHopper.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartHopper.java +index 17f5684cba9d3ed22d9925d1951520cc4751dfe2..3a3563a1bdbc0d84d973b3a04b50b78b4bc3d379 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartHopper.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartHopper.java +@@ -33,4 +33,20 @@ public final class CraftMinecartHopper extends CraftMinecartContainer implements + public void setEnabled(boolean enabled) { + ((MinecartHopper) this.getHandle()).setEnabled(enabled); + } ++ // Paper start ++ @Override ++ public net.minecraft.world.entity.vehicle.MinecartHopper getHandle() { ++ return (net.minecraft.world.entity.vehicle.MinecartHopper) super.getHandle(); ++ } ++ ++ @Override ++ public int getPickupCooldown() { ++ throw new UnsupportedOperationException("Hopper minecarts don't have cooldowns"); ++ } ++ ++ @Override ++ public void setPickupCooldown(int cooldown) { ++ throw new UnsupportedOperationException("Hopper minecarts don't have cooldowns"); ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java +index bd739428a7e5e35ebcdb70cd187379b3d222339b..7cf42f62d91c131b1cab576979f85c58c3cecb3b 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java +@@ -146,4 +146,16 @@ public abstract class CraftMob extends CraftLivingEntity implements Mob { + return getHandle().getMaxHeadXRot(); + } + // Paper end ++ ++ // Paper start ++ @Override ++ public boolean isAggressive() { ++ return this.getHandle().isAggressive(); ++ } ++ ++ @Override ++ public void setAggressive(boolean aggressive) { ++ this.getHandle().setAggressive(aggressive); ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPanda.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPanda.java +index 5467e4a74b70ff57b49d9e6bc686c493178f8511..01d104d91de9e1319d27e39d3f474318c7809486 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPanda.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPanda.java +@@ -41,6 +41,38 @@ public class CraftPanda extends CraftAnimals implements Panda { + this.getHandle().setHiddenGene(CraftPanda.toNms(gene)); + } + ++ // Paper start - Panda API ++ @Override ++ public void setSneezeTicks(int ticks) { ++ this.getHandle().setSneezeCounter(ticks); ++ } ++ ++ @Override ++ public int getSneezeTicks() { ++ return this.getHandle().getSneezeCounter(); ++ } ++ ++ @Override ++ public void setEatingTicks(int ticks) { ++ this.getHandle().setEatCounter(ticks); ++ } ++ ++ @Override ++ public int getEatingTicks() { ++ return this.getHandle().getEatCounter(); ++ } ++ ++ @Override ++ public void setUnhappyTicks(int ticks) { ++ this.getHandle().setUnhappyCounter(ticks); ++ } ++ ++ @Override ++ public Gene getCombinedGene() { ++ return CraftPanda.fromNms(this.getHandle().getVariant()); ++ } ++ // Paper end - Panda API ++ + @Override + public boolean isRolling() { + return this.getHandle().isRolling(); +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java +index 9304e201db1ec96d0916aa8ea781f3e4bc7991e6..83e77c6d287d8e239d2f55f3e9f19ef74946be7c 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPhantom.java +@@ -44,5 +44,17 @@ public class CraftPhantom extends CraftFlying implements Phantom, CraftEnemy { + public void setShouldBurnInDay(boolean shouldBurnInDay) { + getHandle().setShouldBurnInDay(shouldBurnInDay); + } ++ ++ @Override ++ public org.bukkit.Location getAnchorLocation() { ++ net.minecraft.core.BlockPos pos = this.getHandle().anchorPoint; ++ return io.papermc.paper.util.MCUtil.toLocation(this.getHandle().level(), pos); ++ } ++ ++ @Override ++ public void setAnchorLocation(org.bukkit.Location location) { ++ com.google.common.base.Preconditions.checkArgument(location != null, "location cannot be null"); ++ this.getHandle().anchorPoint = io.papermc.paper.util.MCUtil.toBlockPosition(location); ++ } + // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPiglin.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPiglin.java +index f5ecb8c1dc92e5a4b123effd2859123b17a586d3..5124a383b60b2c8de89fa992547d0c61db760c21 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPiglin.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPiglin.java +@@ -84,4 +84,37 @@ public class CraftPiglin extends CraftPiglinAbstract implements Piglin, com.dest + public String toString() { + return "CraftPiglin"; + } ++ // Paper start ++ @Override ++ public void setChargingCrossbow(boolean chargingCrossbow) { ++ this.getHandle().setChargingCrossbow(chargingCrossbow); ++ } ++ ++ @Override ++ public boolean isChargingCrossbow() { ++ return this.getHandle().isChargingCrossbow(); ++ } ++ ++ @Override ++ public void setDancing(boolean dancing) { ++ if (dancing) { ++ this.getHandle().getBrain().setMemory(net.minecraft.world.entity.ai.memory.MemoryModuleType.DANCING, true); ++ this.getHandle().getBrain().setMemory(net.minecraft.world.entity.ai.memory.MemoryModuleType.CELEBRATE_LOCATION, this.getHandle().getOnPos()); ++ } else { ++ this.getHandle().getBrain().eraseMemory(net.minecraft.world.entity.ai.memory.MemoryModuleType.DANCING); ++ this.getHandle().getBrain().eraseMemory(net.minecraft.world.entity.ai.memory.MemoryModuleType.CELEBRATE_LOCATION); ++ } ++ } ++ ++ @Override ++ public void setDancing(long duration) { ++ this.getHandle().getBrain().setMemoryWithExpiry(net.minecraft.world.entity.ai.memory.MemoryModuleType.DANCING, true, duration); ++ this.getHandle().getBrain().setMemoryWithExpiry(net.minecraft.world.entity.ai.memory.MemoryModuleType.CELEBRATE_LOCATION, this.getHandle().getOnPos(), duration); ++ } ++ ++ @Override ++ public boolean isDancing() { ++ return this.getHandle().isDancing(); ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPolarBear.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPolarBear.java +index c7aec6f28e5d3546235b30f6b1112440a76163c5..fe075cfdf3097d6cb768e71b8cc360abb8eaf367 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPolarBear.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPolarBear.java +@@ -17,4 +17,16 @@ public class CraftPolarBear extends CraftAnimals implements PolarBear { + public String toString() { + return "CraftPolarBear"; + } ++ ++ // Paper start ++ @Override ++ public boolean isStanding() { ++ return this.getHandle().isStanding(); ++ } ++ ++ @Override ++ public void setStanding(boolean standing) { ++ this.getHandle().setStanding(standing); ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftRabbit.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftRabbit.java +index 6b48b117a9cba12aae055c0ea981dfb5bc03a86e..519ef701a7d6534f7cb516f6296b95ee521f661d 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftRabbit.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftRabbit.java +@@ -29,4 +29,15 @@ public class CraftRabbit extends CraftAnimals implements Rabbit { + public void setRabbitType(Type type) { + this.getHandle().setVariant(net.minecraft.world.entity.animal.Rabbit.Variant.values()[type.ordinal()]); + } ++ // Paper start ++ @Override ++ public void setMoreCarrotTicks(int ticks) { ++ this.getHandle().moreCarrotTicks = ticks; ++ } ++ ++ @Override ++ public int getMoreCarrotTicks() { ++ return this.getHandle().moreCarrotTicks; ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftRavager.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftRavager.java +index cae59f77c704a5b9515dc4917ed5fdc89631ecfb..09796ce15658e3f7c223a265a547a51ee729ed40 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftRavager.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftRavager.java +@@ -18,4 +18,35 @@ public class CraftRavager extends CraftRaider implements Ravager { + public String toString() { + return "CraftRavager"; + } ++ // Paper start - Missing Entity Behavior ++ @Override ++ public int getAttackTicks() { ++ return this.getHandle().getAttackTick(); ++ } ++ ++ @Override ++ public void setAttackTicks(int ticks) { ++ this.getHandle().attackTick = ticks; ++ } ++ ++ @Override ++ public int getStunnedTicks() { ++ return this.getHandle().getStunnedTick(); ++ } ++ ++ @Override ++ public void setStunnedTicks(int ticks) { ++ this.getHandle().stunnedTick = ticks; ++ } ++ ++ @Override ++ public int getRoarTicks() { ++ return this.getHandle().getRoarTick(); ++ } ++ ++ @Override ++ public void setRoarTicks(int ticks) { ++ this.getHandle().roarTick = ticks; ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftSalmon.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftSalmon.java +index 551c30cb0fb41dfe4a03663d34ecf9764566c215..7660cc21e936002ebb23510f0ec2b58d71e5157d 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftSalmon.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftSalmon.java +@@ -4,7 +4,7 @@ import com.google.common.base.Preconditions; + import org.bukkit.craftbukkit.CraftServer; + import org.bukkit.entity.Salmon; + +-public class CraftSalmon extends CraftFish implements Salmon { ++public class CraftSalmon extends io.papermc.paper.entity.PaperSchoolableFish implements Salmon { // Paper - Schooling Fish API + + public CraftSalmon(CraftServer server, net.minecraft.world.entity.animal.Salmon entity) { + super(server, entity); +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftTNTPrimed.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftTNTPrimed.java +index 36ab282e2c4060bdea4e57f3ab9dfef9f6cd622c..a61aec087fa7cec27a803668bdc1b9e6eb336755 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftTNTPrimed.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftTNTPrimed.java +@@ -67,4 +67,17 @@ public class CraftTNTPrimed extends CraftEntity implements TNTPrimed { + this.getHandle().owner = null; + } + } ++ ++ // Paper start ++ @Override ++ public void setBlockData(org.bukkit.block.data.BlockData data) { ++ com.google.common.base.Preconditions.checkArgument(data != null, "The visual block data of this tnt cannot be null. To reset it just set to the TNT default block data"); ++ this.getHandle().setBlockState(((org.bukkit.craftbukkit.block.data.CraftBlockData) data).getState()); ++ } ++ ++ @Override ++ public org.bukkit.block.data.BlockData getBlockData() { ++ return org.bukkit.craftbukkit.block.data.CraftBlockData.fromData(this.getHandle().getBlockState()); ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftTadpole.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftTadpole.java +index 451a9bfd9b9b6945e224f1bb05c7951ed934b4e3..d7c6a0bbc5671ea8f2488230c94df5146a1e98b9 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftTadpole.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftTadpole.java +@@ -28,4 +28,15 @@ public class CraftTadpole extends CraftFish implements org.bukkit.entity.Tadpole + public void setAge(int age) { + this.getHandle().age = age; + } ++ // Paper start ++ @Override ++ public void setAgeLock(boolean lock) { ++ this.getHandle().ageLocked = lock; ++ } ++ ++ @Override ++ public boolean getAgeLock() { ++ return this.getHandle().ageLocked; ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftTrident.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftTrident.java +index 33d6e8121755ad6cddacb4fc69e795f9831c27bd..e374b9f40eddca13b30855d25a2030f8df98138f 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftTrident.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftTrident.java +@@ -31,4 +31,27 @@ public class CraftTrident extends CraftAbstractArrow implements Trident { + public String toString() { + return "CraftTrident"; + } ++ ++ // Paper start ++ @Override ++ public boolean hasGlint() { ++ return this.getHandle().isFoil(); ++ } ++ ++ @Override ++ public void setGlint(boolean glint) { ++ this.getHandle().setFoil(glint); ++ } ++ ++ @Override ++ public int getLoyaltyLevel() { ++ return this.getHandle().getLoyalty(); ++ } ++ ++ @Override ++ public void setLoyaltyLevel(int loyaltyLevel) { ++ com.google.common.base.Preconditions.checkArgument(loyaltyLevel >= 0 && loyaltyLevel <= 127, "The loyalty level has to be between 0 and 127"); ++ this.getHandle().setLoyalty((byte) loyaltyLevel); ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftTropicalFish.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftTropicalFish.java +index e3bde6d1c0e03407af1382a61748470063bb2e18..9e53c30801c700719c78c0fd521fd615c94e02c8 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftTropicalFish.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftTropicalFish.java +@@ -7,7 +7,7 @@ import org.bukkit.craftbukkit.CraftServer; + import org.bukkit.entity.TropicalFish; + import org.bukkit.entity.TropicalFish.Pattern; + +-public class CraftTropicalFish extends CraftFish implements TropicalFish { ++public class CraftTropicalFish extends io.papermc.paper.entity.PaperSchoolableFish implements TropicalFish { // Paper - Schooling Fish API + + public CraftTropicalFish(CraftServer server, net.minecraft.world.entity.animal.TropicalFish entity) { + super(server, entity); +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftVex.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftVex.java +index 1cfbe9c476f4a254edf3edf4b70696bbaba78558..e9ec3455eabc473e104b5342a615a38c1ac25a4f 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftVex.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftVex.java +@@ -29,6 +29,26 @@ public class CraftVex extends CraftMonster implements Vex { + public void setSummoner(org.bukkit.entity.Mob summoner) { + getHandle().setOwner(summoner == null ? null : ((CraftMob) summoner).getHandle()); + } ++ ++ @Override ++ public boolean hasLimitedLifetime() { ++ return this.getHandle().hasLimitedLife; ++ } ++ ++ @Override ++ public void setLimitedLifetime(boolean hasLimitedLifetime) { ++ this.getHandle().hasLimitedLife = hasLimitedLifetime; ++ } ++ ++ @Override ++ public int getLimitedLifetimeTicks() { ++ return this.getHandle().limitedLifeTicks; ++ } ++ ++ @Override ++ public void setLimitedLifetimeTicks(int ticks) { ++ this.getHandle().limitedLifeTicks = ticks; ++ } + // Paper end + + @Override +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillagerZombie.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillagerZombie.java +index e2a0c11867abee6add8775259c54f2052de7b1ad..3aa23d9f22d5cd22231293fd7d1ca4cb79eb7cb3 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillagerZombie.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillagerZombie.java +@@ -60,13 +60,20 @@ public class CraftVillagerZombie extends CraftZombie implements ZombieVillager { + + @Override + public void setConversionTime(int time) { ++ // Paper start - missing entity behaviour api - converting without entity event ++ this.setConversionTime(time, true); ++ } ++ ++ @Override ++ public void setConversionTime(int time, boolean broadcastEntityEvent) { ++ // Paper end - missing entity behaviour api - converting without entity event + if (time < 0) { + this.getHandle().villagerConversionTime = -1; + this.getHandle().getEntityData().set(net.minecraft.world.entity.monster.ZombieVillager.DATA_CONVERTING_ID, false); + this.getHandle().conversionStarter = null; + this.getHandle().removeEffect(MobEffects.DAMAGE_BOOST, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.CONVERSION); + } else { +- this.getHandle().startConverting(null, time); ++ this.getHandle().startConverting(null, time, broadcastEntityEvent); // Paper - missing entity behaviour api - converting without entity event + } + } + +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftWanderingTrader.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftWanderingTrader.java +index 0e597394a3dd08f022614fc9777302fea581eb55..3cceefa0d6278924a19641a49bdf16bcdacb2233 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftWanderingTrader.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWanderingTrader.java +@@ -49,5 +49,25 @@ public class CraftWanderingTrader extends CraftAbstractVillager implements Wande + public boolean canDrinkMilk() { + return getHandle().canDrinkMilk; + } ++ ++ @Override ++ public org.bukkit.Location getWanderingTowards() { ++ net.minecraft.core.BlockPos pos = this.getHandle().getWanderTarget(); ++ if (pos == null) { ++ return null; ++ } ++ ++ return io.papermc.paper.util.MCUtil.toLocation(this.getHandle().level(), pos); ++ } ++ ++ @Override ++ public void setWanderingTowards(org.bukkit.Location location) { ++ net.minecraft.core.BlockPos pos = null; ++ if (location != null) { ++ pos = io.papermc.paper.util.MCUtil.toBlockPosition(location); ++ } ++ ++ this.getHandle().setWanderTarget(pos); ++ } + // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftWarden.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftWarden.java +index 794e4fe0a3fbd967f665b2707865c15491370c76..c284eb96a1e330078076cbe61f0f6e2ff4ed89bd 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftWarden.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWarden.java +@@ -37,6 +37,13 @@ public class CraftWarden extends CraftMonster implements org.bukkit.entity.Warde + return this.getHandle().getAngerManagement().getActiveAnger(((CraftEntity) entity).getHandle()); + } + ++ // Paper start ++ @Override ++ public int getHighestAnger() { ++ return this.getHandle().getAngerManagement().getActiveAnger(null); ++ } ++ // Paper end ++ + @Override + public void increaseAnger(Entity entity, int increase) { + Preconditions.checkArgument(entity != null, "Entity cannot be null"); +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java +index 1113533d281ed159bb735040fb1f913482debf3a..7881c6253c1d652c0c0d54a9a8accdf0a1ff0f3e 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWither.java +@@ -67,4 +67,36 @@ public class CraftWither extends CraftMonster implements Wither, com.destroystok + + this.getHandle().setInvulnerableTicks(ticks); + } ++ ++ // Paper start ++ @Override ++ public boolean isCharged() { ++ return getHandle().isPowered(); ++ } ++ ++ @Override ++ public int getInvulnerableTicks() { ++ return getHandle().getInvulnerableTicks(); ++ } ++ ++ @Override ++ public void setInvulnerableTicks(int ticks) { ++ getHandle().setInvulnerableTicks(ticks); ++ } ++ ++ @Override ++ public boolean canTravelThroughPortals() { ++ return getHandle().canUsePortal(false); ++ } ++ ++ @Override ++ public void setCanTravelThroughPortals(boolean value) { ++ getHandle().setCanTravelThroughPortals(value); ++ } ++ ++ @Override ++ public void enterInvulnerabilityPhase() { ++ this.getHandle().makeInvulnerable(); ++ } ++ // Paper end + } diff --git a/patches/server/0551-Fix-return-value-of-Block-applyBoneMeal-always-being.patch b/patches/server/0551-Fix-return-value-of-Block-applyBoneMeal-always-being.patch new file mode 100644 index 0000000000..40cc161932 --- /dev/null +++ b/patches/server/0551-Fix-return-value-of-Block-applyBoneMeal-always-being.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jason Penilla <[email protected]> +Date: Mon, 28 Jun 2021 18:16:52 -0700 +Subject: [PATCH] Fix return value of Block#applyBoneMeal always being false + + +diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java +index 54fb380a6896731a18c0100722d12099e590cbc9..9c8aac69f01db647e20d49d272ccc107a7edceaf 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java +@@ -558,7 +558,7 @@ public class CraftBlock implements Block { + } + } + +- return result == InteractionResult.SUCCESS && (event == null || !event.isCancelled()); ++ return result == InteractionResult.CONSUME && (event == null || !event.isCancelled()); // Paper - CONSUME is returned on success server-side (see BoneMealItem.applyBoneMeal and InteractionResult.sidedSuccess(boolean)) + } + + @Override diff --git a/patches/server/0552-Use-getChunkIfLoadedImmediately-in-places.patch b/patches/server/0552-Use-getChunkIfLoadedImmediately-in-places.patch new file mode 100644 index 0000000000..3d73d4e80f --- /dev/null +++ b/patches/server/0552-Use-getChunkIfLoadedImmediately-in-places.patch @@ -0,0 +1,53 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf <[email protected]> +Date: Mon, 8 Jul 2019 00:13:36 -0700 +Subject: [PATCH] Use getChunkIfLoadedImmediately in places + +This prevents us from hitting chunk loads for chunks at or less-than +ticket level 33 (yes getChunkIfLoaded will actually perform a chunk +load in that case). + +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index c05ccab70bd81d536f93352f30aab9b07b90876a..93656336af194994f59072fa89bfc338d89d76af 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -233,7 +233,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + public boolean hasEntityMoveEvent; // Paper - Add EntityMoveEvent + + public LevelChunk getChunkIfLoaded(int x, int z) { +- return this.chunkSource.getChunk(x, z, false); ++ return this.chunkSource.getChunkAtIfLoadedImmediately(x, z); // Paper - Use getChunkIfLoadedImmediately + } + + @Override +diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java +index 2f82f77ba6530dcdb98037aa627ef3cead758b2e..68cee32833a3d683852e67e3727d62b84fa60cc4 100644 +--- a/src/main/java/net/minecraft/world/level/Level.java ++++ b/src/main/java/net/minecraft/world/level/Level.java +@@ -180,6 +180,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + public CraftServer getCraftServer() { + return (CraftServer) Bukkit.getServer(); + } ++ // Paper start - Use getChunkIfLoadedImmediately ++ @Override ++ public boolean hasChunk(int chunkX, int chunkZ) { ++ return this.getChunkIfLoaded(chunkX, chunkZ) != null; ++ } ++ // Paper end - Use getChunkIfLoadedImmediately ++ + + public abstract ResourceKey<LevelStem> getTypeKey(); + +diff --git a/src/main/java/net/minecraft/world/level/gameevent/GameEventDispatcher.java b/src/main/java/net/minecraft/world/level/gameevent/GameEventDispatcher.java +index 13b34e89bd3e55df1bb1d4d0cf013bafae43f502..df6c97be1b278c97a20390be5d3e60f429383702 100644 +--- a/src/main/java/net/minecraft/world/level/gameevent/GameEventDispatcher.java ++++ b/src/main/java/net/minecraft/world/level/gameevent/GameEventDispatcher.java +@@ -56,7 +56,7 @@ public class GameEventDispatcher { + + for (int l1 = j; l1 <= i1; ++l1) { + for (int i2 = l; i2 <= k1; ++i2) { +- LevelChunk chunk = this.level.getChunkSource().getChunkNow(l1, i2); ++ LevelChunk chunk = (LevelChunk) this.level.getChunkIfLoadedImmediately(l1, i2); // Paper - Use getChunkIfLoadedImmediately + + if (chunk != null) { + for (int j2 = k; j2 <= j1; ++j2) { diff --git a/patches/server/0553-Fix-commands-from-signs-not-firing-command-events.patch b/patches/server/0553-Fix-commands-from-signs-not-firing-command-events.patch new file mode 100644 index 0000000000..c851f91672 --- /dev/null +++ b/patches/server/0553-Fix-commands-from-signs-not-firing-command-events.patch @@ -0,0 +1,121 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Fri, 9 Jul 2021 13:50:48 -0700 +Subject: [PATCH] Fix commands from signs not firing command events + +This patch changes sign command logic so that `run_command` click events: + - are logged to the console + - fire PlayerCommandPreprocessEvent + - work with double-slash commands like `//wand` + - sends failure messages to the player who clicked the sign + +diff --git a/src/main/java/io/papermc/paper/commands/DelegatingCommandSource.java b/src/main/java/io/papermc/paper/commands/DelegatingCommandSource.java +new file mode 100644 +index 0000000000000000000000000000000000000000..01a2bc1feec808790bb93618ce46adb9bea5a9c8 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/commands/DelegatingCommandSource.java +@@ -0,0 +1,42 @@ ++package io.papermc.paper.commands; ++ ++import net.minecraft.commands.CommandSource; ++import net.minecraft.commands.CommandSourceStack; ++import net.minecraft.network.chat.Component; ++import org.bukkit.command.CommandSender; ++ ++import java.util.UUID; ++ ++public class DelegatingCommandSource implements CommandSource { ++ ++ private final CommandSource delegate; ++ ++ public DelegatingCommandSource(CommandSource delegate) { ++ this.delegate = delegate; ++ } ++ ++ @Override ++ public void sendSystemMessage(Component message) { ++ delegate.sendSystemMessage(message); ++ } ++ ++ @Override ++ public boolean acceptsSuccess() { ++ return delegate.acceptsSuccess(); ++ } ++ ++ @Override ++ public boolean acceptsFailure() { ++ return delegate.acceptsFailure(); ++ } ++ ++ @Override ++ public boolean shouldInformAdmins() { ++ return delegate.shouldInformAdmins(); ++ } ++ ++ @Override ++ public CommandSender getBukkitSender(CommandSourceStack wrapper) { ++ return delegate.getBukkitSender(wrapper); ++ } ++} +diff --git a/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java +index 9861388bdca8d32b7e9a49a251088c98283d8234..86550d200922ee313019a21fe593c594c967f28b 100644 +--- a/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java ++++ b/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java +@@ -274,7 +274,17 @@ public class SignBlockEntity extends BlockEntity { + ClickEvent chatclickable = chatmodifier.getClickEvent(); + + if (chatclickable != null && chatclickable.getAction() == ClickEvent.Action.RUN_COMMAND) { +- player.getServer().getCommands().performPrefixedCommand(this.createCommandSourceStack(player, world, pos), chatclickable.getValue()); ++ // Paper start - Fix commands from signs not firing command events ++ String command = chatclickable.getValue().startsWith("/") ? chatclickable.getValue() : "/" + chatclickable.getValue(); ++ if (org.spigotmc.SpigotConfig.logCommands) { ++ LOGGER.info("{} issued server command: {}", player.getScoreboardName(), command); ++ } ++ io.papermc.paper.event.player.PlayerSignCommandPreprocessEvent event = new io.papermc.paper.event.player.PlayerSignCommandPreprocessEvent((org.bukkit.entity.Player) player.getBukkitEntity(), command, new org.bukkit.craftbukkit.util.LazyPlayerSet(player.getServer()), (org.bukkit.block.Sign) CraftBlock.at(this.level, this.worldPosition).getState(), front ? Side.FRONT : Side.BACK); ++ if (!event.callEvent()) { ++ return false; ++ } ++ player.getServer().getCommands().performPrefixedCommand(this.createCommandSourceStack(((org.bukkit.craftbukkit.entity.CraftPlayer) event.getPlayer()).getHandle(), world, pos), event.getMessage()); ++ // Paper end - Fix commands from signs not firing command events + flag1 = true; + } + } +@@ -314,8 +324,23 @@ public class SignBlockEntity extends BlockEntity { + String s = player == null ? "Sign" : player.getName().getString(); + Object object = player == null ? Component.literal("Sign") : player.getDisplayName(); + +- // CraftBukkit - commandSource +- return new CommandSourceStack(this.commandSource, Vec3.atCenterOf(pos), Vec2.ZERO, (ServerLevel) world, 2, s, (Component) object, world.getServer(), player); ++ // Paper start - Fix commands from signs not firing command events ++ CommandSource commandSource = this.level.paperConfig().misc.showSignClickCommandFailureMsgsToPlayer ? new io.papermc.paper.commands.DelegatingCommandSource(this.commandSource) { ++ @Override ++ public void sendSystemMessage(Component message) { ++ if (player instanceof final ServerPlayer serverPlayer) { ++ serverPlayer.sendSystemMessage(message); ++ } ++ } ++ ++ @Override ++ public boolean acceptsFailure() { ++ return true; ++ } ++ } : this.commandSource; ++ // Paper end - Fix commands from signs not firing command events ++ // CraftBukkit - this ++ return new CommandSourceStack(commandSource, Vec3.atCenterOf(pos), Vec2.ZERO, (ServerLevel) world, 2, s, (Component) object, world.getServer(), player); // Paper - Fix commands from signs not firing command events + } + + @Override +diff --git a/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java +index d113e54a30db16e2ad955170df6030d15de530d6..21b6f90cf5bd7087d1a0f512289d971f2c3e1afa 100644 +--- a/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java ++++ b/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java +@@ -61,7 +61,7 @@ public class BukkitCommandWrapper implements com.mojang.brigadier.Command<Comman + CommandSender sender = context.getSource().getBukkitSender(); + + try { +- return this.server.dispatchCommand(sender, context.getInput()) ? 1 : 0; ++ return this.server.dispatchCommand(sender, context.getRange().get(context.getInput())) ? 1 : 0; // Paper - Fix commands from signs not firing command events; actually use the StringRange from context + } catch (CommandException ex) { + sender.sendMessage(org.bukkit.ChatColor.RED + "An internal error occurred while attempting to perform this command"); + this.server.getLogger().log(Level.SEVERE, null, ex); diff --git a/patches/server/0554-Add-PlayerArmSwingEvent.patch b/patches/server/0554-Add-PlayerArmSwingEvent.patch new file mode 100644 index 0000000000..e08349e079 --- /dev/null +++ b/patches/server/0554-Add-PlayerArmSwingEvent.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Fri, 12 Mar 2021 19:22:21 -0800 +Subject: [PATCH] Add PlayerArmSwingEvent + + +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index fffefbed068a339f0db607d6734be611b273bbcf..0a5abb1a580310cc26ce3d64e6f878bde3bbfd0a 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -2488,7 +2488,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + } // Paper end - Call interact event + + // Arm swing animation +- PlayerAnimationEvent event = new PlayerAnimationEvent(this.getCraftPlayer(), (packet.getHand() == InteractionHand.MAIN_HAND) ? PlayerAnimationType.ARM_SWING : PlayerAnimationType.OFF_ARM_SWING); ++ io.papermc.paper.event.player.PlayerArmSwingEvent event = new io.papermc.paper.event.player.PlayerArmSwingEvent(this.getCraftPlayer(), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(packet.getHand())); // Paper - Add PlayerArmSwingEvent + this.cserver.getPluginManager().callEvent(event); + + if (event.isCancelled()) return; diff --git a/patches/server/0555-Fix-kick-event-leave-message-not-being-sent.patch b/patches/server/0555-Fix-kick-event-leave-message-not-being-sent.patch new file mode 100644 index 0000000000..ff8a83c615 --- /dev/null +++ b/patches/server/0555-Fix-kick-event-leave-message-not-being-sent.patch @@ -0,0 +1,127 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Wed, 7 Jul 2021 16:19:41 -0700 +Subject: [PATCH] Fix kick event leave message not being sent + + +diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java +index b1224b0ef12a746477047073f5bb94405871ce85..6c3ec21bb61e15becb35c01112770471c2364c5f 100644 +--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java ++++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java +@@ -316,7 +316,6 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player { + public boolean joining = true; + public boolean sentListPacket = false; + public boolean supressTrackerForLogin = false; // Paper - Fire PlayerJoinEvent when Player is actually ready +- public String kickLeaveMessage = null; // SPIGOT-3034: Forward leave message to PlayerQuitEvent + // CraftBukkit end + public boolean isRealPlayer; // Paper + public com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper - PlayerNaturallySpawnCreaturesEvent +diff --git a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java +index 59d20fd62e850a38380d877cef95ed69cb46ecbd..fc242acade3ff06c9213428cde103cf078216382 100644 +--- a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java +@@ -116,6 +116,11 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack + + @Override + public void onDisconnect(DisconnectionDetails info) { ++ // Paper start - Fix kick event leave message not being sent ++ this.onDisconnect(info, null); ++ } ++ public void onDisconnect(DisconnectionDetails info, @Nullable net.kyori.adventure.text.Component quitMessage) { ++ // Paper end - Fix kick event leave message not being sent + if (this.isSingleplayerOwner()) { + ServerCommonPacketListenerImpl.LOGGER.info("Stopping singleplayer server as player logged out"); + this.server.halt(false); +@@ -386,18 +391,17 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack + // Do not kick the player + return; + } +- this.player.kickLeaveMessage = event.getLeaveMessage(); // CraftBukkit - SPIGOT-3034: Forward leave message to PlayerQuitEvent + // Send the possibly modified leave message +- this.disconnect0(new DisconnectionDetails(io.papermc.paper.adventure.PaperAdventure.asVanilla(event.reason()), disconnectionInfo.report(), disconnectionInfo.bugReportLink())); // Paper - Adventure ++ this.disconnect0(new DisconnectionDetails(io.papermc.paper.adventure.PaperAdventure.asVanilla(event.reason()), disconnectionInfo.report(), disconnectionInfo.bugReportLink()), event.leaveMessage()); // Paper - Adventure & use kick event leave message + } + +- private void disconnect0(DisconnectionDetails disconnectiondetails) { ++ private void disconnect0(DisconnectionDetails disconnectiondetails, @Nullable net.kyori.adventure.text.Component leaveMessage) { // Paper - use kick event leave message + // CraftBukkit end + this.player.quitReason = org.bukkit.event.player.PlayerQuitEvent.QuitReason.KICKED; // Paper - Add API for quit reason + this.connection.send(new ClientboundDisconnectPacket(disconnectiondetails.reason()), PacketSendListener.thenRun(() -> { + this.connection.disconnect(disconnectiondetails); + })); +- this.onDisconnect(disconnectiondetails); // CraftBukkit - fire quit instantly ++ this.onDisconnect(disconnectiondetails, leaveMessage); // CraftBukkit - fire quit instantly // Paper - use kick event leave message + this.connection.setReadOnly(); + MinecraftServer minecraftserver = this.server; + Connection networkmanager = this.connection; +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index 0a5abb1a580310cc26ce3d64e6f878bde3bbfd0a..1fb600d3e0f14940d911881ebbe24a6b57d3a81c 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -1975,6 +1975,12 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + + @Override + public void onDisconnect(DisconnectionDetails info) { ++ // Paper start - Fix kick event leave message not being sent ++ this.onDisconnect(info, null); ++ } ++ @Override ++ public void onDisconnect(DisconnectionDetails info, @Nullable net.kyori.adventure.text.Component quitMessage) { ++ // Paper end - Fix kick event leave message not being sent + // CraftBukkit start - Rarely it would send a disconnect line twice + if (this.processedDisconnect) { + return; +@@ -1983,11 +1989,17 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + } + // CraftBukkit end + ServerGamePacketListenerImpl.LOGGER.info("{} lost connection: {}", this.player.getName().getString(), info.reason().getString()); +- this.removePlayerFromWorld(); +- super.onDisconnect(info); ++ this.removePlayerFromWorld(quitMessage); // Paper - Fix kick event leave message not being sent ++ super.onDisconnect(info, quitMessage); // Paper - Fix kick event leave message not being sent + } + ++ // Paper start - Fix kick event leave message not being sent + private void removePlayerFromWorld() { ++ this.removePlayerFromWorld(null); ++ } ++ ++ private void removePlayerFromWorld(@Nullable net.kyori.adventure.text.Component quitMessage) { ++ // Paper end - Fix kick event leave message not being sent + this.chatMessageChain.close(); + // CraftBukkit start - Replace vanilla quit message handling with our own. + /* +@@ -1997,7 +2009,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + + this.player.disconnect(); + // Paper start - Adventure +- net.kyori.adventure.text.Component quitMessage = this.server.getPlayerList().remove(this.player); ++ quitMessage = quitMessage == null ? this.server.getPlayerList().remove(this.player) : this.server.getPlayerList().remove(this.player, quitMessage); // Paper - pass in quitMessage to fix kick message not being used + if ((quitMessage != null) && !quitMessage.equals(net.kyori.adventure.text.Component.empty())) { + this.server.getPlayerList().broadcastSystemMessage(PaperAdventure.asVanilla(quitMessage), false); + // Paper end +diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java +index 26ba0cec3a8492d91df894a69cc1fc8076eeda0d..85bd61bd45690c5532f56d8f11b81f7a11f3e284 100644 +--- a/src/main/java/net/minecraft/server/players/PlayerList.java ++++ b/src/main/java/net/minecraft/server/players/PlayerList.java +@@ -510,6 +510,11 @@ public abstract class PlayerList { + } + + public net.kyori.adventure.text.Component remove(ServerPlayer entityplayer) { // CraftBukkit - return string // Paper - return Component ++ // Paper start - Fix kick event leave message not being sent ++ return this.remove(entityplayer, net.kyori.adventure.text.Component.translatable("multiplayer.player.left", net.kyori.adventure.text.format.NamedTextColor.YELLOW, io.papermc.paper.configuration.GlobalConfiguration.get().messages.useDisplayNameInQuitMessage ? entityplayer.getBukkitEntity().displayName() : io.papermc.paper.adventure.PaperAdventure.asAdventure(entityplayer.getDisplayName()))); ++ } ++ public net.kyori.adventure.text.Component remove(ServerPlayer entityplayer, net.kyori.adventure.text.Component leaveMessage) { ++ // Paper end - Fix kick event leave message not being sent + ServerLevel worldserver = entityplayer.serverLevel(); + + entityplayer.awardStat(Stats.LEAVE_GAME); +@@ -520,7 +525,7 @@ public abstract class PlayerList { + entityplayer.closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason.DISCONNECT); // Paper - Inventory close reason + } + +- PlayerQuitEvent playerQuitEvent = new PlayerQuitEvent(entityplayer.getBukkitEntity(), net.kyori.adventure.text.Component.translatable("multiplayer.player.left", net.kyori.adventure.text.format.NamedTextColor.YELLOW, io.papermc.paper.configuration.GlobalConfiguration.get().messages.useDisplayNameInQuitMessage ? entityplayer.getBukkitEntity().displayName() : io.papermc.paper.adventure.PaperAdventure.asAdventure(entityplayer.getDisplayName())), entityplayer.quitReason); // Paper - Adventure & Add API for quit reason ++ PlayerQuitEvent playerQuitEvent = new PlayerQuitEvent(entityplayer.getBukkitEntity(), leaveMessage, entityplayer.quitReason); // Paper - Adventure & Add API for quit reason + this.cserver.getPluginManager().callEvent(playerQuitEvent); + entityplayer.getBukkitEntity().disconnect(playerQuitEvent.getQuitMessage()); + diff --git a/patches/server/0556-Don-t-apply-cramming-damage-to-players.patch b/patches/server/0556-Don-t-apply-cramming-damage-to-players.patch new file mode 100644 index 0000000000..14f5ce16cf --- /dev/null +++ b/patches/server/0556-Don-t-apply-cramming-damage-to-players.patch @@ -0,0 +1,25 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Phoenix616 <[email protected]> +Date: Sun, 20 Jun 2021 16:35:42 +0100 +Subject: [PATCH] Don't apply cramming damage to players + +It does not make a lot of sense to damage players if they get crammed, + especially as the usecase of teleporting lots of players to the same + location isn't too uncommon and killing all those players isn't + really what one would expect to happen. + +For those who really want it a config option is provided. + +diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java +index 6c3ec21bb61e15becb35c01112770471c2364c5f..4c18d13fdc2221342adb390a6b68be871036c87c 100644 +--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java ++++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java +@@ -1805,7 +1805,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player { + + @Override + public boolean isInvulnerableTo(ServerLevel world, DamageSource source) { +- return super.isInvulnerableTo(world, source) || this.isChangingDimension() && !source.is(DamageTypes.ENDER_PEARL) || !this.hasClientLoaded(); ++ return (super.isInvulnerableTo(world, source) || this.isChangingDimension() && !source.is(DamageTypes.ENDER_PEARL) || !this.hasClientLoaded()) || (!this.level().paperConfig().collisions.allowPlayerCrammingDamage && source.is(DamageTypes.CRAMMING)); // Paper - disable player cramming + } + + @Override diff --git a/patches/server/0557-Rate-options-and-timings-for-sensors-and-behaviors.patch b/patches/server/0557-Rate-options-and-timings-for-sensors-and-behaviors.patch new file mode 100644 index 0000000000..3473ea4ceb --- /dev/null +++ b/patches/server/0557-Rate-options-and-timings-for-sensors-and-behaviors.patch @@ -0,0 +1,88 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Phoenix616 <[email protected]> +Date: Mon, 28 Jun 2021 22:38:29 +0100 +Subject: [PATCH] Rate options and timings for sensors and behaviors + +This adds config options to specify the tick rate for sensors + and behaviors of different entity types as well as timings + for those in order to be able to have some metrics as to which + ones might need tweaking. + +diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java b/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java +index f639cafa64d98a001e622882c647701547f5c3ac..ba951cc1aaa94b58ee7985f197d41cc8be747fc8 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java ++++ b/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java +@@ -14,6 +14,9 @@ public abstract class Behavior<E extends LivingEntity> implements BehaviorContro + private long endTimestamp; + private final int minDuration; + private final int maxDuration; ++ // Paper start - configurable behavior tick rate and timings ++ private final String configKey; ++ // Paper end - configurable behavior tick rate and timings + + public Behavior(Map<MemoryModuleType<?>, MemoryStatus> requiredMemoryState) { + this(requiredMemoryState, 60); +@@ -27,6 +30,14 @@ public abstract class Behavior<E extends LivingEntity> implements BehaviorContro + this.minDuration = minRunTime; + this.maxDuration = maxRunTime; + this.entryCondition = requiredMemoryState; ++ // Paper start - configurable behavior tick rate and timings ++ String key = io.papermc.paper.util.MappingEnvironment.reobf() ? io.papermc.paper.util.ObfHelper.INSTANCE.deobfClassName(this.getClass().getName()) : this.getClass().getName(); ++ int lastSeparator = key.lastIndexOf('.'); ++ if (lastSeparator != -1) { ++ key = key.substring(lastSeparator + 1); ++ } ++ this.configKey = key.toLowerCase(java.util.Locale.ROOT); ++ // Paper end - configurable behavior tick rate and timings + } + + @Override +@@ -36,6 +47,12 @@ public abstract class Behavior<E extends LivingEntity> implements BehaviorContro + + @Override + public final boolean tryStart(ServerLevel world, E entity, long time) { ++ // Paper start - configurable behavior tick rate and timings ++ int tickRate = java.util.Objects.requireNonNullElse(world.paperConfig().tickRates.behavior.get(entity.getType(), this.configKey), -1); ++ if (tickRate > -1 && time < this.endTimestamp + tickRate) { ++ return false; ++ } ++ // Paper end - configurable behavior tick rate and timings + if (this.hasRequiredMemories(entity) && this.checkExtraStartConditions(world, entity)) { + this.status = Behavior.Status.RUNNING; + int i = this.minDuration + world.getRandom().nextInt(this.maxDuration + 1 - this.minDuration); +diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java +index 4d451f6cb5862411848bb9b6b5692ab512dcaa25..fb1f5375eafb030ae08c735a80e9c8149726cda4 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java ++++ b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java +@@ -29,8 +29,19 @@ public abstract class Sensor<E extends LivingEntity> { + .ignoreInvisibilityTesting(); + private final int scanRate; + private long timeToTick; ++ // Paper start - configurable sensor tick rate and timings ++ private final String configKey; ++ // Paper end + + public Sensor(int senseInterval) { ++ // Paper start - configurable sensor tick rate and timings ++ String key = io.papermc.paper.util.MappingEnvironment.reobf() ? io.papermc.paper.util.ObfHelper.INSTANCE.deobfClassName(this.getClass().getName()) : this.getClass().getName(); ++ int lastSeparator = key.lastIndexOf('.'); ++ if (lastSeparator != -1) { ++ key = key.substring(lastSeparator + 1); ++ } ++ this.configKey = key.toLowerCase(java.util.Locale.ROOT); ++ // Paper end + this.scanRate = senseInterval; + this.timeToTick = (long)RANDOM.nextInt(senseInterval); + } +@@ -41,8 +52,10 @@ public abstract class Sensor<E extends LivingEntity> { + + public final void tick(ServerLevel world, E entity) { + if (--this.timeToTick <= 0L) { +- this.timeToTick = (long)this.scanRate; ++ // Paper start - configurable sensor tick rate and timings ++ this.timeToTick = java.util.Objects.requireNonNullElse(world.paperConfig().tickRates.sensor.get(entity.getType(), this.configKey), this.scanRate); + this.updateTargetingConditionRanges(entity); ++ // Paper end + this.doTick(world, entity); + } + } diff --git a/patches/server/0558-Add-missing-forceDrop-toggles.patch b/patches/server/0558-Add-missing-forceDrop-toggles.patch new file mode 100644 index 0000000000..aa609aadba --- /dev/null +++ b/patches/server/0558-Add-missing-forceDrop-toggles.patch @@ -0,0 +1,124 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Tue, 20 Jul 2021 21:25:35 -0700 +Subject: [PATCH] Add missing forceDrop toggles + + +diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/WorkAtComposter.java b/src/main/java/net/minecraft/world/entity/ai/behavior/WorkAtComposter.java +index 4ec1f881c05d96d72814ac3dffd3b4bef40c1bce..c34cb8c918e400636856317cc58356d2677e1d52 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/behavior/WorkAtComposter.java ++++ b/src/main/java/net/minecraft/world/entity/ai/behavior/WorkAtComposter.java +@@ -86,7 +86,9 @@ public class WorkAtComposter extends WorkAtPoi { + simpleContainer.removeItemType(Items.WHEAT, m); + ItemStack itemStack = simpleContainer.addItem(new ItemStack(Items.BREAD, l)); + if (!itemStack.isEmpty()) { ++ villager.forceDrops = true; // Paper - Add missing forceDrop toggles + villager.spawnAtLocation(world, itemStack, 0.5F); ++ villager.forceDrops = false; // Paper - Add missing forceDrop toggles + } + } + } +diff --git a/src/main/java/net/minecraft/world/entity/animal/Panda.java b/src/main/java/net/minecraft/world/entity/animal/Panda.java +index 705c26ceff9371b09311bd7fa796c0efde7ebfee..4f04170b3ec4ff59358e10ccfd0799af3ab590c3 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/Panda.java ++++ b/src/main/java/net/minecraft/world/entity/animal/Panda.java +@@ -540,7 +540,9 @@ public class Panda extends Animal { + + if (world1 instanceof ServerLevel worldserver) { + if (worldserver.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) { ++ this.forceDrops = true; // Paper - Add missing forceDrop toggles + this.dropFromGiftLootTable(worldserver, BuiltInLootTables.PANDA_SNEEZE, this::spawnAtLocation); ++ this.forceDrops = false; // Paper - Add missing forceDrop toggles + } + } + +@@ -664,7 +666,9 @@ public class Panda extends Animal { + ItemStack itemstack1 = this.getItemBySlot(EquipmentSlot.MAINHAND); + + if (!itemstack1.isEmpty() && !player.hasInfiniteMaterials()) { ++ this.forceDrops = true; // Paper - Add missing forceDrop toggles + this.spawnAtLocation(worldserver, itemstack1); ++ this.forceDrops = false; // Paper - Add missing forceDrop toggles + } + + this.setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(itemstack.getItem(), 1)); +@@ -942,7 +946,9 @@ public class Panda extends Animal { + ItemStack itemstack = Panda.this.getItemBySlot(EquipmentSlot.MAINHAND); + + if (!itemstack.isEmpty()) { ++ Panda.this.forceDrops = true; // Paper - Add missing forceDrop toggles + Panda.this.spawnAtLocation(getServerLevel(Panda.this.level()), itemstack); ++ Panda.this.forceDrops = false; // Paper - Add missing forceDrop toggles + Panda.this.setItemSlot(EquipmentSlot.MAINHAND, ItemStack.EMPTY); + int i = Panda.this.isLazy() ? Panda.this.random.nextInt(50) + 10 : Panda.this.random.nextInt(150) + 10; + +diff --git a/src/main/java/net/minecraft/world/entity/monster/Bogged.java b/src/main/java/net/minecraft/world/entity/monster/Bogged.java +index 9d416f775fa19ad1978c7c9c9e0d5bc16728879d..be029746905aeba218684b883282649089657de3 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/Bogged.java ++++ b/src/main/java/net/minecraft/world/entity/monster/Bogged.java +@@ -145,9 +145,11 @@ public class Bogged extends AbstractSkeleton implements Shearable { + } + + private void spawnShearedMushrooms(ServerLevel world, ItemStack shears) { ++ this.forceDrops = true; // Paper - Add missing forceDrop toggles + this.dropFromShearingLootTable(world, BuiltInLootTables.BOGGED_SHEAR, shears, (worldserver1, itemstack1) -> { + this.spawnAtLocation(worldserver1, itemstack1, this.getBbHeight()); + }); ++ this.forceDrops = false; // Paper - Add missing forceDrop toggles + } + + @Override +diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java +index 737c1d5c8ec81d55799ed13560e5e2acc7d8f4e5..5e64a6b94a510ed618a2542ad03e406a181b63d4 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java ++++ b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java +@@ -326,9 +326,11 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento + @Override + protected void finishConversion(ServerLevel world) { + PiglinAi.cancelAdmiring(world, this); ++ this.forceDrops = true; // Paper - Add missing forceDrop toggles + this.inventory.removeAllItems().forEach((itemstack) -> { + this.spawnAtLocation(world, itemstack); + }); ++ this.forceDrops = false; // Paper - Add missing forceDrop toggles + super.finishConversion(world); + } + +diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinAi.java b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinAi.java +index 42b1bd58c6e2c3bd1170171eabfefe315202f340..55868c82bf8bd61ce3494aa9f363c20c88ec6aa6 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinAi.java ++++ b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinAi.java +@@ -273,7 +273,9 @@ public class PiglinAi { + + private static void holdInOffhand(ServerLevel world, Piglin piglin, ItemStack stack) { + if (PiglinAi.isHoldingItemInOffHand(piglin)) { ++ piglin.forceDrops = true; // Paper - Add missing forceDrop toggles + piglin.spawnAtLocation(world, piglin.getItemInHand(InteractionHand.OFF_HAND)); ++ piglin.forceDrops = false; // Paper - Add missing forceDrop toggles + } + + piglin.holdInOffHand(stack); +@@ -333,7 +335,9 @@ public class PiglinAi { + + protected static void cancelAdmiring(ServerLevel world, Piglin piglin) { + if (PiglinAi.isAdmiringItem(piglin) && !piglin.getOffhandItem().isEmpty()) { ++ piglin.forceDrops = true; // Paper - Add missing forceDrop toggles + piglin.spawnAtLocation(world, piglin.getOffhandItem()); ++ piglin.forceDrops = false; // Paper - Add missing forceDrop toggles + piglin.setItemInHand(InteractionHand.OFF_HAND, ItemStack.EMPTY); + } + +diff --git a/src/main/java/net/minecraft/world/entity/raid/Raider.java b/src/main/java/net/minecraft/world/entity/raid/Raider.java +index b6f1d989df3811f423d1cdff98b05ecc4a9268fe..6a7d9b59ff4aa2962a88ae8688c06bc67d70dfda 100644 +--- a/src/main/java/net/minecraft/world/entity/raid/Raider.java ++++ b/src/main/java/net/minecraft/world/entity/raid/Raider.java +@@ -233,7 +233,9 @@ public abstract class Raider extends PatrollingMonster { + double d0 = (double) this.getEquipmentDropChance(enumitemslot); + + if (!itemstack1.isEmpty() && (double) Math.max(this.random.nextFloat() - 0.1F, 0.0F) < d0) { ++ this.forceDrops = true; // Paper - Add missing forceDrop toggles + this.spawnAtLocation(world, itemstack1); ++ this.forceDrops = false; // Paper - Add missing forceDrop toggles + } + + this.onItemPickup(itemEntity); diff --git a/patches/server/0559-Stinger-API.patch b/patches/server/0559-Stinger-API.patch new file mode 100644 index 0000000000..9e851372d0 --- /dev/null +++ b/patches/server/0559-Stinger-API.patch @@ -0,0 +1,50 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Owen1212055 <[email protected]> +Date: Tue, 22 Jun 2021 23:15:44 -0400 +Subject: [PATCH] Stinger API + + +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +index 6020c0c164595db4e2001edf0c1fbe99ed87682d..5d6e4f2aeca9be4dd4504bc93b006e89ef875931 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +@@ -384,6 +384,39 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { + public boolean isInvulnerable() { + return this.getHandle().isInvulnerableTo((ServerLevel) this.getHandle().level(), this.getHandle().damageSources().generic()); + } ++ // Paper start - Bee Stinger API ++ @Override ++ public int getBeeStingerCooldown() { ++ return getHandle().removeStingerTime; ++ } ++ ++ @Override ++ public void setBeeStingerCooldown(int ticks) { ++ getHandle().removeStingerTime = ticks; ++ } ++ ++ @Override ++ public int getBeeStingersInBody() { ++ return getHandle().getStingerCount(); ++ } ++ ++ @Override ++ public void setBeeStingersInBody(int count) { ++ Preconditions.checkArgument(count >= 0, "New bee stinger amount must be >= 0"); ++ getHandle().setStingerCount(count); ++ } ++ ++ @Override ++ public void setNextBeeStingerRemoval(final int ticks) { ++ Preconditions.checkArgument(ticks >= 0, "New amount of ticks before next bee stinger removal must be >= 0"); ++ this.getHandle().removeStingerTime = ticks; ++ } ++ ++ @Override ++ public int getNextBeeStingerRemoval() { ++ return this.getHandle().removeStingerTime; ++ } ++ // Paper end - Bee Stinger API + + @Override + public void damage(double amount) { diff --git a/patches/server/0560-Add-System.out-err-catcher.patch b/patches/server/0560-Add-System.out-err-catcher.patch new file mode 100644 index 0000000000..8b01f8520a --- /dev/null +++ b/patches/server/0560-Add-System.out-err-catcher.patch @@ -0,0 +1,118 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: underscore11code <[email protected]> +Date: Fri, 23 Jul 2021 23:01:42 -0700 +Subject: [PATCH] Add System.out/err catcher + + +diff --git a/src/main/java/io/papermc/paper/logging/SysoutCatcher.java b/src/main/java/io/papermc/paper/logging/SysoutCatcher.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a8e813ca89b033f061e695288b3383bdcf128531 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/logging/SysoutCatcher.java +@@ -0,0 +1,94 @@ ++package io.papermc.paper.logging; ++ ++import org.bukkit.Bukkit; ++import org.bukkit.plugin.java.JavaPlugin; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++ ++import java.io.OutputStream; ++import java.io.PrintStream; ++import java.util.Objects; ++import java.util.concurrent.ConcurrentHashMap; ++import java.util.concurrent.ConcurrentMap; ++import java.util.concurrent.TimeUnit; ++import java.util.logging.Level; ++ ++public final class SysoutCatcher { ++ private static final boolean SUPPRESS_NAGS = Boolean.getBoolean("io.papermc.paper.suppress.sout.nags"); ++ // Nanoseconds between nag at most; if interval is caught first, this is reset. ++ // <= 0 for disabling. ++ private static final long NAG_TIMEOUT = TimeUnit.MILLISECONDS.toNanos( ++ Long.getLong("io.papermc.paper.sout.nags.timeout", TimeUnit.MINUTES.toMillis(5L))); ++ // Count since last nag; if timeout is first, this is reset. ++ // <= 0 for disabling. ++ private static final long NAG_INTERVAL = Long.getLong("io.papermc.paper.sout.nags.interval", 200L); ++ ++ // We don't particularly care about how correct this is at any given moment; let's do it on a best attempt basis. ++ // The records are also pretty small, so let's just go for a size of 64 to start... ++ // ++ // Content: Plugin name => nag object ++ // Why plugin name?: This doesn't store a reference to the plugin; keeps the reload ability. ++ // Why not clean on reload?: Effort. ++ private final ConcurrentMap<String, PluginNag> nagRecords = new ConcurrentHashMap<>(64); ++ ++ public SysoutCatcher() { ++ System.setOut(new WrappedOutStream(System.out, Level.INFO, "[STDOUT] ")); ++ System.setErr(new WrappedOutStream(System.err, Level.SEVERE, "[STDERR] ")); ++ } ++ ++ private final class WrappedOutStream extends PrintStream { ++ private static final StackWalker STACK_WALKER = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); ++ private final Level level; ++ private final String prefix; ++ ++ public WrappedOutStream(@NotNull final OutputStream out, final Level level, final String prefix) { ++ super(out); ++ this.level = level; ++ this.prefix = prefix; ++ } ++ ++ @Override ++ public void println(@Nullable final String line) { ++ final Class<?> clazz = STACK_WALKER.getCallerClass(); ++ try { ++ final JavaPlugin plugin = JavaPlugin.getProvidingPlugin(clazz); ++ ++ // Instead of just printing the message, send it to the plugin's logger ++ plugin.getLogger().log(this.level, this.prefix + line); ++ ++ if (SysoutCatcher.SUPPRESS_NAGS) { ++ return; ++ } ++ if (SysoutCatcher.NAG_INTERVAL > 0 || SysoutCatcher.NAG_TIMEOUT > 0) { ++ final PluginNag nagRecord = SysoutCatcher.this.nagRecords.computeIfAbsent(plugin.getName(), k -> new PluginNag()); ++ final boolean hasTimePassed = SysoutCatcher.NAG_TIMEOUT > 0 ++ && (nagRecord.lastNagTimestamp == Long.MIN_VALUE ++ || nagRecord.lastNagTimestamp + SysoutCatcher.NAG_TIMEOUT <= System.nanoTime()); ++ final boolean hasMessagesPassed = SysoutCatcher.NAG_INTERVAL > 0 ++ && (nagRecord.messagesSinceNag == Long.MIN_VALUE ++ || ++nagRecord.messagesSinceNag >= SysoutCatcher.NAG_INTERVAL); ++ if (!hasMessagesPassed && !hasTimePassed) { ++ return; ++ } ++ nagRecord.lastNagTimestamp = System.nanoTime(); ++ nagRecord.messagesSinceNag = 0; ++ } ++ Bukkit.getLogger().warning( ++ String.format("Nag author(s): '%s' of '%s' about their usage of System.out/err.print. " ++ + "Please use your plugin's logger instead (JavaPlugin#getLogger).", ++ plugin.getPluginMeta().getAuthors(), ++ plugin.getPluginMeta().getDisplayName()) ++ ); ++ } catch (final IllegalArgumentException | IllegalStateException e) { ++ // If anything happens, the calling class doesn't exist, there is no JavaPlugin that "owns" the calling class, etc ++ // Just print out normally, with some added information ++ Bukkit.getLogger().log(this.level, String.format("%s[%s] %s", this.prefix, clazz.getName(), line)); ++ } ++ } ++ } ++ ++ private static class PluginNag { ++ private long lastNagTimestamp = Long.MIN_VALUE; ++ private long messagesSinceNag = Long.MIN_VALUE; ++ } ++} +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index b4af066a21e4893b5ec146d109b5146b6996a0cf..45d887f4143444321f563cdd7d084b2b9ccf911e 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -310,6 +310,7 @@ public final class CraftServer implements Server { + public Set<String> activeCompatibilities = Collections.emptySet(); + private final io.papermc.paper.datapack.PaperDatapackManager datapackManager; // Paper + public static Exception excessiveVelEx; // Paper - Velocity warnings ++ private final io.papermc.paper.logging.SysoutCatcher sysoutCatcher = new io.papermc.paper.logging.SysoutCatcher(); // Paper + + static { + ConfigurationSerialization.registerClass(CraftOfflinePlayer.class); diff --git a/patches/server/0561-Prevent-AFK-kick-while-watching-end-credits.patch b/patches/server/0561-Prevent-AFK-kick-while-watching-end-credits.patch new file mode 100644 index 0000000000..75bfbb054a --- /dev/null +++ b/patches/server/0561-Prevent-AFK-kick-while-watching-end-credits.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Noah van der Aa <[email protected]> +Date: Sat, 24 Jul 2021 16:54:11 +0200 +Subject: [PATCH] Prevent AFK kick while watching end credits + + +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index 1fb600d3e0f14940d911881ebbe24a6b57d3a81c..2df548b68e56a309af4b89f8f1174dde3b046e7d 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -398,7 +398,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + this.tabSpamThrottler.tick(); // Paper - configurable tab spam limits + this.recipeSpamPackets.tick(); // Paper - auto recipe limit + this.dropSpamThrottler.tick(); +- if (this.player.getLastActionTime() > 0L && this.server.getPlayerIdleTimeout() > 0 && Util.getMillis() - this.player.getLastActionTime() > (long) this.server.getPlayerIdleTimeout() * 1000L * 60L) { ++ if (this.player.getLastActionTime() > 0L && this.server.getPlayerIdleTimeout() > 0 && Util.getMillis() - this.player.getLastActionTime() > (long) this.server.getPlayerIdleTimeout() * 1000L * 60L && !this.player.wonGame) { // Paper - Prevent AFK kick while watching end credits + this.player.resetLastActionTime(); // CraftBukkit - SPIGOT-854 + this.disconnect((Component) Component.translatable("multiplayer.disconnect.idling"), org.bukkit.event.player.PlayerKickEvent.Cause.IDLING); // Paper - kick event cause + } diff --git a/patches/server/0562-Allow-skipping-writing-of-comments-to-server.propert.patch b/patches/server/0562-Allow-skipping-writing-of-comments-to-server.propert.patch new file mode 100644 index 0000000000..1fce522892 --- /dev/null +++ b/patches/server/0562-Allow-skipping-writing-of-comments-to-server.propert.patch @@ -0,0 +1,69 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Professor Bloodstone <[email protected]> +Date: Fri, 23 Jul 2021 02:32:04 +0200 +Subject: [PATCH] Allow skipping writing of comments to server.properties + +Makes less git noise, as it won't update the date every single time + +Use -DPaper.skipServerPropertiesComments=true flag to disable writing it + +diff --git a/src/main/java/net/minecraft/server/dedicated/Settings.java b/src/main/java/net/minecraft/server/dedicated/Settings.java +index 0ec3b546db0cf3858dd9cd9ea067d1d6713a8491..d7bd235ef2815890e038091dd625177049d253a5 100644 +--- a/src/main/java/net/minecraft/server/dedicated/Settings.java ++++ b/src/main/java/net/minecraft/server/dedicated/Settings.java +@@ -29,6 +29,7 @@ public abstract class Settings<T extends Settings<T>> { + + private static final Logger LOGGER = LogUtils.getLogger(); + public final Properties properties; ++ private static final boolean skipComments = Boolean.getBoolean("Paper.skipServerPropertiesComments"); // Paper - allow skipping server.properties comments + // CraftBukkit start + private OptionSet options = null; + +@@ -123,7 +124,46 @@ public abstract class Settings<T extends Settings<T>> { + return; + } + // CraftBukkit end +- BufferedWriter bufferedwriter = Files.newBufferedWriter(path, StandardCharsets.UTF_8); ++ // Paper start - allow skipping server.properties comments ++ java.io.OutputStream outputstream = Files.newOutputStream(path); ++ java.io.BufferedOutputStream bufferedOutputStream = !skipComments ? new java.io.BufferedOutputStream(outputstream) : new java.io.BufferedOutputStream(outputstream) { ++ private boolean isRightAfterNewline = true; // If last written char was newline ++ private boolean isComment = false; // Are we writing comment currently? ++ ++ @Override ++ public void write(@org.jetbrains.annotations.NotNull byte[] b) throws IOException { ++ this.write(b, 0, b.length); ++ } ++ ++ @Override ++ public void write(@org.jetbrains.annotations.NotNull byte[] bbuf, int off, int len) throws IOException { ++ int latest_offset = off; // The latest offset, updated when comment ends ++ for (int index = off; index < off + len; ++index ) { ++ byte c = bbuf[index]; ++ boolean isNewline = (c == '\n' || c == '\r'); ++ if (isNewline && this.isComment) { ++ // Comment has ended ++ this.isComment = false; ++ latest_offset = index+1; ++ } ++ if (c == '#' && this.isRightAfterNewline) { ++ this.isComment = true; ++ if (index != latest_offset) { ++ // We got some non-comment data earlier ++ super.write(bbuf, latest_offset, index-latest_offset); ++ } ++ } ++ this.isRightAfterNewline = isNewline; // Store for next iteration ++ ++ } ++ if (latest_offset < off+len && !this.isComment) { ++ // We have some unwritten data, that isn't part of a comment ++ super.write(bbuf, latest_offset, (off + len) - latest_offset); ++ } ++ } ++ }; ++ BufferedWriter bufferedwriter = new BufferedWriter(new java.io.OutputStreamWriter(bufferedOutputStream, java.nio.charset.StandardCharsets.UTF_8.newEncoder())); ++ // Paper end - allow skipping server.properties comments + + try { + this.properties.store(bufferedwriter, "Minecraft server properties"); diff --git a/patches/server/0563-Add-PlayerSetSpawnEvent.patch b/patches/server/0563-Add-PlayerSetSpawnEvent.patch new file mode 100644 index 0000000000..c4df15cc88 --- /dev/null +++ b/patches/server/0563-Add-PlayerSetSpawnEvent.patch @@ -0,0 +1,204 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Wed, 19 May 2021 18:59:10 -0700 +Subject: [PATCH] Add PlayerSetSpawnEvent + + +diff --git a/src/main/java/net/minecraft/server/commands/SetSpawnCommand.java b/src/main/java/net/minecraft/server/commands/SetSpawnCommand.java +index a2d0699e8427b2262a2396495111125eccafbb66..15db9368227dbc29d07d74e85bd126b345b526b6 100644 +--- a/src/main/java/net/minecraft/server/commands/SetSpawnCommand.java ++++ b/src/main/java/net/minecraft/server/commands/SetSpawnCommand.java +@@ -38,24 +38,34 @@ public class SetSpawnCommand { + ResourceKey<Level> resourcekey = source.getLevel().dimension(); + Iterator iterator = targets.iterator(); + ++ final Collection<ServerPlayer> actualTargets = new java.util.ArrayList<>(); // Paper - Add PlayerSetSpawnEvent + while (iterator.hasNext()) { + ServerPlayer entityplayer = (ServerPlayer) iterator.next(); + +- entityplayer.setRespawnPosition(resourcekey, pos, angle, true, false, org.bukkit.event.player.PlayerSpawnChangeEvent.Cause.COMMAND); // CraftBukkit ++ // Paper start - Add PlayerSetSpawnEvent ++ if (entityplayer.setRespawnPosition(resourcekey, pos, angle, true, false, com.destroystokyo.paper.event.player.PlayerSetSpawnEvent.Cause.COMMAND)) { ++ actualTargets.add(entityplayer); ++ } ++ // Paper end - Add PlayerSetSpawnEvent + } ++ // Paper start - Add PlayerSetSpawnEvent ++ if (actualTargets.isEmpty()) { ++ return 0; ++ } ++ // Paper end - Add PlayerSetSpawnEvent + + String s = resourcekey.location().toString(); + +- if (targets.size() == 1) { ++ if (actualTargets.size() == 1) { // Paper - Add PlayerSetSpawnEvent + source.sendSuccess(() -> { +- return Component.translatable("commands.spawnpoint.success.single", pos.getX(), pos.getY(), pos.getZ(), angle, s, ((ServerPlayer) targets.iterator().next()).getDisplayName()); ++ return Component.translatable("commands.spawnpoint.success.single", pos.getX(), pos.getY(), pos.getZ(), angle, s, ((ServerPlayer) actualTargets.iterator().next()).getDisplayName()); // Paper - Add PlayerSetSpawnEvent + }, true); + } else { + source.sendSuccess(() -> { +- return Component.translatable("commands.spawnpoint.success.multiple", pos.getX(), pos.getY(), pos.getZ(), angle, s, targets.size()); ++ return Component.translatable("commands.spawnpoint.success.multiple", pos.getX(), pos.getY(), pos.getZ(), angle, s, actualTargets.size()); // Paper - Add PlayerSetSpawnEvent + }, true); + } + +- return targets.size(); ++ return actualTargets.size(); // Paper - Add PlayerSetSpawnEvent + } + } +diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java +index 4c18d13fdc2221342adb390a6b68be871036c87c..f2cc608d2cf040be2912b604f0d6cab21e33ded0 100644 +--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java ++++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java +@@ -1681,7 +1681,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player { + } else if (this.bedBlocked(blockposition, enumdirection)) { + return Either.left(net.minecraft.world.entity.player.Player.BedSleepingProblem.OBSTRUCTED); + } else { +- this.setRespawnPosition(this.level().dimension(), blockposition, this.getYRot(), false, true, PlayerSpawnChangeEvent.Cause.BED); // CraftBukkit ++ this.setRespawnPosition(this.level().dimension(), blockposition, this.getYRot(), false, true, com.destroystokyo.paper.event.player.PlayerSetSpawnEvent.Cause.BED); // Paper - Add PlayerSetSpawnEvent + if (this.level().isDay()) { + return Either.left(net.minecraft.world.entity.player.Player.BedSleepingProblem.NOT_POSSIBLE_NOW); + } else { +@@ -2617,44 +2617,50 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player { + this.setRespawnPosition(player.getRespawnDimension(), player.getRespawnPosition(), player.getRespawnAngle(), player.isRespawnForced(), false); + } + ++ @Deprecated // Paper - Add PlayerSetSpawnEvent + public void setRespawnPosition(ResourceKey<Level> dimension, @Nullable BlockPos pos, float angle, boolean forced, boolean sendMessage) { +- // CraftBukkit start +- this.setRespawnPosition(dimension, pos, angle, forced, sendMessage, PlayerSpawnChangeEvent.Cause.UNKNOWN); +- } +- +- public void setRespawnPosition(ResourceKey<Level> resourcekey, @Nullable BlockPos blockposition, float f, boolean flag, boolean flag1, PlayerSpawnChangeEvent.Cause cause) { +- ServerLevel newWorld = this.server.getLevel(resourcekey); +- Location newSpawn = (blockposition != null) ? CraftLocation.toBukkit(blockposition, newWorld.getWorld(), f, 0) : null; +- +- PlayerSpawnChangeEvent event = new PlayerSpawnChangeEvent(this.getBukkitEntity(), newSpawn, flag, cause); +- Bukkit.getServer().getPluginManager().callEvent(event); +- if (event.isCancelled()) { +- return; +- } +- newSpawn = event.getNewSpawn(); +- flag = event.isForced(); +- +- if (newSpawn != null) { +- resourcekey = ((CraftWorld) newSpawn.getWorld()).getHandle().dimension(); +- blockposition = BlockPos.containing(newSpawn.getX(), newSpawn.getY(), newSpawn.getZ()); +- f = newSpawn.getYaw(); +- } else { +- resourcekey = Level.OVERWORLD; +- blockposition = null; +- f = 0.0F; ++ // Paper start - Add PlayerSetSpawnEvent ++ this.setRespawnPosition(dimension, pos, angle, forced, sendMessage, com.destroystokyo.paper.event.player.PlayerSetSpawnEvent.Cause.UNKNOWN); ++ } ++ @Deprecated ++ public boolean setRespawnPosition(ResourceKey<Level> dimension, @Nullable BlockPos pos, float angle, boolean forced, boolean sendMessage, PlayerSpawnChangeEvent.Cause cause) { ++ return this.setRespawnPosition(dimension, pos, angle, forced, sendMessage, cause == PlayerSpawnChangeEvent.Cause.RESET ? ++ com.destroystokyo.paper.event.player.PlayerSetSpawnEvent.Cause.PLAYER_RESPAWN : com.destroystokyo.paper.event.player.PlayerSetSpawnEvent.Cause.valueOf(cause.name())); ++ } ++ public boolean setRespawnPosition(ResourceKey<Level> dimension, @Nullable BlockPos pos, float angle, boolean forced, boolean sendMessage, com.destroystokyo.paper.event.player.PlayerSetSpawnEvent.Cause cause) { ++ Location spawnLoc = null; ++ boolean willNotify = false; ++ if (pos != null) { ++ boolean flag2 = pos.equals(this.respawnPosition) && dimension.equals(this.respawnDimension); ++ spawnLoc = io.papermc.paper.util.MCUtil.toLocation(this.getServer().getLevel(dimension), pos); ++ spawnLoc.setYaw(angle); ++ willNotify = sendMessage && !flag2; ++ } ++ ++ PlayerSpawnChangeEvent dumbEvent = new PlayerSpawnChangeEvent(this.getBukkitEntity(), spawnLoc, forced, ++ cause == com.destroystokyo.paper.event.player.PlayerSetSpawnEvent.Cause.PLAYER_RESPAWN ? PlayerSpawnChangeEvent.Cause.RESET : PlayerSpawnChangeEvent.Cause.valueOf(cause.name())); ++ dumbEvent.callEvent(); ++ ++ com.destroystokyo.paper.event.player.PlayerSetSpawnEvent event = new com.destroystokyo.paper.event.player.PlayerSetSpawnEvent(this.getBukkitEntity(), cause, dumbEvent.getNewSpawn(), dumbEvent.isForced(), willNotify, willNotify ? net.kyori.adventure.text.Component.translatable("block.minecraft.set_spawn") : null); ++ event.setCancelled(dumbEvent.isCancelled()); ++ if (!event.callEvent()) { ++ return false; + } +- // CraftBukkit end +- if (blockposition != null) { +- boolean flag2 = blockposition.equals(this.respawnPosition) && resourcekey.equals(this.respawnDimension); ++ if (event.getLocation() != null) { ++ dimension = event.getLocation().getWorld() != null ? ((CraftWorld) event.getLocation().getWorld()).getHandle().dimension() : dimension; ++ pos = io.papermc.paper.util.MCUtil.toBlockPosition(event.getLocation()); ++ angle = event.getLocation().getYaw(); ++ forced = event.isForced(); ++ // Paper end - Add PlayerSetSpawnEvent + +- if (flag1 && !flag2) { +- this.sendSystemMessage(Component.translatable("block.minecraft.set_spawn")); ++ if (event.willNotifyPlayer() && event.getNotification() != null) { // Paper - Add PlayerSetSpawnEvent ++ this.sendSystemMessage(PaperAdventure.asVanilla(event.getNotification())); // Paper - Add PlayerSetSpawnEvent + } + +- this.respawnPosition = blockposition; +- this.respawnDimension = resourcekey; +- this.respawnAngle = f; +- this.respawnForced = flag; ++ this.respawnPosition = pos; ++ this.respawnDimension = dimension; ++ this.respawnAngle = angle; ++ this.respawnForced = forced; + } else { + this.respawnPosition = null; + this.respawnDimension = Level.OVERWORLD; +@@ -2662,6 +2668,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player { + this.respawnForced = false; + } + ++ return true; // Paper - Add PlayerSetSpawnEvent + } + + public SectionPos getLastSectionPos() { +diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java +index 85bd61bd45690c5532f56d8f11b81f7a11f3e284..05d2f3c26d10169f6cf43bcb6c48db5d27b5cbac 100644 +--- a/src/main/java/net/minecraft/server/players/PlayerList.java ++++ b/src/main/java/net/minecraft/server/players/PlayerList.java +@@ -802,7 +802,7 @@ public abstract class PlayerList { + // CraftBukkit end + if (teleporttransition.missingRespawnBlock()) { + entityplayer1.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.NO_RESPAWN_BLOCK_AVAILABLE, 0.0F)); +- entityplayer1.setRespawnPosition(null, null, 0f, false, false, PlayerSpawnChangeEvent.Cause.RESET); // CraftBukkit - SPIGOT-5988: Clear respawn location when obstructed ++ entityplayer1.setRespawnPosition(null, null, 0f, false, false, com.destroystokyo.paper.event.player.PlayerSetSpawnEvent.Cause.PLAYER_RESPAWN); // CraftBukkit - SPIGOT-5988: Clear respawn location when obstructed // Paper - PlayerSetSpawnEvent + } + + int i = flag ? 1 : 0; +diff --git a/src/main/java/net/minecraft/world/level/block/RespawnAnchorBlock.java b/src/main/java/net/minecraft/world/level/block/RespawnAnchorBlock.java +index db26b5a0464bd6087eeacaf6dd61eba37365df92..9117c035d5a6ff114b028fad3380ceb1fc2b9691 100644 +--- a/src/main/java/net/minecraft/world/level/block/RespawnAnchorBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/RespawnAnchorBlock.java +@@ -88,9 +88,14 @@ public class RespawnAnchorBlock extends Block { + ServerPlayer entityplayer = (ServerPlayer) player; + + if (entityplayer.getRespawnDimension() != world.dimension() || !pos.equals(entityplayer.getRespawnPosition())) { +- entityplayer.setRespawnPosition(world.dimension(), pos, 0.0F, false, true, org.bukkit.event.player.PlayerSpawnChangeEvent.Cause.RESPAWN_ANCHOR); // CraftBukkit ++ if (entityplayer.setRespawnPosition(world.dimension(), pos, 0.0F, false, true, com.destroystokyo.paper.event.player.PlayerSetSpawnEvent.Cause.RESPAWN_ANCHOR)) { // Paper - Add PlayerSetSpawnEvent + world.playSound((Player) null, (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, SoundEvents.RESPAWN_ANCHOR_SET_SPAWN, SoundSource.BLOCKS, 1.0F, 1.0F); + return InteractionResult.SUCCESS_SERVER; ++ // Paper start - Add PlayerSetSpawnEvent ++ } else { ++ return InteractionResult.FAIL; ++ } ++ // Paper end - Add PlayerSetSpawnEvent + } + } + +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +index ff8d6a0ceb41258541c0049464dc0923ed4872bd..f363f885b3dc1852b09914f034740794e3025d3d 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -1420,9 +1420,9 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + @Override + public void setRespawnLocation(Location location, boolean override) { + if (location == null) { +- this.getHandle().setRespawnPosition(null, null, 0.0F, override, false, PlayerSpawnChangeEvent.Cause.PLUGIN); ++ this.getHandle().setRespawnPosition(null, null, 0.0F, override, false, com.destroystokyo.paper.event.player.PlayerSetSpawnEvent.Cause.PLUGIN); // Paper - Add PlayerSetSpawnEvent + } else { +- this.getHandle().setRespawnPosition(((CraftWorld) location.getWorld()).getHandle().dimension(), CraftLocation.toBlockPosition(location), location.getYaw(), override, false, PlayerSpawnChangeEvent.Cause.PLUGIN); ++ this.getHandle().setRespawnPosition(((CraftWorld) location.getWorld()).getHandle().dimension(), CraftLocation.toBlockPosition(location), location.getYaw(), override, false, com.destroystokyo.paper.event.player.PlayerSetSpawnEvent.Cause.PLUGIN); // Paper - Add PlayerSetSpawnEvent + } + } + diff --git a/patches/server/0564-Make-hoppers-respect-inventory-max-stack-size.patch b/patches/server/0564-Make-hoppers-respect-inventory-max-stack-size.patch new file mode 100644 index 0000000000..fc01c5cad4 --- /dev/null +++ b/patches/server/0564-Make-hoppers-respect-inventory-max-stack-size.patch @@ -0,0 +1,30 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Wed, 7 Jul 2021 16:30:17 -0700 +Subject: [PATCH] Make hoppers respect inventory max stack size + + +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 8913f434967457a16dd708252834ba001ada1a03..b35b44f0776aeb95ef0eda666ba0b652c5e90274 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 +@@ -495,15 +495,17 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + + if (itemstack1.isEmpty()) { + // Spigot start - SPIGOT-6693, InventorySubcontainer#setItem ++ ItemStack leftover = ItemStack.EMPTY; // Paper - Make hoppers respect inventory max stack size + if (!stack.isEmpty() && stack.getCount() > to.getMaxStackSize()) { ++ leftover = stack; // Paper - Make hoppers respect inventory max stack size + stack = stack.split(to.getMaxStackSize()); + } + // Spigot end + to.setItem(slot, stack); +- stack = ItemStack.EMPTY; ++ stack = leftover; // Paper - Make hoppers respect inventory max stack size + flag = true; + } else if (HopperBlockEntity.canMergeItems(itemstack1, stack)) { +- int j = stack.getMaxStackSize() - itemstack1.getCount(); ++ int j = Math.min(stack.getMaxStackSize(), to.getMaxStackSize()) - itemstack1.getCount(); // Paper - Make hoppers respect inventory max stack size + int k = Math.min(stack.getCount(), j); + + stack.shrink(k); diff --git a/patches/server/0565-Optimize-entity-tracker-passenger-checks.patch b/patches/server/0565-Optimize-entity-tracker-passenger-checks.patch new file mode 100644 index 0000000000..e64be30cfc --- /dev/null +++ b/patches/server/0565-Optimize-entity-tracker-passenger-checks.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Andrew Steinborn <[email protected]> +Date: Sun, 8 Aug 2021 00:52:54 -0400 +Subject: [PATCH] Optimize entity tracker passenger checks + + +diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java +index 50f1e690d499940ce6c2174dbc609af21fd7b0cb..15968ea0b86a35fec8c0301bbdccc4ecd491197b 100644 +--- a/src/main/java/net/minecraft/server/level/ServerEntity.java ++++ b/src/main/java/net/minecraft/server/level/ServerEntity.java +@@ -75,7 +75,7 @@ public class ServerEntity { + private Vec3 lastSentMovement; + private int tickCount; + private int teleportDelay; +- private List<Entity> lastPassengers = Collections.emptyList(); ++ private List<Entity> lastPassengers = com.google.common.collect.ImmutableList.of(); // Paper - optimize passenger checks + private boolean wasRiding; + private boolean wasOnGround; + @Nullable diff --git a/patches/server/0566-Config-option-for-Piglins-guarding-chests.patch b/patches/server/0566-Config-option-for-Piglins-guarding-chests.patch new file mode 100644 index 0000000000..32c61a7ec0 --- /dev/null +++ b/patches/server/0566-Config-option-for-Piglins-guarding-chests.patch @@ -0,0 +1,18 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jason Penilla <[email protected]> +Date: Wed, 2 Dec 2020 03:07:58 -0800 +Subject: [PATCH] Config option for Piglins guarding chests + + +diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinAi.java b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinAi.java +index 55868c82bf8bd61ce3494aa9f363c20c88ec6aa6..4f3048615a34fc2c067e09aec76af94cde6a74e0 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinAi.java ++++ b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinAi.java +@@ -478,6 +478,7 @@ public class PiglinAi { + } + + public static void angerNearbyPiglins(ServerLevel world, Player player, boolean blockOpen) { ++ if (!player.level().paperConfig().entities.behavior.piglinsGuardChests) return; // Paper - Config option for Piglins guarding chests + List<Piglin> list = player.level().getEntitiesOfClass(Piglin.class, player.getBoundingBox().inflate(16.0D)); + + list.stream().filter(PiglinAi::isIdle).filter((entitypiglin) -> { diff --git a/patches/server/0567-Add-EntityDamageItemEvent.patch b/patches/server/0567-Add-EntityDamageItemEvent.patch new file mode 100644 index 0000000000..bfe6c8751a --- /dev/null +++ b/patches/server/0567-Add-EntityDamageItemEvent.patch @@ -0,0 +1,94 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Tue, 22 Dec 2020 13:52:48 -0800 +Subject: [PATCH] Add EntityDamageItemEvent + + +diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java +index 225206a055e2f6bf4dbb18434cb3401d02746387..e320264c2283c2c09910ea70606413c73f443b1f 100644 +--- a/src/main/java/net/minecraft/world/item/ItemStack.java ++++ b/src/main/java/net/minecraft/world/item/ItemStack.java +@@ -697,11 +697,11 @@ public final class ItemStack implements DataComponentHolder { + return this.isDamageableItem() && this.getDamageValue() >= this.getMaxDamage() - 1; + } + +- public void hurtAndBreak(int amount, ServerLevel world, @Nullable ServerPlayer player, Consumer<Item> breakCallback) { ++ public void hurtAndBreak(int amount, ServerLevel world, @Nullable LivingEntity player, Consumer<Item> breakCallback) { // Paper - Add EntityDamageItemEvent + int j = this.processDurabilityChange(amount, world, player); + // CraftBukkit start +- if (player != null) { +- PlayerItemDamageEvent event = new PlayerItemDamageEvent(player.getBukkitEntity(), CraftItemStack.asCraftMirror(this), j); ++ if (player instanceof final ServerPlayer serverPlayer) { // Paper - Add EntityDamageItemEvent ++ PlayerItemDamageEvent event = new PlayerItemDamageEvent(serverPlayer.getBukkitEntity(), CraftItemStack.asCraftMirror(this), j); // Paper - Add EntityDamageItemEvent + event.getPlayer().getServer().getPluginManager().callEvent(event); + + if (j != event.getDamage() || event.isCancelled()) { +@@ -712,6 +712,14 @@ public final class ItemStack implements DataComponentHolder { + } + + j = event.getDamage(); ++ // Paper start - Add EntityDamageItemEvent ++ } else if (player != null) { ++ io.papermc.paper.event.entity.EntityDamageItemEvent event = new io.papermc.paper.event.entity.EntityDamageItemEvent(player.getBukkitLivingEntity(), CraftItemStack.asCraftMirror(this), amount); ++ if (!event.callEvent()) { ++ return; ++ } ++ j = event.getDamage(); ++ // Paper end - Add EntityDamageItemEvent + } + // CraftBukkit end + +@@ -721,21 +729,21 @@ public final class ItemStack implements DataComponentHolder { + + } + +- private int processDurabilityChange(int baseDamage, ServerLevel world, @Nullable ServerPlayer player) { +- return !this.isDamageableItem() ? 0 : (player != null && player.hasInfiniteMaterials() ? 0 : (baseDamage > 0 ? EnchantmentHelper.processDurabilityChange(world, this, baseDamage) : baseDamage)); ++ private int processDurabilityChange(int baseDamage, ServerLevel world, @Nullable LivingEntity player) { // Paper - Add EntityDamageItemEvent ++ return !this.isDamageableItem() ? 0 : (player instanceof ServerPlayer && player.hasInfiniteMaterials() ? 0 : (baseDamage > 0 ? EnchantmentHelper.processDurabilityChange(world, this, baseDamage) : baseDamage)); // Paper - Add EntityDamageItemEvent + } + +- private void applyDamage(int damage, @Nullable ServerPlayer player, Consumer<Item> breakCallback) { +- if (player != null) { +- CriteriaTriggers.ITEM_DURABILITY_CHANGED.trigger(player, this, damage); ++ private void applyDamage(int damage, @Nullable LivingEntity player, Consumer<Item> breakCallback) { // Paper - Add EntityDamageItemEvent ++ if (player instanceof final ServerPlayer serverPlayer) { // Paper - Add EntityDamageItemEvent ++ CriteriaTriggers.ITEM_DURABILITY_CHANGED.trigger(serverPlayer, this, damage); // Paper - Add EntityDamageItemEvent + } + + this.setDamageValue(damage); + if (this.isBroken()) { + Item item = this.getItem(); + // CraftBukkit start - Check for item breaking +- if (this.count == 1 && player != null) { +- org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerItemBreakEvent(player, this); ++ if (this.count == 1 && player instanceof final ServerPlayer serverPlayer) { // Paper - Add EntityDamageItemEvent ++ org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerItemBreakEvent(serverPlayer, this); // Paper - Add EntityDamageItemEvent + } + // CraftBukkit end + +@@ -773,7 +781,7 @@ public final class ItemStack implements DataComponentHolder { + entityplayer = null; + } + +- this.hurtAndBreak(amount, worldserver, entityplayer, (item) -> { ++ this.hurtAndBreak(amount, worldserver, entity, (item) -> { // Paper - Add EntityDamageItemEvent + entity.onEquippedItemBroken(item, slot); + }); + } +diff --git a/src/main/java/net/minecraft/world/item/enchantment/effects/ChangeItemDamage.java b/src/main/java/net/minecraft/world/item/enchantment/effects/ChangeItemDamage.java +index 0019c8548aa4901b248ced32cc1475ad14b725bf..7e21c8b10debd70c35a138c14b9b177ef6fff37a 100644 +--- a/src/main/java/net/minecraft/world/item/enchantment/effects/ChangeItemDamage.java ++++ b/src/main/java/net/minecraft/world/item/enchantment/effects/ChangeItemDamage.java +@@ -21,9 +21,9 @@ public record ChangeItemDamage(LevelBasedValue amount) implements EnchantmentEnt + public void apply(ServerLevel world, int level, EnchantedItemInUse context, Entity user, Vec3 pos) { + ItemStack itemStack = context.itemStack(); + if (itemStack.has(DataComponents.MAX_DAMAGE) && itemStack.has(DataComponents.DAMAGE)) { +- ServerPlayer serverPlayer2 = context.owner() instanceof ServerPlayer serverPlayer ? serverPlayer : null; ++ // ServerPlayer serverPlayer2 = context.owner() instanceof ServerPlayer serverPlayer ? serverPlayer : null; // Paper - EntityDamageItemEvent - always pass in entity + int i = (int)this.amount.calculate(level); +- itemStack.hurtAndBreak(i, world, serverPlayer2, context.onBreak()); ++ itemStack.hurtAndBreak(i, world, context.owner(), context.onBreak()); // Paper - EntityDamageItemEvent - always pass in entity + } + } + diff --git a/patches/server/0568-Optimize-indirect-passenger-iteration.patch b/patches/server/0568-Optimize-indirect-passenger-iteration.patch new file mode 100644 index 0000000000..91517284fb --- /dev/null +++ b/patches/server/0568-Optimize-indirect-passenger-iteration.patch @@ -0,0 +1,53 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Andrew Steinborn <[email protected]> +Date: Mon, 9 Aug 2021 00:38:37 -0400 +Subject: [PATCH] Optimize indirect passenger iteration + + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index a18e716a8c1c6f1fe23dcd3f4da033da95417e22..ddb3d68f064d2dd82b42d0013c65f3685ef1065d 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -4118,20 +4118,34 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + + private Stream<Entity> getIndirectPassengersStream() { ++ if (this.passengers.isEmpty()) { return Stream.of(); } // Paper - Optimize indirect passenger iteration + return this.passengers.stream().flatMap(Entity::getSelfAndPassengers); + } + + @Override + public Stream<Entity> getSelfAndPassengers() { ++ if (this.passengers.isEmpty()) { return Stream.of(this); } // Paper - Optimize indirect passenger iteration + return Stream.concat(Stream.of(this), this.getIndirectPassengersStream()); + } + + @Override + public Stream<Entity> getPassengersAndSelf() { ++ if (this.passengers.isEmpty()) { return Stream.of(this); } // Paper - Optimize indirect passenger iteration + return Stream.concat(this.passengers.stream().flatMap(Entity::getPassengersAndSelf), Stream.of(this)); + } + + public Iterable<Entity> getIndirectPassengers() { ++ // Paper start - Optimize indirect passenger iteration ++ if (this.passengers.isEmpty()) { return ImmutableList.of(); } ++ ImmutableList.Builder<Entity> indirectPassengers = ImmutableList.builder(); ++ for (Entity passenger : this.passengers) { ++ indirectPassengers.add(passenger); ++ indirectPassengers.addAll(passenger.getIndirectPassengers()); ++ } ++ return indirectPassengers.build(); ++ } ++ private Iterable<Entity> getIndirectPassengers_old() { ++ // Paper end - Optimize indirect passenger iteration + return () -> { + return this.getIndirectPassengersStream().iterator(); + }; +@@ -4144,6 +4158,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + + public boolean hasExactlyOnePlayerPassenger() { ++ if (this.passengers.isEmpty()) { return false; } // Paper - Optimize indirect passenger iteration + return this.countPlayerPassengers() == 1; + } + diff --git a/patches/server/0569-Configurable-item-frame-map-cursor-update-interval.patch b/patches/server/0569-Configurable-item-frame-map-cursor-update-interval.patch new file mode 100644 index 0000000000..03aaddc9be --- /dev/null +++ b/patches/server/0569-Configurable-item-frame-map-cursor-update-interval.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Warrior <[email protected]> +Date: Fri, 13 Aug 2021 01:14:38 +0200 +Subject: [PATCH] Configurable item frame map cursor update interval + + +diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java +index 15968ea0b86a35fec8c0301bbdccc4ecd491197b..78d3aa089d990190cdf5cd7ac14410909235f133 100644 +--- a/src/main/java/net/minecraft/server/level/ServerEntity.java ++++ b/src/main/java/net/minecraft/server/level/ServerEntity.java +@@ -120,7 +120,7 @@ public class ServerEntity { + if (true || this.tickCount % 10 == 0) { // CraftBukkit - Moved below, should always enter this block + ItemStack itemstack = entityitemframe.getItem(); + +- if (this.tickCount % 10 == 0 && itemstack.getItem() instanceof MapItem) { // CraftBukkit - Moved this.tickCounter % 10 logic here so item frames do not enter the other blocks ++ if (this.level.paperConfig().maps.itemFrameCursorUpdateInterval > 0 && this.tickCount % this.level.paperConfig().maps.itemFrameCursorUpdateInterval == 0 && itemstack.getItem() instanceof MapItem) { // CraftBukkit - Moved this.tickCounter % 10 logic here so item frames do not enter the other blocks // Paper - Make item frame map cursor update interval configurable + MapId mapid = (MapId) itemstack.get(DataComponents.MAP_ID); + MapItemSavedData worldmap = MapItem.getSavedData(mapid, this.level); + diff --git a/patches/server/0570-Change-EnderEye-target-without-changing-other-things.patch b/patches/server/0570-Change-EnderEye-target-without-changing-other-things.patch new file mode 100644 index 0000000000..5612422ea7 --- /dev/null +++ b/patches/server/0570-Change-EnderEye-target-without-changing-other-things.patch @@ -0,0 +1,54 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sat, 21 Aug 2021 12:13:53 -0700 +Subject: [PATCH] Change EnderEye target without changing other things + + +diff --git a/src/main/java/net/minecraft/world/entity/projectile/EyeOfEnder.java b/src/main/java/net/minecraft/world/entity/projectile/EyeOfEnder.java +index 573b96c9e0c89860c3da031c5aa239f6a7ad0c6e..fd1f5de7dc151dfd187d23e022b2c5435ed8accc 100644 +--- a/src/main/java/net/minecraft/world/entity/projectile/EyeOfEnder.java ++++ b/src/main/java/net/minecraft/world/entity/projectile/EyeOfEnder.java +@@ -76,6 +76,11 @@ public class EyeOfEnder extends Entity implements ItemSupplier { + } + + public void signalTo(BlockPos pos) { ++ // Paper start - Change EnderEye target without changing other things ++ this.signalTo(pos, true); ++ } ++ public void signalTo(BlockPos pos, boolean update) { ++ // Paper end - Change EnderEye target without changing other things + double d0 = (double) pos.getX(); + int i = pos.getY(); + double d1 = (double) pos.getZ(); +@@ -93,8 +98,10 @@ public class EyeOfEnder extends Entity implements ItemSupplier { + this.tz = d1; + } + ++ if (update) { // Paper - Change EnderEye target without changing other things + this.life = 0; + this.surviveAfterDeath = this.random.nextInt(5) > 0; ++ } // Paper - Change EnderEye target without changing other things + } + + @Override +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderSignal.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderSignal.java +index d4dfc7a0266086b9bf2c3966c6e149453d0583ba..27f56fa4b7ef92a9a4dfa6b782350424b88210f2 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderSignal.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderSignal.java +@@ -32,8 +32,15 @@ public class CraftEnderSignal extends CraftEntity implements EnderSignal { + + @Override + public void setTargetLocation(Location location) { ++ // Paper start - Change EnderEye target without changing other things ++ this.setTargetLocation(location, true); ++ } ++ ++ @Override ++ public void setTargetLocation(Location location, boolean update) { ++ // Paper end - Change EnderEye target without changing other things + Preconditions.checkArgument(this.getWorld().equals(location.getWorld()), "Cannot target EnderSignal across worlds"); +- this.getHandle().signalTo(CraftLocation.toBlockPosition(location)); ++ this.getHandle().signalTo(CraftLocation.toBlockPosition(location), update); // Paper - Change EnderEye target without changing other things + } + + @Override diff --git a/patches/server/0571-Add-BlockBreakBlockEvent.patch b/patches/server/0571-Add-BlockBreakBlockEvent.patch new file mode 100644 index 0000000000..36b29ddf20 --- /dev/null +++ b/patches/server/0571-Add-BlockBreakBlockEvent.patch @@ -0,0 +1,87 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sun, 3 Jan 2021 17:58:11 -0800 +Subject: [PATCH] Add BlockBreakBlockEvent + + +diff --git a/src/main/java/net/minecraft/world/level/block/Block.java b/src/main/java/net/minecraft/world/level/block/Block.java +index 533df8ff84ec4224637dfb0837104a6db1ea9901..3c7e373eb1601670d923e6ffa46817ca53e321db 100644 +--- a/src/main/java/net/minecraft/world/level/block/Block.java ++++ b/src/main/java/net/minecraft/world/level/block/Block.java +@@ -295,6 +295,24 @@ public class Block extends BlockBehaviour implements ItemLike { + + } + ++ // Paper start - Add BlockBreakBlockEvent ++ public static boolean dropResources(BlockState state, LevelAccessor levelAccessor, BlockPos pos, @Nullable BlockEntity blockEntity, BlockPos source) { ++ if (levelAccessor instanceof ServerLevel serverLevel) { ++ List<org.bukkit.inventory.ItemStack> items = new java.util.ArrayList<>(); ++ for (ItemStack drop : Block.getDrops(state, serverLevel, pos, blockEntity)) { ++ items.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(drop)); ++ } ++ io.papermc.paper.event.block.BlockBreakBlockEvent event = new io.papermc.paper.event.block.BlockBreakBlockEvent(org.bukkit.craftbukkit.block.CraftBlock.at(levelAccessor, pos), org.bukkit.craftbukkit.block.CraftBlock.at(levelAccessor, source), items); ++ event.callEvent(); ++ for (org.bukkit.inventory.ItemStack drop : event.getDrops()) { ++ popResource(serverLevel, pos, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(drop)); ++ } ++ state.spawnAfterBreak(serverLevel, pos, ItemStack.EMPTY, true); ++ } ++ return true; ++ } ++ // Paper end - Add BlockBreakBlockEvent ++ + public static void dropResources(BlockState state, Level world, BlockPos pos, @Nullable BlockEntity blockEntity, @Nullable Entity entity, ItemStack tool) { + if (world instanceof ServerLevel) { + Block.getDrops(state, (ServerLevel) world, pos, blockEntity, entity, tool).forEach((itemstack1) -> { +diff --git a/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java b/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java +index 0c6b517196d48ba4384eac240b7e580adfdbc4d4..4973d75b26880e39d42b5ef533896f43a1f07cba 100644 +--- a/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java +@@ -406,7 +406,7 @@ public class PistonBaseBlock extends DirectionalBlock { + iblockdata1 = world.getBlockState(blockposition3); + BlockEntity tileentity = iblockdata1.hasBlockEntity() ? world.getBlockEntity(blockposition3) : null; + +- dropResources(iblockdata1, world, blockposition3, tileentity); ++ dropResources(iblockdata1, world, blockposition3, tileentity, pos); // Paper - Add BlockBreakBlockEvent + world.setBlock(blockposition3, Blocks.AIR.defaultBlockState(), 18); + world.gameEvent((Holder) GameEvent.BLOCK_DESTROY, blockposition3, GameEvent.Context.of(iblockdata1)); + if (!iblockdata1.is(BlockTags.FIRE)) { +diff --git a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java +index 83dc8bcd9e2b8ecbd32225e4e10aec392ef28325..261e5994d13f8bc30490b86691c80c0a21e7640a 100644 +--- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java ++++ b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java +@@ -316,7 +316,7 @@ public abstract class FlowingFluid extends Fluid { + ifluidcontainer.placeLiquid(world, pos, state, fluidState); + } else { + if (!state.isAir()) { +- this.beforeDestroyingBlock(world, pos, state); ++ this.beforeDestroyingBlock(world, pos, state, pos.relative(direction.getOpposite())); // Paper - Add BlockBreakBlockEvent + } + + world.setBlock(pos, fluidState.createLegacyBlock(), 3); +@@ -324,6 +324,7 @@ public abstract class FlowingFluid extends Fluid { + + } + ++ protected void beforeDestroyingBlock(LevelAccessor world, BlockPos pos, BlockState state, BlockPos source) { beforeDestroyingBlock(world, pos, state); } // Paper - Add BlockBreakBlockEvent + protected abstract void beforeDestroyingBlock(LevelAccessor world, BlockPos pos, BlockState state); + + protected int getSlopeDistance(LevelReader world, BlockPos pos, int i, Direction direction, BlockState state, FlowingFluid.SpreadContext spreadCache) { +diff --git a/src/main/java/net/minecraft/world/level/material/WaterFluid.java b/src/main/java/net/minecraft/world/level/material/WaterFluid.java +index 421f5d9a57d87a87a801213d562ad5fe244e7b65..0a7c05c08bf8c6c331b91e399dc4103a91dc20fe 100644 +--- a/src/main/java/net/minecraft/world/level/material/WaterFluid.java ++++ b/src/main/java/net/minecraft/world/level/material/WaterFluid.java +@@ -81,6 +81,13 @@ public abstract class WaterFluid extends FlowingFluid { + return world.getGameRules().getBoolean(GameRules.RULE_WATER_SOURCE_CONVERSION); + } + ++ // Paper start - Add BlockBreakBlockEvent ++ @Override ++ protected void beforeDestroyingBlock(LevelAccessor world, BlockPos pos, BlockState state, BlockPos source) { ++ BlockEntity tileentity = state.hasBlockEntity() ? world.getBlockEntity(pos) : null; ++ Block.dropResources(state, world, pos, tileentity, source); ++ } ++ // Paper end - Add BlockBreakBlockEvent + @Override + protected void beforeDestroyingBlock(LevelAccessor world, BlockPos pos, BlockState state) { + BlockEntity blockEntity = state.hasBlockEntity() ? world.getBlockEntity(pos) : null; diff --git a/patches/server/0572-Option-to-prevent-data-components-copy-in-smithing-r.patch b/patches/server/0572-Option-to-prevent-data-components-copy-in-smithing-r.patch new file mode 100644 index 0000000000..1c9a4f5da5 --- /dev/null +++ b/patches/server/0572-Option-to-prevent-data-components-copy-in-smithing-r.patch @@ -0,0 +1,157 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sun, 26 Sep 2021 12:57:28 -0700 +Subject: [PATCH] Option to prevent data components copy in smithing recipes + + +diff --git a/src/main/java/net/minecraft/world/item/crafting/SmithingTransformRecipe.java b/src/main/java/net/minecraft/world/item/crafting/SmithingTransformRecipe.java +index fa003ce16020eaab554bfd833ace779c8cefc617..017748b26590364d846a5cddaa3490b2293ad276 100644 +--- a/src/main/java/net/minecraft/world/item/crafting/SmithingTransformRecipe.java ++++ b/src/main/java/net/minecraft/world/item/crafting/SmithingTransformRecipe.java +@@ -30,8 +30,15 @@ public class SmithingTransformRecipe implements SmithingRecipe { + final ItemStack result; + @Nullable + private PlacementInfo placementInfo; ++ final boolean copyDataComponents; // Paper - Option to prevent data components copy + + public SmithingTransformRecipe(Optional<Ingredient> template, Optional<Ingredient> base, Optional<Ingredient> addition, ItemStack result) { ++ // Paper start - Option to prevent data components copy ++ this(template, base, addition, result, true); ++ } ++ public SmithingTransformRecipe(Optional<Ingredient> template, Optional<Ingredient> base, Optional<Ingredient> addition, ItemStack result, boolean copyDataComponents) { ++ this.copyDataComponents = copyDataComponents; ++ // Paper end - Option to prevent data components copy + this.template = template; + this.base = base; + this.addition = addition; +@@ -41,7 +48,9 @@ public class SmithingTransformRecipe implements SmithingRecipe { + public ItemStack assemble(SmithingRecipeInput input, HolderLookup.Provider registries) { + ItemStack itemstack = input.base().transmuteCopy(this.result.getItem(), this.result.getCount()); + ++ if (this.copyDataComponents) { // Paper - Option to prevent data components copy + itemstack.applyComponents(this.result.getComponentsPatch()); ++ } // Paper - Option to prevent data components copy + return itemstack; + } + +@@ -84,7 +93,7 @@ public class SmithingTransformRecipe implements SmithingRecipe { + public Recipe toBukkitRecipe(NamespacedKey id) { + CraftItemStack result = CraftItemStack.asCraftMirror(this.result); + +- CraftSmithingTransformRecipe recipe = new CraftSmithingTransformRecipe(id, result, CraftRecipe.toBukkit(this.template), CraftRecipe.toBukkit(this.base), CraftRecipe.toBukkit(this.addition)); ++ CraftSmithingTransformRecipe recipe = new CraftSmithingTransformRecipe(id, result, CraftRecipe.toBukkit(this.template), CraftRecipe.toBukkit(this.base), CraftRecipe.toBukkit(this.addition), this.copyDataComponents); // Paper - Option to prevent data components copy + + return recipe; + } +diff --git a/src/main/java/net/minecraft/world/item/crafting/SmithingTrimRecipe.java b/src/main/java/net/minecraft/world/item/crafting/SmithingTrimRecipe.java +index a5adce474b2e78233c39cbed367e3d14515923b1..7209170454f10225d7d4a4a107e6717fc18a02ad 100644 +--- a/src/main/java/net/minecraft/world/item/crafting/SmithingTrimRecipe.java ++++ b/src/main/java/net/minecraft/world/item/crafting/SmithingTrimRecipe.java +@@ -35,18 +35,28 @@ public class SmithingTrimRecipe implements SmithingRecipe { + final Optional<Ingredient> addition; + @Nullable + private PlacementInfo placementInfo; ++ final boolean copyDataComponents; // Paper - Option to prevent data components copy + + public SmithingTrimRecipe(Optional<Ingredient> template, Optional<Ingredient> base, Optional<Ingredient> addition) { ++ // Paper start - Option to prevent data components copy ++ this(template, base, addition, true); ++ } ++ public SmithingTrimRecipe(Optional<Ingredient> template, Optional<Ingredient> base, Optional<Ingredient> addition, boolean copyDataComponents) { ++ this.copyDataComponents = copyDataComponents; ++ // Paper end - Option to prevent data components copy + this.template = template; + this.base = base; + this.addition = addition; + } + + public ItemStack assemble(SmithingRecipeInput input, HolderLookup.Provider registries) { +- return SmithingTrimRecipe.applyTrim(registries, input.base(), input.addition(), input.template()); ++ return SmithingTrimRecipe.applyTrim(registries, input.base(), input.addition(), input.template(), this.copyDataComponents); + } + + public static ItemStack applyTrim(HolderLookup.Provider registries, ItemStack base, ItemStack addition, ItemStack template) { ++ return applyTrim(registries, base, addition, template, true); ++ } ++ public static ItemStack applyTrim(HolderLookup.Provider registries, ItemStack base, ItemStack addition, ItemStack template, boolean copyDataComponents) { + Optional<Holder.Reference<TrimMaterial>> optional = TrimMaterials.getFromIngredient(registries, addition); + Optional<Holder.Reference<TrimPattern>> optional1 = TrimPatterns.getFromTemplate(registries, template); + +@@ -56,7 +66,7 @@ public class SmithingTrimRecipe implements SmithingRecipe { + if (armortrim != null && armortrim.hasPatternAndMaterial((Holder) optional1.get(), (Holder) optional.get())) { + return ItemStack.EMPTY; + } else { +- ItemStack itemstack3 = base.copyWithCount(1); ++ ItemStack itemstack3 = copyDataComponents ? base.copyWithCount(1) : new ItemStack(base.getItem(), 1); // Paper - Option to prevent data components copy + + itemstack3.set(DataComponents.TRIM, new ArmorTrim((Holder) optional.get(), (Holder) optional1.get())); + return itemstack3; +@@ -107,7 +117,7 @@ public class SmithingTrimRecipe implements SmithingRecipe { + // CraftBukkit start + @Override + public Recipe toBukkitRecipe(NamespacedKey id) { +- return new CraftSmithingTrimRecipe(id, CraftRecipe.toBukkit(this.template), CraftRecipe.toBukkit(this.base), CraftRecipe.toBukkit(this.addition)); ++ return new CraftSmithingTrimRecipe(id, CraftRecipe.toBukkit(this.template), CraftRecipe.toBukkit(this.base), CraftRecipe.toBukkit(this.addition), this.copyDataComponents); // Paper - Option to prevent data components copy + } + // CraftBukkit end + +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmithingTransformRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmithingTransformRecipe.java +index 0dc2be8f502a50f13d8fe860c209ebfa43a931ea..af6c1ccdf2b91b1284daee5552eb44cc9a34cd5f 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmithingTransformRecipe.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmithingTransformRecipe.java +@@ -11,12 +11,17 @@ public class CraftSmithingTransformRecipe extends SmithingTransformRecipe implem + public CraftSmithingTransformRecipe(NamespacedKey key, ItemStack result, RecipeChoice template, RecipeChoice base, RecipeChoice addition) { + super(key, result, template, base, addition); + } ++ // Paper start - Option to prevent data components copy ++ public CraftSmithingTransformRecipe(NamespacedKey key, ItemStack result, RecipeChoice template, RecipeChoice base, RecipeChoice addition, boolean copyDataComponents) { ++ super(key, result, template, base, addition, copyDataComponents); ++ } ++ // Paper end - Option to prevent data components copy + + public static CraftSmithingTransformRecipe fromBukkitRecipe(SmithingTransformRecipe recipe) { + if (recipe instanceof CraftSmithingTransformRecipe) { + return (CraftSmithingTransformRecipe) recipe; + } +- CraftSmithingTransformRecipe ret = new CraftSmithingTransformRecipe(recipe.getKey(), recipe.getResult(), recipe.getTemplate(), recipe.getBase(), recipe.getAddition()); ++ CraftSmithingTransformRecipe ret = new CraftSmithingTransformRecipe(recipe.getKey(), recipe.getResult(), recipe.getTemplate(), recipe.getBase(), recipe.getAddition(), recipe.willCopyDataComponents()); // Paper - Option to prevent data components copy + return ret; + } + +@@ -24,6 +29,6 @@ public class CraftSmithingTransformRecipe extends SmithingTransformRecipe implem + public void addToCraftingManager() { + ItemStack result = this.getResult(); + +- MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftRecipe.toMinecraft(this.getKey()), new net.minecraft.world.item.crafting.SmithingTransformRecipe(this.toNMSOptional(this.getTemplate(), false), this.toNMSOptional(this.getBase(), false), this.toNMSOptional(this.getAddition(), false), CraftItemStack.asNMSCopy(result)))); ++ MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftRecipe.toMinecraft(this.getKey()), new net.minecraft.world.item.crafting.SmithingTransformRecipe(this.toNMSOptional(this.getTemplate(), false), this.toNMSOptional(this.getBase(), false), this.toNMSOptional(this.getAddition(), false), CraftItemStack.asNMSCopy(result), this.willCopyDataComponents()))); // Paper - Option to prevent data components copy + } + } +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmithingTrimRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmithingTrimRecipe.java +index 202963e2f53b5e7d6fd43c58b27ad49ce009cc3c..fb710aa6dc416a3423345ad5b6e9494507eb0cb4 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmithingTrimRecipe.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmithingTrimRecipe.java +@@ -11,17 +11,22 @@ public class CraftSmithingTrimRecipe extends SmithingTrimRecipe implements Craft + public CraftSmithingTrimRecipe(NamespacedKey key, RecipeChoice template, RecipeChoice base, RecipeChoice addition) { + super(key, template, base, addition); + } ++ // Paper start - Option to prevent data components copy ++ public CraftSmithingTrimRecipe(NamespacedKey key, RecipeChoice template, RecipeChoice base, RecipeChoice addition, boolean copyDataComponents) { ++ super(key, template, base, addition, copyDataComponents); ++ } ++ // Paper end - Option to prevent data components copy + + public static CraftSmithingTrimRecipe fromBukkitRecipe(SmithingTrimRecipe recipe) { + if (recipe instanceof CraftSmithingTrimRecipe) { + return (CraftSmithingTrimRecipe) recipe; + } +- CraftSmithingTrimRecipe ret = new CraftSmithingTrimRecipe(recipe.getKey(), recipe.getTemplate(), recipe.getBase(), recipe.getAddition()); ++ CraftSmithingTrimRecipe ret = new CraftSmithingTrimRecipe(recipe.getKey(), recipe.getTemplate(), recipe.getBase(), recipe.getAddition(), recipe.willCopyDataComponents()); // Paper - Option to prevent data components copy + return ret; + } + + @Override + public void addToCraftingManager() { +- MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftRecipe.toMinecraft(this.getKey()), new net.minecraft.world.item.crafting.SmithingTrimRecipe(this.toNMSOptional(this.getTemplate(), false), this.toNMSOptional(this.getBase(), false), this.toNMSOptional(this.getAddition(), false)))); ++ MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftRecipe.toMinecraft(this.getKey()), new net.minecraft.world.item.crafting.SmithingTrimRecipe(this.toNMSOptional(this.getTemplate(), false), this.toNMSOptional(this.getBase(), false), this.toNMSOptional(this.getAddition(), false), this.willCopyDataComponents()))); // Paper - Option to prevent data components copy + } + } diff --git a/patches/server/0573-More-CommandBlock-API.patch b/patches/server/0573-More-CommandBlock-API.patch new file mode 100644 index 0000000000..5fa4fa81e1 --- /dev/null +++ b/patches/server/0573-More-CommandBlock-API.patch @@ -0,0 +1,101 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Thu, 23 Sep 2021 10:40:09 -0700 +Subject: [PATCH] More CommandBlock API + + +diff --git a/src/main/java/io/papermc/paper/commands/PaperCommandBlockHolder.java b/src/main/java/io/papermc/paper/commands/PaperCommandBlockHolder.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0463aef1c8be238b00e73907bde927f1522a46ce +--- /dev/null ++++ b/src/main/java/io/papermc/paper/commands/PaperCommandBlockHolder.java +@@ -0,0 +1,34 @@ ++package io.papermc.paper.commands; ++ ++import io.papermc.paper.adventure.PaperAdventure; ++import io.papermc.paper.command.CommandBlockHolder; ++import net.kyori.adventure.text.Component; ++import net.minecraft.world.level.BaseCommandBlock; ++import org.jspecify.annotations.NullMarked; ++import org.jspecify.annotations.Nullable; ++ ++@NullMarked ++public interface PaperCommandBlockHolder extends CommandBlockHolder { ++ ++ BaseCommandBlock getCommandBlockHandle(); ++ ++ @Override ++ default Component lastOutput() { ++ return PaperAdventure.asAdventure(this.getCommandBlockHandle().getLastOutput()); ++ } ++ ++ @Override ++ default void lastOutput(final @Nullable Component lastOutput) { ++ this.getCommandBlockHandle().setLastOutput(PaperAdventure.asVanilla(lastOutput)); ++ } ++ ++ @Override ++ default int getSuccessCount() { ++ return this.getCommandBlockHandle().getSuccessCount(); ++ } ++ ++ @Override ++ default void setSuccessCount(final int successCount) { ++ this.getCommandBlockHandle().setSuccessCount(successCount); ++ } ++} +diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftCommandBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftCommandBlock.java +index f5b0bec4c1164fe7ef6da1f19a6ce9bb3d6864d0..138e6539a7786ded482a24aa88a367da7beaabf9 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/CraftCommandBlock.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/CraftCommandBlock.java +@@ -6,7 +6,7 @@ import org.bukkit.World; + import org.bukkit.block.CommandBlock; + import org.bukkit.craftbukkit.util.CraftChatMessage; + +-public class CraftCommandBlock extends CraftBlockEntityState<CommandBlockEntity> implements CommandBlock { ++public class CraftCommandBlock extends CraftBlockEntityState<CommandBlockEntity> implements CommandBlock, io.papermc.paper.commands.PaperCommandBlockHolder { + + public CraftCommandBlock(World world, CommandBlockEntity tileEntity) { + super(world, tileEntity); +@@ -56,5 +56,10 @@ public class CraftCommandBlock extends CraftBlockEntityState<CommandBlockEntity> + public void name(net.kyori.adventure.text.Component name) { + getSnapshot().getCommandBlock().setCustomName(name == null ? net.minecraft.network.chat.Component.literal("@") : io.papermc.paper.adventure.PaperAdventure.asVanilla(name)); + } ++ ++ @Override ++ public net.minecraft.world.level.BaseCommandBlock getCommandBlockHandle() { ++ return getSnapshot().getCommandBlock(); ++ } + // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartCommand.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartCommand.java +index 9ea1537408ff2d790747b6e5a681d9171a4233ae..f34fa6715e477936097367a7aefd1a2bf87d3d90 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartCommand.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartCommand.java +@@ -13,7 +13,7 @@ import org.bukkit.permissions.PermissionAttachment; + import org.bukkit.permissions.PermissionAttachmentInfo; + import org.bukkit.plugin.Plugin; + +-public class CraftMinecartCommand extends CraftMinecart implements CommandMinecart { ++public class CraftMinecartCommand extends CraftMinecart implements CommandMinecart, io.papermc.paper.commands.PaperCommandBlockHolder { + private final PermissibleBase perm = new PermissibleBase(this); + + public CraftMinecartCommand(CraftServer server, MinecartCommandBlock entity) { +@@ -64,6 +64,17 @@ public class CraftMinecartCommand extends CraftMinecart implements CommandMineca + public [email protected] Component name() { + return io.papermc.paper.adventure.PaperAdventure.asAdventure(this.getHandle().getCommandBlock().getName()); + } ++ ++ @Override ++ public net.minecraft.world.level.BaseCommandBlock getCommandBlockHandle() { ++ return getHandle().getCommandBlock(); ++ } ++ ++ @Override ++ public void lastOutput(net.kyori.adventure.text.Component lastOutput) { ++ io.papermc.paper.commands.PaperCommandBlockHolder.super.lastOutput(lastOutput); ++ getCommandBlockHandle().onUpdated(); ++ } + // Paper end + + @Override diff --git a/patches/server/0574-Add-missing-team-sidebar-display-slots.patch b/patches/server/0574-Add-missing-team-sidebar-display-slots.patch new file mode 100644 index 0000000000..de7d3fc8e5 --- /dev/null +++ b/patches/server/0574-Add-missing-team-sidebar-display-slots.patch @@ -0,0 +1,114 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Fri, 1 Oct 2021 08:04:39 -0700 +Subject: [PATCH] Add missing team sidebar display slots + +== AT == +public org.bukkit.craftbukkit.scoreboard.CraftScoreboardTranslations +public org.bukkit.craftbukkit.scoreboard.CraftScoreboardTranslations toBukkitSlot(Lnet/minecraft/world/scores/DisplaySlot;)Lorg/bukkit/scoreboard/DisplaySlot; +public org.bukkit.craftbukkit.scoreboard.CraftScoreboardTranslations fromBukkitSlot(Lorg/bukkit/scoreboard/DisplaySlot;)Lnet/minecraft/world/scores/DisplaySlot; + +diff --git a/src/main/java/org/bukkit/craftbukkit/legacy/FieldRename.java b/src/main/java/org/bukkit/craftbukkit/legacy/FieldRename.java +index 12fe2f8d0dcb715545e071023490a32125b9c4a4..fe29c08270854d37a4b111f66ebf261260200f28 100644 +--- a/src/main/java/org/bukkit/craftbukkit/legacy/FieldRename.java ++++ b/src/main/java/org/bukkit/craftbukkit/legacy/FieldRename.java +@@ -35,6 +35,7 @@ public class FieldRename { + } + + return switch (owner) { ++ case "org/bukkit/scoreboard/DisplaySlot" -> FieldRename.convertDisplaySlot(from); // Paper - DisplaySlot + case "org/bukkit/block/banner/PatternType" -> FieldRename.convertPatternTypeName(apiVersion, from); + case "org/bukkit/enchantments/Enchantment" -> FieldRename.convertEnchantmentName(apiVersion, from); + case "org/bukkit/block/Biome" -> FieldRename.convertBiomeName(apiVersion, from); +@@ -60,6 +61,16 @@ public class FieldRename { + //} + // Paper end + ++ // Paper start - DisplaySlot ++ @DoNotReroute ++ public static String convertDisplaySlot(final String from) { ++ if (from.startsWith("SIDEBAR_") && !from.startsWith("SIDEBAR_TEAM_")) { ++ return from.replace("SIDEBAR_", "SIDEBAR_TEAM_"); ++ } ++ return from; ++ } ++ // Paper end - DisplaySlot ++ + // PatternType + private static final FieldRenameData PATTERN_TYPE_DATA = FieldRenameData.Builder.newBuilder() + .forVersionsBefore(ApiVersion.FIELD_NAME_PARITY) +diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardTranslations.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardTranslations.java +index 73c5ffff70605b32188a9bb5fb6c0ee04cb66efe..711d227f5ee6d63356a94a0567968da48e9f284c 100644 +--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardTranslations.java ++++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardTranslations.java +@@ -7,35 +7,22 @@ import org.bukkit.scoreboard.RenderType; + + public final class CraftScoreboardTranslations { + static final int MAX_DISPLAY_SLOT = 19; ++ @Deprecated // Paper + static final ImmutableBiMap<DisplaySlot, String> SLOTS = ImmutableBiMap.<DisplaySlot, String>builder() + .put(DisplaySlot.BELOW_NAME, "below_name") + .put(DisplaySlot.PLAYER_LIST, "list") + .put(DisplaySlot.SIDEBAR, "sidebar") +- .put(DisplaySlot.SIDEBAR_BLACK, "sidebar.team.black") +- .put(DisplaySlot.SIDEBAR_DARK_BLUE, "sidebar.team.dark_blue") +- .put(DisplaySlot.SIDEBAR_DARK_GREEN, "sidebar.team.dark_green") +- .put(DisplaySlot.SIDEBAR_DARK_AQUA, "sidebar.team.dark_aqua") +- .put(DisplaySlot.SIDEBAR_DARK_RED, "sidebar.team.dark_red") +- .put(DisplaySlot.SIDEBAR_DARK_PURPLE, "sidebar.team.dark_purple") +- .put(DisplaySlot.SIDEBAR_GOLD, "sidebar.team.gold") +- .put(DisplaySlot.SIDEBAR_GRAY, "sidebar.team.gray") +- .put(DisplaySlot.SIDEBAR_DARK_GRAY, "sidebar.team.dark_gray") +- .put(DisplaySlot.SIDEBAR_BLUE, "sidebar.team.blue") +- .put(DisplaySlot.SIDEBAR_GREEN, "sidebar.team.green") +- .put(DisplaySlot.SIDEBAR_AQUA, "sidebar.team.aqua") +- .put(DisplaySlot.SIDEBAR_RED, "sidebar.team.red") +- .put(DisplaySlot.SIDEBAR_LIGHT_PURPLE, "sidebar.team.light_purple") +- .put(DisplaySlot.SIDEBAR_YELLOW, "sidebar.team.yellow") +- .put(DisplaySlot.SIDEBAR_WHITE, "sidebar.team.white") + .buildOrThrow(); + + private CraftScoreboardTranslations() {} + + public static DisplaySlot toBukkitSlot(net.minecraft.world.scores.DisplaySlot minecraft) { ++ if (true) return DisplaySlot.NAMES.value(minecraft.getSerializedName()); // Paper + return CraftScoreboardTranslations.SLOTS.inverse().get(minecraft.getSerializedName()); + } + + public static net.minecraft.world.scores.DisplaySlot fromBukkitSlot(DisplaySlot slot) { ++ if (true) return net.minecraft.world.scores.DisplaySlot.CODEC.byName(slot.getId()); // Paper + return net.minecraft.world.scores.DisplaySlot.CODEC.byName(CraftScoreboardTranslations.SLOTS.get(slot)); + } + +diff --git a/src/test/java/io/papermc/paper/scoreboard/DisplaySlotTest.java b/src/test/java/io/papermc/paper/scoreboard/DisplaySlotTest.java +new file mode 100644 +index 0000000000000000000000000000000000000000..345c96bfb6c559b41c2b6682067198a74d35b440 +--- /dev/null ++++ b/src/test/java/io/papermc/paper/scoreboard/DisplaySlotTest.java +@@ -0,0 +1,26 @@ ++package io.papermc.paper.scoreboard; ++ ++import org.bukkit.craftbukkit.scoreboard.CraftScoreboardTranslations; ++import org.bukkit.scoreboard.DisplaySlot; ++import org.bukkit.support.environment.Normal; ++import org.junit.jupiter.api.Test; ++ ++import static org.junit.jupiter.api.Assertions.assertNotNull; ++ ++@Normal ++public class DisplaySlotTest { ++ ++ @Test ++ public void testBukkitToMinecraftDisplaySlots() { ++ for (DisplaySlot bukkitSlot : DisplaySlot.values()) { ++ assertNotNull(CraftScoreboardTranslations.fromBukkitSlot(bukkitSlot)); ++ } ++ } ++ ++ @Test ++ public void testMinecraftToBukkitDisplaySlots() { ++ for (net.minecraft.world.scores.DisplaySlot nmsSlot : net.minecraft.world.scores.DisplaySlot.values()) { ++ assertNotNull(CraftScoreboardTranslations.toBukkitSlot(nmsSlot)); ++ } ++ } ++} diff --git a/patches/server/0575-Add-back-EntityPortalExitEvent.patch b/patches/server/0575-Add-back-EntityPortalExitEvent.patch new file mode 100644 index 0000000000..d9d52b0184 --- /dev/null +++ b/patches/server/0575-Add-back-EntityPortalExitEvent.patch @@ -0,0 +1,48 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sun, 16 May 2021 09:39:46 -0700 +Subject: [PATCH] Add back EntityPortalExitEvent + + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index ddb3d68f064d2dd82b42d0013c65f3685ef1065d..f9e52b8794d62cc79c420008a1a95707efa14321 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -3522,6 +3522,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + if (!this.isRemoved()) { + // CraftBukkit start + PositionMoveRotation absolutePosition = PositionMoveRotation.calculateAbsolute(PositionMoveRotation.of(this), PositionMoveRotation.of(teleportTarget), teleportTarget.relatives()); ++ Vec3 velocity = absolutePosition.deltaMovement(); // Paper + Location to = CraftLocation.toBukkit(absolutePosition.position(), teleportTarget.newLevel().getWorld(), absolutePosition.yRot(), absolutePosition.xRot()); + // Paper start - gateway-specific teleport event + final EntityTeleportEvent teleEvent; +@@ -3538,7 +3539,29 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + if (!to.equals(teleEvent.getTo())) { + to = teleEvent.getTo(); + teleportTarget = new TeleportTransition(((CraftWorld) to.getWorld()).getHandle(), CraftLocation.toVec3D(to), Vec3.ZERO, to.getYaw(), to.getPitch(), teleportTarget.missingRespawnBlock(), teleportTarget.asPassenger(), Set.of(), teleportTarget.postTeleportTransition(), teleportTarget.cause()); ++ // Paper start - Call EntityPortalExitEvent ++ velocity = Vec3.ZERO; + } ++ if (this.portalProcess != null) { // if in a portal ++ CraftEntity bukkitEntity = this.getBukkitEntity(); ++ org.bukkit.event.entity.EntityPortalExitEvent event = new org.bukkit.event.entity.EntityPortalExitEvent( ++ bukkitEntity, ++ bukkitEntity.getLocation(), to.clone(), ++ bukkitEntity.getVelocity(), org.bukkit.craftbukkit.util.CraftVector.toBukkit(velocity) ++ ); ++ event.callEvent(); ++ ++ // Only change the target if actually needed, since we reset relative flags ++ if (!event.isCancelled() && event.getTo() != null && (!event.getTo().equals(event.getFrom()) || !event.getAfter().equals(event.getBefore()))) { ++ to = event.getTo().clone(); ++ velocity = org.bukkit.craftbukkit.util.CraftVector.toNMS(event.getAfter()); ++ teleportTarget = new TeleportTransition(((CraftWorld) to.getWorld()).getHandle(), CraftLocation.toVec3D(to), velocity, to.getYaw(), to.getPitch(), teleportTarget.missingRespawnBlock(), teleportTarget.asPassenger(), Set.of(), teleportTarget.postTeleportTransition(), teleportTarget.cause()); ++ } ++ } ++ if (this.isRemoved()) { ++ return null; ++ } ++ // Paper end - Call EntityPortalExitEvent + // CraftBukkit end + ServerLevel worldserver1 = teleportTarget.newLevel(); + boolean flag = worldserver1.dimension() != worldserver.dimension(); diff --git a/patches/server/0576-Add-methods-to-find-targets-for-lightning-strikes.patch b/patches/server/0576-Add-methods-to-find-targets-for-lightning-strikes.patch new file mode 100644 index 0000000000..a21e15c6fc --- /dev/null +++ b/patches/server/0576-Add-methods-to-find-targets-for-lightning-strikes.patch @@ -0,0 +1,60 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jakub Zacek <[email protected]> +Date: Mon, 4 Oct 2021 10:16:44 +0200 +Subject: [PATCH] Add methods to find targets for lightning strikes + +== AT == +public net.minecraft.server.level.ServerLevel findLightningRod(Lnet/minecraft/core/BlockPos;)Ljava/util/Optional; + +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index 93656336af194994f59072fa89bfc338d89d76af..e6ff03143b3639f375323393814b1256b98687ad 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -741,6 +741,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + } + + protected BlockPos findLightningTargetAround(BlockPos pos) { ++ // Paper start - Add methods to find targets for lightning strikes ++ return this.findLightningTargetAround(pos, false); ++ } ++ public BlockPos findLightningTargetAround(BlockPos pos, boolean returnNullWhenNoTarget) { ++ // Paper end - Add methods to find targets for lightning strikes + BlockPos blockposition1 = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, pos); + Optional<BlockPos> optional = this.findLightningRod(blockposition1); + +@@ -755,6 +760,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + if (!list.isEmpty()) { + return ((LivingEntity) list.get(this.random.nextInt(list.size()))).blockPosition(); + } else { ++ if (returnNullWhenNoTarget) return null; // Paper - Add methods to find targets for lightning strikes + if (blockposition1.getY() == this.getMinY() - 1) { + blockposition1 = blockposition1.above(2); + } +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index d72ceb866f8632a8daa7dc19acdc57b6b78fd906..d650822155f4628ea9d61ecbd4208520746aa59f 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -676,6 +676,23 @@ public class CraftWorld extends CraftRegionAccessor implements World { + return (LightningStrike) lightning.getBukkitEntity(); + } + ++ // Paper start - Add methods to find targets for lightning strikes ++ @Override ++ public Location findLightningRod(Location location) { ++ return this.world.findLightningRod(io.papermc.paper.util.MCUtil.toBlockPosition(location)) ++ .map(blockPos -> io.papermc.paper.util.MCUtil.toLocation(this.world, blockPos) ++ // get the actual rod pos ++ .subtract(0, 1, 0)) ++ .orElse(null); ++ } ++ ++ @Override ++ public Location findLightningTarget(Location location) { ++ final BlockPos pos = this.world.findLightningTargetAround(io.papermc.paper.util.MCUtil.toBlockPosition(location), true); ++ return pos == null ? null : io.papermc.paper.util.MCUtil.toLocation(this.world, pos); ++ } ++ // Paper end - Add methods to find targets for lightning strikes ++ + @Override + public boolean generateTree(Location loc, TreeType type) { + return this.generateTree(loc, CraftWorld.rand, type); diff --git a/patches/server/0577-Get-entity-default-attributes.patch b/patches/server/0577-Get-entity-default-attributes.patch new file mode 100644 index 0000000000..1f2a14bb52 --- /dev/null +++ b/patches/server/0577-Get-entity-default-attributes.patch @@ -0,0 +1,151 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Fri, 20 Aug 2021 13:03:21 -0700 +Subject: [PATCH] Get entity default attributes + +== AT == +public net.minecraft.world.entity.ai.attributes.AttributeSupplier getAttributeInstance(Lnet/minecraft/core/Holder;)Lnet/minecraft/world/entity/ai/attributes/AttributeInstance; + +diff --git a/src/main/java/io/papermc/paper/attribute/UnmodifiableAttributeInstance.java b/src/main/java/io/papermc/paper/attribute/UnmodifiableAttributeInstance.java +new file mode 100644 +index 0000000000000000000000000000000000000000..12135ffeacd648f6bc4d7d327059ea1a7e8c79c4 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/attribute/UnmodifiableAttributeInstance.java +@@ -0,0 +1,30 @@ ++package io.papermc.paper.attribute; ++ ++import net.minecraft.world.entity.ai.attributes.AttributeInstance; ++import org.bukkit.attribute.Attribute; ++import org.bukkit.attribute.AttributeModifier; ++import org.bukkit.craftbukkit.attribute.CraftAttributeInstance; ++ ++import java.util.Collection; ++ ++public class UnmodifiableAttributeInstance extends CraftAttributeInstance { ++ ++ public UnmodifiableAttributeInstance(AttributeInstance handle, Attribute attribute) { ++ super(handle, attribute); ++ } ++ ++ @Override ++ public void setBaseValue(double d) { ++ throw new UnsupportedOperationException("Cannot modify default attributes"); ++ } ++ ++ @Override ++ public void addModifier(AttributeModifier modifier) { ++ throw new UnsupportedOperationException("Cannot modify default attributes"); ++ } ++ ++ @Override ++ public void removeModifier(AttributeModifier modifier) { ++ throw new UnsupportedOperationException("Cannot modify default attributes"); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/attribute/UnmodifiableAttributeMap.java b/src/main/java/io/papermc/paper/attribute/UnmodifiableAttributeMap.java +new file mode 100644 +index 0000000000000000000000000000000000000000..ec9ebd2d539333293c51b7edfa18f18b066d7e43 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/attribute/UnmodifiableAttributeMap.java +@@ -0,0 +1,32 @@ ++package io.papermc.paper.attribute; ++ ++import net.minecraft.world.entity.ai.attributes.AttributeSupplier; ++import org.bukkit.attribute.Attributable; ++import org.bukkit.attribute.Attribute; ++import org.bukkit.attribute.AttributeInstance; ++import org.bukkit.craftbukkit.attribute.CraftAttribute; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++ ++public class UnmodifiableAttributeMap implements Attributable { ++ ++ private final AttributeSupplier handle; ++ ++ public UnmodifiableAttributeMap(@NotNull AttributeSupplier handle) { ++ this.handle = handle; ++ } ++ ++ @Override ++ public @Nullable AttributeInstance getAttribute(@NotNull Attribute attribute) { ++ net.minecraft.core.Holder<net.minecraft.world.entity.ai.attributes.Attribute> nmsAttribute = CraftAttribute.bukkitToMinecraftHolder(attribute); ++ if (!this.handle.hasAttribute(nmsAttribute)) { ++ return null; ++ } ++ return new UnmodifiableAttributeInstance(this.handle.getAttributeInstance(nmsAttribute), attribute); ++ } ++ ++ @Override ++ public void registerAttribute(@NotNull Attribute attribute) { ++ throw new UnsupportedOperationException("Cannot register new attributes here"); ++ } ++} +diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +index e613ea0eba9a1d162b8f7dfe32c9c31d82f17dd2..6f780b76ebadb6155195b93f3e6d382141eb0bcc 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +@@ -560,6 +560,18 @@ public final class CraftMagicNumbers implements UnsafeValues { + } + return CraftItemStack.unwrap(itemToBeRepaired).isValidRepairItem(CraftItemStack.unwrap(repairMaterial)); + } ++ ++ @Override ++ public boolean hasDefaultEntityAttributes(NamespacedKey bukkitEntityKey) { ++ return net.minecraft.world.entity.ai.attributes.DefaultAttributes.hasSupplier(net.minecraft.core.registries.BuiltInRegistries.ENTITY_TYPE.getValue(CraftNamespacedKey.toMinecraft(bukkitEntityKey))); ++ } ++ ++ @Override ++ public org.bukkit.attribute.Attributable getDefaultEntityAttributes(NamespacedKey bukkitEntityKey) { ++ Preconditions.checkArgument(hasDefaultEntityAttributes(bukkitEntityKey), bukkitEntityKey + " doesn't have default attributes"); ++ var supplier = net.minecraft.world.entity.ai.attributes.DefaultAttributes.getSupplier((net.minecraft.world.entity.EntityType<? extends net.minecraft.world.entity.LivingEntity>) net.minecraft.core.registries.BuiltInRegistries.ENTITY_TYPE.getValue(CraftNamespacedKey.toMinecraft(bukkitEntityKey))); ++ return new io.papermc.paper.attribute.UnmodifiableAttributeMap(supplier); ++ } + // Paper end + + /** +diff --git a/src/test/java/io/papermc/paper/attribute/EntityTypeAttributesTest.java b/src/test/java/io/papermc/paper/attribute/EntityTypeAttributesTest.java +new file mode 100644 +index 0000000000000000000000000000000000000000..fa9fb37993f4025f85dac084efb4b5eda0ede637 +--- /dev/null ++++ b/src/test/java/io/papermc/paper/attribute/EntityTypeAttributesTest.java +@@ -0,0 +1,40 @@ ++package io.papermc.paper.attribute; ++ ++import org.bukkit.attribute.Attributable; ++import org.bukkit.attribute.Attribute; ++import org.bukkit.attribute.AttributeInstance; ++import org.bukkit.attribute.AttributeModifier; ++import org.bukkit.entity.EntityType; ++import org.bukkit.support.environment.AllFeatures; ++import org.junit.jupiter.api.Test; ++ ++import static org.junit.jupiter.api.Assertions.assertFalse; ++import static org.junit.jupiter.api.Assertions.assertNotNull; ++import static org.junit.jupiter.api.Assertions.assertThrows; ++import static org.junit.jupiter.api.Assertions.assertTrue; ++ ++@AllFeatures ++public class EntityTypeAttributesTest { ++ ++ @Test ++ public void testIllegalEntity() { ++ assertFalse(EntityType.EGG.hasDefaultAttributes()); ++ assertThrows(IllegalArgumentException.class, EntityType.EGG::getDefaultAttributes); ++ } ++ ++ @Test ++ public void testLegalEntity() { ++ assertTrue(EntityType.ZOMBIE.hasDefaultAttributes()); ++ EntityType.ZOMBIE.getDefaultAttributes(); ++ } ++ ++ @Test ++ public void testUnmodifiabilityOfAttributable() { ++ Attributable attributable = EntityType.ZOMBIE.getDefaultAttributes(); ++ assertThrows(UnsupportedOperationException.class, () -> attributable.registerAttribute(Attribute.ATTACK_DAMAGE)); ++ AttributeInstance instance = attributable.getAttribute(Attribute.FOLLOW_RANGE); ++ assertNotNull(instance); ++ assertThrows(UnsupportedOperationException.class, () -> instance.addModifier(new AttributeModifier("test", 3, AttributeModifier.Operation.ADD_NUMBER))); ++ assertThrows(UnsupportedOperationException.class, () -> instance.setBaseValue(3.2)); ++ } ++} diff --git a/patches/server/0578-Left-handed-API.patch b/patches/server/0578-Left-handed-API.patch new file mode 100644 index 0000000000..50bb0d4ebe --- /dev/null +++ b/patches/server/0578-Left-handed-API.patch @@ -0,0 +1,27 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath <[email protected]> +Date: Thu, 14 Oct 2021 12:36:58 -0500 +Subject: [PATCH] Left handed API + + +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java +index 7cf42f62d91c131b1cab576979f85c58c3cecb3b..e226a99d00c990a4ca4f21b93fcae7a556e01dbb 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java +@@ -145,6 +145,16 @@ public abstract class CraftMob extends CraftLivingEntity implements Mob { + public int getMaxHeadPitch() { + return getHandle().getMaxHeadXRot(); + } ++ ++ @Override ++ public boolean isLeftHanded() { ++ return getHandle().isLeftHanded(); ++ } ++ ++ @Override ++ public void setLeftHanded(boolean leftHanded) { ++ getHandle().setLeftHanded(leftHanded); ++ } + // Paper end + + // Paper start diff --git a/patches/server/0579-Add-more-advancement-API.patch b/patches/server/0579-Add-more-advancement-API.patch new file mode 100644 index 0000000000..70ac177106 --- /dev/null +++ b/patches/server/0579-Add-more-advancement-API.patch @@ -0,0 +1,213 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: syldium <[email protected]> +Date: Fri, 9 Jul 2021 18:50:40 +0200 +Subject: [PATCH] Add more advancement API + +== AT == +public net.minecraft.advancements.Advancement decorateName(Lnet/minecraft/advancements/DisplayInfo;)Lnet/minecraft/network/chat/Component; + +Co-authored-by: Jake Potrebic <[email protected]> + +diff --git a/src/main/java/io/papermc/paper/advancement/PaperAdvancementDisplay.java b/src/main/java/io/papermc/paper/advancement/PaperAdvancementDisplay.java +new file mode 100644 +index 0000000000000000000000000000000000000000..adac21ce6db3ff7a56dbcd6bffc02143c2db4046 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/advancement/PaperAdvancementDisplay.java +@@ -0,0 +1,69 @@ ++package io.papermc.paper.advancement; ++ ++import io.papermc.paper.adventure.PaperAdventure; ++import net.kyori.adventure.text.Component; ++import net.minecraft.advancements.Advancement; ++import net.minecraft.advancements.AdvancementType; ++import net.minecraft.advancements.DisplayInfo; ++import org.bukkit.NamespacedKey; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.craftbukkit.util.CraftNamespacedKey; ++import org.bukkit.inventory.ItemStack; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++ ++public record PaperAdvancementDisplay(DisplayInfo handle) implements AdvancementDisplay { ++ ++ @Override ++ public @NotNull Frame frame() { ++ return asPaperFrame(this.handle.getType()); ++ } ++ ++ @Override ++ public @NotNull Component title() { ++ return PaperAdventure.asAdventure(this.handle.getTitle()); ++ } ++ ++ @Override ++ public @NotNull Component description() { ++ return PaperAdventure.asAdventure(this.handle.getDescription()); ++ } ++ ++ @Override ++ public @NotNull ItemStack icon() { ++ return CraftItemStack.asBukkitCopy(this.handle.getIcon()); ++ } ++ ++ @Override ++ public boolean doesShowToast() { ++ return this.handle.shouldShowToast(); ++ } ++ ++ @Override ++ public boolean doesAnnounceToChat() { ++ return this.handle.shouldAnnounceChat(); ++ } ++ ++ @Override ++ public boolean isHidden() { ++ return this.handle.isHidden(); ++ } ++ ++ @Override ++ public @Nullable NamespacedKey backgroundPath() { ++ return this.handle.getBackground().map(CraftNamespacedKey::fromMinecraft).orElse(null); ++ } ++ ++ @Override ++ public @NotNull Component displayName() { ++ return PaperAdventure.asAdventure(Advancement.decorateName(java.util.Objects.requireNonNull(this.handle, "cannot build display name for null handle, invalid state"))); ++ } ++ ++ public static @NotNull Frame asPaperFrame(final @NotNull AdvancementType frameType) { ++ return switch (frameType) { ++ case TASK -> Frame.TASK; ++ case CHALLENGE -> Frame.CHALLENGE; ++ case GOAL -> Frame.GOAL; ++ }; ++ } ++} +diff --git a/src/main/java/net/minecraft/advancements/DisplayInfo.java b/src/main/java/net/minecraft/advancements/DisplayInfo.java +index 05de12414a3ad1c8f0f02f7973898dda84b89b82..6581cdbec730d5d184566e7b611369b3c424fecf 100644 +--- a/src/main/java/net/minecraft/advancements/DisplayInfo.java ++++ b/src/main/java/net/minecraft/advancements/DisplayInfo.java +@@ -37,6 +37,7 @@ public class DisplayInfo { + private final boolean hidden; + private float x; + private float y; ++ public final io.papermc.paper.advancement.AdvancementDisplay paper = new io.papermc.paper.advancement.PaperAdvancementDisplay(this); // Paper - Add more advancement API + + public DisplayInfo( + ItemStack icon, +diff --git a/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancement.java b/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancement.java +index d119093d3a68c5a378fcd20860e36dbd2ba5024f..d12d9913f15c274df9c23f68e4e0304d41fccbdb 100644 +--- a/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancement.java ++++ b/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancement.java +@@ -35,12 +35,47 @@ public class CraftAdvancement implements org.bukkit.advancement.Advancement { + return new CraftAdvancementRequirements(this.handle.value().requirements()); + } + ++ // Paper start - Add more advancement API + @Override +- public AdvancementDisplay getDisplay() { +- if (this.handle.value().display().isEmpty()) { +- return null; ++ public io.papermc.paper.advancement.AdvancementDisplay getDisplay() { ++ return this.handle.value().display().map(d -> d.paper).orElse(null); ++ } ++ ++ @Deprecated ++ @io.papermc.paper.annotation.DoNotUse ++ public AdvancementDisplay getDisplay0() { // May be called by plugins via Commodore ++ return this.handle.value().display().map(CraftAdvancementDisplay::new).orElse(null); ++ } ++ ++ @Override ++ public net.kyori.adventure.text.Component displayName() { ++ return io.papermc.paper.adventure.PaperAdventure.asAdventure(net.minecraft.advancements.Advancement.name(this.handle)); ++ } ++ ++ @Override ++ public org.bukkit.advancement.Advancement getParent() { ++ return this.handle.value().parent() ++ .map(net.minecraft.server.MinecraftServer.getServer().getAdvancements()::get) ++ .map(AdvancementHolder::toBukkit) ++ .orElse(null); ++ } ++ ++ @Override ++ public Collection<org.bukkit.advancement.Advancement> getChildren() { ++ final com.google.common.collect.ImmutableList.Builder<org.bukkit.advancement.Advancement> children = com.google.common.collect.ImmutableList.<org.bukkit.advancement.Advancement>builder(); ++ final net.minecraft.advancements.AdvancementNode advancementNode = net.minecraft.server.MinecraftServer.getServer().getAdvancements().tree().get(this.handle); ++ if (advancementNode != null) { ++ for (final net.minecraft.advancements.AdvancementNode child : advancementNode.children()) { ++ children.add(child.holder().toBukkit()); ++ } + } ++ return children.build(); ++ } + +- return new CraftAdvancementDisplay(this.handle.value().display().get()); ++ @Override ++ public org.bukkit.advancement.Advancement getRoot() { ++ final net.minecraft.advancements.AdvancementNode advancementNode = net.minecraft.server.MinecraftServer.getServer().getAdvancements().tree().get(this.handle); ++ return java.util.Objects.requireNonNull(advancementNode, "could not find internal advancement node for advancement " + this.handle.id()).root().holder().toBukkit(); + } ++ // Paper end - Add more advancement API + } +diff --git a/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancementDisplay.java b/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancementDisplay.java +index 8ca86852319d7463f60832bc98b825b0b4325995..62ada73302c6b3ce3fb2dcc8c31a1d9c0ac4fd09 100644 +--- a/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancementDisplay.java ++++ b/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancementDisplay.java +@@ -6,6 +6,7 @@ import org.bukkit.craftbukkit.inventory.CraftItemStack; + import org.bukkit.craftbukkit.util.CraftChatMessage; + import org.bukkit.inventory.ItemStack; + ++@Deprecated // Paper + public class CraftAdvancementDisplay implements org.bukkit.advancement.AdvancementDisplay { + + private final DisplayInfo handle; +diff --git a/src/main/java/org/bukkit/craftbukkit/util/Commodore.java b/src/main/java/org/bukkit/craftbukkit/util/Commodore.java +index c9b789c2a904c2caff516ee9aeff4a6b368766f4..52a507ff2770bd46494e805956b4569f0d4d21ee 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/Commodore.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/Commodore.java +@@ -458,6 +458,11 @@ public class Commodore { + super.visitMethodInsn(opcode, owner, name, "()Lcom/destroystokyo/paper/profile/PlayerProfile;", itf); + return; + } ++ if (owner.equals("org/bukkit/advancement/Advancement") && name.equals("getDisplay") && desc.endsWith(")Lorg/bukkit/advancement/AdvancementDisplay;")) { ++ super.visitTypeInsn(Opcodes.CHECKCAST, runtimeCbPkgPrefix() + "advancement/CraftAdvancement"); ++ super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, runtimeCbPkgPrefix() + "advancement/CraftAdvancement", "getDisplay0", desc, false); ++ return; ++ } + // Paper end + + if (modern) { +diff --git a/src/test/java/io/papermc/paper/advancement/AdvancementFrameTest.java b/src/test/java/io/papermc/paper/advancement/AdvancementFrameTest.java +new file mode 100644 +index 0000000000000000000000000000000000000000..6fe9c10ffd1dd5244ead05642609794623054cce +--- /dev/null ++++ b/src/test/java/io/papermc/paper/advancement/AdvancementFrameTest.java +@@ -0,0 +1,26 @@ ++package io.papermc.paper.advancement; ++ ++import io.papermc.paper.adventure.PaperAdventure; ++import net.kyori.adventure.text.format.TextColor; ++import net.minecraft.advancements.AdvancementType; ++import net.minecraft.network.chat.contents.TranslatableContents; ++import org.bukkit.support.environment.Normal; ++import org.junit.jupiter.api.Test; ++ ++import static org.junit.jupiter.api.Assertions.assertEquals; ++ ++@Normal ++public class AdvancementFrameTest { ++ ++ @Test ++ public void test() { ++ for (final AdvancementType advancementType : AdvancementType.values()) { ++ final TextColor expectedColor = PaperAdventure.asAdventure(advancementType.getChatColor()); ++ final String expectedTranslationKey = ((TranslatableContents) advancementType.getDisplayName().getContents()).getKey(); ++ final var frame = PaperAdvancementDisplay.asPaperFrame(advancementType); ++ assertEquals(expectedTranslationKey, frame.translationKey(), "The translation keys should be the same"); ++ assertEquals(expectedColor, frame.color(), "The frame colors should be the same"); ++ assertEquals(advancementType.getSerializedName(), AdvancementDisplay.Frame.NAMES.key(frame)); ++ } ++ } ++} diff --git a/patches/server/0580-Add-ItemFactory-getSpawnEgg-API.patch b/patches/server/0580-Add-ItemFactory-getSpawnEgg-API.patch new file mode 100644 index 0000000000..b15779d35b --- /dev/null +++ b/patches/server/0580-Add-ItemFactory-getSpawnEgg-API.patch @@ -0,0 +1,58 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: William Blake Galbreath <[email protected]> +Date: Thu, 14 Oct 2021 12:09:39 -0500 +Subject: [PATCH] Add ItemFactory#getSpawnEgg API + + +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java +index 68798c90f2a116d82f6f25e920c54c929df6fca9..a04840b77e2d84e754c9cfa79bc593608bc22c90 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java +@@ -9,6 +9,7 @@ import net.minecraft.core.HolderSet; + import net.minecraft.core.RegistryAccess; + import net.minecraft.core.component.DataComponentPatch; + import net.minecraft.core.registries.Registries; ++import net.minecraft.resources.ResourceLocation; + import net.minecraft.server.MinecraftServer; + import net.minecraft.tags.EnchantmentTags; + import net.minecraft.util.RandomSource; +@@ -291,4 +292,19 @@ public final class CraftItemFactory implements ItemFactory { + new net.md_5.bungee.api.chat.TextComponent(customName)); + } + // Paper end - bungee hover events ++ ++ // Paper start - old getSpawnEgg API ++ // @Override // used to override, upstream added conflicting method, is called via Commodore now ++ @Deprecated ++ public ItemStack getSpawnEgg0(org.bukkit.entity.EntityType type) { ++ if (type == null) { ++ return null; ++ } ++ String typeId = type.getKey().toString(); ++ net.minecraft.resources.ResourceLocation typeKey = ResourceLocation.parse(typeId); ++ net.minecraft.world.entity.EntityType<?> nmsType = net.minecraft.core.registries.BuiltInRegistries.ENTITY_TYPE.getValue(typeKey); ++ net.minecraft.world.item.SpawnEggItem eggItem = net.minecraft.world.item.SpawnEggItem.byId(nmsType); ++ return eggItem == null ? null : new net.minecraft.world.item.ItemStack(eggItem).asBukkitMirror(); ++ } ++ // Paper end - old getSpawnEgg API + } +diff --git a/src/main/java/org/bukkit/craftbukkit/util/Commodore.java b/src/main/java/org/bukkit/craftbukkit/util/Commodore.java +index 52a507ff2770bd46494e805956b4569f0d4d21ee..5a7aa491e0846ca8133bc4bb6e74b93cff85fb17 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/Commodore.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/Commodore.java +@@ -465,6 +465,15 @@ public class Commodore { + } + // Paper end + ++ // Paper start - ItemFactory#getSpawnEgg (paper had original method that returned ItemStack, upstream added identical but returned Material) ++ if (owner.equals("org/bukkit/inventory/ItemFactory") && name.equals("getSpawnEgg") && desc.equals("(Lorg/bukkit/entity/EntityType;)Lorg/bukkit/inventory/ItemStack;")) { ++ super.visitInsn(Opcodes.SWAP); // has 1 param, this moves the owner instance to the top for the checkcast ++ super.visitTypeInsn(Opcodes.CHECKCAST, runtimeCbPkgPrefix() + "inventory/CraftItemFactory"); ++ super.visitInsn(Opcodes.SWAP); // moves param back to the the top of stack ++ super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, runtimeCbPkgPrefix() + "inventory/CraftItemFactory", "getSpawnEgg0", desc, false); ++ return; ++ } ++ // Paper end - ItemFactory#getSpawnEgg + if (modern) { + if (owner.equals("org/bukkit/Material") || (instantiatedMethodType != null && instantiatedMethodType.getDescriptor().startsWith("(Lorg/bukkit/Material;)"))) { + switch (name) { diff --git a/patches/server/0581-Add-critical-damage-API.patch b/patches/server/0581-Add-critical-damage-API.patch new file mode 100644 index 0000000000..a4fa089b63 --- /dev/null +++ b/patches/server/0581-Add-critical-damage-API.patch @@ -0,0 +1,101 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: dodison <[email protected]> +Date: Mon, 26 Jul 2021 17:32:36 +0200 +Subject: [PATCH] Add critical damage API + + +diff --git a/src/main/java/net/minecraft/world/damagesource/DamageSource.java b/src/main/java/net/minecraft/world/damagesource/DamageSource.java +index c1d121d83591ca1b5bf9d9406c9622b4f24eafef..aee26dd78953ff43306aaa64161f5b9edcdd4b83 100644 +--- a/src/main/java/net/minecraft/world/damagesource/DamageSource.java ++++ b/src/main/java/net/minecraft/world/damagesource/DamageSource.java +@@ -276,4 +276,18 @@ public class DamageSource { + public Holder<DamageType> typeHolder() { + return this.type; + } ++ ++ // Paper start - add critical damage API ++ private boolean critical; ++ public boolean isCritical() { ++ return this.critical; ++ } ++ public DamageSource critical() { ++ return this.critical(true); ++ } ++ public DamageSource critical(boolean critical) { ++ this.critical = critical; ++ return this; ++ } ++ // Paper end - add critical damage API + } +diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java +index 13f21822d0eb1d167b9b71addb46c4f508301c8d..4e33c6c612939e6970d5984825115768ebb6a5ea 100644 +--- a/src/main/java/net/minecraft/world/entity/player/Player.java ++++ b/src/main/java/net/minecraft/world/entity/player/Player.java +@@ -1261,6 +1261,7 @@ public abstract class Player extends LivingEntity { + + flag2 = flag2 && !this.level().paperConfig().entities.behavior.disablePlayerCrits; // Paper - Toggleable player crits + if (flag2) { ++ damagesource = damagesource.critical(true); // Paper start - critical damage API + f *= 1.5F; + } + +@@ -1320,7 +1321,7 @@ public abstract class Player extends LivingEntity { + float f7 = this.getEnchantedDamage(entityliving2, f6, damagesource) * f2; + + // CraftBukkit start - Only apply knockback if the damage hits +- if (!entityliving2.hurtServer((ServerLevel) this.level(), this.damageSources().playerAttack(this).sweep(), f7)) { ++ if (!entityliving2.hurtServer((ServerLevel) this.level(), this.damageSources().playerAttack(this).sweep().critical(flag2), f7)) { // Paper - add critical damage API + continue; + } + // CraftBukkit end +diff --git a/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java b/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java +index 52765e1b9f5a026e7108ff5a7d97681cdb2870e9..aab7d546317d93876ccd0e02c0631ccc7c8f9bf5 100644 +--- a/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java ++++ b/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java +@@ -465,6 +465,7 @@ public abstract class AbstractArrow extends Projectile { + entityliving.setLastHurtMob(entity); + } + ++ if (this.isCritArrow()) damagesource = damagesource.critical(); // Paper - add critical damage API + boolean flag = entity.getType() == EntityType.ENDERMAN; + int k = entity.getRemainingFireTicks(); + +diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +index 24f86724012bb8bcd6d24683a1c78ce66b42ca30..a5285a8952b2d99bfbb928b1bb31d3ddcf8ed57b 100644 +--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java ++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +@@ -1078,7 +1078,7 @@ public class CraftEventFactory { + return CraftEventFactory.callEntityDamageEvent(source.getDirectBlock(), source.getDirectBlockState(), entity, DamageCause.BLOCK_EXPLOSION, bukkitDamageSource, modifiers, modifierFunctions, cancelled); + } + DamageCause damageCause = (damager.getBukkitEntity() instanceof org.bukkit.entity.TNTPrimed) ? DamageCause.BLOCK_EXPLOSION : DamageCause.ENTITY_EXPLOSION; +- return CraftEventFactory.callEntityDamageEvent(damager, entity, damageCause, bukkitDamageSource, modifiers, modifierFunctions, cancelled); ++ return CraftEventFactory.callEntityDamageEvent(damager, entity, damageCause, bukkitDamageSource, modifiers, modifierFunctions, cancelled, source.isCritical()); // Paper - add critical damage API + } else if (damager != null || source.getDirectEntity() != null) { + DamageCause cause = (source.isSweep()) ? DamageCause.ENTITY_SWEEP_ATTACK : DamageCause.ENTITY_ATTACK; + +@@ -1104,7 +1104,7 @@ public class CraftEventFactory { + cause = DamageCause.MAGIC; + } + +- return CraftEventFactory.callEntityDamageEvent(damager, entity, cause, bukkitDamageSource, modifiers, modifierFunctions, cancelled); ++ return CraftEventFactory.callEntityDamageEvent(damager, entity, cause, bukkitDamageSource, modifiers, modifierFunctions, cancelled, source.isCritical()); // Paper - add critical damage API + } else if (source.is(DamageTypes.FELL_OUT_OF_WORLD)) { + return CraftEventFactory.callEntityDamageEvent(source.getDirectBlock(), source.getDirectBlockState(), entity, DamageCause.VOID, bukkitDamageSource, modifiers, modifierFunctions, cancelled); + } else if (source.is(DamageTypes.LAVA)) { +@@ -1164,13 +1164,13 @@ public class CraftEventFactory { + cause = DamageCause.CUSTOM; + } + +- return CraftEventFactory.callEntityDamageEvent((Entity) null, entity, cause, bukkitDamageSource, modifiers, modifierFunctions, cancelled); ++ return CraftEventFactory.callEntityDamageEvent((Entity) null, entity, cause, bukkitDamageSource, modifiers, modifierFunctions, cancelled, source.isCritical()); // Paper - add critical damage API + } + +- private static EntityDamageEvent callEntityDamageEvent(Entity damager, Entity damagee, DamageCause cause, org.bukkit.damage.DamageSource bukkitDamageSource, Map<DamageModifier, Double> modifiers, Map<DamageModifier, Function<? super Double, Double>> modifierFunctions, boolean cancelled) { ++ private static EntityDamageEvent callEntityDamageEvent(Entity damager, Entity damagee, DamageCause cause, org.bukkit.damage.DamageSource bukkitDamageSource, Map<DamageModifier, Double> modifiers, Map<DamageModifier, Function<? super Double, Double>> modifierFunctions, boolean cancelled, boolean critical) { // Paper - add critical damage API + EntityDamageEvent event; + if (damager != null) { +- event = new EntityDamageByEntityEvent(damager.getBukkitEntity(), damagee.getBukkitEntity(), cause, bukkitDamageSource, modifiers, modifierFunctions); ++ event = new EntityDamageByEntityEvent(damager.getBukkitEntity(), damagee.getBukkitEntity(), cause, bukkitDamageSource, modifiers, modifierFunctions, critical); + } else { + event = new EntityDamageEvent(damagee.getBukkitEntity(), cause, bukkitDamageSource, modifiers, modifierFunctions); + } diff --git a/patches/server/0582-Fix-issues-with-mob-conversion.patch b/patches/server/0582-Fix-issues-with-mob-conversion.patch new file mode 100644 index 0000000000..c286b73b30 --- /dev/null +++ b/patches/server/0582-Fix-issues-with-mob-conversion.patch @@ -0,0 +1,74 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sun, 24 Oct 2021 20:29:45 -0700 +Subject: [PATCH] Fix issues with mob conversion + + +diff --git a/src/main/java/net/minecraft/world/entity/monster/Skeleton.java b/src/main/java/net/minecraft/world/entity/monster/Skeleton.java +index 6f6454bcec7e0d1cefbf818fc2fc6eb90adeec83..3972e2ed0554e2550519e994888e068df0a151e5 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/Skeleton.java ++++ b/src/main/java/net/minecraft/world/entity/monster/Skeleton.java +@@ -94,12 +94,19 @@ public class Skeleton extends AbstractSkeleton { + } + + protected void doFreezeConversion() { +- this.convertTo(EntityType.STRAY, ConversionParams.single(this, true, true), (entityskeletonstray) -> { ++ final Stray stray = this.convertTo(EntityType.STRAY, ConversionParams.single(this, true, true), (entityskeletonstray) -> { // Paper - Fix issues with mob conversion; reset conversion time to prevent event spam + if (!this.isSilent()) { + this.level().levelEvent((Player) null, 1048, this.blockPosition(), 0); + } + +- }, org.bukkit.event.entity.EntityTransformEvent.TransformReason.FROZEN, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.FROZEN); // CraftBukkit - add spawn and transform reasons ++ }, org.bukkit.event.entity.EntityTransformEvent.TransformReason.FROZEN, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.FROZEN);// CraftBukkit - add spawn and transform reasons ++ ++ // Paper start - Fix issues with mob conversion; reset conversion time to prevent event spam ++ if (stray == null) { ++ this.conversionTime = 300; ++ } ++ // Paper end - Fix issues with mob conversion ++ + } + + @Override +diff --git a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java +index 34fff71edbdcefe87985f31cbf1ef282435ace06..92270912ef26924f611a1df7cb3d5b485b0a262d 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java ++++ b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java +@@ -241,9 +241,15 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { + } + + private void finishConversion() { +- this.convertTo(EntityType.ZOGLIN, ConversionParams.single(this, true, false), (entityzoglin) -> { ++ net.minecraft.world.entity.Entity converted = this.convertTo(EntityType.ZOGLIN, ConversionParams.single(this, true, false), (entityzoglin) -> { + entityzoglin.addEffect(new MobEffectInstance(MobEffects.CONFUSION, 200, 0)); + }, org.bukkit.event.entity.EntityTransformEvent.TransformReason.PIGLIN_ZOMBIFIED, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.PIGLIN_ZOMBIFIED); // CraftBukkit - add spawn and transform reasons ++ ++ // Paper start - Fix issues with mob conversion; reset to prevent event spam ++ if (converted == null) { ++ this.timeInOverworld = 0; ++ } ++ // Paper end - Fix issues with mob conversion + } + + @Override +diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/AbstractPiglin.java b/src/main/java/net/minecraft/world/entity/monster/piglin/AbstractPiglin.java +index e2075fb22596b6dc4dbbb9c20c91c63f0ddb7ba7..e3a8aa0ae0d1fa6f2b697e20464224f8b8c6b8ea 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/piglin/AbstractPiglin.java ++++ b/src/main/java/net/minecraft/world/entity/monster/piglin/AbstractPiglin.java +@@ -100,9 +100,15 @@ public abstract class AbstractPiglin extends Monster { + } + + protected void finishConversion(ServerLevel world) { +- this.convertTo(EntityType.ZOMBIFIED_PIGLIN, ConversionParams.single(this, true, true), (entitypigzombie) -> { ++ net.minecraft.world.entity.Entity converted = this.convertTo(EntityType.ZOMBIFIED_PIGLIN, ConversionParams.single(this, true, true), (entitypigzombie) -> { // Paper - Fix issues with mob conversion; reset to prevent event spam + entitypigzombie.addEffect(new MobEffectInstance(MobEffects.CONFUSION, 200, 0)); + }, org.bukkit.event.entity.EntityTransformEvent.TransformReason.PIGLIN_ZOMBIFIED, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.PIGLIN_ZOMBIFIED); // CraftBukkit - add spawn and transform reasons ++ ++ // Paper start - Fix issues with mob conversion; reset to prevent event spam ++ if (converted == null) { ++ this.timeInOverworld = 0; ++ } ++ // Paper end - Fix issues with mob conversion + } + + public boolean isAdult() { diff --git a/patches/server/0583-Add-hasCollision-methods-to-various-places.patch b/patches/server/0583-Add-hasCollision-methods-to-various-places.patch new file mode 100644 index 0000000000..421b0ba92e --- /dev/null +++ b/patches/server/0583-Add-hasCollision-methods-to-various-places.patch @@ -0,0 +1,56 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Thu, 4 Nov 2021 11:50:40 -0700 +Subject: [PATCH] Add hasCollision methods to various places + +== AT == +public net.minecraft.world.level.block.state.BlockBehaviour hasCollision + +diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java +index 9c8aac69f01db647e20d49d272ccc107a7edceaf..d5b495b5a3ca7f4411d1a700f7149042a16509f1 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java +@@ -457,6 +457,11 @@ public class CraftBlock implements Block { + public boolean isSolid() { + return this.getNMS().blocksMotion(); + } ++ ++ @Override ++ public boolean isCollidable() { ++ return getNMS().getBlock().hasCollision; ++ } + // Paper end + + @Override +diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java +index 1002123cd0c6f57cecc4e80f5f21cc6ff5886d37..e96023b71845526383288917e8d7c5759a4c0e9b 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java +@@ -341,4 +341,11 @@ public class CraftBlockState implements BlockState { + public BlockState copy(Location location) { + return new CraftBlockState(this, location); + } ++ ++ // Paper start ++ @Override ++ public boolean isCollidable() { ++ return this.data.getBlock().hasCollision; ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockType.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockType.java +index 2d8a509446c0ed0d7358f10f67ef29c4df683696..785d3fe4929e008d4150f3ecab258fd5b6d7cdaf 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockType.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockType.java +@@ -241,4 +241,11 @@ public class CraftBlockType<B extends BlockData> implements BlockType.Typed<B>, + return this.block.getDescriptionId(); + } + // Paper end - add Translatable ++ ++ // Paper start - hasCollision API ++ @Override ++ public boolean hasCollision() { ++ return this.block.hasCollision; ++ } ++ // Paper end - hasCollision API + } diff --git a/patches/server/0584-Goat-ram-API.patch b/patches/server/0584-Goat-ram-API.patch new file mode 100644 index 0000000000..21d6196f4c --- /dev/null +++ b/patches/server/0584-Goat-ram-API.patch @@ -0,0 +1,42 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Seggan <[email protected]> +Date: Thu, 5 Aug 2021 13:10:27 -0400 +Subject: [PATCH] Goat ram API + + +diff --git a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java +index cccf0084d273eaded91abe249d39a843f11d351b..14e02f9b0169db8388c515a68315ad5cc3f13d22 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java ++++ b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java +@@ -395,4 +395,15 @@ public class Goat extends Animal { + public static boolean checkGoatSpawnRules(EntityType<? extends Animal> entityType, LevelAccessor world, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random) { + return world.getBlockState(pos.below()).is(BlockTags.GOATS_SPAWNABLE_ON) && isBrightEnoughToSpawn(world, pos); + } ++ ++ // Paper start - Goat ram API ++ public void ram(net.minecraft.world.entity.LivingEntity entity) { ++ Brain<Goat> brain = this.getBrain(); ++ brain.setMemory(MemoryModuleType.RAM_TARGET, entity.position()); ++ brain.eraseMemory(MemoryModuleType.RAM_COOLDOWN_TICKS); ++ brain.eraseMemory(MemoryModuleType.BREED_TARGET); ++ brain.eraseMemory(MemoryModuleType.TEMPTING_PLAYER); ++ brain.setActiveActivityIfPossible(net.minecraft.world.entity.schedule.Activity.RAM); ++ } ++ // Paper end - Goat ram API + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftGoat.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftGoat.java +index 65fcb36e849da6949c123a6f3672b485036f368e..2c21de478bff9cdf13ba46cd041831d54c11e924 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftGoat.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftGoat.java +@@ -48,4 +48,11 @@ public class CraftGoat extends CraftAnimals implements Goat { + public void setScreaming(boolean screaming) { + this.getHandle().setScreamingGoat(screaming); + } ++ ++ // Paper start - Goat ram API ++ @Override ++ public void ram(@org.jetbrains.annotations.NotNull org.bukkit.entity.LivingEntity entity) { ++ this.getHandle().ram(((CraftLivingEntity) entity).getHandle()); ++ } ++ // Paper end + } diff --git a/patches/server/0585-Add-API-for-resetting-a-single-score.patch b/patches/server/0585-Add-API-for-resetting-a-single-score.patch new file mode 100644 index 0000000000..b9dd1c2be3 --- /dev/null +++ b/patches/server/0585-Add-API-for-resetting-a-single-score.patch @@ -0,0 +1,24 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: booky10 <[email protected]> +Date: Fri, 5 Nov 2021 21:01:36 +0100 +Subject: [PATCH] Add API for resetting a single score + +It was only possible to reset all scores for a specific entry, instead of resetting only specific scores. + +diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java +index 29e24461e29e4cf3d31497198debcde18761ad73..ceb1a39c02c3cfa7632a0fdca414c7046888fcb1 100644 +--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java ++++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java +@@ -66,4 +66,12 @@ final class CraftScore implements Score { + public CraftScoreboard getScoreboard() { + return this.objective.getScoreboard(); + } ++ ++ // Paper start ++ @Override ++ public void resetScore() { ++ Scoreboard board = this.objective.checkState().board; ++ board.resetSinglePlayerScore(entry, this.objective.getHandle()); ++ } ++ // Paper end + } diff --git a/patches/server/0586-Add-Raw-Byte-Entity-Serialization.patch b/patches/server/0586-Add-Raw-Byte-Entity-Serialization.patch new file mode 100644 index 0000000000..e2701e2fc7 --- /dev/null +++ b/patches/server/0586-Add-Raw-Byte-Entity-Serialization.patch @@ -0,0 +1,90 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Mariell Hoversholm <[email protected]> +Date: Sun, 24 Oct 2021 16:20:31 -0400 +Subject: [PATCH] Add Raw Byte Entity Serialization + +== AT == +public net.minecraft.world.entity.Entity setLevel(Lnet/minecraft/world/level/Level;)V + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index f9e52b8794d62cc79c420008a1a95707efa14321..ee2625301ebb1dd601618ae43915891f3b57b7c3 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -2284,6 +2284,15 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + } + ++ // Paper start - Entity serialization api ++ public boolean serializeEntity(CompoundTag compound) { ++ List<Entity> pass = new java.util.ArrayList<>(this.getPassengers()); ++ this.passengers = ImmutableList.of(); ++ boolean result = save(compound); ++ this.passengers = ImmutableList.copyOf(pass); ++ return result; ++ } ++ // Paper end - Entity serialization api + public boolean save(CompoundTag nbt) { + return this.isPassenger() ? false : this.saveAsPassenger(nbt); + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +index 888a75423ac90ca85308eeb6d67bac5348bf31e0..b13c947d2cfe085017b30cb0f8340dd64f338914 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +@@ -1083,6 +1083,18 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { + } + // Paper end - tracked players API + ++ // Paper start - raw entity serialization API ++ @Override ++ public boolean spawnAt(Location location, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) { ++ Preconditions.checkNotNull(location, "location cannot be null"); ++ Preconditions.checkNotNull(reason, "reason cannot be null"); ++ this.entity.setLevel(((CraftWorld) location.getWorld()).getHandle()); ++ this.entity.setPos(location.getX(), location.getY(), location.getZ()); ++ this.entity.setRot(location.getYaw(), location.getPitch()); ++ return !this.entity.valid && this.entity.level().addFreshEntity(this.entity, reason); ++ } ++ // Paper end - raw entity serialization API ++ + // Paper start - missing entity api + @Override + public boolean isInvisible() { // Paper - moved up from LivingEntity +diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +index 6f780b76ebadb6155195b93f3e6d382141eb0bcc..d7698f8ae59195458148397406a104224676b76e 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +@@ -510,7 +510,33 @@ public final class CraftMagicNumbers implements UnsafeValues { + return CraftItemStack.asCraftMirror(net.minecraft.world.item.ItemStack.parse(MinecraftServer.getServer().registryAccess(), compound).orElseThrow()); + } + +- private byte[] serializeNbtToBytes(CompoundTag compound) { ++ @Override ++ public byte[] serializeEntity(org.bukkit.entity.Entity entity) { ++ Preconditions.checkNotNull(entity, "null cannot be serialized"); ++ Preconditions.checkArgument(entity instanceof org.bukkit.craftbukkit.entity.CraftEntity, "only CraftEntities can be serialized"); ++ ++ net.minecraft.nbt.CompoundTag compound = new net.minecraft.nbt.CompoundTag(); ++ ((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().serializeEntity(compound); ++ return serializeNbtToBytes(compound); ++ } ++ ++ @Override ++ public org.bukkit.entity.Entity deserializeEntity(byte[] data, org.bukkit.World world, boolean preserveUUID) { ++ Preconditions.checkNotNull(data, "null cannot be deserialized"); ++ Preconditions.checkArgument(data.length > 0, "cannot deserialize nothing"); ++ ++ net.minecraft.nbt.CompoundTag compound = deserializeNbtFromBytes(data); ++ int dataVersion = compound.getInt("DataVersion"); ++ compound = (net.minecraft.nbt.CompoundTag) MinecraftServer.getServer().fixerUpper.update(References.ENTITY, new Dynamic<>(NbtOps.INSTANCE, compound), dataVersion, this.getDataVersion()).getValue(); ++ if (!preserveUUID) { ++ // Generate a new UUID so we don't have to worry about deserializing the same entity twice ++ compound.remove("UUID"); ++ } ++ return net.minecraft.world.entity.EntityType.create(compound, ((org.bukkit.craftbukkit.CraftWorld) world).getHandle(), net.minecraft.world.entity.EntitySpawnReason.LOAD) ++ .orElseThrow(() -> new IllegalArgumentException("An ID was not found for the data. Did you downgrade?")).getBukkitEntity(); ++ } ++ ++ private byte[] serializeNbtToBytes(net.minecraft.nbt.CompoundTag compound) { + compound.putInt("DataVersion", getDataVersion()); + java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream(); + try { diff --git a/patches/server/0587-Vanilla-command-permission-fixes.patch b/patches/server/0587-Vanilla-command-permission-fixes.patch new file mode 100644 index 0000000000..90d31ae40c --- /dev/null +++ b/patches/server/0587-Vanilla-command-permission-fixes.patch @@ -0,0 +1,79 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jason Penilla <[email protected]> +Date: Wed, 25 Aug 2021 13:19:53 -0700 +Subject: [PATCH] Vanilla command permission fixes + +Fixes permission checks for vanilla commands which don't have a +requirement, as well as for namespaced vanilla commands. + +== AT == +public-f com.mojang.brigadier.tree.CommandNode requirement + +diff --git a/src/main/java/com/mojang/brigadier/builder/ArgumentBuilder.java b/src/main/java/com/mojang/brigadier/builder/ArgumentBuilder.java +index 899008b2980d13f1be6280cd8cb959c53a29bebf..d5f7da3502575f6847f3c22ab0e942848a7c6031 100644 +--- a/src/main/java/com/mojang/brigadier/builder/ArgumentBuilder.java ++++ b/src/main/java/com/mojang/brigadier/builder/ArgumentBuilder.java +@@ -14,9 +14,17 @@ import java.util.Collections; + import java.util.function.Predicate; + + public abstract class ArgumentBuilder<S, T extends ArgumentBuilder<S, T>> { ++ // Paper start - Vanilla command permission fixes ++ private static final Predicate<Object> DEFAULT_REQUIREMENT = s -> true; ++ ++ @SuppressWarnings("unchecked") ++ public static <S> Predicate<S> defaultRequirement() { ++ return (Predicate<S>) DEFAULT_REQUIREMENT; ++ } ++ // Paper end - Vanilla command permission fixes + private final RootCommandNode<S> arguments = new RootCommandNode<>(); + private Command<S> command; +- private Predicate<S> requirement = s -> true; ++ private Predicate<S> requirement = defaultRequirement(); // Paper - Vanilla command permission fixes + private CommandNode<S> target; + private RedirectModifier<S> modifier = null; + private boolean forks; +diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java +index d602c713696e23ba6a2d542b2e9e2cce46d79a66..800d1756db8c27b7d129a90addc125c4fc81e134 100644 +--- a/src/main/java/net/minecraft/commands/Commands.java ++++ b/src/main/java/net/minecraft/commands/Commands.java +@@ -261,6 +261,13 @@ public class Commands { + PublishCommand.register(this.dispatcher); + } + ++ // Paper start - Vanilla command permission fixes ++ for (final CommandNode<CommandSourceStack> node : this.dispatcher.getRoot().getChildren()) { ++ if (node.getRequirement() == com.mojang.brigadier.builder.ArgumentBuilder.<CommandSourceStack>defaultRequirement()) { ++ node.requirement = stack -> stack.source == CommandSource.NULL || stack.getBukkitSender().hasPermission(org.bukkit.craftbukkit.command.VanillaCommandWrapper.getPermission(node)); ++ } ++ } ++ // Paper end - Vanilla command permission fixes + // CraftBukkit start + } + +diff --git a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java +index 9b453830e4b949d67c2a01adc309ddc6ad019be1..35b05f9321ddbbbdf62f4bf726b58cf1205f0cfb 100644 +--- a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java ++++ b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java +@@ -91,7 +91,21 @@ public final class VanillaCommandWrapper extends BukkitCommand { + } + + public static String getPermission(CommandNode<CommandSourceStack> vanillaCommand) { +- return "minecraft.command." + ((vanillaCommand.getRedirect() == null) ? vanillaCommand.getName() : vanillaCommand.getRedirect().getName()); ++ // Paper start - Vanilla command permission fixes ++ while (vanillaCommand.getRedirect() != null) { ++ vanillaCommand = vanillaCommand.getRedirect(); ++ } ++ final String commandName = vanillaCommand.getName(); ++ return "minecraft.command." + stripDefaultNamespace(commandName); ++ } ++ ++ private static String stripDefaultNamespace(final String maybeNamespaced) { ++ final String prefix = "minecraft:"; ++ if (maybeNamespaced.startsWith(prefix)) { ++ return maybeNamespaced.substring(prefix.length()); ++ } ++ return maybeNamespaced; ++ // Paper end - Vanilla command permission fixes + } + + private String toDispatcher(String[] args, String name) { diff --git a/patches/server/0588-Do-not-run-close-logic-for-inventories-on-chunk-unlo.patch b/patches/server/0588-Do-not-run-close-logic-for-inventories-on-chunk-unlo.patch new file mode 100644 index 0000000000..f403613911 --- /dev/null +++ b/patches/server/0588-Do-not-run-close-logic-for-inventories-on-chunk-unlo.patch @@ -0,0 +1,68 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf <[email protected]> +Date: Thu, 11 Mar 2021 03:03:32 -0800 +Subject: [PATCH] Do not run close logic for inventories on chunk unload + +Still call the event and change the active container though. We +want to avoid close logic because it's possible to load the +chunk through it. This should also be OK from a leak prevention/ +state desync POV because the TE is getting unloaded anyways. + +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index e6ff03143b3639f375323393814b1256b98687ad..2ac09e8eece6ff772c94bb1efd01cc1c45194435 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -1245,9 +1245,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + // Spigot Start + for (net.minecraft.world.level.block.entity.BlockEntity tileentity : chunk.getBlockEntities().values()) { + if (tileentity instanceof net.minecraft.world.Container) { ++ // Paper start - this area looks like it can load chunks, change the behavior ++ // chests for example can apply physics to the world ++ // so instead we just change the active container and call the event + for (org.bukkit.entity.HumanEntity h : Lists.newArrayList(((net.minecraft.world.Container) tileentity).getViewers())) { +- h.closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNLOADED); // Paper - Inventory close reason ++ ((org.bukkit.craftbukkit.entity.CraftHumanEntity) h).getHandle().closeUnloadedInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNLOADED); // Paper - Inventory close reason + } ++ // Paper end - this area looks like it can load chunks, change the behavior + } + } + // Spigot End +diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java +index f2cc608d2cf040be2912b604f0d6cab21e33ded0..215be207e29254312bb85f259a684a7de6876aa9 100644 +--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java ++++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java +@@ -1966,6 +1966,18 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player { + this.connection.send(new ClientboundContainerClosePacket(this.containerMenu.containerId)); + this.doCloseContainer(); + } ++ // Paper start - special close for unloaded inventory ++ @Override ++ public void closeUnloadedInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason reason) { ++ // copied from above ++ CraftEventFactory.handleInventoryCloseEvent(this, reason); // CraftBukkit ++ // Paper end ++ // copied from below ++ this.connection.send(new ClientboundContainerClosePacket(this.containerMenu.containerId)); ++ this.containerMenu = this.inventoryMenu; ++ // do not run close logic ++ } ++ // Paper end - special close for unloaded inventory + + @Override + public void doCloseContainer() { +diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java +index 4e33c6c612939e6970d5984825115768ebb6a5ea..81e3072ac80623db47ddc0b9c52e0e56dab9e10e 100644 +--- a/src/main/java/net/minecraft/world/entity/player/Player.java ++++ b/src/main/java/net/minecraft/world/entity/player/Player.java +@@ -547,6 +547,11 @@ public abstract class Player extends LivingEntity { + this.containerMenu = this.inventoryMenu; + } + // Paper end - Inventory close reason ++ // Paper start - special close for unloaded inventory ++ public void closeUnloadedInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason reason) { ++ this.containerMenu = this.inventoryMenu; ++ } ++ // Paper end - special close for unloaded inventory + + public void closeContainer() { + this.containerMenu = this.inventoryMenu; diff --git a/patches/server/0589-Fix-GameProfileCache-concurrency.patch b/patches/server/0589-Fix-GameProfileCache-concurrency.patch new file mode 100644 index 0000000000..6cb7e5235f --- /dev/null +++ b/patches/server/0589-Fix-GameProfileCache-concurrency.patch @@ -0,0 +1,126 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf <[email protected]> +Date: Sat, 11 Jul 2020 05:09:28 -0700 +Subject: [PATCH] Fix GameProfileCache concurrency + +Separate lookup and state access locks prevent lookups +from stalling simple state access/write calls + +diff --git a/src/main/java/net/minecraft/server/players/GameProfileCache.java b/src/main/java/net/minecraft/server/players/GameProfileCache.java +index 774d81c702edb76a2f6184d4dc53687de6734a79..34b4166adfae8ff7d1eb73d56a72931b005330a7 100644 +--- a/src/main/java/net/minecraft/server/players/GameProfileCache.java ++++ b/src/main/java/net/minecraft/server/players/GameProfileCache.java +@@ -60,6 +60,11 @@ public class GameProfileCache { + @Nullable + private Executor executor; + ++ // Paper start - Fix GameProfileCache concurrency ++ protected final java.util.concurrent.locks.ReentrantLock stateLock = new java.util.concurrent.locks.ReentrantLock(); ++ protected final java.util.concurrent.locks.ReentrantLock lookupLock = new java.util.concurrent.locks.ReentrantLock(); ++ // Paper end - Fix GameProfileCache concurrency ++ + public GameProfileCache(GameProfileRepository profileRepository, File cacheFile) { + this.profileRepository = profileRepository; + this.file = cacheFile; +@@ -67,11 +72,13 @@ public class GameProfileCache { + } + + private void safeAdd(GameProfileCache.GameProfileInfo entry) { ++ try { this.stateLock.lock(); // Paper - Fix GameProfileCache concurrency + GameProfile gameprofile = entry.getProfile(); + + entry.setLastAccess(this.getNextOperation()); + this.profilesByName.put(gameprofile.getName().toLowerCase(Locale.ROOT), entry); + this.profilesByUUID.put(gameprofile.getId(), entry); ++ } finally { this.stateLock.unlock(); } // Paper - Fix GameProfileCache concurrency + } + + private static Optional<GameProfile> lookupGameProfile(GameProfileRepository repository, String name) { +@@ -128,17 +135,20 @@ public class GameProfileCache { + + // Paper start + public @Nullable GameProfile getProfileIfCached(String name) { ++ try { this.stateLock.lock(); // Paper - Fix GameProfileCache concurrency + GameProfileCache.GameProfileInfo entry = this.profilesByName.get(name.toLowerCase(Locale.ROOT)); + if (entry == null) { + return null; + } + entry.setLastAccess(this.getNextOperation()); + return entry.getProfile(); ++ } finally { this.stateLock.unlock(); } // Paper - Fix GameProfileCache concurrency + } + // Paper end + + public Optional<GameProfile> get(String name) { + String s1 = name.toLowerCase(Locale.ROOT); ++ boolean stateLocked = true; try { this.stateLock.lock(); // Paper - Fix GameProfileCache concurrency + GameProfileCache.GameProfileInfo usercache_usercacheentry = (GameProfileCache.GameProfileInfo) this.profilesByName.get(s1); + boolean flag = false; + +@@ -154,8 +164,12 @@ public class GameProfileCache { + if (usercache_usercacheentry != null) { + usercache_usercacheentry.setLastAccess(this.getNextOperation()); + optional = Optional.of(usercache_usercacheentry.getProfile()); ++ stateLocked = false; this.stateLock.unlock(); // Paper - Fix GameProfileCache concurrency + } else { ++ stateLocked = false; this.stateLock.unlock(); // Paper - Fix GameProfileCache concurrency ++ try { this.lookupLock.lock(); // Paper - Fix GameProfileCache concurrency + optional = GameProfileCache.lookupGameProfile(this.profileRepository, name); // CraftBukkit - use correct case for offline players ++ } finally { this.lookupLock.unlock(); } // Paper - Fix GameProfileCache concurrency + if (optional.isPresent()) { + this.add((GameProfile) optional.get()); + flag = false; +@@ -167,6 +181,7 @@ public class GameProfileCache { + } + + return optional; ++ } finally { if (stateLocked) { this.stateLock.unlock(); } } // Paper - Fix GameProfileCache concurrency + } + + public CompletableFuture<Optional<GameProfile>> getAsync(String username) { +@@ -191,6 +206,7 @@ public class GameProfileCache { + } + + public Optional<GameProfile> get(UUID uuid) { ++ try { this.stateLock.lock(); // Paper - Fix GameProfileCache concurrency + GameProfileCache.GameProfileInfo usercache_usercacheentry = (GameProfileCache.GameProfileInfo) this.profilesByUUID.get(uuid); + + if (usercache_usercacheentry == null) { +@@ -199,6 +215,7 @@ public class GameProfileCache { + usercache_usercacheentry.setLastAccess(this.getNextOperation()); + return Optional.of(usercache_usercacheentry.getProfile()); + } ++ } finally { this.stateLock.unlock(); } // Paper - Fix GameProfileCache concurrency + } + + public void setExecutor(Executor executor) { +@@ -279,7 +296,7 @@ public class GameProfileCache { + JsonArray jsonarray = new JsonArray(); + DateFormat dateformat = GameProfileCache.createDateFormat(); + +- this.getTopMRUProfiles(org.spigotmc.SpigotConfig.userCacheCap).forEach((usercache_usercacheentry) -> { // Spigot ++ this.listTopMRUProfiles(org.spigotmc.SpigotConfig.userCacheCap).forEach((usercache_usercacheentry) -> { // Spigot // Paper - Fix GameProfileCache concurrency + jsonarray.add(GameProfileCache.writeGameProfile(usercache_usercacheentry, dateformat)); + }); + String s = this.gson.toJson(jsonarray); +@@ -320,8 +337,19 @@ public class GameProfileCache { + } + + private Stream<GameProfileCache.GameProfileInfo> getTopMRUProfiles(int limit) { +- return ImmutableList.copyOf(this.profilesByUUID.values()).stream().sorted(Comparator.comparing(GameProfileCache.GameProfileInfo::getLastAccess).reversed()).limit((long) limit); ++ // Paper start - Fix GameProfileCache concurrency ++ return this.listTopMRUProfiles(limit).stream(); ++ } ++ ++ private List<GameProfileCache.GameProfileInfo> listTopMRUProfiles(int limit) { ++ try { ++ this.stateLock.lock(); ++ return this.profilesByUUID.values().stream().sorted(Comparator.comparing(GameProfileCache.GameProfileInfo::getLastAccess).reversed()).limit(limit).toList(); ++ } finally { ++ this.stateLock.unlock(); ++ } + } ++ // Paper end - Fix GameProfileCache concurrency + + private static JsonElement writeGameProfile(GameProfileCache.GameProfileInfo entry, DateFormat dateFormat) { + JsonObject jsonobject = new JsonObject(); diff --git a/patches/server/0590-Improve-and-expand-AsyncCatcher.patch b/patches/server/0590-Improve-and-expand-AsyncCatcher.patch new file mode 100644 index 0000000000..ae6607cb0d --- /dev/null +++ b/patches/server/0590-Improve-and-expand-AsyncCatcher.patch @@ -0,0 +1,227 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf <[email protected]> +Date: Wed, 25 Aug 2021 20:17:12 -0700 +Subject: [PATCH] Improve and expand AsyncCatcher + +Log when the async catcher is tripped + The chunk system can swallow the exception given it's all + built with completablefuture, so ensure it is at least printed. + +Add/move several async catchers + +Async catch modifications to critical entity state + These used to be here from Spigot, but were dropped with 1.17. + Now in 1.17, this state is _even more_ critical than it was before, + so these must exist to catch stupid plugins. + +Co-authored-by: Jake Potrebic <[email protected]> + +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index 2df548b68e56a309af4b89f8f1174dde3b046e7d..dcc87047769094241a0f0b682dc31cc4767ab5e9 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -1651,6 +1651,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + } + + public void internalTeleport(PositionMoveRotation positionmoverotation, Set<Relative> set) { ++ org.spigotmc.AsyncCatcher.catchOp("teleport"); // Paper + // Paper start - Prevent teleporting dead entities + if (player.isRemoved()) { + LOGGER.info("Attempt to teleport removed player {} restricted", player.getScoreboardName()); +diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java +index b7c854614643cc6fcb78ce7ac378f5dbbb8eb305..fb3cec13dd9e426904ce28a8755e6cc57296a42e 100644 +--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java ++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java +@@ -1143,7 +1143,7 @@ public abstract class LivingEntity extends Entity implements Attackable { + } + + public boolean addEffect(MobEffectInstance mobeffect, @Nullable Entity entity, EntityPotionEffectEvent.Cause cause) { +- org.spigotmc.AsyncCatcher.catchOp("effect add"); // Spigot ++ // org.spigotmc.AsyncCatcher.catchOp("effect add"); // Spigot // Paper - move to API + if (this.isTickingEffects) { + this.effectsToProcess.add(new ProcessableEffect(mobeffect, cause)); + return true; +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 4eb0b0969325f39a7ae65492cccd482515a50142..5aa74c00a61282830d82359eae2b114e2a48b6d9 100644 +--- a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java ++++ b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java +@@ -78,6 +78,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A + } + + private boolean addEntityUuid(T entity) { ++ org.spigotmc.AsyncCatcher.catchOp("Entity add by UUID"); // Paper + if (!this.knownUuids.add(entity.getUUID())) { + PersistentEntitySectionManager.LOGGER.warn("UUID of added entity already exists: {}", entity); + return false; +@@ -91,6 +92,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A + } + + private boolean addEntity(T entity, boolean existing) { ++ org.spigotmc.AsyncCatcher.catchOp("Entity add"); // Paper + // Paper start - chunk system hooks + // I don't want to know why this is a generic type. + Entity entityCasted = (Entity)entity; +@@ -144,19 +146,23 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A + } + + void startTicking(T entity) { ++ org.spigotmc.AsyncCatcher.catchOp("Entity start ticking"); // Paper + this.callbacks.onTickingStart(entity); + } + + void stopTicking(T entity) { ++ org.spigotmc.AsyncCatcher.catchOp("Entity stop ticking"); // Paper + this.callbacks.onTickingEnd(entity); + } + + void startTracking(T entity) { ++ org.spigotmc.AsyncCatcher.catchOp("Entity start tracking"); // Paper + this.visibleEntityStorage.add(entity); + this.callbacks.onTrackingStart(entity); + } + + void stopTracking(T entity) { ++ org.spigotmc.AsyncCatcher.catchOp("Entity stop tracking"); // Paper + this.callbacks.onTrackingEnd(entity); + this.visibleEntityStorage.remove(entity); + } +@@ -168,6 +174,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A + } + + public void updateChunkStatus(ChunkPos chunkPos, Visibility trackingStatus) { ++ org.spigotmc.AsyncCatcher.catchOp("Update chunk status"); // Paper + long i = chunkPos.toLong(); + + if (trackingStatus == Visibility.HIDDEN) { +@@ -212,6 +219,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A + } + + public void ensureChunkQueuedForLoad(long chunkPos) { ++ org.spigotmc.AsyncCatcher.catchOp("Entity chunk save"); // Paper + PersistentEntitySectionManager.ChunkLoadStatus persistententitysectionmanager_b = (PersistentEntitySectionManager.ChunkLoadStatus) this.chunkLoadStatuses.get(chunkPos); + + if (persistententitysectionmanager_b == PersistentEntitySectionManager.ChunkLoadStatus.FRESH) { +@@ -256,6 +264,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A + } + + private void requestChunkLoad(long chunkPos) { ++ org.spigotmc.AsyncCatcher.catchOp("Entity chunk load request"); // Paper + this.chunkLoadStatuses.put(chunkPos, PersistentEntitySectionManager.ChunkLoadStatus.PENDING); + ChunkPos chunkcoordintpair = new ChunkPos(chunkPos); + CompletableFuture completablefuture = this.permanentStorage.loadEntities(chunkcoordintpair); +@@ -269,6 +278,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A + } + + private boolean processChunkUnload(long chunkPos) { ++ org.spigotmc.AsyncCatcher.catchOp("Entity chunk unload process"); // Paper + boolean flag = this.storeChunkSections(chunkPos, (entityaccess) -> { + entityaccess.getPassengersAndSelf().forEach(this::unloadEntity); + }, true); // CraftBukkit - add boolean for event call +@@ -293,6 +303,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A + } + + private void processPendingLoads() { ++ org.spigotmc.AsyncCatcher.catchOp("Entity chunk process pending loads"); // Paper + ChunkEntities<T> chunkentities; // CraftBukkit - decompile error + + while ((chunkentities = (ChunkEntities) this.loadingInbox.poll()) != null) { +@@ -309,6 +320,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A + } + + public void tick() { ++ org.spigotmc.AsyncCatcher.catchOp("Entity manager tick"); // Paper + this.processPendingLoads(); + this.processUnloads(); + } +@@ -329,6 +341,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A + } + + public void autoSave() { ++ org.spigotmc.AsyncCatcher.catchOp("Entity manager autosave"); // Paper + this.getAllChunksToSave().forEach((java.util.function.LongConsumer) (i) -> { // CraftBukkit - decompile error + boolean flag = this.chunkVisibility.get(i) == Visibility.HIDDEN; + +@@ -343,6 +356,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A + } + + public void saveAll() { ++ org.spigotmc.AsyncCatcher.catchOp("Entity manager save"); // Paper + LongSet longset = this.getAllChunksToSave(); + + while (!longset.isEmpty()) { +@@ -450,6 +464,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A + long i = SectionPos.asLong(blockposition); + + if (i != this.currentSectionKey) { ++ org.spigotmc.AsyncCatcher.catchOp("Entity move"); // Paper + Visibility visibility = this.currentSection.getStatus(); + + if (!this.currentSection.remove(this.entity)) { +@@ -504,6 +519,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A + + @Override + public void onRemove(Entity.RemovalReason reason) { ++ org.spigotmc.AsyncCatcher.catchOp("Entity remove"); // Paper + if (!this.currentSection.remove(this.entity)) { + PersistentEntitySectionManager.LOGGER.warn("Entity {} wasn't found in section {} (destroying due to {})", new Object[]{this.entity, SectionPos.of(this.currentSectionKey), reason}); + } +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index d650822155f4628ea9d61ecbd4208520746aa59f..0ab6a9496e76aca6815bb8deebb8facd80a6b912 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -1755,6 +1755,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { + + @Override + public void playSound(Location loc, Sound sound, org.bukkit.SoundCategory category, float volume, float pitch, long seed) { ++ org.spigotmc.AsyncCatcher.catchOp("play sound"); // Paper + if (loc == null || sound == null || category == null) return; + + double x = loc.getX(); +@@ -1766,6 +1767,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { + + @Override + public void playSound(Location loc, String sound, org.bukkit.SoundCategory category, float volume, float pitch, long seed) { ++ org.spigotmc.AsyncCatcher.catchOp("play sound"); // Paper + if (loc == null || sound == null || category == null) return; + + double x = loc.getX(); +@@ -1798,6 +1800,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { + + @Override + public void playSound(Entity entity, Sound sound, org.bukkit.SoundCategory category, float volume, float pitch, long seed) { ++ org.spigotmc.AsyncCatcher.catchOp("play sound"); // Paper + if (!(entity instanceof CraftEntity craftEntity) || entity.getWorld() != this || sound == null || category == null) return; + + ClientboundSoundEntityPacket packet = new ClientboundSoundEntityPacket(CraftSound.bukkitToMinecraftHolder(sound), net.minecraft.sounds.SoundSource.valueOf(category.name()), craftEntity.getHandle(), volume, pitch, seed); +@@ -1818,6 +1821,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { + + @Override + public void playSound(Entity entity, String sound, org.bukkit.SoundCategory category, float volume, float pitch, long seed) { ++ org.spigotmc.AsyncCatcher.catchOp("play sound"); // Paper + if (!(entity instanceof CraftEntity craftEntity) || entity.getWorld() != this || sound == null || category == null) return; + + ClientboundSoundEntityPacket packet = new ClientboundSoundEntityPacket(Holder.direct(SoundEvent.createVariableRangeEvent(ResourceLocation.parse(sound))), net.minecraft.sounds.SoundSource.valueOf(category.name()), craftEntity.getHandle(), volume, pitch, seed); +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +index 5d6e4f2aeca9be4dd4504bc93b006e89ef875931..10a95c1a40597867ffd2974037bfed86dd6deda4 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +@@ -534,6 +534,7 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { + + @Override + public boolean addPotionEffect(PotionEffect effect, boolean force) { ++ org.spigotmc.AsyncCatcher.catchOp("effect add"); // Paper + this.getHandle().addEffect(org.bukkit.craftbukkit.potion.CraftPotionUtil.fromBukkit(effect), EntityPotionEffectEvent.Cause.PLUGIN); // Paper - Don't ignore icon + return true; + } +diff --git a/src/main/java/org/spigotmc/AsyncCatcher.java b/src/main/java/org/spigotmc/AsyncCatcher.java +index bbf0d9d9c44fe8d7add2f978994ec129420814c7..ef2598760458833021ef1bee92137f42c9fe591f 100644 +--- a/src/main/java/org/spigotmc/AsyncCatcher.java ++++ b/src/main/java/org/spigotmc/AsyncCatcher.java +@@ -11,6 +11,7 @@ public class AsyncCatcher + { + if ( AsyncCatcher.enabled && Thread.currentThread() != MinecraftServer.getServer().serverThread ) + { ++ MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable()); // Paper + throw new IllegalStateException( "Asynchronous " + reason + "!" ); + } + } diff --git a/patches/server/0591-Add-paper-mobcaps-and-paper-playermobcaps.patch b/patches/server/0591-Add-paper-mobcaps-and-paper-playermobcaps.patch new file mode 100644 index 0000000000..d04dadb215 --- /dev/null +++ b/patches/server/0591-Add-paper-mobcaps-and-paper-playermobcaps.patch @@ -0,0 +1,343 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jason Penilla <[email protected]> +Date: Mon, 16 Aug 2021 01:31:54 -0500 +Subject: [PATCH] Add '/paper mobcaps' and '/paper playermobcaps' + +Add commands to get the mobcaps for a world, as well as the mobcaps for +each player when per-player mob spawning is enabled. + +Also has a hover text on each mob category listing what entity types are +in said category + +diff --git a/src/main/java/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java +index cdad0fd5257ae842f83b9c1c98b4565b468d4f54..fd4f37711989431f997d77fb0917f8a9232ce53f 100644 +--- a/src/main/java/io/papermc/paper/command/PaperCommand.java ++++ b/src/main/java/io/papermc/paper/command/PaperCommand.java +@@ -40,6 +40,7 @@ public final class PaperCommand extends Command { + commands.put(Set.of("dumpplugins"), new DumpPluginsCommand()); + commands.put(Set.of("syncloadinfo"), new SyncLoadInfoCommand()); + commands.put(Set.of("dumpitem"), new DumpItemCommand()); ++ commands.put(Set.of("mobcaps", "playermobcaps"), new MobcapsCommand()); + + return commands.entrySet().stream() + .flatMap(entry -> entry.getKey().stream().map(s -> Map.entry(s, entry.getValue()))) +diff --git a/src/main/java/io/papermc/paper/command/subcommands/MobcapsCommand.java b/src/main/java/io/papermc/paper/command/subcommands/MobcapsCommand.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d3b39d88a72ca25057fd8574d32f28db0d420818 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/subcommands/MobcapsCommand.java +@@ -0,0 +1,229 @@ ++package io.papermc.paper.command.subcommands; ++ ++import com.google.common.collect.ImmutableMap; ++import io.papermc.paper.command.CommandUtil; ++import io.papermc.paper.command.PaperSubcommand; ++import java.util.ArrayList; ++import java.util.Collections; ++import java.util.List; ++import java.util.Map; ++import java.util.function.ToIntFunction; ++import net.kyori.adventure.text.Component; ++import net.kyori.adventure.text.ComponentLike; ++import net.kyori.adventure.text.JoinConfiguration; ++import net.kyori.adventure.text.TextComponent; ++import net.kyori.adventure.text.format.NamedTextColor; ++import net.kyori.adventure.text.format.TextColor; ++import net.minecraft.core.registries.BuiltInRegistries; ++import net.minecraft.server.level.ServerLevel; ++import net.minecraft.server.level.ServerPlayer; ++import net.minecraft.world.entity.MobCategory; ++import net.minecraft.world.level.NaturalSpawner; ++import org.bukkit.Bukkit; ++import org.bukkit.World; ++import org.bukkit.command.CommandSender; ++import org.bukkit.craftbukkit.CraftWorld; ++import org.bukkit.craftbukkit.entity.CraftPlayer; ++import org.bukkit.entity.Player; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.checker.nullness.qual.Nullable; ++import org.checkerframework.framework.qual.DefaultQualifier; ++ ++@DefaultQualifier(NonNull.class) ++public final class MobcapsCommand implements PaperSubcommand { ++ static final Map<MobCategory, TextColor> MOB_CATEGORY_COLORS = ImmutableMap.<MobCategory, TextColor>builder() ++ .put(MobCategory.MONSTER, NamedTextColor.RED) ++ .put(MobCategory.CREATURE, NamedTextColor.GREEN) ++ .put(MobCategory.AMBIENT, NamedTextColor.GRAY) ++ .put(MobCategory.AXOLOTLS, TextColor.color(0x7324FF)) ++ .put(MobCategory.UNDERGROUND_WATER_CREATURE, TextColor.color(0x3541E6)) ++ .put(MobCategory.WATER_CREATURE, TextColor.color(0x006EFF)) ++ .put(MobCategory.WATER_AMBIENT, TextColor.color(0x00B3FF)) ++ .put(MobCategory.MISC, TextColor.color(0x636363)) ++ .build(); ++ ++ @Override ++ public boolean execute(final CommandSender sender, final String subCommand, final String[] args) { ++ switch (subCommand) { ++ case "mobcaps" -> this.printMobcaps(sender, args); ++ case "playermobcaps" -> this.printPlayerMobcaps(sender, args); ++ } ++ return true; ++ } ++ ++ @Override ++ public List<String> tabComplete(final CommandSender sender, final String subCommand, final String[] args) { ++ return switch (subCommand) { ++ case "mobcaps" -> CommandUtil.getListMatchingLast(sender, args, this.suggestMobcaps(args)); ++ case "playermobcaps" -> CommandUtil.getListMatchingLast(sender, args, this.suggestPlayerMobcaps(sender, args)); ++ default -> throw new IllegalArgumentException(); ++ }; ++ } ++ ++ private List<String> suggestMobcaps(final String[] args) { ++ if (args.length == 1) { ++ final List<String> worlds = new ArrayList<>(Bukkit.getWorlds().stream().map(World::getName).toList()); ++ worlds.add("*"); ++ return worlds; ++ } ++ ++ return Collections.emptyList(); ++ } ++ ++ private List<String> suggestPlayerMobcaps(final CommandSender sender, final String[] args) { ++ if (args.length == 1) { ++ final List<String> list = new ArrayList<>(); ++ for (final Player player : Bukkit.getOnlinePlayers()) { ++ if (!(sender instanceof Player senderPlayer) || senderPlayer.canSee(player)) { ++ list.add(player.getName()); ++ } ++ } ++ return list; ++ } ++ ++ return Collections.emptyList(); ++ } ++ ++ private void printMobcaps(final CommandSender sender, final String[] args) { ++ final List<World> worlds; ++ if (args.length == 0) { ++ if (sender instanceof Player player) { ++ worlds = List.of(player.getWorld()); ++ } else { ++ sender.sendMessage(Component.text("Must specify a world! ex: '/paper mobcaps world'", NamedTextColor.RED)); ++ return; ++ } ++ } else if (args.length == 1) { ++ final String input = args[0]; ++ if (input.equals("*")) { ++ worlds = Bukkit.getWorlds(); ++ } else { ++ final @Nullable World world = Bukkit.getWorld(input); ++ if (world == null) { ++ sender.sendMessage(Component.text("'" + input + "' is not a valid world!", NamedTextColor.RED)); ++ return; ++ } else { ++ worlds = List.of(world); ++ } ++ } ++ } else { ++ sender.sendMessage(Component.text("Too many arguments!", NamedTextColor.RED)); ++ return; ++ } ++ ++ for (final World world : worlds) { ++ final ServerLevel level = ((CraftWorld) world).getHandle(); ++ final NaturalSpawner.@Nullable SpawnState state = level.getChunkSource().getLastSpawnState(); ++ ++ final int chunks; ++ if (state == null) { ++ chunks = 0; ++ } else { ++ chunks = state.getSpawnableChunkCount(); ++ } ++ sender.sendMessage(Component.join(JoinConfiguration.noSeparators(), ++ Component.text("Mobcaps for world: "), ++ Component.text(world.getName(), NamedTextColor.AQUA), ++ Component.text(" (" + chunks + " spawnable chunks)") ++ )); ++ ++ sender.sendMessage(createMobcapsComponent( ++ category -> { ++ if (state == null) { ++ return 0; ++ } else { ++ return state.getMobCategoryCounts().getOrDefault(category, 0); ++ } ++ }, ++ category -> NaturalSpawner.globalLimitForCategory(level, category, chunks) ++ )); ++ } ++ } ++ ++ private void printPlayerMobcaps(final CommandSender sender, final String[] args) { ++ final @Nullable Player player; ++ if (args.length == 0) { ++ if (sender instanceof Player pl) { ++ player = pl; ++ } else { ++ sender.sendMessage(Component.text("Must specify a player! ex: '/paper playermobcount playerName'", NamedTextColor.RED)); ++ return; ++ } ++ } else if (args.length == 1) { ++ final String input = args[0]; ++ player = Bukkit.getPlayerExact(input); ++ if (player == null) { ++ sender.sendMessage(Component.text("Could not find player named '" + input + "'", NamedTextColor.RED)); ++ return; ++ } ++ } else { ++ sender.sendMessage(Component.text("Too many arguments!", NamedTextColor.RED)); ++ return; ++ } ++ ++ final ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); ++ final ServerLevel level = serverPlayer.serverLevel(); ++ ++ if (!level.paperConfig().entities.spawning.perPlayerMobSpawns) { ++ sender.sendMessage(Component.text("Use '/paper mobcaps' for worlds where per-player mob spawning is disabled.", NamedTextColor.RED)); ++ return; ++ } ++ ++ sender.sendMessage(Component.join(JoinConfiguration.noSeparators(), Component.text("Mobcaps for player: "), Component.text(player.getName(), NamedTextColor.GREEN))); ++ sender.sendMessage(createMobcapsComponent( ++ category -> level.chunkSource.chunkMap.getMobCountNear(serverPlayer, category), ++ category -> level.getWorld().getSpawnLimitUnsafe(org.bukkit.craftbukkit.util.CraftSpawnCategory.toBukkit(category)) ++ )); ++ } ++ ++ private static Component createMobcapsComponent(final ToIntFunction<MobCategory> countGetter, final ToIntFunction<MobCategory> limitGetter) { ++ return MOB_CATEGORY_COLORS.entrySet().stream() ++ .map(entry -> { ++ final MobCategory category = entry.getKey(); ++ final TextColor color = entry.getValue(); ++ ++ final Component categoryHover = Component.join(JoinConfiguration.noSeparators(), ++ Component.text("Entity types in category ", TextColor.color(0xE0E0E0)), ++ Component.text(category.getName(), color), ++ Component.text(':', NamedTextColor.GRAY), ++ Component.newline(), ++ Component.newline(), ++ BuiltInRegistries.ENTITY_TYPE.entrySet().stream() ++ .filter(it -> it.getValue().getCategory() == category) ++ .map(it -> Component.translatable(it.getValue().getDescriptionId())) ++ .collect(Component.toComponent(Component.text(", ", NamedTextColor.GRAY))) ++ ); ++ ++ final Component categoryComponent = Component.text() ++ .content(" " + category.getName()) ++ .color(color) ++ .hoverEvent(categoryHover) ++ .build(); ++ ++ final TextComponent.Builder builder = Component.text() ++ .append( ++ categoryComponent, ++ Component.text(": ", NamedTextColor.GRAY) ++ ); ++ final int limit = limitGetter.applyAsInt(category); ++ if (limit != -1) { ++ builder.append( ++ Component.text(countGetter.applyAsInt(category)), ++ Component.text("/", NamedTextColor.GRAY), ++ Component.text(limit) ++ ); ++ } else { ++ builder.append(Component.text() ++ .append( ++ Component.text('n'), ++ Component.text("/", NamedTextColor.GRAY), ++ Component.text('a') ++ ) ++ .hoverEvent(Component.text("This category does not naturally spawn."))); ++ } ++ return builder; ++ }) ++ .map(ComponentLike::asComponent) ++ .collect(Component.toComponent(Component.newline())); ++ } ++} +diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java +index 606a60fe273974b71ed2bd40be819d848627e777..bf943feca387b77a3154773a59da7190d38d8621 100644 +--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java ++++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java +@@ -172,6 +172,16 @@ public final class NaturalSpawner { + gameprofilerfiller.pop(); + } + ++ // Paper start - Add mobcaps commands ++ public static int globalLimitForCategory(final ServerLevel level, final MobCategory category, final int spawnableChunkCount) { ++ final int categoryLimit = level.getWorld().getSpawnLimitUnsafe(CraftSpawnCategory.toBukkit(category)); ++ if (categoryLimit < 1) { ++ return categoryLimit; ++ } ++ return categoryLimit * spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER; ++ } ++ // Paper end - Add mobcaps commands ++ + public static void spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) { + BlockPos blockposition = NaturalSpawner.getRandomPosWithin(world, chunk); + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index 45d887f4143444321f563cdd7d084b2b9ccf911e..33d9f3778996eedc83064332a2fbbdc7c6a8ba90 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -2334,6 +2334,11 @@ public final class CraftServer implements Server { + + @Override + public int getSpawnLimit(SpawnCategory spawnCategory) { ++ // Paper start - Add mobcaps commands ++ return this.getSpawnLimitUnsafe(spawnCategory); ++ } ++ public int getSpawnLimitUnsafe(final SpawnCategory spawnCategory) { ++ // Paper end - Add mobcaps commands + return this.spawnCategoryLimit.getOrDefault(spawnCategory, -1); + } + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index 0ab6a9496e76aca6815bb8deebb8facd80a6b912..744e3631cd2d5c157c9b6023ca813e57c6f860d6 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -1713,9 +1713,14 @@ public class CraftWorld extends CraftRegionAccessor implements World { + Preconditions.checkArgument(spawnCategory != null, "SpawnCategory cannot be null"); + Preconditions.checkArgument(CraftSpawnCategory.isValidForLimits(spawnCategory), "SpawnCategory.%s are not supported", spawnCategory); + ++ // Paper start - Add mobcaps commands ++ return this.getSpawnLimitUnsafe(spawnCategory); ++ } ++ public final int getSpawnLimitUnsafe(final SpawnCategory spawnCategory) { + int limit = this.spawnCategoryLimit.getOrDefault(spawnCategory, -1); + if (limit < 0) { +- limit = this.server.getSpawnLimit(spawnCategory); ++ limit = this.server.getSpawnLimitUnsafe(spawnCategory); ++ // Paper end - Add mobcaps commands + } + return limit; + } +diff --git a/src/test/java/io/papermc/paper/command/subcommands/MobcapsCommandTest.java b/src/test/java/io/papermc/paper/command/subcommands/MobcapsCommandTest.java +new file mode 100644 +index 0000000000000000000000000000000000000000..6fdc77caa74845786c78a6ba087062b4d698cb82 +--- /dev/null ++++ b/src/test/java/io/papermc/paper/command/subcommands/MobcapsCommandTest.java +@@ -0,0 +1,22 @@ ++package io.papermc.paper.command.subcommands; ++ ++import java.util.HashSet; ++import java.util.Set; ++import net.minecraft.world.entity.MobCategory; ++import org.bukkit.support.environment.Normal; ++import org.junit.jupiter.api.Assertions; ++import org.junit.jupiter.api.Test; ++ ++@Normal ++public class MobcapsCommandTest { ++ @Test ++ public void testMobCategoryColors() { ++ final Set<String> missing = new HashSet<>(); ++ for (final MobCategory value : MobCategory.values()) { ++ if (!MobcapsCommand.MOB_CATEGORY_COLORS.containsKey(value)) { ++ missing.add(value.getName()); ++ } ++ } ++ Assertions.assertTrue(missing.isEmpty(), "MobcapsCommand.MOB_CATEGORY_COLORS map missing TextColors for [" + String.join(", ", missing + "]")); ++ } ++} diff --git a/patches/server/0592-Sanitize-ResourceLocation-error-logging.patch b/patches/server/0592-Sanitize-ResourceLocation-error-logging.patch new file mode 100644 index 0000000000..602f0d3b27 --- /dev/null +++ b/patches/server/0592-Sanitize-ResourceLocation-error-logging.patch @@ -0,0 +1,28 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Nassim Jahnke <[email protected]> +Date: Thu, 26 Aug 2021 12:09:47 +0200 +Subject: [PATCH] Sanitize ResourceLocation error logging + + +diff --git a/src/main/java/net/minecraft/resources/ResourceLocation.java b/src/main/java/net/minecraft/resources/ResourceLocation.java +index 262660d115a5d5cbecfbae995955a24283e666b0..87afe84791af2d5e9f869cd4c09eed4bb5fee75b 100644 +--- a/src/main/java/net/minecraft/resources/ResourceLocation.java ++++ b/src/main/java/net/minecraft/resources/ResourceLocation.java +@@ -247,7 +247,7 @@ public final class ResourceLocation implements Comparable<ResourceLocation> { + + private static String assertValidNamespace(String namespace, String path) { + if (!isValidNamespace(namespace)) { +- throw new ResourceLocationException("Non [a-z0-9_.-] character in namespace of location: " + namespace + ":" + path); ++ throw new ResourceLocationException("Non [a-z0-9_.-] character in namespace of location: " + org.apache.commons.lang3.StringUtils.normalizeSpace(namespace) + ":" + org.apache.commons.lang3.StringUtils.normalizeSpace(path)); // Paper - Sanitize ResourceLocation error logging + } else { + return namespace; + } +@@ -268,7 +268,7 @@ public final class ResourceLocation implements Comparable<ResourceLocation> { + + private static String assertValidPath(String namespace, String path) { + if (!isValidPath(path)) { +- throw new ResourceLocationException("Non [a-z0-9/._-] character in path of location: " + namespace + ":" + path); ++ throw new ResourceLocationException("Non [a-z0-9/._-] character in path of location: " + namespace + ":" + org.apache.commons.lang3.StringUtils.normalizeSpace(path)); // Paper - Sanitize ResourceLocation error logging + } else { + return path; + } diff --git a/patches/server/0593-Manually-inline-methods-in-BlockPosition.patch b/patches/server/0593-Manually-inline-methods-in-BlockPosition.patch new file mode 100644 index 0000000000..67bbeaf54d --- /dev/null +++ b/patches/server/0593-Manually-inline-methods-in-BlockPosition.patch @@ -0,0 +1,63 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf <[email protected]> +Date: Mon, 6 Jul 2020 22:48:48 -0700 +Subject: [PATCH] Manually inline methods in BlockPosition + + +diff --git a/src/main/java/net/minecraft/core/BlockPos.java b/src/main/java/net/minecraft/core/BlockPos.java +index 57340f0f46d5429585d99712dd39347d30d9951b..bf3eb9259f97d6d5af01244f103dc6b19c185d50 100644 +--- a/src/main/java/net/minecraft/core/BlockPos.java ++++ b/src/main/java/net/minecraft/core/BlockPos.java +@@ -588,9 +588,9 @@ public class BlockPos extends Vec3i { + } + + public BlockPos.MutableBlockPos set(int x, int y, int z) { +- this.setX(x); +- this.setY(y); +- this.setZ(z); ++ this.x = x; // Paper - Perf: Manually inline methods in BlockPosition ++ this.y = y; // Paper - Perf: Manually inline methods in BlockPosition ++ this.z = z; // Paper - Perf: Manually inline methods in BlockPosition + return this; + } + +@@ -655,19 +655,19 @@ public class BlockPos extends Vec3i { + + @Override + public BlockPos.MutableBlockPos setX(int i) { +- super.setX(i); ++ this.x = i; // Paper - Perf: Manually inline methods in BlockPosition + return this; + } + + @Override + public BlockPos.MutableBlockPos setY(int i) { +- super.setY(i); ++ this.y = i; // Paper - Perf: Manually inline methods in BlockPosition + return this; + } + + @Override + public BlockPos.MutableBlockPos setZ(int i) { +- super.setZ(i); ++ this.z = i; // Paper - Perf: Manually inline methods in BlockPosition + return this; + } + +diff --git a/src/main/java/net/minecraft/core/Vec3i.java b/src/main/java/net/minecraft/core/Vec3i.java +index 7d5f99cac756c54e5922bf85d5d359edcc21f1e8..2f2bcc1b9b32e58bf70ae6c171177ceb333ed6cd 100644 +--- a/src/main/java/net/minecraft/core/Vec3i.java ++++ b/src/main/java/net/minecraft/core/Vec3i.java +@@ -16,9 +16,9 @@ public class Vec3i implements Comparable<Vec3i> { + vec -> IntStream.of(vec.getX(), vec.getY(), vec.getZ()) + ); + public static final Vec3i ZERO = new Vec3i(0, 0, 0); +- private int x; +- private int y; +- private int z; ++ protected int x; // Paper - Perf: Manually inline methods in BlockPosition; protected ++ protected int y; // Paper - Perf: Manually inline methods in BlockPosition; protected ++ protected int z; // Paper - Perf: Manually inline methods in BlockPosition; protected + + public static Codec<Vec3i> offsetCodec(int maxAbsValue) { + return CODEC.validate( diff --git a/patches/server/0594-Name-craft-scheduler-threads-according-to-the-plugin.patch b/patches/server/0594-Name-craft-scheduler-threads-according-to-the-plugin.patch new file mode 100644 index 0000000000..30f250f235 --- /dev/null +++ b/patches/server/0594-Name-craft-scheduler-threads-according-to-the-plugin.patch @@ -0,0 +1,33 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf <[email protected]> +Date: Sun, 19 Jul 2020 15:17:01 -0700 +Subject: [PATCH] Name craft scheduler threads according to the plugin using + them + +Provides quick access to culprits running far more threads than +they should be + +diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java +index 365b7e7c665bec357fb76d1479bf17da6f603590..e97f6b76ef2fe21c7c2eca8d4a707e5866d70de9 100644 +--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java ++++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java +@@ -25,7 +25,10 @@ class CraftAsyncTask extends CraftTask { + @Override + public void run() { + final Thread thread = Thread.currentThread(); +- synchronized (this.workers) { ++ // Paper start - name threads according to running plugin ++ final String nameBefore = thread.getName(); ++ thread.setName(nameBefore + " - " + this.getOwner().getName()); ++ try { synchronized (this.workers) { // Paper end - name threads according to running plugin + if (this.getPeriod() == CraftTask.CANCEL) { + // Never continue running after cancelled. + // Checking this with the lock is important! +@@ -92,6 +95,7 @@ class CraftAsyncTask extends CraftTask { + } + } + } ++ } finally { thread.setName(nameBefore); } // Paper - name threads according to running plugin + } + + LinkedList<BukkitWorker> getWorkers() { diff --git a/patches/server/0595-Make-sure-inlined-getChunkAt-has-inlined-logic-for-l.patch b/patches/server/0595-Make-sure-inlined-getChunkAt-has-inlined-logic-for-l.patch new file mode 100644 index 0000000000..73d62bea98 --- /dev/null +++ b/patches/server/0595-Make-sure-inlined-getChunkAt-has-inlined-logic-for-l.patch @@ -0,0 +1,31 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf <[email protected]> +Date: Sun, 20 Sep 2020 16:10:49 -0700 +Subject: [PATCH] Make sure inlined getChunkAt has inlined logic for loaded + chunks + +Tux did some profiling some time ago and showed that the +previous getChunkAt method which had inlined logic for loaded +chunks did get inlined, but the standard CPS.getChunkAt +method was not inlined. + +diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java +index 68cee32833a3d683852e67e3727d62b84fa60cc4..a1731f09e724794be90dbf47e1d463a10cdb3052 100644 +--- a/src/main/java/net/minecraft/world/level/Level.java ++++ b/src/main/java/net/minecraft/world/level/Level.java +@@ -351,7 +351,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + + @Override + public final LevelChunk getChunk(int chunkX, int chunkZ) { // Paper - final to help inline +- return (LevelChunk) this.getChunk(chunkX, chunkZ, ChunkStatus.FULL, true); // Paper - avoid a method jump ++ // Paper start - Perf: make sure loaded chunks get the inlined variant of this function ++ net.minecraft.server.level.ServerChunkCache cps = ((ServerLevel)this).getChunkSource(); ++ LevelChunk ifLoaded = cps.getChunkAtIfLoadedImmediately(chunkX, chunkZ); ++ if (ifLoaded != null) { ++ return ifLoaded; ++ } ++ return (LevelChunk) cps.getChunk(chunkX, chunkZ, ChunkStatus.FULL, true); // Paper - avoid a method jump ++ // Paper end - Perf: make sure loaded chunks get the inlined variant of this function + } + + // Paper start - if loaded diff --git a/patches/server/0596-Don-t-read-neighbour-chunk-data-off-disk-when-conver.patch b/patches/server/0596-Don-t-read-neighbour-chunk-data-off-disk-when-conver.patch new file mode 100644 index 0000000000..eff1d0a95f --- /dev/null +++ b/patches/server/0596-Don-t-read-neighbour-chunk-data-off-disk-when-conver.patch @@ -0,0 +1,21 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf <[email protected]> +Date: Sun, 11 Apr 2021 02:58:48 -0700 +Subject: [PATCH] Don't read neighbour chunk data off disk when converting + chunks + +Lighting is purged on update anyways, so let's not add more +into the conversion process + +diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java +index 909acbf5b8c6edcb4529647c11464d911d6f8c15..7d5e2e6e96ea9017334dddade54a9dcb37518642 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java ++++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java +@@ -47,6 +47,7 @@ public class ChunkStorage implements AutoCloseable { + + // CraftBukkit start + private boolean check(ServerChunkCache cps, int x, int z) { ++ if (true) return true; // Paper - Perf: this isn't even needed anymore, light is purged updating to 1.14+, why are we holding up the conversion process reading chunk data off disk - return true, we need to set light populated to true so the converter recognizes the chunk as being "full" + ChunkPos pos = new ChunkPos(x, z); + if (cps != null) { + com.google.common.base.Preconditions.checkState(org.bukkit.Bukkit.isPrimaryThread(), "primary thread"); diff --git a/patches/server/0597-Don-t-lookup-fluid-state-when-raytracing-skip-air-bl.patch b/patches/server/0597-Don-t-lookup-fluid-state-when-raytracing-skip-air-bl.patch new file mode 100644 index 0000000000..1626fb7b50 --- /dev/null +++ b/patches/server/0597-Don-t-lookup-fluid-state-when-raytracing-skip-air-bl.patch @@ -0,0 +1,26 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf <[email protected]> +Date: Fri, 28 Aug 2020 12:33:47 -0700 +Subject: [PATCH] Don't lookup fluid state when raytracing, skip air blocks + +Just use the iblockdata already retrieved, removes a getType call. + +Also save approx. 5% for the raytrace call, as most (expensive) +raytracing tends to go through air and returning early is an +easy win. The remaining problems with this function +are mostly with the block getting itself. + +diff --git a/src/main/java/net/minecraft/world/level/BlockGetter.java b/src/main/java/net/minecraft/world/level/BlockGetter.java +index 6bf3019ff06501e82de5976417fd98a10ae6334a..e69473307b30d2b805fc1723ae8f6f2be76310ef 100644 +--- a/src/main/java/net/minecraft/world/level/BlockGetter.java ++++ b/src/main/java/net/minecraft/world/level/BlockGetter.java +@@ -80,7 +80,8 @@ public interface BlockGetter extends LevelHeightAccessor { + return BlockHitResult.miss(raytrace1.getTo(), Direction.getApproximateNearest(vec3d.x, vec3d.y, vec3d.z), BlockPos.containing(raytrace1.getTo())); + } + // Paper end - Prevent raytrace from loading chunks +- FluidState fluid = this.getFluidState(blockposition); ++ if (iblockdata.isAir()) return null; // Paper - Perf: optimise air cases ++ FluidState fluid = iblockdata.getFluidState(); // Paper - Perf: don't need to go to world state again + Vec3 vec3d = raytrace1.getFrom(); + Vec3 vec3d1 = raytrace1.getTo(); + VoxelShape voxelshape = raytrace1.getBlockShape(iblockdata, this, blockposition); diff --git a/patches/server/0598-Oprimise-map-impl-for-tracked-players.patch b/patches/server/0598-Oprimise-map-impl-for-tracked-players.patch new file mode 100644 index 0000000000..cea8c3752e --- /dev/null +++ b/patches/server/0598-Oprimise-map-impl-for-tracked-players.patch @@ -0,0 +1,21 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf <[email protected]> +Date: Fri, 19 Feb 2021 22:51:52 -0800 +Subject: [PATCH] Oprimise map impl for tracked players + +Reference2BooleanOpenHashMap is going to have +better lookups than HashMap. + +diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java +index 69f54e812794b23e5f54606da86f71163f5f0bbe..2d325c998e40a65af10d6adbb0dc304bea50e3d8 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkMap.java ++++ b/src/main/java/net/minecraft/server/level/ChunkMap.java +@@ -1508,7 +1508,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + final Entity entity; + private final int range; + SectionPos lastSectionPos; +- public final Set<ServerPlayerConnection> seenBy = Sets.newIdentityHashSet(); ++ public final Set<ServerPlayerConnection> seenBy = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl + + public TrackedEntity(final Entity entity, final int i, final int j, final boolean flag) { + this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, j, flag, this::broadcast, this.seenBy); // CraftBukkit diff --git a/patches/server/0599-Add-missing-InventoryType.patch b/patches/server/0599-Add-missing-InventoryType.patch new file mode 100644 index 0000000000..768c150fc1 --- /dev/null +++ b/patches/server/0599-Add-missing-InventoryType.patch @@ -0,0 +1,22 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Wed, 27 Dec 2023 16:46:07 -0800 +Subject: [PATCH] Add missing InventoryType + +Upstream did not add a DECORATED_POT inventory type + +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java +index bc5ec42a54401a2275c05f0f506ba89f00c19ec9..c6159c70f7a37b9bffe268b91905ce848d1d2927 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java +@@ -529,6 +529,10 @@ public class CraftInventory implements Inventory { + return InventoryType.COMPOSTER; + } else if (this.inventory instanceof JukeboxBlockEntity) { + return InventoryType.JUKEBOX; ++ // Paper start ++ } else if (this.inventory instanceof net.minecraft.world.level.block.entity.DecoratedPotBlockEntity) { ++ return org.bukkit.event.inventory.InventoryType.DECORATED_POT; ++ // Paper end + } else { + return InventoryType.CHEST; + } diff --git a/patches/server/0600-Optimise-BlockSoil-nearby-water-lookup.patch b/patches/server/0600-Optimise-BlockSoil-nearby-water-lookup.patch new file mode 100644 index 0000000000..4a5d6c5b9d --- /dev/null +++ b/patches/server/0600-Optimise-BlockSoil-nearby-water-lookup.patch @@ -0,0 +1,52 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf <[email protected]> +Date: Thu, 10 Jun 2021 14:36:00 -0700 +Subject: [PATCH] Optimise BlockSoil nearby water lookup + +Apparently the abstract block iteration was taking about +75% of the method call. + +diff --git a/src/main/java/net/minecraft/world/level/block/FarmBlock.java b/src/main/java/net/minecraft/world/level/block/FarmBlock.java +index a87f8345aa5520a867a8dd769b43526b20b8c16a..c3dba0c2c94f3804338f86621dc42405e380a6b3 100644 +--- a/src/main/java/net/minecraft/world/level/block/FarmBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/FarmBlock.java +@@ -154,19 +154,28 @@ public class FarmBlock extends Block { + } + + private static boolean isNearWater(LevelReader world, BlockPos pos) { +- Iterator iterator = BlockPos.betweenClosed(pos.offset(-4, 0, -4), pos.offset(4, 1, 4)).iterator(); +- +- BlockPos blockposition1; +- +- do { +- if (!iterator.hasNext()) { +- return false; ++ // Paper start - Perf: remove abstract block iteration ++ int xOff = pos.getX(); ++ int yOff = pos.getY(); ++ int zOff = pos.getZ(); ++ ++ for (int dz = -4; dz <= 4; ++dz) { ++ int z = dz + zOff; ++ for (int dx = -4; dx <= 4; ++dx) { ++ int x = xOff + dx; ++ for (int dy = 0; dy <= 1; ++dy) { ++ int y = dy + yOff; ++ net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk)world.getChunk(x >> 4, z >> 4); ++ net.minecraft.world.level.material.FluidState fluid = chunk.getBlockStateFinal(x, y, z).getFluidState(); ++ if (fluid.is(FluidTags.WATER)) { ++ return true; ++ } ++ } + } ++ } + +- blockposition1 = (BlockPos) iterator.next(); +- } while (!world.getFluidState(blockposition1).is(FluidTags.WATER)); +- +- return true; ++ return false; ++ // Paper end - Perf: remove abstract block iteration + } + + @Override diff --git a/patches/server/0601-Fix-merchant-inventory-not-closing-on-entity-removal.patch b/patches/server/0601-Fix-merchant-inventory-not-closing-on-entity-removal.patch new file mode 100644 index 0000000000..70a334e012 --- /dev/null +++ b/patches/server/0601-Fix-merchant-inventory-not-closing-on-entity-removal.patch @@ -0,0 +1,22 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Thu, 2 Sep 2021 00:24:06 -0700 +Subject: [PATCH] Fix merchant inventory not closing on entity removal + + +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index 2ac09e8eece6ff772c94bb1efd01cc1c45194435..3205ef2b0027a2fa7f9ba5ed3437f71f1c6e02b5 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -2309,6 +2309,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + // Spigot end + // Spigot Start + if (entity.getBukkitEntity() instanceof org.bukkit.inventory.InventoryHolder && (!(entity instanceof ServerPlayer) || entity.getRemovalReason() != Entity.RemovalReason.KILLED)) { // SPIGOT-6876: closeInventory clears death message ++ // Paper start - Fix merchant inventory not closing on entity removal ++ if (entity.getBukkitEntity() instanceof org.bukkit.inventory.Merchant merchant && merchant.getTrader() != null) { ++ merchant.getTrader().closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNLOADED); ++ } ++ // Paper end - Fix merchant inventory not closing on entity removal + for (org.bukkit.entity.HumanEntity h : Lists.newArrayList(((org.bukkit.inventory.InventoryHolder) entity.getBukkitEntity()).getInventory().getViewers())) { + h.closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNLOADED); // Paper - Inventory close reason + } diff --git a/patches/server/0602-Check-requirement-before-suggesting-root-nodes.patch b/patches/server/0602-Check-requirement-before-suggesting-root-nodes.patch new file mode 100644 index 0000000000..fce5e326f9 --- /dev/null +++ b/patches/server/0602-Check-requirement-before-suggesting-root-nodes.patch @@ -0,0 +1,32 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: stonar96 <[email protected]> +Date: Sun, 12 Sep 2021 00:14:21 +0200 +Subject: [PATCH] Check requirement before suggesting root nodes + +Child nodes are handled by CommandDispatcher#parse checking +requirements. + +Vanilla clients only send ServerboundCommandSuggestionPacket when +encountering a command node with ASK_SERVER suggestions, however a +modified client can send this packet whenever it wants. + +diff --git a/src/main/java/com/mojang/brigadier/CommandDispatcher.java b/src/main/java/com/mojang/brigadier/CommandDispatcher.java +index 858e41fba7dc285dd21a93f3918cc5de3887df5f..92848b64a78fce7a92e1657c2da6fc5ee53eea44 100644 +--- a/src/main/java/com/mojang/brigadier/CommandDispatcher.java ++++ b/src/main/java/com/mojang/brigadier/CommandDispatcher.java +@@ -538,10 +538,14 @@ public class CommandDispatcher<S> { + int i = 0; + for (final CommandNode<S> node : parent.getChildren()) { + CompletableFuture<Suggestions> future = Suggestions.empty(); ++ // Paper start - Don't suggest if the requirement isn't met ++ if (parent != this.root || node.canUse(context.getSource())) { + try { +- if (node.canUse(parse.getContext().getSource())) future = node.listSuggestions(context.build(truncatedInput), new SuggestionsBuilder(truncatedInput, truncatedInputLowerCase, start)); // CraftBukkit ++ future = node.listSuggestions(context.build(truncatedInput), new SuggestionsBuilder(truncatedInput, truncatedInputLowerCase, start)); // CraftBukkit + } catch (final CommandSyntaxException ignored) { + } ++ } ++ // Paper end - Don't suggest if the requirement isn't met + futures[i++] = future; + } + diff --git a/patches/server/0603-Don-t-respond-to-ServerboundCommandSuggestionPacket-.patch b/patches/server/0603-Don-t-respond-to-ServerboundCommandSuggestionPacket-.patch new file mode 100644 index 0000000000..0330fe314a --- /dev/null +++ b/patches/server/0603-Don-t-respond-to-ServerboundCommandSuggestionPacket-.patch @@ -0,0 +1,23 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: stonar96 <[email protected]> +Date: Sun, 12 Sep 2021 00:14:21 +0200 +Subject: [PATCH] Don't respond to ServerboundCommandSuggestionPacket when + tab-complete is disabled + + +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index dcc87047769094241a0f0b682dc31cc4767ab5e9..d76df6ed588b1109cdebf239884d4c9ce5de47a6 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -758,6 +758,11 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + return; + } + // CraftBukkit end ++ // Paper start - Don't suggest if tab-complete is disabled ++ if (org.spigotmc.SpigotConfig.tabComplete < 0) { ++ return; ++ } ++ // Paper end - Don't suggest if tab-complete is disabled + // Paper start - AsyncTabCompleteEvent + TAB_COMPLETE_EXECUTOR.execute(() -> this.handleCustomCommandSuggestions0(packet)); + } diff --git a/patches/server/0604-Add-packet-limiter-config.patch b/patches/server/0604-Add-packet-limiter-config.patch new file mode 100644 index 0000000000..e49a44446e --- /dev/null +++ b/patches/server/0604-Add-packet-limiter-config.patch @@ -0,0 +1,108 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf <[email protected]> +Date: Fri, 30 Oct 2020 22:37:16 -0700 +Subject: [PATCH] Add packet limiter config + +Example config: +packet-limiter: + kick-message: '&cSent too many packets' + limits: + all: + interval: 7.0 + max-packet-rate: 500.0 + ServerboundPlaceRecipePacket: + interval: 4.0 + max-packet-rate: 5.0 + action: DROP + +all section refers to all incoming packets, the action for all is +hard coded to KICK. + +For specific limits, the section name is the class's name, +and an action can be defined: DROP or KICK + +If interval or rate are less-than 0, the limit is ignored + +diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java +index f5fe87103ce53ba07b43c6c10fc9dafcc5ea4958..72e69f60fb687f102c963b61042b19162a5ec0b2 100644 +--- a/src/main/java/net/minecraft/network/Connection.java ++++ b/src/main/java/net/minecraft/network/Connection.java +@@ -137,6 +137,22 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> { + return null; + } + // Paper end - add utility methods ++ // Paper start - packet limiter ++ protected final Object PACKET_LIMIT_LOCK = new Object(); ++ protected final @Nullable io.papermc.paper.util.IntervalledCounter allPacketCounts = io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.allPackets.isEnabled() ? new io.papermc.paper.util.IntervalledCounter( ++ (long)(io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.allPackets.interval() * 1.0e9) ++ ) : null; ++ protected final java.util.Map<Class<? extends net.minecraft.network.protocol.Packet<?>>, io.papermc.paper.util.IntervalledCounter> packetSpecificLimits = new java.util.HashMap<>(); ++ ++ private boolean stopReadingPackets; ++ private void killForPacketSpam() { ++ this.sendPacket(new ClientboundDisconnectPacket(io.papermc.paper.adventure.PaperAdventure.asVanilla(io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.kickMessage)), PacketSendListener.thenRun(() -> { ++ this.disconnect(io.papermc.paper.adventure.PaperAdventure.asVanilla(io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.kickMessage)); ++ }), true); ++ this.setReadOnly(); ++ this.stopReadingPackets = true; ++ } ++ // Paper end - packet limiter + + public Connection(PacketFlow side) { + this.receiving = side; +@@ -215,6 +231,55 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> { + if (packetlistener == null) { + throw new IllegalStateException("Received a packet before the packet listener was initialized"); + } else { ++ // Paper start - packet limiter ++ if (this.stopReadingPackets) { ++ return; ++ } ++ if (this.allPacketCounts != null || ++ io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.overrides.containsKey(packet.getClass())) { ++ long time = System.nanoTime(); ++ synchronized (PACKET_LIMIT_LOCK) { ++ if (this.allPacketCounts != null) { ++ this.allPacketCounts.updateAndAdd(1, time); ++ if (this.allPacketCounts.getRate() >= io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.allPackets.maxPacketRate()) { ++ this.killForPacketSpam(); ++ return; ++ } ++ } ++ ++ for (Class<?> check = packet.getClass(); check != Object.class; check = check.getSuperclass()) { ++ io.papermc.paper.configuration.GlobalConfiguration.PacketLimiter.PacketLimit packetSpecificLimit = ++ io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.overrides.get(check); ++ if (packetSpecificLimit == null || !packetSpecificLimit.isEnabled()) { ++ continue; ++ } ++ io.papermc.paper.util.IntervalledCounter counter = this.packetSpecificLimits.computeIfAbsent((Class)check, (clazz) -> { ++ return new io.papermc.paper.util.IntervalledCounter((long)(packetSpecificLimit.interval() * 1.0e9)); ++ }); ++ counter.updateAndAdd(1, time); ++ if (counter.getRate() >= packetSpecificLimit.maxPacketRate()) { ++ switch (packetSpecificLimit.action()) { ++ case DROP: ++ return; ++ case KICK: ++ String deobfedPacketName = io.papermc.paper.util.ObfHelper.INSTANCE.deobfClassName(check.getName()); ++ ++ String playerName; ++ if (this.packetListener instanceof net.minecraft.server.network.ServerCommonPacketListenerImpl impl) { ++ playerName = impl.getOwner().getName(); ++ } else { ++ playerName = this.getLoggableAddress(net.minecraft.server.MinecraftServer.getServer().logIPs()); ++ } ++ ++ Connection.LOGGER.warn("{} kicked for packet spamming: {}", playerName, deobfedPacketName.substring(deobfedPacketName.lastIndexOf(".") + 1)); ++ this.killForPacketSpam(); ++ return; ++ } ++ } ++ } ++ } ++ } ++ // Paper end - packet limiter + if (packetlistener.shouldHandleMessage(packet)) { + try { + Connection.genericsFtw(packet, packetlistener); diff --git a/patches/server/0605-Fix-setPatternColor-on-tropical-fish-bucket-meta.patch b/patches/server/0605-Fix-setPatternColor-on-tropical-fish-bucket-meta.patch new file mode 100644 index 0000000000..f5c3a81df3 --- /dev/null +++ b/patches/server/0605-Fix-setPatternColor-on-tropical-fish-bucket-meta.patch @@ -0,0 +1,72 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Bjarne Koll <[email protected]> +Date: Sat, 6 Nov 2021 23:15:20 +0100 +Subject: [PATCH] Fix setPatternColor on tropical fish bucket meta + +Prior to this commit, the tropical fish bucket meta would set the body +color to the previous metas pattern colour when updating the pattern +colour. + +This commit hence simply fixes this by using the proper body colour +value when updating the pattern color. + +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java +index 8169d08c1bccf7c9896bb083eba388f918fac6c9..a514fe98d3d2b65d2cfd029079c69189bcb99c01 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java +@@ -128,7 +128,7 @@ class CraftMetaTropicalFishBucket extends CraftMetaItem implements TropicalFishB + if (this.variant == null) { + this.variant = 0; + } +- this.variant = CraftTropicalFish.getData(color, this.getPatternColor(), this.getPattern()); ++ this.variant = CraftTropicalFish.getData(color, this.getBodyColor(), this.getPattern()); // Paper - properly set tropical fish pattern color without mutating body color + } + + @Override +diff --git a/src/test/java/io/papermc/paper/inventory/CraftMetaTropicalFishBucketTest.java b/src/test/java/io/papermc/paper/inventory/CraftMetaTropicalFishBucketTest.java +new file mode 100644 +index 0000000000000000000000000000000000000000..68a557cedbffb41f27ba21096e2ae5eebbf13f5c +--- /dev/null ++++ b/src/test/java/io/papermc/paper/inventory/CraftMetaTropicalFishBucketTest.java +@@ -0,0 +1,41 @@ ++package io.papermc.paper.inventory; ++ ++import org.bukkit.DyeColor; ++import org.bukkit.Material; ++import org.bukkit.entity.TropicalFish; ++import org.bukkit.inventory.ItemStack; ++import org.bukkit.inventory.meta.TropicalFishBucketMeta; ++import org.bukkit.support.environment.AllFeatures; ++import org.junit.jupiter.api.Assertions; ++import org.junit.jupiter.api.Test; ++ ++@AllFeatures ++public class CraftMetaTropicalFishBucketTest { ++ ++ @Test ++ public void testAllCombinations() { ++ final var rawMeta = new ItemStack(Material.TROPICAL_FISH_BUCKET).getItemMeta(); ++ Assertions.assertTrue(rawMeta instanceof TropicalFishBucketMeta, "Meta was not a tropical fish bucket"); ++ ++ final var meta = (TropicalFishBucketMeta) rawMeta; ++ ++ for (final var bodyColor : DyeColor.values()) { ++ for (final var pattern : TropicalFish.Pattern.values()) { ++ for (final var patternColor : DyeColor.values()) { ++ meta.setBodyColor(bodyColor); ++ Assertions.assertEquals(bodyColor, meta.getBodyColor(), "Body color did not match post body color!"); ++ ++ meta.setPattern(pattern); ++ Assertions.assertEquals(pattern, meta.getPattern(), "Pattern did not match post pattern!"); ++ Assertions.assertEquals(bodyColor, meta.getBodyColor(), "Body color did not match post pattern!"); ++ ++ meta.setPatternColor(patternColor); ++ Assertions.assertEquals(pattern, meta.getPattern(), "Pattern did not match post pattern color!"); ++ Assertions.assertEquals(bodyColor, meta.getBodyColor(), "Body color did not match post pattern color!"); ++ Assertions.assertEquals(patternColor, meta.getPatternColor(), "Pattern color did not match post pattern color!"); ++ } ++ } ++ } ++ } ++ ++} diff --git a/patches/server/0606-Ensure-valid-vehicle-status.patch b/patches/server/0606-Ensure-valid-vehicle-status.patch new file mode 100644 index 0000000000..88fa596782 --- /dev/null +++ b/patches/server/0606-Ensure-valid-vehicle-status.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Nassim Jahnke <[email protected]> +Date: Tue, 28 Sep 2021 09:47:47 +0200 +Subject: [PATCH] Ensure valid vehicle status + + +diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java +index 215be207e29254312bb85f259a684a7de6876aa9..e2a8e0291a5c0c00d7d6135a0d34fb8bab7b4cd5 100644 +--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java ++++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java +@@ -696,7 +696,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player { + } + } + +- if (persistVehicle && entity1 != null && entity != this && entity.hasExactlyOnePlayerPassenger()) { ++ if (persistVehicle && entity1 != null && entity != this && entity.hasExactlyOnePlayerPassenger() && !entity.isRemoved()) { // Paper - Ensure valid vehicle status + // CraftBukkit end + CompoundTag nbttagcompound1 = new CompoundTag(); + CompoundTag nbttagcompound2 = new CompoundTag(); diff --git a/patches/server/0607-Prevent-softlocked-end-exit-portal-generation.patch b/patches/server/0607-Prevent-softlocked-end-exit-portal-generation.patch new file mode 100644 index 0000000000..4c54f27f94 --- /dev/null +++ b/patches/server/0607-Prevent-softlocked-end-exit-portal-generation.patch @@ -0,0 +1,22 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Noah van der Aa <[email protected]> +Date: Mon, 30 Aug 2021 15:22:18 +0200 +Subject: [PATCH] Prevent softlocked end exit portal generation + + +diff --git a/src/main/java/net/minecraft/world/level/dimension/end/EndDragonFight.java b/src/main/java/net/minecraft/world/level/dimension/end/EndDragonFight.java +index 3d1a49d865e17a61ff74c6fe0efbd908447ee730..bc6426c04ac1fd19949d587d2b7061895db0893b 100644 +--- a/src/main/java/net/minecraft/world/level/dimension/end/EndDragonFight.java ++++ b/src/main/java/net/minecraft/world/level/dimension/end/EndDragonFight.java +@@ -469,6 +469,11 @@ public class EndDragonFight { + } + } + ++ // Paper start - Prevent "softlocked" exit portal generation ++ if (this.portalLocation.getY() <= this.level.getMinY()) { ++ this.portalLocation = this.portalLocation.atY(this.level.getMinY() + 1); ++ } ++ // Paper end - Prevent "softlocked" exit portal generation + if (worldgenendtrophy.place(FeatureConfiguration.NONE, this.level, this.level.getChunkSource().getGenerator(), RandomSource.create(), this.portalLocation)) { + int i = Mth.positiveCeilDiv(4, 16); + diff --git a/patches/server/0608-Fix-CocaoDecorator-causing-a-crash-when-trying-to-ge.patch b/patches/server/0608-Fix-CocaoDecorator-causing-a-crash-when-trying-to-ge.patch new file mode 100644 index 0000000000..a145d67c1a --- /dev/null +++ b/patches/server/0608-Fix-CocaoDecorator-causing-a-crash-when-trying-to-ge.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Shane Freeder <[email protected]> +Date: Tue, 7 Sep 2021 21:29:38 +0100 +Subject: [PATCH] Fix CocaoDecorator causing a crash when trying to generate + without logs + + +diff --git a/src/main/java/net/minecraft/world/level/levelgen/feature/treedecorators/CocoaDecorator.java b/src/main/java/net/minecraft/world/level/levelgen/feature/treedecorators/CocoaDecorator.java +index 3acae3afbfc7c15442ddde7aaf250dac5be2bde5..a6c6d0fe89af4265d0034b8d03f4fdb8d5c1a04f 100644 +--- a/src/main/java/net/minecraft/world/level/levelgen/feature/treedecorators/CocoaDecorator.java ++++ b/src/main/java/net/minecraft/world/level/levelgen/feature/treedecorators/CocoaDecorator.java +@@ -26,6 +26,7 @@ public class CocoaDecorator extends TreeDecorator { + + @Override + public void place(TreeDecorator.Context generator) { ++ if (generator.logs().isEmpty()) return; // Paper - Fix crash when trying to generate without logs + RandomSource randomSource = generator.random(); + if (!(randomSource.nextFloat() >= this.probability)) { + List<BlockPos> list = generator.logs(); diff --git a/patches/server/0609-Don-t-log-debug-logging-being-disabled.patch b/patches/server/0609-Don-t-log-debug-logging-being-disabled.patch new file mode 100644 index 0000000000..8e733b1cea --- /dev/null +++ b/patches/server/0609-Don-t-log-debug-logging-being-disabled.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Noah van der Aa <[email protected]> +Date: Tue, 14 Sep 2021 16:24:45 +0200 +Subject: [PATCH] Don't log debug logging being disabled + + +diff --git a/src/main/java/org/spigotmc/SpigotConfig.java b/src/main/java/org/spigotmc/SpigotConfig.java +index 85f3433860abd91a89961907940a807a8b190a46..4dbb109d0526afee99b9190fc256585121aac9b5 100644 +--- a/src/main/java/org/spigotmc/SpigotConfig.java ++++ b/src/main/java/org/spigotmc/SpigotConfig.java +@@ -381,7 +381,7 @@ public class SpigotConfig + Bukkit.getLogger().info( "Debug logging is enabled" ); + } else + { +- Bukkit.getLogger().info( "Debug logging is disabled" ); ++ // Bukkit.getLogger().info( "Debug logging is disabled" ); // Paper - Don't log if debug logging isn't enabled. + } + } + diff --git a/patches/server/0610-fix-various-menus-with-empty-level-accesses.patch b/patches/server/0610-fix-various-menus-with-empty-level-accesses.patch new file mode 100644 index 0000000000..efe5d1ddce --- /dev/null +++ b/patches/server/0610-fix-various-menus-with-empty-level-accesses.patch @@ -0,0 +1,23 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sun, 11 Jul 2021 12:52:56 -0700 +Subject: [PATCH] fix various menus with empty level accesses + + +diff --git a/src/main/java/net/minecraft/world/inventory/ContainerLevelAccess.java b/src/main/java/net/minecraft/world/inventory/ContainerLevelAccess.java +index c96b04f045dda384cdee9254a1765ef97e5f7f03..85e336637db8643fc5aca1dba724c9b341cbf46f 100644 +--- a/src/main/java/net/minecraft/world/inventory/ContainerLevelAccess.java ++++ b/src/main/java/net/minecraft/world/inventory/ContainerLevelAccess.java +@@ -27,6 +27,12 @@ public interface ContainerLevelAccess { + public <T> Optional<T> evaluate(BiFunction<Level, BlockPos, T> getter) { + return Optional.empty(); + } ++ // Paper start - fix menus with empty level accesses ++ @Override ++ public org.bukkit.Location getLocation() { ++ return null; ++ } ++ // Paper end - fix menus with empty level accesses + }; + + static ContainerLevelAccess create(final Level world, final BlockPos pos) { diff --git a/patches/server/0611-Preserve-overstacked-loot.patch b/patches/server/0611-Preserve-overstacked-loot.patch new file mode 100644 index 0000000000..db50d2fd8a --- /dev/null +++ b/patches/server/0611-Preserve-overstacked-loot.patch @@ -0,0 +1,27 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: lexikiq <[email protected]> +Date: Mon, 21 Jun 2021 23:21:58 -0400 +Subject: [PATCH] Preserve overstacked loot + +Preserves overstacked items in loot tables, such as shulker box drops, to prevent the items +from being deleted (as they'd overflow past the bounds of the container)-- or worse, causing +chunk bans via the large amount of NBT created by unstacking the items. + +Fixes GH-5140 and GH-4748. + +diff --git a/src/main/java/net/minecraft/world/level/storage/loot/LootTable.java b/src/main/java/net/minecraft/world/level/storage/loot/LootTable.java +index 4d4d413b8527e1a109276928611b8c857ad6f6aa..c2bded5094097f5615a2ddb0718942486ede93b5 100644 +--- a/src/main/java/net/minecraft/world/level/storage/loot/LootTable.java ++++ b/src/main/java/net/minecraft/world/level/storage/loot/LootTable.java +@@ -72,9 +72,10 @@ public class LootTable { + } + + public static Consumer<ItemStack> createStackSplitter(ServerLevel world, Consumer<ItemStack> consumer) { ++ boolean skipSplitter = world != null && !world.paperConfig().fixes.splitOverstackedLoot; // Paper - preserve overstacked items + return (itemstack) -> { + if (itemstack.isItemEnabled(world.enabledFeatures())) { +- if (itemstack.getCount() < itemstack.getMaxStackSize()) { ++ if (skipSplitter || itemstack.getCount() < itemstack.getMaxStackSize()) { // Paper - preserve overstacked items + consumer.accept(itemstack); + } else { + int i = itemstack.getCount(); diff --git a/patches/server/0612-Update-head-rotation-in-missing-places.patch b/patches/server/0612-Update-head-rotation-in-missing-places.patch new file mode 100644 index 0000000000..d9477bbb64 --- /dev/null +++ b/patches/server/0612-Update-head-rotation-in-missing-places.patch @@ -0,0 +1,29 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Owen1212055 <[email protected]> +Date: Mon, 21 Jun 2021 21:55:23 -0400 +Subject: [PATCH] Update head rotation in missing places + +In certain areas the player's head rotation could be desynced when teleported/moved. +This is because bukkit uses a separate head rotation field for yaw. +This issue only applies to players. + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index ee2625301ebb1dd601618ae43915891f3b57b7c3..9d95be758a905209eb1de9ee9d3b0f5af5b14074 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -1938,6 +1938,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + this.setXRot(Mth.clamp(pitch, -90.0F, 90.0F) % 360.0F); + this.yRotO = this.getYRot(); + this.xRotO = this.getXRot(); ++ this.setYHeadRot(yaw); // Paper - Update head rotation + } + + public void absMoveTo(double x, double y, double z) { +@@ -1980,6 +1981,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + this.setXRot(pitch); + this.setOldPosAndRot(); + this.reapplyPosition(); ++ this.setYHeadRot(yaw); // Paper - Update head rotation + } + + public final void setOldPosAndRot() { diff --git a/patches/server/0613-prevent-unintended-light-block-manipulation.patch b/patches/server/0613-prevent-unintended-light-block-manipulation.patch new file mode 100644 index 0000000000..e9851e3744 --- /dev/null +++ b/patches/server/0613-prevent-unintended-light-block-manipulation.patch @@ -0,0 +1,25 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Mon, 13 Sep 2021 18:55:45 -0700 +Subject: [PATCH] prevent unintended light block manipulation + + +diff --git a/src/main/java/net/minecraft/world/level/block/LightBlock.java b/src/main/java/net/minecraft/world/level/block/LightBlock.java +index 2be8855b2cd50e599d80e1ff5dc13d66451dff9d..9f6bb23b6021a99d4bb09d57659943cfdb4b673f 100644 +--- a/src/main/java/net/minecraft/world/level/block/LightBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/LightBlock.java +@@ -50,6 +50,14 @@ public class LightBlock extends Block implements SimpleWaterloggedBlock { + builder.add(LEVEL, WATERLOGGED); + } + ++ // Paper start - prevent unintended light block manipulation ++ @Override ++ protected InteractionResult useItemOn(ItemStack stack, BlockState state, Level world, BlockPos pos, Player player, net.minecraft.world.InteractionHand hand, BlockHitResult hit) { ++ if (player.getItemInHand(hand).getItem() != Items.LIGHT || (world instanceof final net.minecraft.server.level.ServerLevel serverLevel && !player.mayInteract(serverLevel, pos)) || !player.mayUseItemAt(pos, hit.getDirection(), player.getItemInHand(hand))) { return net.minecraft.world.InteractionResult.PASS; } // Paper - Prevent unintended light block manipulation ++ return super.useItemOn(stack, state, world, pos, player, hand, hit); ++ } ++ // Paper end - prevent unintended light block manipulation ++ + @Override + protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) { + if (!world.isClientSide && player.canUseGameMasterBlocks()) { diff --git a/patches/server/0614-Fix-CraftCriteria-defaults-map.patch b/patches/server/0614-Fix-CraftCriteria-defaults-map.patch new file mode 100644 index 0000000000..3e79bd7fb7 --- /dev/null +++ b/patches/server/0614-Fix-CraftCriteria-defaults-map.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Mon, 4 Oct 2021 22:31:51 -0700 +Subject: [PATCH] Fix CraftCriteria defaults map + + +diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java +index 441e2122f837712a21328eb7659cc9925ff9b6f8..8464531a4ee400834d25c23b1eb723f49be8689e 100644 +--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java ++++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java +@@ -54,7 +54,7 @@ public final class CraftCriteria implements Criteria { + } + + static CraftCriteria getFromNMS(Objective objective) { +- return CraftCriteria.DEFAULTS.get(objective.getCriteria().getName()); ++ return java.util.Objects.requireNonNullElseGet(CraftCriteria.DEFAULTS.get(objective.getCriteria().getName()), () -> new CraftCriteria(objective.getCriteria())); // Paper + } + + public static CraftCriteria getFromBukkit(String name) { diff --git a/patches/server/0615-Fix-upstreams-block-state-factories.patch b/patches/server/0615-Fix-upstreams-block-state-factories.patch new file mode 100644 index 0000000000..d5198201c5 --- /dev/null +++ b/patches/server/0615-Fix-upstreams-block-state-factories.patch @@ -0,0 +1,491 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Wed, 6 Oct 2021 20:50:48 -0700 +Subject: [PATCH] Fix upstreams block state factories + +Sometimes, blocks are changed and then logic is called before the associated +tile entity is removed. When this happens, the factories were relying on the +block at the position, not the tile entity. This change prioritizes using the +tile entity type to determine the block state factory and falls back on +the material type of the block at that location. + +== AT == +public net.minecraft.world.level.block.entity.BlockEntityType validBlocks + +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 8e091ac441acf88a5c8597c6d2923efa5662092e..b1ed43287d522e08a967ba751a851776351916e7 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 +@@ -370,7 +370,7 @@ public abstract class BlockEntity { + // Paper end + if (this.level == null) return null; + org.bukkit.block.Block block = this.level.getWorld().getBlockAt(this.worldPosition.getX(), this.worldPosition.getY(), this.worldPosition.getZ()); +- if (block.getType() == org.bukkit.Material.AIR) return null; ++ // if (block.getType() == org.bukkit.Material.AIR) return null; // Paper - actually get the tile entity if it still exists + org.bukkit.block.BlockState state = block.getState(useSnapshot); // Paper + if (state instanceof InventoryHolder) return (InventoryHolder) state; + return null; +diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java +index 80bd6e8a6dadb74356a9fa9aa394efbd31c49c37..fe7e3e0628783d8d1be9635b689da8a9cb46c5d7 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java +@@ -20,7 +20,7 @@ import org.bukkit.persistence.PersistentDataContainer; + import org.jetbrains.annotations.NotNull; + import org.jetbrains.annotations.Nullable; + +-public class CraftBlockEntityState<T extends BlockEntity> extends CraftBlockState implements TileState { ++public abstract class CraftBlockEntityState<T extends BlockEntity> extends CraftBlockState implements TileState { // Paper - revert upstream's revert of the block state changes + + private final T tileEntity; + private final T snapshot; +@@ -196,14 +196,10 @@ public class CraftBlockEntityState<T extends BlockEntity> extends CraftBlockStat + } + + @Override +- public CraftBlockEntityState<T> copy() { +- return new CraftBlockEntityState<>(this, null); +- } ++ public abstract CraftBlockEntityState<T> copy(); // Paper - make abstract + + @Override +- public CraftBlockEntityState<T> copy(Location location) { +- return new CraftBlockEntityState<>(this, location); +- } ++ public abstract CraftBlockEntityState<T> copy(Location location); // Paper - make abstract + + // Paper start + @Override +diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java +index 1a8dcde39a252a45046866349b848d79e1b13260..56453454cbd4b9e9270fc833f8ab38d5fa7a3763 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java +@@ -22,6 +22,7 @@ import net.minecraft.world.level.block.entity.BeehiveBlockEntity; + import net.minecraft.world.level.block.entity.BellBlockEntity; + import net.minecraft.world.level.block.entity.BlastFurnaceBlockEntity; + import net.minecraft.world.level.block.entity.BlockEntity; ++import net.minecraft.world.level.block.entity.BlockEntityType; // Paper + import net.minecraft.world.level.block.entity.BrewingStandBlockEntity; + import net.minecraft.world.level.block.entity.BrushableBlockEntity; + import net.minecraft.world.level.block.entity.CalibratedSculkSensorBlockEntity; +@@ -88,9 +89,9 @@ public final class CraftBlockStates { + private static class BlockEntityStateFactory<T extends BlockEntity, B extends CraftBlockEntityState<T>> extends BlockStateFactory<B> { + + private final BiFunction<World, T, B> blockStateConstructor; +- private final BiFunction<BlockPos, net.minecraft.world.level.block.state.BlockState, T> tileEntityConstructor; ++ private final BlockEntityType<? extends T> tileEntityConstructor; // Paper + +- protected BlockEntityStateFactory(Class<B> blockStateType, BiFunction<World, T, B> blockStateConstructor, BiFunction<BlockPos, net.minecraft.world.level.block.state.BlockState, T> tileEntityConstructor) { ++ protected BlockEntityStateFactory(Class<B> blockStateType, BiFunction<World, T, B> blockStateConstructor, BlockEntityType<? extends T> tileEntityConstructor) { // Paper + super(blockStateType); + this.blockStateConstructor = blockStateConstructor; + this.tileEntityConstructor = tileEntityConstructor; +@@ -107,7 +108,7 @@ public final class CraftBlockStates { + } + + private T createTileEntity(BlockPos blockPosition, net.minecraft.world.level.block.state.BlockState blockData) { +- return this.tileEntityConstructor.apply(blockPosition, blockData); ++ return this.tileEntityConstructor.create(blockPosition, blockData); // Paper + } + + private B createBlockState(World world, T tileEntity) { +@@ -119,233 +120,66 @@ public final class CraftBlockStates { + private static final BlockStateFactory<?> DEFAULT_FACTORY = new BlockStateFactory<CraftBlockState>(CraftBlockState.class) { + @Override + public CraftBlockState createBlockState(World world, BlockPos blockPosition, net.minecraft.world.level.block.state.BlockState blockData, BlockEntity tileEntity) { +- // SPIGOT-6754, SPIGOT-6817: Restore previous behaviour for tile entities with removed blocks (loot generation post-destroy) +- if (tileEntity != null) { +- // block with unhandled TileEntity: +- return new CraftBlockEntityState<>(world, tileEntity); +- } ++ // Paper - revert upstream's revert of the block state changes. Block entities that have already had the block type set to AIR are still valid, upstream decided to ignore them + Preconditions.checkState(tileEntity == null, "Unexpected BlockState for %s", CraftBlockType.minecraftToBukkit(blockData.getBlock())); + return new CraftBlockState(world, blockPosition, blockData); + } + }; ++ // Paper start ++ private static final Map<BlockEntityType<?>, BlockStateFactory<?>> FACTORIES_BY_BLOCK_ENTITY_TYPE = new HashMap<>(); ++ private static void register(BlockEntityType<?> type, BlockStateFactory<?> factory) { ++ FACTORIES_BY_BLOCK_ENTITY_TYPE.put(type, factory); ++ } ++ // Paper end + + static { +- register( +- Arrays.asList( +- Material.ACACIA_SIGN, +- Material.ACACIA_WALL_SIGN, +- Material.BAMBOO_SIGN, +- Material.BAMBOO_WALL_SIGN, +- Material.BIRCH_SIGN, +- Material.BIRCH_WALL_SIGN, +- Material.CHERRY_SIGN, +- Material.CHERRY_WALL_SIGN, +- Material.CRIMSON_SIGN, +- Material.CRIMSON_WALL_SIGN, +- Material.DARK_OAK_SIGN, +- Material.DARK_OAK_WALL_SIGN, +- Material.JUNGLE_SIGN, +- Material.JUNGLE_WALL_SIGN, +- Material.MANGROVE_SIGN, +- Material.MANGROVE_WALL_SIGN, +- Material.OAK_SIGN, +- Material.OAK_WALL_SIGN, +- Material.PALE_OAK_SIGN, +- Material.PALE_OAK_WALL_SIGN, +- Material.SPRUCE_SIGN, +- Material.SPRUCE_WALL_SIGN, +- Material.WARPED_SIGN, +- Material.WARPED_WALL_SIGN +- ), CraftSign.class, CraftSign::new, SignBlockEntity::new +- ); +- +- register( +- Arrays.asList( +- Material.ACACIA_HANGING_SIGN, +- Material.ACACIA_WALL_HANGING_SIGN, +- Material.BAMBOO_HANGING_SIGN, +- Material.BAMBOO_WALL_HANGING_SIGN, +- Material.BIRCH_HANGING_SIGN, +- Material.BIRCH_WALL_HANGING_SIGN, +- Material.CHERRY_HANGING_SIGN, +- Material.CHERRY_WALL_HANGING_SIGN, +- Material.CRIMSON_HANGING_SIGN, +- Material.CRIMSON_WALL_HANGING_SIGN, +- Material.DARK_OAK_HANGING_SIGN, +- Material.DARK_OAK_WALL_HANGING_SIGN, +- Material.JUNGLE_HANGING_SIGN, +- Material.JUNGLE_WALL_HANGING_SIGN, +- Material.MANGROVE_HANGING_SIGN, +- Material.MANGROVE_WALL_HANGING_SIGN, +- Material.OAK_HANGING_SIGN, +- Material.OAK_WALL_HANGING_SIGN, +- Material.PALE_OAK_HANGING_SIGN, +- Material.PALE_OAK_WALL_HANGING_SIGN, +- Material.SPRUCE_HANGING_SIGN, +- Material.SPRUCE_WALL_HANGING_SIGN, +- Material.WARPED_HANGING_SIGN, +- Material.WARPED_WALL_HANGING_SIGN +- ), CraftHangingSign.class, CraftHangingSign::new, HangingSignBlockEntity::new +- ); +- +- register( +- Arrays.asList( +- Material.CREEPER_HEAD, +- Material.CREEPER_WALL_HEAD, +- Material.DRAGON_HEAD, +- Material.DRAGON_WALL_HEAD, +- Material.PIGLIN_HEAD, +- Material.PIGLIN_WALL_HEAD, +- Material.PLAYER_HEAD, +- Material.PLAYER_WALL_HEAD, +- Material.SKELETON_SKULL, +- Material.SKELETON_WALL_SKULL, +- Material.WITHER_SKELETON_SKULL, +- Material.WITHER_SKELETON_WALL_SKULL, +- Material.ZOMBIE_HEAD, +- Material.ZOMBIE_WALL_HEAD +- ), CraftSkull.class, CraftSkull::new, SkullBlockEntity::new +- ); +- +- register( +- Arrays.asList( +- Material.COMMAND_BLOCK, +- Material.REPEATING_COMMAND_BLOCK, +- Material.CHAIN_COMMAND_BLOCK +- ), CraftCommandBlock.class, CraftCommandBlock::new, CommandBlockEntity::new +- ); +- +- register( +- Arrays.asList( +- Material.BLACK_BANNER, +- Material.BLACK_WALL_BANNER, +- Material.BLUE_BANNER, +- Material.BLUE_WALL_BANNER, +- Material.BROWN_BANNER, +- Material.BROWN_WALL_BANNER, +- Material.CYAN_BANNER, +- Material.CYAN_WALL_BANNER, +- Material.GRAY_BANNER, +- Material.GRAY_WALL_BANNER, +- Material.GREEN_BANNER, +- Material.GREEN_WALL_BANNER, +- Material.LIGHT_BLUE_BANNER, +- Material.LIGHT_BLUE_WALL_BANNER, +- Material.LIGHT_GRAY_BANNER, +- Material.LIGHT_GRAY_WALL_BANNER, +- Material.LIME_BANNER, +- Material.LIME_WALL_BANNER, +- Material.MAGENTA_BANNER, +- Material.MAGENTA_WALL_BANNER, +- Material.ORANGE_BANNER, +- Material.ORANGE_WALL_BANNER, +- Material.PINK_BANNER, +- Material.PINK_WALL_BANNER, +- Material.PURPLE_BANNER, +- Material.PURPLE_WALL_BANNER, +- Material.RED_BANNER, +- Material.RED_WALL_BANNER, +- Material.WHITE_BANNER, +- Material.WHITE_WALL_BANNER, +- Material.YELLOW_BANNER, +- Material.YELLOW_WALL_BANNER +- ), CraftBanner.class, CraftBanner::new, BannerBlockEntity::new +- ); +- +- register( +- Arrays.asList( +- Material.SHULKER_BOX, +- Material.WHITE_SHULKER_BOX, +- Material.ORANGE_SHULKER_BOX, +- Material.MAGENTA_SHULKER_BOX, +- Material.LIGHT_BLUE_SHULKER_BOX, +- Material.YELLOW_SHULKER_BOX, +- Material.LIME_SHULKER_BOX, +- Material.PINK_SHULKER_BOX, +- Material.GRAY_SHULKER_BOX, +- Material.LIGHT_GRAY_SHULKER_BOX, +- Material.CYAN_SHULKER_BOX, +- Material.PURPLE_SHULKER_BOX, +- Material.BLUE_SHULKER_BOX, +- Material.BROWN_SHULKER_BOX, +- Material.GREEN_SHULKER_BOX, +- Material.RED_SHULKER_BOX, +- Material.BLACK_SHULKER_BOX +- ), CraftShulkerBox.class, CraftShulkerBox::new, ShulkerBoxBlockEntity::new +- ); +- +- register( +- Arrays.asList( +- Material.BLACK_BED, +- Material.BLUE_BED, +- Material.BROWN_BED, +- Material.CYAN_BED, +- Material.GRAY_BED, +- Material.GREEN_BED, +- Material.LIGHT_BLUE_BED, +- Material.LIGHT_GRAY_BED, +- Material.LIME_BED, +- Material.MAGENTA_BED, +- Material.ORANGE_BED, +- Material.PINK_BED, +- Material.PURPLE_BED, +- Material.RED_BED, +- Material.WHITE_BED, +- Material.YELLOW_BED +- ), CraftBed.class, CraftBed::new, BedBlockEntity::new +- ); +- +- register( +- Arrays.asList( +- Material.BEEHIVE, +- Material.BEE_NEST +- ), CraftBeehive.class, CraftBeehive::new, BeehiveBlockEntity::new +- ); +- +- register( +- Arrays.asList( +- Material.CAMPFIRE, +- Material.SOUL_CAMPFIRE +- ), CraftCampfire.class, CraftCampfire::new, CampfireBlockEntity::new +- ); +- +- register(Material.BARREL, CraftBarrel.class, CraftBarrel::new, BarrelBlockEntity::new); +- register(Material.BEACON, CraftBeacon.class, CraftBeacon::new, BeaconBlockEntity::new); +- register(Material.BELL, CraftBell.class, CraftBell::new, BellBlockEntity::new); +- register(Material.BLAST_FURNACE, CraftBlastFurnace.class, CraftBlastFurnace::new, BlastFurnaceBlockEntity::new); +- register(Material.BREWING_STAND, CraftBrewingStand.class, CraftBrewingStand::new, BrewingStandBlockEntity::new); +- register(Material.CHEST, CraftChest.class, CraftChest::new, ChestBlockEntity::new); +- register(Material.CHISELED_BOOKSHELF, CraftChiseledBookshelf.class, CraftChiseledBookshelf::new, ChiseledBookShelfBlockEntity::new); +- register(Material.COMPARATOR, CraftComparator.class, CraftComparator::new, ComparatorBlockEntity::new); +- register(Material.CONDUIT, CraftConduit.class, CraftConduit::new, ConduitBlockEntity::new); +- register(Material.CREAKING_HEART, CraftCreakingHeart.class, CraftCreakingHeart::new, CreakingHeartBlockEntity::new); +- register(Material.DAYLIGHT_DETECTOR, CraftDaylightDetector.class, CraftDaylightDetector::new, DaylightDetectorBlockEntity::new); +- register(Material.DECORATED_POT, CraftDecoratedPot.class, CraftDecoratedPot::new, DecoratedPotBlockEntity::new); +- register(Material.DISPENSER, CraftDispenser.class, CraftDispenser::new, DispenserBlockEntity::new); +- register(Material.DROPPER, CraftDropper.class, CraftDropper::new, DropperBlockEntity::new); +- register(Material.ENCHANTING_TABLE, CraftEnchantingTable.class, CraftEnchantingTable::new, EnchantingTableBlockEntity::new); +- register(Material.ENDER_CHEST, CraftEnderChest.class, CraftEnderChest::new, EnderChestBlockEntity::new); +- register(Material.END_GATEWAY, CraftEndGateway.class, CraftEndGateway::new, TheEndGatewayBlockEntity::new); +- register(Material.END_PORTAL, CraftEndPortal.class, CraftEndPortal::new, TheEndPortalBlockEntity::new); +- register(Material.FURNACE, CraftFurnaceFurnace.class, CraftFurnaceFurnace::new, FurnaceBlockEntity::new); +- register(Material.HOPPER, CraftHopper.class, CraftHopper::new, HopperBlockEntity::new); +- register(Material.JIGSAW, CraftJigsaw.class, CraftJigsaw::new, JigsawBlockEntity::new); +- register(Material.JUKEBOX, CraftJukebox.class, CraftJukebox::new, JukeboxBlockEntity::new); +- register(Material.LECTERN, CraftLectern.class, CraftLectern::new, LecternBlockEntity::new); +- register(Material.MOVING_PISTON, CraftMovingPiston.class, CraftMovingPiston::new, PistonMovingBlockEntity::new); +- register(Material.SCULK_CATALYST, CraftSculkCatalyst.class, CraftSculkCatalyst::new, SculkCatalystBlockEntity::new); +- register(Material.CALIBRATED_SCULK_SENSOR, CraftCalibratedSculkSensor.class, CraftCalibratedSculkSensor::new, CalibratedSculkSensorBlockEntity::new); +- register(Material.SCULK_SENSOR, CraftSculkSensor.class, CraftSculkSensor::new, SculkSensorBlockEntity::new); +- register(Material.SCULK_SHRIEKER, CraftSculkShrieker.class, CraftSculkShrieker::new, SculkShriekerBlockEntity::new); +- register(Material.SMOKER, CraftSmoker.class, CraftSmoker::new, SmokerBlockEntity::new); +- register(Material.SPAWNER, CraftCreatureSpawner.class, CraftCreatureSpawner::new, SpawnerBlockEntity::new); +- register(Material.STRUCTURE_BLOCK, CraftStructureBlock.class, CraftStructureBlock::new, StructureBlockEntity::new); +- register(Material.SUSPICIOUS_SAND, CraftSuspiciousSand.class, CraftSuspiciousSand::new, BrushableBlockEntity::new); +- register(Material.SUSPICIOUS_GRAVEL, CraftBrushableBlock.class, CraftBrushableBlock::new, BrushableBlockEntity::new); +- register(Material.TRAPPED_CHEST, CraftChest.class, CraftChest::new, TrappedChestBlockEntity::new); +- register(Material.CRAFTER, CraftCrafter.class, CraftCrafter::new, CrafterBlockEntity::new); +- register(Material.TRIAL_SPAWNER, CraftTrialSpawner.class, CraftTrialSpawner::new, TrialSpawnerBlockEntity::new); +- register(Material.VAULT, CraftVault.class, CraftVault::new, VaultBlockEntity::new); ++ // Paper start - simplify ++ register(BlockEntityType.SIGN, CraftSign.class, CraftSign::new); ++ register(BlockEntityType.HANGING_SIGN, CraftHangingSign.class, CraftHangingSign::new); ++ register(BlockEntityType.SKULL, CraftSkull.class, CraftSkull::new); ++ register(BlockEntityType.COMMAND_BLOCK, CraftCommandBlock.class, CraftCommandBlock::new); ++ register(BlockEntityType.BANNER, CraftBanner.class, CraftBanner::new); ++ register(BlockEntityType.SHULKER_BOX, CraftShulkerBox.class, CraftShulkerBox::new); ++ register(BlockEntityType.BED, CraftBed.class, CraftBed::new); ++ register(BlockEntityType.BEEHIVE, CraftBeehive.class, CraftBeehive::new); ++ register(BlockEntityType.CAMPFIRE, CraftCampfire.class, CraftCampfire::new); ++ register(BlockEntityType.BARREL, CraftBarrel.class, CraftBarrel::new); ++ register(BlockEntityType.BEACON, CraftBeacon.class, CraftBeacon::new); ++ register(BlockEntityType.BELL, CraftBell.class, CraftBell::new); ++ register(BlockEntityType.BLAST_FURNACE, CraftBlastFurnace.class, CraftBlastFurnace::new); ++ register(BlockEntityType.BREWING_STAND, CraftBrewingStand.class, CraftBrewingStand::new); ++ register(BlockEntityType.CHEST, CraftChest.class, CraftChest::new); ++ register(BlockEntityType.CHISELED_BOOKSHELF, CraftChiseledBookshelf.class, CraftChiseledBookshelf::new); ++ register(BlockEntityType.COMPARATOR, CraftComparator.class, CraftComparator::new); ++ register(BlockEntityType.CONDUIT, CraftConduit.class, CraftConduit::new); ++ register(BlockEntityType.CREAKING_HEART, CraftCreakingHeart.class, CraftCreakingHeart::new); ++ register(BlockEntityType.DAYLIGHT_DETECTOR, CraftDaylightDetector.class, CraftDaylightDetector::new); ++ register(BlockEntityType.DECORATED_POT, CraftDecoratedPot.class, CraftDecoratedPot::new); ++ register(BlockEntityType.DISPENSER, CraftDispenser.class, CraftDispenser::new); ++ register(BlockEntityType.DROPPER, CraftDropper.class, CraftDropper::new); ++ register(BlockEntityType.ENCHANTING_TABLE, CraftEnchantingTable.class, CraftEnchantingTable::new); ++ register(BlockEntityType.ENDER_CHEST, CraftEnderChest.class, CraftEnderChest::new); ++ register(BlockEntityType.END_GATEWAY, CraftEndGateway.class, CraftEndGateway::new); ++ register(BlockEntityType.END_PORTAL, CraftEndPortal.class, CraftEndPortal::new); ++ register(BlockEntityType.FURNACE, CraftFurnaceFurnace.class, CraftFurnaceFurnace::new); ++ register(BlockEntityType.HOPPER, CraftHopper.class, CraftHopper::new); ++ register(BlockEntityType.JIGSAW, CraftJigsaw.class, CraftJigsaw::new); ++ register(BlockEntityType.JUKEBOX, CraftJukebox.class, CraftJukebox::new); ++ register(BlockEntityType.LECTERN, CraftLectern.class, CraftLectern::new); ++ register(BlockEntityType.PISTON, CraftMovingPiston.class, CraftMovingPiston::new); ++ register(BlockEntityType.SCULK_CATALYST, CraftSculkCatalyst.class, CraftSculkCatalyst::new); ++ register(BlockEntityType.SCULK_SENSOR, CraftSculkSensor.class, CraftSculkSensor::new); ++ register(BlockEntityType.SCULK_SHRIEKER, CraftSculkShrieker.class, CraftSculkShrieker::new); ++ register(BlockEntityType.CALIBRATED_SCULK_SENSOR, CraftCalibratedSculkSensor.class, CraftCalibratedSculkSensor::new); ++ register(BlockEntityType.SMOKER, CraftSmoker.class, CraftSmoker::new); ++ register(BlockEntityType.MOB_SPAWNER, CraftCreatureSpawner.class, CraftCreatureSpawner::new); ++ register(BlockEntityType.STRUCTURE_BLOCK, CraftStructureBlock.class, CraftStructureBlock::new); ++ register(BlockEntityType.BRUSHABLE_BLOCK, CraftBrushableBlock.class, CraftBrushableBlock::new); // note: spigot still uses CraftSuspiciousSand impl for that block type ++ register(BlockEntityType.TRAPPED_CHEST, CraftChest.class, CraftChest::new); ++ register(BlockEntityType.CRAFTER, CraftCrafter.class, CraftCrafter::new); ++ register(BlockEntityType.TRIAL_SPAWNER, CraftTrialSpawner.class, CraftTrialSpawner::new); ++ register(BlockEntityType.VAULT, CraftVault.class, CraftVault::new); ++ // Paper end + } + + private static void register(Material blockType, BlockStateFactory<?> factory) { +@@ -353,30 +187,33 @@ public final class CraftBlockStates { + } + + private static <T extends BlockEntity, B extends CraftBlockEntityState<T>> void register( +- Material blockType, ++ net.minecraft.world.level.block.entity.BlockEntityType<? extends T> blockEntityType, // Paper + Class<B> blockStateType, +- BiFunction<World, T, B> blockStateConstructor, +- BiFunction<BlockPos, net.minecraft.world.level.block.state.BlockState, T> tileEntityConstructor ++ BiFunction<World, T, B> blockStateConstructor // Paper + ) { +- CraftBlockStates.register(Collections.singletonList(blockType), blockStateType, blockStateConstructor, tileEntityConstructor); +- } +- +- private static <T extends BlockEntity, B extends CraftBlockEntityState<T>> void register( +- List<Material> blockTypes, +- Class<B> blockStateType, +- BiFunction<World, T, B> blockStateConstructor, +- BiFunction<BlockPos, net.minecraft.world.level.block.state.BlockState, T> tileEntityConstructor +- ) { +- BlockStateFactory<B> factory = new BlockEntityStateFactory<>(blockStateType, blockStateConstructor, tileEntityConstructor); +- for (Material blockType : blockTypes) { +- CraftBlockStates.register(blockType, factory); ++ // Paper start ++ BlockStateFactory<B> factory = new BlockEntityStateFactory<>(blockStateType, blockStateConstructor, blockEntityType); // Paper ++ for (net.minecraft.world.level.block.Block block : blockEntityType.validBlocks) { ++ CraftBlockStates.register(CraftBlockType.minecraftToBukkit(block), factory); + } ++ CraftBlockStates.register(blockEntityType, factory); ++ // Paper end + } + + private static BlockStateFactory<?> getFactory(Material material) { + return CraftBlockStates.FACTORIES.getOrDefault(material, CraftBlockStates.DEFAULT_FACTORY); + } + ++ // Paper start ++ private static BlockStateFactory<?> getFactory(Material material, BlockEntityType<?> type) { ++ if (type != null) { ++ return CraftBlockStates.FACTORIES_BY_BLOCK_ENTITY_TYPE.getOrDefault(type, getFactory(material)); ++ } else { ++ return getFactory(material); ++ } ++ } ++ // Paper end ++ + public static Class<? extends CraftBlockState> getBlockStateType(Material material) { + Preconditions.checkNotNull(material, "material is null"); + return CraftBlockStates.getFactory(material).blockStateType; +@@ -392,6 +229,13 @@ public final class CraftBlockStates { + return null; + } + ++ // Paper start ++ public static Class<? extends CraftBlockState> getBlockStateType(BlockEntityType<?> blockEntityType) { ++ Preconditions.checkNotNull(blockEntityType, "blockEntityType is null"); ++ return CraftBlockStates.getFactory(null, blockEntityType).blockStateType; ++ } ++ // Paper end ++ + public static BlockState getBlockState(Block block) { + // Paper start + return CraftBlockStates.getBlockState(block, true); +@@ -459,7 +303,7 @@ public final class CraftBlockStates { + if (world != null && tileEntity == null && CraftBlockStates.isTileEntityOptional(material)) { + factory = CraftBlockStates.DEFAULT_FACTORY; + } else { +- factory = CraftBlockStates.getFactory(material); ++ factory = CraftBlockStates.getFactory(material, tileEntity != null ? tileEntity.getType() : null); // Paper + } + return factory.createBlockState(world, blockPosition, blockData, tileEntity); + } +@@ -478,6 +322,14 @@ public final class CraftBlockStates { + return new CraftBlockState(CraftBlock.at(world, pos), flag); + } + ++ // Paper start ++ @Nullable ++ public static BlockEntityType<?> getBlockEntityType(final Material material) { ++ final BlockStateFactory<?> factory = org.bukkit.craftbukkit.block.CraftBlockStates.FACTORIES.get(material); ++ return factory instanceof final BlockEntityStateFactory<?,?> blockEntityStateFactory ? blockEntityStateFactory.tileEntityConstructor : null; ++ } ++ // Paper end ++ + private CraftBlockStates() { + } + } +diff --git a/src/test/java/org/bukkit/craftbukkit/block/BlockStateTest.java b/src/test/java/org/bukkit/craftbukkit/block/BlockStateTest.java +index c032daa6957df2ad8b621379e415ad925f5cc162..a9810c88e05ebc1af60c051faa45e50ee183924f 100644 +--- a/src/test/java/org/bukkit/craftbukkit/block/BlockStateTest.java ++++ b/src/test/java/org/bukkit/craftbukkit/block/BlockStateTest.java +@@ -7,6 +7,7 @@ import net.minecraft.core.registries.BuiltInRegistries; + import net.minecraft.world.level.block.Block; + import net.minecraft.world.level.block.EntityBlock; + import net.minecraft.world.level.block.entity.BlockEntity; ++import net.minecraft.world.level.block.entity.BlockEntityType; + import org.bukkit.Material; + import org.bukkit.support.environment.AllFeatures; + import org.junit.jupiter.api.Test; +@@ -42,4 +43,13 @@ public class BlockStateTest { + } + } + } ++ ++ // Paper start ++ @Test ++ public void testBlockEntityTypes() { ++ for (var blockEntityType : BuiltInRegistries.BLOCK_ENTITY_TYPE) { ++ org.junit.jupiter.api.Assertions.assertNotNull(CraftBlockStates.getBlockStateType(blockEntityType)); ++ } ++ } ++ // Paper end + } diff --git a/patches/server/0616-Configurable-feature-seeds.patch b/patches/server/0616-Configurable-feature-seeds.patch new file mode 100644 index 0000000000..9a0502f3a7 --- /dev/null +++ b/patches/server/0616-Configurable-feature-seeds.patch @@ -0,0 +1,27 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Nassim Jahnke <[email protected]> +Date: Tue, 31 Aug 2021 17:05:27 +0200 +Subject: [PATCH] Configurable feature seeds + +Co-authored-by: Thonk <[email protected]> + +diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java +index 416b1afcbab093f45900a4d55708609ba5a7de7a..fde17b4e2607fc443a33aea3a631aae6ccb71e2c 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java ++++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java +@@ -437,7 +437,14 @@ public abstract class ChunkGenerator { + return (String) optional.orElseGet(placedfeature::toString); + }; + +- seededrandom.setFeatureSeed(i, l1, l); ++ // Paper start - Configurable feature seeds; change populationSeed used in random ++ long featurePopulationSeed = i; ++ final long configFeatureSeed = generatoraccessseed.getMinecraftWorld().paperConfig().featureSeeds.features.getLong(placedfeature.feature()); ++ if (configFeatureSeed != -1) { ++ featurePopulationSeed = seededrandom.setDecorationSeed(configFeatureSeed, blockposition.getX(), blockposition.getZ()); // See seededrandom.setDecorationSeed from above ++ } ++ seededrandom.setFeatureSeed(featurePopulationSeed, l1, l); ++ // Paper end - Configurable feature seeds + + try { + generatoraccessseed.setCurrentlyGenerating(supplier1); diff --git a/patches/server/0617-Add-root-admin-user-detection.patch b/patches/server/0617-Add-root-admin-user-detection.patch new file mode 100644 index 0000000000..df739cc33c --- /dev/null +++ b/patches/server/0617-Add-root-admin-user-detection.patch @@ -0,0 +1,62 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: egg82 <[email protected]> +Date: Sat, 11 Sep 2021 22:55:14 +0200 +Subject: [PATCH] Add root/admin user detection + +This patch detects whether or not the server is currently executing as a privileged user and spits out a warning. +The warning serves as a sort-of PSA for newer server admins who don't understand the risks of running as root. +We've seen plenty of bad/malicious plugins hit markets, and there's been a few close-calls with exploits in the past. +Hopefully this helps mitigate some potential damage to servers, even if it is just a warning. + +Co-authored-by: Noah van der Aa <[email protected]> + +diff --git a/src/main/java/io/papermc/paper/util/ServerEnvironment.java b/src/main/java/io/papermc/paper/util/ServerEnvironment.java +new file mode 100644 +index 0000000000000000000000000000000000000000..68098dfe716e93aafcca4d8d5b5a81d8648b3654 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/util/ServerEnvironment.java +@@ -0,0 +1,23 @@ ++package io.papermc.paper.util; ++ ++import com.sun.security.auth.module.NTSystem; ++import com.sun.security.auth.module.UnixSystem; ++import java.util.Set; ++import org.apache.commons.lang.SystemUtils; ++ ++public class ServerEnvironment { ++ private static final boolean RUNNING_AS_ROOT_OR_ADMIN; ++ private static final String WINDOWS_HIGH_INTEGRITY_LEVEL = "S-1-16-12288"; ++ ++ static { ++ if (SystemUtils.IS_OS_WINDOWS) { ++ RUNNING_AS_ROOT_OR_ADMIN = Set.of(new NTSystem().getGroupIDs()).contains(WINDOWS_HIGH_INTEGRITY_LEVEL); ++ } else { ++ RUNNING_AS_ROOT_OR_ADMIN = new UnixSystem().getUid() == 0; ++ } ++ } ++ ++ public static boolean userIsRootOrAdmin() { ++ return RUNNING_AS_ROOT_OR_ADMIN; ++ } ++} +diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +index 21d6f728d6ecd35a05933e9406a386c36135a456..ac7fc2497b860a143ef08498f5f477a373a29be7 100644 +--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java ++++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +@@ -193,6 +193,16 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface + DedicatedServer.LOGGER.warn("To start the server with more ram, launch it as \"java -Xmx1024M -Xms1024M -jar minecraft_server.jar\""); + } + ++ // Paper start - detect running as root ++ if (io.papermc.paper.util.ServerEnvironment.userIsRootOrAdmin()) { ++ DedicatedServer.LOGGER.warn("****************************"); ++ DedicatedServer.LOGGER.warn("YOU ARE RUNNING THIS SERVER AS AN ADMINISTRATIVE OR ROOT USER. THIS IS NOT ADVISED."); ++ DedicatedServer.LOGGER.warn("YOU ARE OPENING YOURSELF UP TO POTENTIAL RISKS WHEN DOING THIS."); ++ DedicatedServer.LOGGER.warn("FOR MORE INFORMATION, SEE https://madelinemiller.dev/blog/root-minecraft-server/"); ++ DedicatedServer.LOGGER.warn("****************************"); ++ } ++ // Paper end - detect running as root ++ + DedicatedServer.LOGGER.info("Loading properties"); + DedicatedServerProperties dedicatedserverproperties = this.settings.getProperties(); + diff --git a/patches/server/0618-don-t-attempt-to-teleport-dead-entities.patch b/patches/server/0618-don-t-attempt-to-teleport-dead-entities.patch new file mode 100644 index 0000000000..6dc9c02ca4 --- /dev/null +++ b/patches/server/0618-don-t-attempt-to-teleport-dead-entities.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: sulu5890 <[email protected]> +Date: Sun, 24 Oct 2021 22:48:14 -0500 +Subject: [PATCH] don't attempt to teleport dead entities + + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 9d95be758a905209eb1de9ee9d3b0f5af5b14074..6e0b22b81d25db7a8f98bbf0fc6b4b69f4a678fd 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -718,7 +718,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + // CraftBukkit start + public void postTick() { + // No clean way to break out of ticking once the entity has been copied to a new world, so instead we move the portalling later in the tick cycle +- if (!(this instanceof ServerPlayer)) { ++ if (!(this instanceof ServerPlayer) && this.isAlive()) { // Paper - don't attempt to teleport dead entities + this.handlePortal(); + } + } diff --git a/patches/server/0619-Prevent-excessive-velocity-through-repeated-crits.patch b/patches/server/0619-Prevent-excessive-velocity-through-repeated-crits.patch new file mode 100644 index 0000000000..f0f62f6f2a --- /dev/null +++ b/patches/server/0619-Prevent-excessive-velocity-through-repeated-crits.patch @@ -0,0 +1,41 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Nassim Jahnke <[email protected]> +Date: Thu, 25 Nov 2021 10:25:09 +0100 +Subject: [PATCH] Prevent excessive velocity through repeated crits + + +diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java +index fb3cec13dd9e426904ce28a8755e6cc57296a42e..dfa1e3548f9b48c20f28712ecb06310fe9c856f5 100644 +--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java ++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java +@@ -2875,17 +2875,29 @@ public abstract class LivingEntity extends Entity implements Attackable { + return this.hasEffect(MobEffects.JUMP) ? 0.1F * ((float) this.getEffect(MobEffects.JUMP).getAmplifier() + 1.0F) : 0.0F; + } + ++ protected long lastJumpTime = 0L; // Paper - Prevent excessive velocity through repeated crits + @VisibleForTesting + public void jumpFromGround() { + float f = this.getJumpPower(); + + if (f > 1.0E-5F) { + Vec3 vec3d = this.getDeltaMovement(); ++ // Paper start - Prevent excessive velocity through repeated crits ++ long time = System.nanoTime(); ++ boolean canCrit = true; ++ if (this instanceof net.minecraft.world.entity.player.Player) { ++ canCrit = false; ++ if (time - this.lastJumpTime > (long)(0.250e9)) { ++ this.lastJumpTime = time; ++ canCrit = true; ++ } ++ } ++ // Paper end - Prevent excessive velocity through repeated crits + + this.setDeltaMovement(vec3d.x, Math.max((double) f, vec3d.y), vec3d.z); + if (this.isSprinting()) { + float f1 = this.getYRot() * 0.017453292F; +- ++ if (canCrit) // Paper - Prevent excessive velocity through repeated crits + this.addDeltaMovement(new Vec3((double) (-Mth.sin(f1)) * 0.2D, 0.0D, (double) Mth.cos(f1) * 0.2D)); + } + diff --git a/patches/server/0620-Remove-client-side-code-using-deprecated-for-removal.patch b/patches/server/0620-Remove-client-side-code-using-deprecated-for-removal.patch new file mode 100644 index 0000000000..4a04ac055a --- /dev/null +++ b/patches/server/0620-Remove-client-side-code-using-deprecated-for-removal.patch @@ -0,0 +1,30 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jason Penilla <[email protected]> +Date: Fri, 26 Nov 2021 15:09:58 -0800 +Subject: [PATCH] Remove client-side code using deprecated for removal + AccessController + +Fixes warnings on build + +diff --git a/src/main/java/net/minecraft/Util.java b/src/main/java/net/minecraft/Util.java +index e78f40f7f10a2ed0675b3cb9a2c5730dbc0141db..cc5e2710d3edeaf60284ed95c33999c701d0678a 100644 +--- a/src/main/java/net/minecraft/Util.java ++++ b/src/main/java/net/minecraft/Util.java +@@ -1087,16 +1087,7 @@ public class Util { + } + + public void openUri(URI uri) { +- try { +- Process process = AccessController.doPrivileged( +- (PrivilegedExceptionAction<Process>)(() -> Runtime.getRuntime().exec(this.getOpenUriArguments(uri))) +- ); +- process.getInputStream().close(); +- process.getErrorStream().close(); +- process.getOutputStream().close(); +- } catch (IOException | PrivilegedActionException var3) { +- Util.LOGGER.error("Couldn't open location '{}'", uri, var3); +- } ++ throw new IllegalStateException("This method is not useful on dedicated servers."); // Paper - Fix warnings on build by removing client-only code + } + + public void openFile(File file) { diff --git a/patches/server/0621-Fix-Spigot-growth-modifiers.patch b/patches/server/0621-Fix-Spigot-growth-modifiers.patch new file mode 100644 index 0000000000..4e7f192262 --- /dev/null +++ b/patches/server/0621-Fix-Spigot-growth-modifiers.patch @@ -0,0 +1,141 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jason Penilla <[email protected]> +Date: Fri, 3 Dec 2021 17:09:24 -0800 +Subject: [PATCH] Fix Spigot growth modifiers + +Fixes kelp modifier changing growth for other crops +Also add growth modifiers for glow berries, mangrove propagules, +torchflower crops and pitcher plant crops +Also fix above-mentioned modifiers from having the reverse effect + +Co-authored-by: Jake Potrebic <[email protected]> +Co-authored-by: Noah van der Aa <[email protected]> +Co-authored-by: Lulu13022002 <[email protected]> + +diff --git a/src/main/java/net/minecraft/world/level/block/CaveVinesBlock.java b/src/main/java/net/minecraft/world/level/block/CaveVinesBlock.java +index b4618129397fa5dbec3a6438fc20c57d91d1d1dd..81e572783157926383dd9baa58d30f5419c1616f 100644 +--- a/src/main/java/net/minecraft/world/level/block/CaveVinesBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/CaveVinesBlock.java +@@ -50,9 +50,18 @@ public class CaveVinesBlock extends GrowingPlantHeadBlock implements CaveVines { + return to.setValue(BERRIES, from.getValue(BERRIES)); + } + ++ // Paper start - Fix Spigot growth modifiers ++ @Override ++ protected BlockState getGrowIntoState(BlockState state, RandomSource random, @javax.annotation.Nullable Level level) { ++ final boolean value = random.nextFloat() < (level != null ? (0.11F * (level.spigotConfig.glowBerryModifier / 100.0F)) : 0.11F); ++ return (BlockState) super.getGrowIntoState(state, random).setValue(CaveVinesBlock.BERRIES, value); ++ } ++ // Paper end - Fix Spigot growth modifiers ++ + @Override + protected BlockState getGrowIntoState(BlockState state, RandomSource random) { +- return super.getGrowIntoState(state, random).setValue(BERRIES, Boolean.valueOf(random.nextFloat() < 0.11F)); ++ // Paper start - Fix Spigot growth modifiers ++ return this.getGrowIntoState(state, random, null); + } + + @Override +diff --git a/src/main/java/net/minecraft/world/level/block/CropBlock.java b/src/main/java/net/minecraft/world/level/block/CropBlock.java +index 6fe896079c0ae622976c2055f8d13cda5ed3ea3d..1967ff3fcb94988be85985c4754904f0077de066 100644 +--- a/src/main/java/net/minecraft/world/level/block/CropBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/CropBlock.java +@@ -91,6 +91,10 @@ public class CropBlock extends BushBlock implements BonemealableBlock { + modifier = world.spigotConfig.carrotModifier; + } else if (this == Blocks.POTATOES) { + modifier = world.spigotConfig.potatoModifier; ++ // Paper start - Fix Spigot growth modifiers ++ } else if (this == Blocks.TORCHFLOWER_CROP) { ++ modifier = world.spigotConfig.torchFlowerModifier; ++ // Paper end - Fix Spigot growth modifiers + } else { + modifier = world.spigotConfig.wheatModifier; + } +diff --git a/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java b/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java +index c527f2d8f594d711d047a2a149efe37caaeb0308..9b424d7661fedf8ee1eb9f3167c62e563f04d4d1 100644 +--- a/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/GrowingPlantHeadBlock.java +@@ -60,12 +60,18 @@ public abstract class GrowingPlantHeadBlock extends GrowingPlantBlock implements + BlockPos blockposition1 = pos.relative(this.growthDirection); + + if (this.canGrowInto(world.getBlockState(blockposition1))) { +- org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, pos, blockposition1, this.getGrowIntoState(state, world.random)); // CraftBukkit ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, pos, blockposition1, this.getGrowIntoState(state, world.random, world)); // CraftBukkit // Paper - Fix Spigot growth modifiers + } + } + + } + ++ // Paper start - Fix Spigot growth modifiers ++ protected BlockState getGrowIntoState(BlockState state, RandomSource random, @javax.annotation.Nullable Level level) { ++ return this.getGrowIntoState(state, random); ++ } ++ // Paper end - Fix Spigot growth modifiers ++ + protected BlockState getGrowIntoState(BlockState state, RandomSource random) { + return (BlockState) state.cycle(GrowingPlantHeadBlock.AGE); + } +diff --git a/src/main/java/net/minecraft/world/level/block/MangrovePropaguleBlock.java b/src/main/java/net/minecraft/world/level/block/MangrovePropaguleBlock.java +index 10e2b80b0b6010982258548b5084b9591177b558..6b18db68c8b95480992199126c6063ea728d2314 100644 +--- a/src/main/java/net/minecraft/world/level/block/MangrovePropaguleBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/MangrovePropaguleBlock.java +@@ -123,7 +123,7 @@ public class MangrovePropaguleBlock extends SaplingBlock implements SimpleWaterl + @Override + protected void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { + if (!isHanging(state)) { +- if (random.nextInt(7) == 0) { ++ if (random.nextFloat() < (world.spigotConfig.saplingModifier / (100.0F * 7))) { // Paper - Fix Spigot growth modifiers + this.advanceTree(world, pos, state, random); + } + } else { +diff --git a/src/main/java/net/minecraft/world/level/block/PitcherCropBlock.java b/src/main/java/net/minecraft/world/level/block/PitcherCropBlock.java +index 972d8833127090c01d620cab10b3eca3d3601710..ea6135edc76c06b7cd55930b620dfb05f939e4f6 100644 +--- a/src/main/java/net/minecraft/world/level/block/PitcherCropBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/PitcherCropBlock.java +@@ -132,7 +132,7 @@ public class PitcherCropBlock extends DoublePlantBlock implements BonemealableBl + @Override + public void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { + float f = CropBlock.getGrowthSpeed(this, world, pos); +- boolean bl = random.nextInt((int)(25.0F / f) + 1) == 0; ++ boolean bl = random.nextFloat() < (world.spigotConfig.pitcherPlantModifier / (100.0F * (Math.floor(25.0F / f) + 1))); // Paper - Fix Spigot growth modifiers + if (bl) { + this.grow(world, state, pos, 1); + } +diff --git a/src/main/java/org/spigotmc/SpigotWorldConfig.java b/src/main/java/org/spigotmc/SpigotWorldConfig.java +index 491dd55b2870093184a606efabd251c68cc24719..e76f96a5c48d1eda2f9bbb3e11dd79f23f9ab75c 100644 +--- a/src/main/java/org/spigotmc/SpigotWorldConfig.java ++++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java +@@ -96,6 +96,7 @@ public class SpigotWorldConfig + public int beetrootModifier; + public int carrotModifier; + public int potatoModifier; ++ public int torchFlowerModifier; // Paper + public int wheatModifier; + public int wartModifier; + public int vineModifier; +@@ -106,6 +107,8 @@ public class SpigotWorldConfig + public int twistingVinesModifier; + public int weepingVinesModifier; + public int caveVinesModifier; ++ public int glowBerryModifier; // Paper ++ public int pitcherPlantModifier; // Paper + private int getAndValidateGrowth(String crop) + { + int modifier = this.getInt( "growth." + crop.toLowerCase(java.util.Locale.ENGLISH) + "-modifier", 100 ); +@@ -129,6 +132,7 @@ public class SpigotWorldConfig + this.beetrootModifier = this.getAndValidateGrowth( "Beetroot" ); + this.carrotModifier = this.getAndValidateGrowth( "Carrot" ); + this.potatoModifier = this.getAndValidateGrowth( "Potato" ); ++ this.torchFlowerModifier = this.getAndValidateGrowth("TorchFlower"); // Paper + this.wheatModifier = this.getAndValidateGrowth( "Wheat" ); + this.wartModifier = this.getAndValidateGrowth( "NetherWart" ); + this.vineModifier = this.getAndValidateGrowth( "Vine" ); +@@ -139,6 +143,8 @@ public class SpigotWorldConfig + this.twistingVinesModifier = this.getAndValidateGrowth( "TwistingVines" ); + this.weepingVinesModifier = this.getAndValidateGrowth( "WeepingVines" ); + this.caveVinesModifier = this.getAndValidateGrowth( "CaveVines" ); ++ this.glowBerryModifier = this.getAndValidateGrowth("GlowBerry"); // Paper ++ this.pitcherPlantModifier = this.getAndValidateGrowth("PitcherPlant"); // Paper + } + + public double itemMerge; diff --git a/patches/server/0622-Prevent-ContainerOpenersCounter-openCount-from-going.patch b/patches/server/0622-Prevent-ContainerOpenersCounter-openCount-from-going.patch new file mode 100644 index 0000000000..b017de75d1 --- /dev/null +++ b/patches/server/0622-Prevent-ContainerOpenersCounter-openCount-from-going.patch @@ -0,0 +1,18 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Shane Freeder <[email protected]> +Date: Tue, 30 Nov 2021 05:30:35 +0000 +Subject: [PATCH] Prevent ContainerOpenersCounter openCount from going negative + + +diff --git a/src/main/java/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java b/src/main/java/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java +index 43e4e893f0e7eed1959e6ccfa43c39ff00f083b3..86dce6796f92a5b0ae2b1bd837267c4e3f6754d0 100644 +--- a/src/main/java/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java ++++ b/src/main/java/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java +@@ -69,6 +69,7 @@ public abstract class ContainerOpenersCounter { + + public void decrementOpeners(Player player, Level world, BlockPos pos, BlockState state) { + int oldPower = Math.max(0, Math.min(15, this.openCount)); // CraftBukkit - Get power before new viewer is added ++ if (this.openCount == 0) return; // Paper - Prevent ContainerOpenersCounter openCount from going negative + int i = this.openCount--; + + // CraftBukkit start - Call redstone event diff --git a/patches/server/0623-Add-PlayerItemFrameChangeEvent.patch b/patches/server/0623-Add-PlayerItemFrameChangeEvent.patch new file mode 100644 index 0000000000..b7e908ab6e --- /dev/null +++ b/patches/server/0623-Add-PlayerItemFrameChangeEvent.patch @@ -0,0 +1,61 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: SamB440 <[email protected]> +Date: Mon, 15 Nov 2021 18:10:10 +0000 +Subject: [PATCH] Add PlayerItemFrameChangeEvent + + +diff --git a/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java b/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java +index c0deabf9d86d086fbd8cbaac5a02badf5c01c870..30af4cbb17148c247a46c0346419d6c838dbc9d2 100644 +--- a/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java ++++ b/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java +@@ -1,6 +1,7 @@ + package net.minecraft.world.entity.decoration; + + import javax.annotation.Nullable; ++import io.papermc.paper.event.player.PlayerItemFrameChangeEvent; // Paper - Add PlayerItemFrameChangeEvent + import net.minecraft.core.BlockPos; + import net.minecraft.core.Direction; + import net.minecraft.core.component.DataComponents; +@@ -166,6 +167,13 @@ public class ItemFrame extends HangingEntity { + return true; + } + // CraftBukkit end ++ // Paper start - Add PlayerItemFrameChangeEvent ++ if (source.getEntity() instanceof Player player) { ++ var event = new PlayerItemFrameChangeEvent((org.bukkit.entity.Player) player.getBukkitEntity(), (org.bukkit.entity.ItemFrame) this.getBukkitEntity(), this.getItem().asBukkitCopy(), PlayerItemFrameChangeEvent.ItemFrameChangeAction.REMOVE); ++ if (!event.callEvent()) return true; // return true here because you aren't cancelling the damage, just the change ++ this.setItem(ItemStack.fromBukkitCopy(event.getItemStack()), false); ++ } ++ // Paper end - Add PlayerItemFrameChangeEvent + this.dropItem(world, source.getEntity(), false); + this.gameEvent(GameEvent.BLOCK_CHANGE, source.getEntity()); + this.playSound(this.getRemoveItemSound(), 1.0F, 1.0F); +@@ -403,7 +411,13 @@ public class ItemFrame extends HangingEntity { + if (worldmap != null && worldmap.isTrackedCountOverLimit(256)) { + return InteractionResult.FAIL; + } else { +- this.setItem(itemstack); ++ // Paper start - Add PlayerItemFrameChangeEvent ++ PlayerItemFrameChangeEvent event = new PlayerItemFrameChangeEvent((org.bukkit.entity.Player) player.getBukkitEntity(), (org.bukkit.entity.ItemFrame) this.getBukkitEntity(), itemstack.asBukkitCopy(), PlayerItemFrameChangeEvent.ItemFrameChangeAction.PLACE); ++ if (!event.callEvent()) { ++ return InteractionResult.FAIL; ++ } ++ this.setItem(ItemStack.fromBukkitCopy(event.getItemStack())); ++ // Paper end - Add PlayerItemFrameChangeEvent + this.gameEvent(GameEvent.BLOCK_CHANGE, player); + itemstack.consume(1, player); + return InteractionResult.SUCCESS; +@@ -412,6 +426,13 @@ public class ItemFrame extends HangingEntity { + return InteractionResult.PASS; + } + } else { ++ // Paper start - Add PlayerItemFrameChangeEvent ++ PlayerItemFrameChangeEvent event = new PlayerItemFrameChangeEvent((org.bukkit.entity.Player) player.getBukkitEntity(), (org.bukkit.entity.ItemFrame) this.getBukkitEntity(), this.getItem().asBukkitCopy(), PlayerItemFrameChangeEvent.ItemFrameChangeAction.ROTATE); ++ if (!event.callEvent()) { ++ return InteractionResult.FAIL; ++ } ++ setItem(ItemStack.fromBukkitCopy(event.getItemStack()), false, false); ++ // Paper end - Add PlayerItemFrameChangeEvent + this.playSound(this.getRotateItemSound(), 1.0F, 1.0F); + this.setRotation(this.getRotation() + 1); + this.gameEvent(GameEvent.BLOCK_CHANGE, player); diff --git a/patches/server/0624-Optimize-HashMapPalette.patch b/patches/server/0624-Optimize-HashMapPalette.patch new file mode 100644 index 0000000000..a490eb0c20 --- /dev/null +++ b/patches/server/0624-Optimize-HashMapPalette.patch @@ -0,0 +1,57 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: stonar96 <[email protected]> +Date: Sun, 17 Jan 2021 01:11:36 +0100 +Subject: [PATCH] Optimize HashMapPalette + +HashMapPalette uses an instance of CrudeIncrementalIntIdentityHashBiMap +internally. A Palette has a preset maximum size = 1 << bits. +CrudeIncrementalIntIdentityHashBiMap has an initial size but is +automatically resized. The CrudeIncrementalIntIdentityHashBiMap is created +with the maximum size in the constructor of HashMapPalette, with the aim +that it doesn't need to be resized anymore. However, there are two things +that I think Mojang hasn't considered here: +1) The CrudeIncrementalIntIdentityHashBiMap is resized, when its initial +size is reached and not the next time, when a further object is added. +2) HashMapPalette adds objects (unnecessarily) before checking if the +initial size of CrudeIncrementalIntIdentityHashBiMap is reached. +This means to actually avoid resize operations in +CrudeIncrementalIntIdentityHashBiMap, one has to add 2 to the initial size +or add 1 and check the size before adding objects. This commit implements +the second approach. Note that this isn't only an optimization but also +makes async reads of Palettes fail-safe. An async read while the +CrudeIncrementalIntIdentityHashBiMap is resized is fatal and can even lead +to corrupted data. This is also something that Anti-Xray is currently +relying on. + +diff --git a/src/main/java/net/minecraft/world/level/chunk/HashMapPalette.java b/src/main/java/net/minecraft/world/level/chunk/HashMapPalette.java +index ac673cb38755852eef37e915f157f6a702117306..98dbeaf8bde15940e5b5d5d1f13fd4bb32f0a10d 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/HashMapPalette.java ++++ b/src/main/java/net/minecraft/world/level/chunk/HashMapPalette.java +@@ -20,7 +20,7 @@ public class HashMapPalette<T> implements Palette<T> { + } + + public HashMapPalette(IdMap<T> idList, int indexBits, PaletteResize<T> listener) { +- this(idList, indexBits, listener, CrudeIncrementalIntIdentityHashBiMap.create(1 << indexBits)); ++ this(idList, indexBits, listener, CrudeIncrementalIntIdentityHashBiMap.create((1 << indexBits) + 1)); // Paper - Perf: Avoid unnecessary resize operation in CrudeIncrementalIntIdentityHashBiMap + } + + private HashMapPalette(IdMap<T> idList, int indexBits, PaletteResize<T> listener, CrudeIncrementalIntIdentityHashBiMap<T> map) { +@@ -38,10 +38,16 @@ public class HashMapPalette<T> implements Palette<T> { + public int idFor(T object) { + int i = this.values.getId(object); + if (i == -1) { +- i = this.values.add(object); +- if (i >= 1 << this.bits) { ++ // Paper start - Perf: Avoid unnecessary resize operation in CrudeIncrementalIntIdentityHashBiMap and optimize ++ // We use size() instead of the result from add(K) ++ // This avoids adding another object unnecessarily ++ // Without this change, + 2 would be required in the constructor ++ if (this.values.size() >= 1 << this.bits) { + i = this.resizeHandler.onResize(this.bits + 1, object); ++ } else { ++ i = this.values.add(object); + } ++ // Paper end - Perf: Avoid unnecessary resize operation in CrudeIncrementalIntIdentityHashBiMap and optimize + } + + return i; diff --git a/patches/server/0625-Fix-ChunkSnapshot-isSectionEmpty-int-and-optimize-Pa.patch b/patches/server/0625-Fix-ChunkSnapshot-isSectionEmpty-int-and-optimize-Pa.patch new file mode 100644 index 0000000000..cde811d346 --- /dev/null +++ b/patches/server/0625-Fix-ChunkSnapshot-isSectionEmpty-int-and-optimize-Pa.patch @@ -0,0 +1,44 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jason Penilla <[email protected]> +Date: Thu, 9 Dec 2021 00:08:11 -0800 +Subject: [PATCH] Fix ChunkSnapshot#isSectionEmpty(int) and optimize + PalettedContainer copying by not using codecs + + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java +index be44b03527bd17344f5d835ba9d0b47e4b55d45f..08956b81b9a3e5caf3adce6699149491ff190d90 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java +@@ -338,14 +338,17 @@ public class CraftChunk implements Chunk { + PalettedContainerRO<Holder<net.minecraft.world.level.biome.Biome>>[] biome = (includeBiome || includeBiomeTempRain) ? new PalettedContainer[cs.length] : null; + + Registry<net.minecraft.world.level.biome.Biome> iregistry = this.worldServer.registryAccess().lookupOrThrow(Registries.BIOME); +- Codec<PalettedContainerRO<Holder<net.minecraft.world.level.biome.Biome>>> biomeCodec = PalettedContainer.codecRO(iregistry.asHolderIdMap(), iregistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, iregistry.getOrThrow(Biomes.PLAINS)); + + for (int i = 0; i < cs.length; i++) { +- CompoundTag data = new CompoundTag(); + +- data.put("block_states", SerializableChunkData.BLOCK_STATE_CODEC.encodeStart(NbtOps.INSTANCE, cs[i].getStates()).getOrThrow()); +- sectionBlockIDs[i] = SerializableChunkData.BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, data.getCompound("block_states")).getOrThrow(SerializableChunkData.ChunkReadException::new); +- sectionEmpty[i] = cs[i].hasOnlyAir(); ++ // Paper start - Fix ChunkSnapshot#isSectionEmpty(int); and remove codec usage ++ sectionEmpty[i] = cs[i].hasOnlyAir(); // fix sectionEmpty array not being filled ++ if (!sectionEmpty[i]) { ++ sectionBlockIDs[i] = cs[i].getStates().copy(); // use copy instead of round tripping with codecs ++ } else { ++ sectionBlockIDs[i] = CraftChunk.emptyBlockIDs; // use cached instance for empty block sections ++ } ++ // Paper end - Fix ChunkSnapshot#isSectionEmpty(int) + + LevelLightEngine lightengine = this.worldServer.getLightEngine(); + DataLayer skyLightArray = lightengine.getLayerListener(LightLayer.SKY).getDataLayerData(SectionPos.of(this.x, chunk.getSectionYFromSectionIndex(i), this.z)); // SPIGOT-7498: Convert section index +@@ -364,8 +367,7 @@ public class CraftChunk implements Chunk { + } + + if (biome != null) { +- data.put("biomes", biomeCodec.encodeStart(NbtOps.INSTANCE, cs[i].getBiomes()).getOrThrow()); +- biome[i] = biomeCodec.parse(NbtOps.INSTANCE, data.getCompound("biomes")).getOrThrow(SerializableChunkData.ChunkReadException::new); ++ biome[i] = ((PalettedContainer<Holder<net.minecraft.world.level.biome.Biome>>) cs[i].getBiomes()).copy(); // Paper - Perf: use copy instead of round tripping with codecs + } + } + diff --git a/patches/server/0626-Add-more-Campfire-API.patch b/patches/server/0626-Add-more-Campfire-API.patch new file mode 100644 index 0000000000..c490c151c3 --- /dev/null +++ b/patches/server/0626-Add-more-Campfire-API.patch @@ -0,0 +1,111 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: LemonCaramel <[email protected]> +Date: Fri, 16 Jul 2021 00:39:03 +0900 +Subject: [PATCH] Add more Campfire API + + +diff --git a/src/main/java/net/minecraft/world/level/block/entity/CampfireBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/CampfireBlockEntity.java +index 7fa1aea7942a1bc4d9779a9f8ab020ccd5566923..94072a9b65f69dfc3337907f8573081989467662 100644 +--- a/src/main/java/net/minecraft/world/level/block/entity/CampfireBlockEntity.java ++++ b/src/main/java/net/minecraft/world/level/block/entity/CampfireBlockEntity.java +@@ -46,12 +46,14 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable { + private final NonNullList<ItemStack> items; + public final int[] cookingProgress; + public final int[] cookingTime; ++ public final boolean[] stopCooking; // Paper - Add more Campfire API + + public CampfireBlockEntity(BlockPos pos, BlockState state) { + super(BlockEntityType.CAMPFIRE, pos, state); + this.items = NonNullList.withSize(4, ItemStack.EMPTY); + this.cookingProgress = new int[4]; + this.cookingTime = new int[4]; ++ this.stopCooking = new boolean[4]; // Paper - Add more Campfire API + } + + public static void cookTick(ServerLevel world, BlockPos pos, BlockState state, CampfireBlockEntity blockEntity, RecipeManager.CachedCheck<SingleRecipeInput, CampfireCookingRecipe> recipeMatchGetter) { +@@ -62,7 +64,9 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable { + + if (!itemstack.isEmpty()) { + flag = true; ++ if (!blockEntity.stopCooking[i]) { // Paper - Add more Campfire API + int j = blockEntity.cookingProgress[i]++; ++ } // Paper - Add more Campfire API + + if (blockEntity.cookingProgress[i] >= blockEntity.cookingTime[i]) { + SingleRecipeInput singlerecipeinput = new SingleRecipeInput(itemstack); +@@ -175,6 +179,16 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable { + System.arraycopy(aint, 0, this.cookingTime, 0, Math.min(this.cookingTime.length, aint.length)); + } + ++ // Paper start - Add more Campfire API ++ if (nbt.contains("Paper.StopCooking", org.bukkit.craftbukkit.util.CraftMagicNumbers.NBT.TAG_BYTE_ARRAY)) { ++ byte[] abyte = nbt.getByteArray("Paper.StopCooking"); ++ boolean[] cookingState = new boolean[4]; ++ for (int index = 0; index < abyte.length; index++) { ++ cookingState[index] = abyte[index] == 1; ++ } ++ System.arraycopy(cookingState, 0, this.stopCooking, 0, Math.min(this.stopCooking.length, abyte.length)); ++ } ++ // Paper end - Add more Campfire API + } + + @Override +@@ -183,6 +197,13 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable { + ContainerHelper.saveAllItems(nbt, this.items, true, registries); + nbt.putIntArray("CookingTimes", this.cookingProgress); + nbt.putIntArray("CookingTotalTimes", this.cookingTime); ++ // Paper start - Add more Campfire API ++ byte[] cookingState = new byte[4]; ++ for (int index = 0; index < cookingState.length; index++) { ++ cookingState[index] = (byte) (this.stopCooking[index] ? 1 : 0); ++ } ++ nbt.putByteArray("Paper.StopCooking", cookingState); ++ // Paper end - Add more Campfire API + } + + @Override +diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftCampfire.java b/src/main/java/org/bukkit/craftbukkit/block/CraftCampfire.java +index 4850cbf2c326f1155e04a204abed2d200c02342d..a776bba2ec51c6aecce98a3abceb2c235522d99d 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/CraftCampfire.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/CraftCampfire.java +@@ -62,4 +62,40 @@ public class CraftCampfire extends CraftBlockEntityState<CampfireBlockEntity> im + public CraftCampfire copy(Location location) { + return new CraftCampfire(this, location); + } ++ ++ // Paper start ++ @Override ++ public void stopCooking() { ++ for (int i = 0; i < getSnapshot().stopCooking.length; ++i) ++ this.stopCooking(i); ++ } ++ ++ @Override ++ public void startCooking() { ++ for (int i = 0; i < getSnapshot().stopCooking.length; ++i) ++ this.startCooking(i); ++ } ++ ++ @Override ++ public boolean stopCooking(int index) { ++ org.apache.commons.lang.Validate.isTrue(-1 < index && index < 4, "Slot index must be between 0 (incl) to 3 (incl)"); ++ boolean previous = this.isCookingDisabled(index); ++ getSnapshot().stopCooking[index] = true; ++ return previous; ++ } ++ ++ @Override ++ public boolean startCooking(int index) { ++ org.apache.commons.lang.Validate.isTrue(-1 < index && index < 4, "Slot index must be between 0 (incl) to 3 (incl)"); ++ boolean previous = this.isCookingDisabled(index); ++ getSnapshot().stopCooking[index] = false; ++ return previous; ++ } ++ ++ @Override ++ public boolean isCookingDisabled(int index) { ++ org.apache.commons.lang.Validate.isTrue(-1 < index && index < 4, "Slot index must be between 0 (incl) to 3 (incl)"); ++ return getSnapshot().stopCooking[index]; ++ } ++ // Paper end + } diff --git a/patches/server/0627-Forward-CraftEntity-in-teleport-command.patch b/patches/server/0627-Forward-CraftEntity-in-teleport-command.patch new file mode 100644 index 0000000000..ea5a8eee4a --- /dev/null +++ b/patches/server/0627-Forward-CraftEntity-in-teleport-command.patch @@ -0,0 +1,35 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sat, 4 Dec 2021 17:04:47 -0800 +Subject: [PATCH] Forward CraftEntity in teleport command + + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 6e0b22b81d25db7a8f98bbf0fc6b4b69f4a678fd..17c8a369e5a09276a3918dfb2bb004441e35a8e0 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -3511,6 +3511,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + + public void restoreFrom(Entity original) { ++ // Paper start - Forward CraftEntity in teleport command ++ CraftEntity bukkitEntity = original.bukkitEntity; ++ if (bukkitEntity != null) { ++ bukkitEntity.setHandle(this); ++ this.bukkitEntity = bukkitEntity; ++ } ++ // Paper end - Forward CraftEntity in teleport command + CompoundTag nbttagcompound = original.saveWithoutId(new CompoundTag()); + + nbttagcompound.remove("Dimension"); +@@ -3648,8 +3655,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + entity.restoreFrom(this); + this.removeAfterChangingDimensions(); + // CraftBukkit start - Forward the CraftEntity to the new entity +- this.getBukkitEntity().setHandle(entity); +- entity.bukkitEntity = this.getBukkitEntity(); ++ //this.getBukkitEntity().setHandle(entity); ++ //entity.bukkitEntity = this.getBukkitEntity(); // Paper - forward CraftEntity in teleport command; moved to Entity#restoreFrom + // CraftBukkit end + entity.teleportSetPosition(PositionMoveRotation.of(teleportTarget), teleportTarget.relatives()); + if (this.inWorld) world.addDuringTeleport(entity); // CraftBukkit - Don't spawn the new entity if the current entity isn't spawned diff --git a/patches/server/0628-Improve-scoreboard-entries.patch b/patches/server/0628-Improve-scoreboard-entries.patch new file mode 100644 index 0000000000..8acac86ce3 --- /dev/null +++ b/patches/server/0628-Improve-scoreboard-entries.patch @@ -0,0 +1,88 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Thu, 4 Nov 2021 12:31:24 -0700 +Subject: [PATCH] Improve scoreboard entries + + +diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java +index da1e4496d78a2c1b258ff8bb316414cb8a662ba2..b36e5574c10e6d70a399e2ac0704fd4f43dbb444 100644 +--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java ++++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java +@@ -144,6 +144,15 @@ final class CraftObjective extends CraftScoreboardComponent implements Objective + return new CraftScore(this, CraftScoreboard.getScoreHolder(entry)); + } + ++ // Paper start ++ @Override ++ public Score getScoreFor(org.bukkit.entity.Entity entity) throws IllegalArgumentException, IllegalStateException { ++ Preconditions.checkArgument(entity != null, "Entity cannot be null"); ++ this.checkState(); ++ return new CraftScore(this, ((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle()); ++ } ++ // Paper end ++ + @Override + public void unregister() { + CraftScoreboard scoreboard = this.checkState(); +diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java +index c650fc3712de01184509a03f1d1b388859e163d7..253574890a9ed23d38a84680ba1eb221dc72b310 100644 +--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java ++++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java +@@ -235,6 +235,26 @@ public final class CraftScoreboard implements org.bukkit.scoreboard.Scoreboard { + this.board.setDisplayObjective(CraftScoreboardTranslations.fromBukkitSlot(slot), null); + } + ++ // Paper start ++ @Override ++ public ImmutableSet<Score> getScoresFor(org.bukkit.entity.Entity entity) throws IllegalArgumentException { ++ Preconditions.checkArgument(entity != null, "Entity cannot be null"); ++ return this.getScores(((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle()); ++ } ++ ++ @Override ++ public void resetScoresFor(org.bukkit.entity.Entity entity) throws IllegalArgumentException { ++ Preconditions.checkArgument(entity != null, "Entity cannot be null"); ++ this.resetScores(((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle()); ++ } ++ ++ @Override ++ public Team getEntityTeam(org.bukkit.entity.Entity entity) throws IllegalArgumentException { ++ Preconditions.checkArgument(entity != null, "Entity cannot be null"); ++ return this.getEntryTeam(((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().getScoreboardName()); ++ } ++ // Paper end ++ + // CraftBukkit method + public Scoreboard getHandle() { + return this.board; +diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java +index 7900adb0b158bc17dd792dd082c338547bc1aa0a..27219bf2f16aed64c78623d44c3cc84aa9f47065 100644 +--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java ++++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java +@@ -304,6 +304,26 @@ final class CraftTeam extends CraftScoreboardComponent implements Team { + } + } + ++ // Paper start ++ @Override ++ public void addEntity(org.bukkit.entity.Entity entity) throws IllegalStateException, IllegalArgumentException { ++ Preconditions.checkArgument(entity != null, "Entity cannot be null"); ++ this.addEntry(((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().getScoreboardName()); ++ } ++ ++ @Override ++ public boolean removeEntity(org.bukkit.entity.Entity entity) throws IllegalStateException, IllegalArgumentException { ++ Preconditions.checkArgument(entity != null, "Entity cannot be null"); ++ return this.removeEntry(((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().getScoreboardName()); ++ } ++ ++ @Override ++ public boolean hasEntity(org.bukkit.entity.Entity entity) throws IllegalStateException, IllegalArgumentException { ++ Preconditions.checkArgument(entity != null, "Entity cannot be null"); ++ return this.hasEntry(((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().getScoreboardName()); ++ } ++ // Paper end ++ + public static Visibility bukkitToNotch(NameTagVisibility visibility) { + switch (visibility) { + case ALWAYS: diff --git a/patches/server/0629-Entity-powdered-snow-API.patch b/patches/server/0629-Entity-powdered-snow-API.patch new file mode 100644 index 0000000000..acfaef73e3 --- /dev/null +++ b/patches/server/0629-Entity-powdered-snow-API.patch @@ -0,0 +1,42 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sun, 24 Oct 2021 20:58:43 -0700 +Subject: [PATCH] Entity powdered snow API + +== AT == +public net.minecraft.world.entity.monster.Skeleton inPowderSnowTime + +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +index b13c947d2cfe085017b30cb0f8340dd64f338914..442b5f13e976dd63bf1dccc12eb8c3f16314c581 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +@@ -1095,6 +1095,13 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { + } + // Paper end - raw entity serialization API + ++ // Paper start - entity powdered snow API ++ @Override ++ public boolean isInPowderedSnow() { ++ return getHandle().isInPowderSnow || getHandle().wasInPowderSnow; // depending on the location in the entity "tick" either could be needed. ++ } ++ // Paper end - entity powdered snow API ++ + // Paper start - missing entity api + @Override + public boolean isInvisible() { // Paper - moved up from LivingEntity +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftSkeleton.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftSkeleton.java +index a0ea54181de6c6685deef265cbe9f66aabbca42b..6f98da9be6aef35e3b5c940188b872459a383c8e 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftSkeleton.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftSkeleton.java +@@ -45,4 +45,11 @@ public class CraftSkeleton extends CraftAbstractSkeleton implements Skeleton { + public SkeletonType getSkeletonType() { + return SkeletonType.NORMAL; + } ++ ++ // Paper start ++ @Override ++ public int inPowderedSnowTime() { ++ return getHandle().inPowderSnowTime; ++ } ++ // Paper end + } diff --git a/patches/server/0630-Add-API-for-item-entity-health.patch b/patches/server/0630-Add-API-for-item-entity-health.patch new file mode 100644 index 0000000000..cd2757b544 --- /dev/null +++ b/patches/server/0630-Add-API-for-item-entity-health.patch @@ -0,0 +1,34 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sat, 28 Aug 2021 09:00:45 -0700 +Subject: [PATCH] Add API for item entity health + +== AT == +public net.minecraft.world.entity.item.ItemEntity health + +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java +index 4a15c3786edbfeae3367c0b20fb6aee11d62aea6..1a291dd8a287db30e71dcb315599fc4b038764c4 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java +@@ -98,6 +98,21 @@ public class CraftItem extends CraftEntity implements Item { + public void setWillAge(boolean willAge) { + this.getHandle().age = willAge ? 0 : NO_AGE_TIME; + } ++ ++ @Override ++ public int getHealth() { ++ return this.getHandle().health; ++ } ++ ++ @Override ++ public void setHealth(int health) { ++ if (health <= 0) { ++ this.getHandle().getItem().onDestroyed(this.getHandle()); ++ this.getHandle().discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.PLUGIN); ++ } else { ++ this.getHandle().health = health; ++ } ++ } + // Paper end + + @Override diff --git a/patches/server/0631-Configurable-max-block-light-for-monster-spawning.patch b/patches/server/0631-Configurable-max-block-light-for-monster-spawning.patch new file mode 100644 index 0000000000..8c19486a5f --- /dev/null +++ b/patches/server/0631-Configurable-max-block-light-for-monster-spawning.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Nassim Jahnke <[email protected]> +Date: Thu, 16 Dec 2021 09:40:39 +0100 +Subject: [PATCH] Configurable max block light for monster spawning + + +diff --git a/src/main/java/net/minecraft/world/entity/monster/Monster.java b/src/main/java/net/minecraft/world/entity/monster/Monster.java +index c49853f25bdf1fbc7ec9700d421c6ddccabae05f..e2de074bbe7bab0e5a7aecc1fae4c5914a203dd4 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/Monster.java ++++ b/src/main/java/net/minecraft/world/entity/monster/Monster.java +@@ -92,7 +92,7 @@ public abstract class Monster extends PathfinderMob implements Enemy { + return false; + } else { + DimensionType dimensionType = world.dimensionType(); +- int i = dimensionType.monsterSpawnBlockLightLimit(); ++ int i = world.getLevel().paperConfig().entities.spawning.monsterSpawnMaxLightLevel.or(dimensionType.monsterSpawnBlockLightLimit()); // Paper - Configurable max block light for monster spawning + if (i < 15 && world.getBrightness(LightLayer.BLOCK, pos) > i) { + return false; + } else { diff --git a/patches/server/0632-Fix-sticky-pistons-and-BlockPistonRetractEvent.patch b/patches/server/0632-Fix-sticky-pistons-and-BlockPistonRetractEvent.patch new file mode 100644 index 0000000000..b239d74e25 --- /dev/null +++ b/patches/server/0632-Fix-sticky-pistons-and-BlockPistonRetractEvent.patch @@ -0,0 +1,85 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Wed, 22 Dec 2021 09:51:48 -0800 +Subject: [PATCH] Fix sticky pistons and BlockPistonRetractEvent + +There is an explicit check in the handling code for empty pistons that +prevents sticky pistons from firing the event. However when we look back +at the history we see that this check was originally added so that ONLY +sticky pistons would fire the retract event. I'm not sure why. +https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/commits/1092acbddf07edfa4100bc6824504ac75088e913 + +Over the course of several updates, the meaning of that field appears to +have changed from "is NOT sticky" to "is sticky". So now its having the +opposite effect. Only normal pistons fire the retraction event. And like +all things in CB, it's just been carried around since. + +If we are to believe the history, the correct fix for this issue is to +flip it so it only fires for sticky pistons, but that puts us in a +bind. It's already firing for non-sticky pistons, changing it now would +likely result in breakage. Furthermore, there is little documentation as +to WHY that was ever intended to be the case. + +Instead we opt to remove the check entirely so that the event fires for +all piston types. + +Co-authored-by: Zach Brown <[email protected]> +Co-authored-by: Madeline Miller <[email protected]> + +diff --git a/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java b/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java +index 4973d75b26880e39d42b5ef533896f43a1f07cba..e841fccb8f298ef692677583b468869f56dc722c 100644 +--- a/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java +@@ -163,15 +163,15 @@ public class PistonBaseBlock extends DirectionalBlock { + } + + // CraftBukkit start +- if (!this.isSticky) { +- org.bukkit.block.Block block = world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()); +- BlockPistonRetractEvent event = new BlockPistonRetractEvent(block, ImmutableList.<org.bukkit.block.Block>of(), CraftBlock.notchToBlockFace(enumdirection)); +- world.getCraftServer().getPluginManager().callEvent(event); +- +- if (event.isCancelled()) { +- return; +- } +- } ++ // if (!this.isSticky) { // Paper - Fix sticky pistons and BlockPistonRetractEvent; Move further down ++ // org.bukkit.block.Block block = world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()); ++ // BlockPistonRetractEvent event = new BlockPistonRetractEvent(block, ImmutableList.<org.bukkit.block.Block>of(), CraftBlock.notchToBlockFace(enumdirection)); ++ // world.getCraftServer().getPluginManager().callEvent(event); ++ // ++ // if (event.isCancelled()) { ++ // return; ++ // } ++ // } + // PAIL: checkME - what happened to setTypeAndData? + // CraftBukkit end + world.blockEvent(pos, this, b0, enumdirection.get3DDataValue()); +@@ -248,6 +248,13 @@ public class PistonBaseBlock extends DirectionalBlock { + + BlockState iblockdata2 = (BlockState) ((BlockState) Blocks.MOVING_PISTON.defaultBlockState().setValue(MovingPistonBlock.FACING, enumdirection)).setValue(MovingPistonBlock.TYPE, this.isSticky ? PistonType.STICKY : PistonType.DEFAULT); + ++ // Paper start - Fix sticky pistons and BlockPistonRetractEvent; Move empty piston retract call to fix multiple event fires ++ if (!this.isSticky) { ++ if (!new BlockPistonRetractEvent(CraftBlock.at(world, pos), java.util.Collections.emptyList(), CraftBlock.notchToBlockFace(enumdirection)).callEvent()) { ++ return false; ++ } ++ } ++ // Paper end - Fix sticky pistons and BlockPistonRetractEvent + world.setBlock(pos, iblockdata2, 20); + world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(pos, iblockdata2, (BlockState) this.defaultBlockState().setValue(PistonBaseBlock.FACING, Direction.from3DDataValue(data & 7)), enumdirection, false, true)); + world.blockUpdated(pos, iblockdata2.getBlock()); +@@ -274,6 +281,13 @@ public class PistonBaseBlock extends DirectionalBlock { + if (type == 1 && !iblockdata3.isAir() && PistonBaseBlock.isPushable(iblockdata3, world, blockposition1, enumdirection.getOpposite(), false, enumdirection) && (iblockdata3.getPistonPushReaction() == PushReaction.NORMAL || iblockdata3.is(Blocks.PISTON) || iblockdata3.is(Blocks.STICKY_PISTON))) { + this.moveBlocks(world, pos, enumdirection, false); + } else { ++ // Paper start - Fix sticky pistons and BlockPistonRetractEvent; fire BlockPistonRetractEvent for sticky pistons retracting nothing (air) ++ if (type == TRIGGER_CONTRACT && iblockdata2.isAir()) { ++ if (!new BlockPistonRetractEvent(CraftBlock.at(world, pos), java.util.Collections.emptyList(), CraftBlock.notchToBlockFace(enumdirection)).callEvent()) { ++ return false; ++ } ++ } ++ // Paper end - Fix sticky pistons and BlockPistonRetractEvent + world.removeBlock(pos.relative(enumdirection), false); + } + } diff --git a/patches/server/0633-Expose-isFuel-and-canSmelt-methods-to-FurnaceInvento.patch b/patches/server/0633-Expose-isFuel-and-canSmelt-methods-to-FurnaceInvento.patch new file mode 100644 index 0000000000..cfaa295706 --- /dev/null +++ b/patches/server/0633-Expose-isFuel-and-canSmelt-methods-to-FurnaceInvento.patch @@ -0,0 +1,32 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: BillyGalbreath <[email protected]> +Date: Thu, 23 Dec 2021 15:32:50 -0600 +Subject: [PATCH] Expose isFuel and canSmelt methods to FurnaceInventory + + +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryFurnace.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryFurnace.java +index 29a8cd7667860c4598a556e6ef3af39c731683db..33c970b467675429ad952925830ed334632fd3b6 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryFurnace.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryFurnace.java +@@ -40,6 +40,21 @@ public class CraftInventoryFurnace extends CraftInventory implements FurnaceInve + this.setItem(0, stack); + } + ++ // Paper start ++ @Override ++ public boolean isFuel(ItemStack stack) { ++ net.minecraft.server.level.ServerLevel world = ((org.bukkit.craftbukkit.CraftWorld) org.bukkit.Bukkit.getWorlds().get(0)).getHandle(); ++ return stack != null && !stack.getType().isEmpty() && world.fuelValues().isFuel(CraftItemStack.asNMSCopy(stack)); ++ } ++ ++ @Override ++ public boolean canSmelt(ItemStack stack) { ++ // data packs are always loaded in the main world ++ net.minecraft.server.level.ServerLevel world = ((org.bukkit.craftbukkit.CraftWorld) org.bukkit.Bukkit.getWorlds().get(0)).getHandle(); ++ return stack != null && !stack.getType().isEmpty() && world.recipeAccess().getRecipeFor(((AbstractFurnaceBlockEntity) this.inventory).recipeType, new net.minecraft.world.item.crafting.SingleRecipeInput(CraftItemStack.asNMSCopy(stack)), world).isPresent(); ++ } ++ // Paper end ++ + @Override + public Furnace getHolder() { + return (Furnace) this.inventory.getOwner(); diff --git a/patches/server/0634-Bucketable-API.patch b/patches/server/0634-Bucketable-API.patch new file mode 100644 index 0000000000..274ccde059 --- /dev/null +++ b/patches/server/0634-Bucketable-API.patch @@ -0,0 +1,69 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Owen1212055 <[email protected]> +Date: Sun, 26 Dec 2021 14:03:17 -0500 +Subject: [PATCH] Bucketable API + + +diff --git a/src/main/java/io/papermc/paper/entity/PaperBucketable.java b/src/main/java/io/papermc/paper/entity/PaperBucketable.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d3fc2e5db9f3c20120b403bf03c3c340b9956cbd +--- /dev/null ++++ b/src/main/java/io/papermc/paper/entity/PaperBucketable.java +@@ -0,0 +1,31 @@ ++package io.papermc.paper.entity; ++ ++import org.bukkit.Sound; ++import org.bukkit.craftbukkit.CraftSound; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.inventory.ItemStack; ++ ++public interface PaperBucketable extends Bucketable { ++ ++ net.minecraft.world.entity.animal.Bucketable getHandle(); ++ ++ @Override ++ default boolean isFromBucket() { ++ return this.getHandle().fromBucket(); ++ } ++ ++ @Override ++ default void setFromBucket(boolean fromBucket) { ++ this.getHandle().setFromBucket(fromBucket); ++ } ++ ++ @Override ++ default ItemStack getBaseBucketItem() { ++ return CraftItemStack.asBukkitCopy(this.getHandle().getBucketItemStack()); ++ } ++ ++ @Override ++ default Sound getPickupSound() { ++ return CraftSound.minecraftToBukkit(this.getHandle().getPickupSound()); ++ } ++} +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftAxolotl.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftAxolotl.java +index e730292edca4624400bdb89d555922c5f61db7a5..cbfca242f820d238b112f8ce64e9de8398c48a1c 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftAxolotl.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftAxolotl.java +@@ -4,7 +4,7 @@ import com.google.common.base.Preconditions; + import org.bukkit.craftbukkit.CraftServer; + import org.bukkit.entity.Axolotl; + +-public class CraftAxolotl extends CraftAnimals implements Axolotl { ++public class CraftAxolotl extends CraftAnimals implements Axolotl, io.papermc.paper.entity.PaperBucketable { // Paper - Bucketable API + + public CraftAxolotl(CraftServer server, net.minecraft.world.entity.animal.axolotl.Axolotl entity) { + super(server, entity); +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftFish.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftFish.java +index da5150f4ca0397bf10053aab0c3ff18af5bc3f3c..eb10f94d5ed8ca89d3786138647dd43357609a6c 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftFish.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftFish.java +@@ -4,7 +4,7 @@ import net.minecraft.world.entity.animal.AbstractFish; + import org.bukkit.craftbukkit.CraftServer; + import org.bukkit.entity.Fish; + +-public class CraftFish extends CraftWaterMob implements Fish { ++public class CraftFish extends CraftWaterMob implements Fish, io.papermc.paper.entity.PaperBucketable { // Paper - Bucketable API + + public CraftFish(CraftServer server, AbstractFish entity) { + super(server, entity); diff --git a/patches/server/0635-Validate-usernames.patch b/patches/server/0635-Validate-usernames.patch new file mode 100644 index 0000000000..e8287bc641 --- /dev/null +++ b/patches/server/0635-Validate-usernames.patch @@ -0,0 +1,76 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf <[email protected]> +Date: Sat, 1 Jan 2022 05:19:37 -0800 +Subject: [PATCH] Validate usernames + + +diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +index 1e4b288f20153ce0c91fabf164c5c8320c90ba7d..cb5dd77892283a1aaec45434fb99bb7f08ee5394 100644 +--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +@@ -90,6 +90,7 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, + private final String serverId; + private final boolean transferred; + private ServerPlayer player; // CraftBukkit ++ public boolean iKnowThisMayNotBeTheBestIdeaButPleaseDisableUsernameValidation = false; // Paper - username validation overriding + + public ServerLoginPacketListenerImpl(MinecraftServer server, Connection connection, boolean transferred) { + this.state = ServerLoginPacketListenerImpl.State.HELLO; +@@ -171,7 +172,13 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, + @Override + public void handleHello(ServerboundHelloPacket packet) { + Validate.validState(this.state == ServerLoginPacketListenerImpl.State.HELLO, "Unexpected hello packet", new Object[0]); +- Validate.validState(StringUtil.isValidPlayerName(packet.name()), "Invalid characters in username", new Object[0]); ++ // Paper start - Validate usernames ++ if (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() ++ && io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.performUsernameValidation ++ && !this.iKnowThisMayNotBeTheBestIdeaButPleaseDisableUsernameValidation) { ++ Validate.validState(StringUtil.isReasonablePlayerName(packet.name()), "Invalid characters in username", new Object[0]); ++ } ++ // Paper end - Validate usernames + this.requestedUsername = packet.name(); + GameProfile gameprofile = this.server.getSingleplayerProfile(); + +diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java +index 05d2f3c26d10169f6cf43bcb6c48db5d27b5cbac..3a0e0196f5bfa554b23fff9ff1a18a189b36452e 100644 +--- a/src/main/java/net/minecraft/server/players/PlayerList.java ++++ b/src/main/java/net/minecraft/server/players/PlayerList.java +@@ -627,7 +627,7 @@ public abstract class PlayerList { + + for (int i = 0; i < this.players.size(); ++i) { + entityplayer = (ServerPlayer) this.players.get(i); +- if (entityplayer.getUUID().equals(uuid)) { ++ if (entityplayer.getUUID().equals(uuid) || (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() && entityplayer.getGameProfile().getName().equalsIgnoreCase(gameprofile.getName()))) { // Paper - validate usernames + list.add(entityplayer); + } + } +diff --git a/src/main/java/net/minecraft/util/StringUtil.java b/src/main/java/net/minecraft/util/StringUtil.java +index e588bd7ef0616dc88ce4c0feeeabadc29dcaa550..6c33002dc8bbb3759c3156302ab7d1f26ce5e8ee 100644 +--- a/src/main/java/net/minecraft/util/StringUtil.java ++++ b/src/main/java/net/minecraft/util/StringUtil.java +@@ -67,6 +67,25 @@ public class StringUtil { + return name.length() <= 16 && name.chars().filter(c -> c <= 32 || c >= 127).findAny().isEmpty(); + } + ++ // Paper start - Username validation ++ public static boolean isReasonablePlayerName(final String name) { ++ if (name.isEmpty() || name.length() > 16) { ++ return false; ++ } ++ ++ for (int i = 0, len = name.length(); i < len; ++i) { ++ final char c = name.charAt(i); ++ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '_' || c == '.')) { ++ continue; ++ } ++ ++ return false; ++ } ++ ++ return true; ++ } ++ // Paper end - Username validation ++ + public static String filterText(String string) { + return filterText(string, false); + } diff --git a/patches/server/0636-Make-water-animal-spawn-height-configurable.patch b/patches/server/0636-Make-water-animal-spawn-height-configurable.patch new file mode 100644 index 0000000000..341b55b1f6 --- /dev/null +++ b/patches/server/0636-Make-water-animal-spawn-height-configurable.patch @@ -0,0 +1,21 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Brokkonaut <[email protected]> +Date: Sat, 18 Dec 2021 08:26:55 +0100 +Subject: [PATCH] Make water animal spawn height configurable + + +diff --git a/src/main/java/net/minecraft/world/entity/animal/WaterAnimal.java b/src/main/java/net/minecraft/world/entity/animal/WaterAnimal.java +index 6a8a90059dcc524a0724264c8d604bf39228a650..8c4532a250f8679d729a35c17e9b5bd339264450 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/WaterAnimal.java ++++ b/src/main/java/net/minecraft/world/entity/animal/WaterAnimal.java +@@ -70,6 +70,10 @@ public abstract class WaterAnimal extends PathfinderMob { + ) { + int i = world.getSeaLevel(); + int j = i - 13; ++ // Paper start - Make water animal spawn height configurable ++ i = world.getMinecraftWorld().paperConfig().entities.spawning.wateranimalSpawnHeight.maximum.or(i); ++ j = world.getMinecraftWorld().paperConfig().entities.spawning.wateranimalSpawnHeight.minimum.or(j); ++ // Paper end - Make water animal spawn height configurable + return pos.getY() >= j && pos.getY() <= i && world.getFluidState(pos.below()).is(FluidTags.WATER) && world.getBlockState(pos.above()).is(Blocks.WATER); + } + } diff --git a/patches/server/0637-Expose-vanilla-BiomeProvider-from-WorldInfo.patch b/patches/server/0637-Expose-vanilla-BiomeProvider-from-WorldInfo.patch new file mode 100644 index 0000000000..af63e382de --- /dev/null +++ b/patches/server/0637-Expose-vanilla-BiomeProvider-from-WorldInfo.patch @@ -0,0 +1,177 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jason Penilla <[email protected]> +Date: Thu, 6 Jan 2022 15:59:06 -0800 +Subject: [PATCH] Expose vanilla BiomeProvider from WorldInfo + + +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index edd2e83df282b0e24d4c7e3a34776a5b039c2c6b..c133a646baf88e0489d358e302d67f21f76b47c3 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -625,7 +625,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa + List<CustomSpawner> list = ImmutableList.of(new PhantomSpawner(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(iworlddataserver)); + LevelStem worlddimension = (LevelStem) dimensions.getValue(dimensionKey); + +- org.bukkit.generator.WorldInfo worldInfo = new org.bukkit.craftbukkit.generator.CraftWorldInfo(iworlddataserver, worldSession, org.bukkit.World.Environment.getEnvironment(dimension), worlddimension.type().value()); ++ org.bukkit.generator.WorldInfo worldInfo = new org.bukkit.craftbukkit.generator.CraftWorldInfo(iworlddataserver, worldSession, org.bukkit.World.Environment.getEnvironment(dimension), worlddimension.type().value(), worlddimension.generator(), this.registryAccess()); // Paper - Expose vanilla BiomeProvider from WorldInfo + if (biomeProvider == null && gen != null) { + biomeProvider = gen.getDefaultBiomeProvider(worldInfo); + } +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index 3205ef2b0027a2fa7f9ba5ed3437f71f1c6e02b5..fda4b5f2b848b432138207eff9a77fed6aaf3805 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -363,7 +363,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + this.serverLevelData.setWorld(this); + + if (biomeProvider != null) { +- BiomeSource worldChunkManager = new CustomWorldChunkManager(this.getWorld(), biomeProvider, this.server.registryAccess().lookupOrThrow(Registries.BIOME)); ++ BiomeSource worldChunkManager = new CustomWorldChunkManager(this.getWorld(), biomeProvider, this.server.registryAccess().lookupOrThrow(Registries.BIOME), chunkgenerator.getBiomeSource()); // Paper - add vanillaBiomeProvider + if (chunkgenerator instanceof NoiseBasedChunkGenerator cga) { + chunkgenerator = new NoiseBasedChunkGenerator(worldChunkManager, cga.settings); + } else if (chunkgenerator instanceof FlatLevelSource cpf) { +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index 33d9f3778996eedc83064332a2fbbdc7c6a8ba90..62ab88e022230d25ffb359981ce7da4e64a9be5a 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -1310,7 +1310,7 @@ public final class CraftServer implements Server { + List<CustomSpawner> list = ImmutableList.of(new PhantomSpawner(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(worlddata)); + LevelStem worlddimension = iregistry.getValue(actualDimension); + +- WorldInfo worldInfo = new CraftWorldInfo(worlddata, worldSession, creator.environment(), worlddimension.type().value()); ++ WorldInfo worldInfo = new CraftWorldInfo(worlddata, worldSession, creator.environment(), worlddimension.type().value(), worlddimension.generator(), this.getHandle().getServer().registryAccess()); // Paper - Expose vanilla BiomeProvider from WorldInfo + if (biomeProvider == null && generator != null) { + biomeProvider = generator.getDefaultBiomeProvider(worldInfo); + } +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index 744e3631cd2d5c157c9b6023ca813e57c6f860d6..66778ebd82563823f692c7151f40a373e8d7427a 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -209,6 +209,39 @@ public class CraftWorld extends CraftRegionAccessor implements World { + public int getPlayerCount() { + return world.players().size(); + } ++ ++ @Override ++ public BiomeProvider vanillaBiomeProvider() { ++ net.minecraft.server.level.ServerChunkCache serverCache = this.getHandle().chunkSource; ++ ++ final net.minecraft.world.level.chunk.ChunkGenerator gen = serverCache.getGenerator(); ++ net.minecraft.world.level.biome.BiomeSource biomeSource; ++ if (gen instanceof org.bukkit.craftbukkit.generator.CustomChunkGenerator custom) { ++ biomeSource = custom.getDelegate().getBiomeSource(); ++ } else { ++ biomeSource = gen.getBiomeSource(); ++ } ++ if (biomeSource instanceof org.bukkit.craftbukkit.generator.CustomWorldChunkManager customBiomeSource) { ++ biomeSource = customBiomeSource.vanillaBiomeSource; ++ } ++ final net.minecraft.world.level.biome.BiomeSource finalBiomeSource = biomeSource; ++ final net.minecraft.world.level.biome.Climate.Sampler sampler = serverCache.randomState().sampler(); ++ ++ final List<Biome> possibleBiomes = finalBiomeSource.possibleBiomes().stream() ++ .map(CraftBiome::minecraftHolderToBukkit) ++ .toList(); ++ return new BiomeProvider() { ++ @Override ++ public Biome getBiome(final org.bukkit.generator.WorldInfo worldInfo, final int x, final int y, final int z) { ++ return CraftBiome.minecraftHolderToBukkit(finalBiomeSource.getNoiseBiome(x >> 2, y >> 2, z >> 2, sampler)); ++ } ++ ++ @Override ++ public List<Biome> getBiomes(final org.bukkit.generator.WorldInfo worldInfo) { ++ return possibleBiomes; ++ } ++ }; ++ } + // Paper end + + private static final Random rand = new Random(); +diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CraftWorldInfo.java b/src/main/java/org/bukkit/craftbukkit/generator/CraftWorldInfo.java +index 5d655d6cd3e23e0287069f8bdf77601487e862fd..c81455a4ee9a3185f125ebf8cec325f4ed2e501d 100644 +--- a/src/main/java/org/bukkit/craftbukkit/generator/CraftWorldInfo.java ++++ b/src/main/java/org/bukkit/craftbukkit/generator/CraftWorldInfo.java +@@ -17,8 +17,14 @@ public class CraftWorldInfo implements WorldInfo { + private final long seed; + private final int minHeight; + private final int maxHeight; ++ // Paper start ++ private final net.minecraft.world.level.chunk.ChunkGenerator vanillaChunkGenerator; ++ private final net.minecraft.core.RegistryAccess.Frozen registryAccess; + +- public CraftWorldInfo(ServerLevelData worldDataServer, LevelStorageSource.LevelStorageAccess session, World.Environment environment, DimensionType dimensionManager) { ++ public CraftWorldInfo(PrimaryLevelData worldDataServer, LevelStorageSource.LevelStorageAccess session, World.Environment environment, DimensionType dimensionManager, net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator, net.minecraft.core.RegistryAccess.Frozen registryAccess) { ++ this.registryAccess = registryAccess; ++ this.vanillaChunkGenerator = chunkGenerator; ++ // Paper end + this.name = worldDataServer.getLevelName(); + this.uuid = WorldUUID.getUUID(session.levelDirectory.path().toFile()); + this.environment = environment; +@@ -27,15 +33,6 @@ public class CraftWorldInfo implements WorldInfo { + this.maxHeight = dimensionManager.minY() + dimensionManager.height(); + } + +- public CraftWorldInfo(String name, UUID uuid, World.Environment environment, long seed, int minHeight, int maxHeight) { +- this.name = name; +- this.uuid = uuid; +- this.environment = environment; +- this.seed = seed; +- this.minHeight = minHeight; +- this.maxHeight = maxHeight; +- } +- + @Override + public String getName() { + return this.name; +@@ -65,4 +62,34 @@ public class CraftWorldInfo implements WorldInfo { + public int getMaxHeight() { + return this.maxHeight; + } ++ ++ // Paper start ++ @Override ++ public org.bukkit.generator.BiomeProvider vanillaBiomeProvider() { ++ final net.minecraft.world.level.levelgen.RandomState randomState; ++ if (vanillaChunkGenerator instanceof net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator noiseBasedChunkGenerator) { ++ randomState = net.minecraft.world.level.levelgen.RandomState.create(noiseBasedChunkGenerator.generatorSettings().value(), ++ registryAccess.lookupOrThrow(net.minecraft.core.registries.Registries.NOISE), getSeed()); ++ } else { ++ randomState = net.minecraft.world.level.levelgen.RandomState.create(net.minecraft.world.level.levelgen.NoiseGeneratorSettings.dummy(), ++ registryAccess.lookupOrThrow(net.minecraft.core.registries.Registries.NOISE), getSeed()); ++ } ++ ++ final java.util.List<org.bukkit.block.Biome> possibleBiomes = CraftWorldInfo.this.vanillaChunkGenerator.getBiomeSource().possibleBiomes().stream() ++ .map(biome -> org.bukkit.craftbukkit.block.CraftBiome.minecraftHolderToBukkit(biome)) ++ .toList(); ++ return new org.bukkit.generator.BiomeProvider() { ++ @Override ++ public org.bukkit.block.Biome getBiome(final WorldInfo worldInfo, final int x, final int y, final int z) { ++ return org.bukkit.craftbukkit.block.CraftBiome.minecraftHolderToBukkit( ++ CraftWorldInfo.this.vanillaChunkGenerator.getBiomeSource().getNoiseBiome(x >> 2, y >> 2, z >> 2, randomState.sampler())); ++ } ++ ++ @Override ++ public java.util.List<org.bukkit.block.Biome> getBiomes(final org.bukkit.generator.WorldInfo worldInfo) { ++ return possibleBiomes; ++ } ++ }; ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CustomWorldChunkManager.java b/src/main/java/org/bukkit/craftbukkit/generator/CustomWorldChunkManager.java +index 0063c4c17d05a77adf81164fb9307a29860cbe12..0bac128d6faff0063b03f595b82deea78d1ae161 100644 +--- a/src/main/java/org/bukkit/craftbukkit/generator/CustomWorldChunkManager.java ++++ b/src/main/java/org/bukkit/craftbukkit/generator/CustomWorldChunkManager.java +@@ -31,7 +31,11 @@ public class CustomWorldChunkManager extends BiomeSource { + return biomeBases; + } + +- public CustomWorldChunkManager(WorldInfo worldInfo, BiomeProvider biomeProvider, Registry<net.minecraft.world.level.biome.Biome> registry) { ++ // Paper start - add vanillaBiomeProvider ++ public final BiomeSource vanillaBiomeSource; ++ public CustomWorldChunkManager(WorldInfo worldInfo, BiomeProvider biomeProvider, Registry<net.minecraft.world.level.biome.Biome> registry, BiomeSource vanillaBiomeSource) { ++ this.vanillaBiomeSource = vanillaBiomeSource; ++ // Paper end - add vanillaBiomeProvider + this.worldInfo = worldInfo; + this.biomeProvider = biomeProvider; + this.registry = registry; diff --git a/patches/server/0638-Add-config-option-for-worlds-affected-by-time-cmd.patch b/patches/server/0638-Add-config-option-for-worlds-affected-by-time-cmd.patch new file mode 100644 index 0000000000..2146865230 --- /dev/null +++ b/patches/server/0638-Add-config-option-for-worlds-affected-by-time-cmd.patch @@ -0,0 +1,28 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sun, 2 Jan 2022 22:34:51 -0800 +Subject: [PATCH] Add config option for worlds affected by time cmd + + +diff --git a/src/main/java/net/minecraft/server/commands/TimeCommand.java b/src/main/java/net/minecraft/server/commands/TimeCommand.java +index ec4ea21f14f8dbf6a26b6124256386ff51c0628b..8b83d747de831878ff45dc74b4ae7cd9efb21d8c 100644 +--- a/src/main/java/net/minecraft/server/commands/TimeCommand.java ++++ b/src/main/java/net/minecraft/server/commands/TimeCommand.java +@@ -53,7 +53,7 @@ public class TimeCommand { + } + + public static int setTime(CommandSourceStack source, int time) { +- Iterator iterator = com.google.common.collect.Iterators.singletonIterator(source.getLevel()); // CraftBukkit - SPIGOT-6496: Only set the time for the world the command originates in ++ Iterator iterator = io.papermc.paper.configuration.GlobalConfiguration.get().commands.timeCommandAffectsAllWorlds ? source.getServer().getAllLevels().iterator() : com.google.common.collect.Iterators.singletonIterator(source.getLevel()); // CraftBukkit - SPIGOT-6496: Only set the time for the world the command originates in // Paper - add config option for spigot's change + + while (iterator.hasNext()) { + ServerLevel worldserver = (ServerLevel) iterator.next(); +@@ -75,7 +75,7 @@ public class TimeCommand { + } + + public static int addTime(CommandSourceStack source, int time) { +- Iterator iterator = com.google.common.collect.Iterators.singletonIterator(source.getLevel()); // CraftBukkit - SPIGOT-6496: Only set the time for the world the command originates in ++ Iterator iterator = io.papermc.paper.configuration.GlobalConfiguration.get().commands.timeCommandAffectsAllWorlds ? source.getServer().getAllLevels().iterator() : com.google.common.collect.Iterators.singletonIterator(source.getLevel()); // CraftBukkit - SPIGOT-6496: Only set the time for the world the command originates in // Paper - add config option for spigot's change + + while (iterator.hasNext()) { + ServerLevel worldserver = (ServerLevel) iterator.next(); diff --git a/patches/server/0639-Add-missing-IAE-check-for-PersistentDataContainer-ha.patch b/patches/server/0639-Add-missing-IAE-check-for-PersistentDataContainer-ha.patch new file mode 100644 index 0000000000..be0eedab93 --- /dev/null +++ b/patches/server/0639-Add-missing-IAE-check-for-PersistentDataContainer-ha.patch @@ -0,0 +1,18 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: u9g <[email protected]> +Date: Mon, 3 Jan 2022 23:32:42 -0500 +Subject: [PATCH] Add missing IAE check for PersistentDataContainer#has + + +diff --git a/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java b/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java +index 3001bb0e3d4af9b16645a0136093db594b89ab01..984e988a47aa55a3fd92198e379d0f92f511daef 100644 +--- a/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java ++++ b/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java +@@ -57,6 +57,7 @@ public class CraftPersistentDataContainer implements PersistentDataContainer { + + @Override + public boolean has(NamespacedKey key) { ++ Preconditions.checkArgument(key != null, "The provided key for the custom value was null"); // Paper + return this.customDataTags.get(key.toString()) != null; + } + diff --git a/patches/server/0640-Multiple-Entries-with-Scoreboards.patch b/patches/server/0640-Multiple-Entries-with-Scoreboards.patch new file mode 100644 index 0000000000..558cc93da5 --- /dev/null +++ b/patches/server/0640-Multiple-Entries-with-Scoreboards.patch @@ -0,0 +1,125 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Cryptite <[email protected]> +Date: Tue, 21 Sep 2021 18:17:33 -0500 +Subject: [PATCH] Multiple Entries with Scoreboards + + +diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket.java +index 1d0c473442b5c72245c356054440323e3c5d4711..f8fe125f12a6a00899d1d6acfa448be882b81557 100644 +--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket.java ++++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundSetPlayerTeamPacket.java +@@ -58,6 +58,11 @@ public class ClientboundSetPlayerTeamPacket implements Packet<ClientGamePacketLi + ); + } + ++ // Paper start - Multiple Entries with Scoreboards ++ public static ClientboundSetPlayerTeamPacket createMultiplePlayerPacket(PlayerTeam team, Collection<String> players, ClientboundSetPlayerTeamPacket.Action operation) { ++ return new ClientboundSetPlayerTeamPacket(team.getName(), operation == ClientboundSetPlayerTeamPacket.Action.ADD ? 3 : 4, Optional.empty(), players); ++ } ++ // Paper end - Multiple Entries with Scoreboards + private ClientboundSetPlayerTeamPacket(RegistryFriendlyByteBuf buf) { + this.name = buf.readUtf(); + this.method = buf.readByte(); +diff --git a/src/main/java/net/minecraft/server/ServerScoreboard.java b/src/main/java/net/minecraft/server/ServerScoreboard.java +index f1e72463ca11658390f662efbaf3e551c05fe799..8fd4d8ffcc9e1a0fcf83730d26c3bb9bef0f73f2 100644 +--- a/src/main/java/net/minecraft/server/ServerScoreboard.java ++++ b/src/main/java/net/minecraft/server/ServerScoreboard.java +@@ -106,6 +106,25 @@ public class ServerScoreboard extends Scoreboard { + } + } + ++ // Paper start - Multiple Entries with Scoreboards ++ public boolean addPlayersToTeam(java.util.Collection<String> players, PlayerTeam team) { ++ boolean anyAdded = false; ++ for (String playerName : players) { ++ if (super.addPlayerToTeam(playerName, team)) { ++ anyAdded = true; ++ } ++ } ++ ++ if (anyAdded) { ++ this.broadcastAll(ClientboundSetPlayerTeamPacket.createMultiplePlayerPacket(team, players, ClientboundSetPlayerTeamPacket.Action.ADD)); ++ this.setDirty(); ++ return true; ++ } else { ++ return false; ++ } ++ } ++ // Paper end - Multiple Entries with Scoreboards ++ + @Override + public void removePlayerFromTeam(String scoreHolderName, PlayerTeam team) { + super.removePlayerFromTeam(scoreHolderName, team); +@@ -113,6 +132,17 @@ public class ServerScoreboard extends Scoreboard { + this.setDirty(); + } + ++ // Paper start - Multiple Entries with Scoreboards ++ public void removePlayersFromTeam(java.util.Collection<String> players, PlayerTeam team) { ++ for (String playerName : players) { ++ super.removePlayerFromTeam(playerName, team); ++ } ++ ++ this.broadcastAll(ClientboundSetPlayerTeamPacket.createMultiplePlayerPacket(team, players, ClientboundSetPlayerTeamPacket.Action.REMOVE)); ++ this.setDirty(); ++ } ++ // Paper end - Multiple Entries with Scoreboards ++ + @Override + public void onObjectiveAdded(Objective objective) { + super.onObjectiveAdded(objective); +diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java +index 27219bf2f16aed64c78623d44c3cc84aa9f47065..2b335c750ce5f9ccc2651a8701497ca9b8f46704 100644 +--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java ++++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java +@@ -229,6 +229,21 @@ final class CraftTeam extends CraftScoreboardComponent implements Team { + scoreboard.board.addPlayerToTeam(entry, this.team); + } + ++ // Paper start - Multiple Entries with Scoreboards ++ @Override ++ public void addEntities(java.util.Collection<org.bukkit.entity.Entity> entities) throws IllegalStateException, IllegalArgumentException { ++ this.addEntries(entities.stream().map(entity -> ((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().getScoreboardName()).toList()); ++ } ++ ++ @Override ++ public void addEntries(java.util.Collection<String> entries) throws IllegalStateException, IllegalArgumentException { ++ Preconditions.checkArgument(entries != null, "Entries cannot be null"); ++ CraftScoreboard scoreboard = this.checkState(); ++ ++ ((net.minecraft.server.ServerScoreboard) scoreboard.board).addPlayersToTeam(entries, this.team); ++ } ++ // Paper end - Multiple Entries with Scoreboards ++ + @Override + public boolean removePlayer(OfflinePlayer player) { + Preconditions.checkArgument(player != null, "OfflinePlayer cannot be null"); +@@ -248,6 +263,28 @@ final class CraftTeam extends CraftScoreboardComponent implements Team { + return true; + } + ++ // Paper start - Multiple Entries with Scoreboards ++ @Override ++ public boolean removeEntities(java.util.Collection<org.bukkit.entity.Entity> entities) throws IllegalStateException, IllegalArgumentException { ++ return this.removeEntries(entities.stream().map(entity -> ((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().getScoreboardName()).toList()); ++ } ++ ++ @Override ++ public boolean removeEntries(java.util.Collection<String> entries) throws IllegalStateException, IllegalArgumentException { ++ Preconditions.checkArgument(entries != null, "Entry cannot be null"); ++ CraftScoreboard scoreboard = this.checkState(); ++ ++ for (String entry : entries) { ++ if (this.team.getPlayers().contains(entry)) { ++ ((net.minecraft.server.ServerScoreboard) scoreboard.board).removePlayersFromTeam(entries, this.team); ++ return true; ++ } ++ } ++ ++ return false; ++ } ++ // Paper end - Multiple Entries with Scoreboards ++ + @Override + public boolean hasPlayer(OfflinePlayer player) throws IllegalArgumentException, IllegalStateException { + Preconditions.checkArgument(player != null, "OfflinePlayer cannot be null"); diff --git a/patches/server/0641-Reset-placed-block-on-exception.patch b/patches/server/0641-Reset-placed-block-on-exception.patch new file mode 100644 index 0000000000..09609b372b --- /dev/null +++ b/patches/server/0641-Reset-placed-block-on-exception.patch @@ -0,0 +1,39 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Nassim Jahnke <[email protected]> +Date: Fri, 7 Jan 2022 11:45:15 +0100 +Subject: [PATCH] Reset placed block on exception + + +diff --git a/src/main/java/net/minecraft/world/item/BlockItem.java b/src/main/java/net/minecraft/world/item/BlockItem.java +index 2527fea68885c6911d01cd5a9b08a347d30844c8..cdd41eb371b1dd244a72c863cc5c8c4e84a9a71a 100644 +--- a/src/main/java/net/minecraft/world/item/BlockItem.java ++++ b/src/main/java/net/minecraft/world/item/BlockItem.java +@@ -71,6 +71,7 @@ public class BlockItem extends Item { + if (this instanceof PlaceOnWaterBlockItem || this instanceof SolidBucketItem) { + blockstate = org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(blockactioncontext1.getLevel(), blockactioncontext1.getClickedPos()); + } ++ final org.bukkit.block.BlockState oldBlockstate = blockstate != null ? blockstate : org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(blockactioncontext1.getLevel(), blockactioncontext1.getClickedPos()); // Paper - Reset placed block on exception + // CraftBukkit end + + if (iblockdata == null) { +@@ -86,8 +87,20 @@ public class BlockItem extends Item { + + if (iblockdata1.is(iblockdata.getBlock())) { + iblockdata1 = this.updateBlockStateFromTag(blockposition, world, itemstack, iblockdata1); ++ // Paper start - Reset placed block on exception ++ try { + this.updateCustomBlockEntityTag(blockposition, world, entityhuman, itemstack, iblockdata1); + BlockItem.updateBlockEntityComponents(world, blockposition, itemstack); ++ } catch (Exception e) { ++ oldBlockstate.update(true, false); ++ if (entityhuman instanceof ServerPlayer player) { ++ org.apache.logging.log4j.LogManager.getLogger().error("Player {} tried placing invalid block", player.getScoreboardName(), e); ++ player.getBukkitEntity().kickPlayer("Packet processing error"); ++ return InteractionResult.FAIL; ++ } ++ throw e; // Rethrow exception if not placed by a player ++ } ++ // Paper end - Reset placed block on exception + iblockdata1.getBlock().setPlacedBy(world, blockposition, iblockdata1, entityhuman, itemstack); + // CraftBukkit start + if (blockstate != null) { diff --git a/patches/server/0642-Add-configurable-height-for-slime-spawn.patch b/patches/server/0642-Add-configurable-height-for-slime-spawn.patch new file mode 100644 index 0000000000..dfc8ecb349 --- /dev/null +++ b/patches/server/0642-Add-configurable-height-for-slime-spawn.patch @@ -0,0 +1,35 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Doc <[email protected]> +Date: Mon, 2 Aug 2021 11:24:39 -0400 +Subject: [PATCH] Add configurable height for slime spawn + + +diff --git a/src/main/java/net/minecraft/world/entity/monster/Slime.java b/src/main/java/net/minecraft/world/entity/monster/Slime.java +index 129f0cbc0469cb2804db6088b53347d88d91f4eb..72346a7e5269c91e3143933ac37e65ad9639b791 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/Slime.java ++++ b/src/main/java/net/minecraft/world/entity/monster/Slime.java +@@ -340,7 +340,11 @@ public class Slime extends Mob implements Enemy { + return checkMobSpawnRules(type, world, spawnReason, pos, random); + } + +- if (world.getBiome(pos).is(BiomeTags.ALLOWS_SURFACE_SLIME_SPAWNS) && pos.getY() > 50 && pos.getY() < 70 && random.nextFloat() < 0.5F && random.nextFloat() < world.getMoonBrightness() && world.getMaxLocalRawBrightness(pos) <= random.nextInt(8)) { ++ // Paper start - Replace rules for Height in Swamp Biome ++ final double maxHeightSwamp = world.getMinecraftWorld().paperConfig().entities.spawning.slimeSpawnHeight.surfaceBiome.maximum; ++ final double minHeightSwamp = world.getMinecraftWorld().paperConfig().entities.spawning.slimeSpawnHeight.surfaceBiome.minimum; ++ if (world.getBiome(pos).is(BiomeTags.ALLOWS_SURFACE_SLIME_SPAWNS) && pos.getY() > minHeightSwamp && pos.getY() < maxHeightSwamp && random.nextFloat() < 0.5F && random.nextFloat() < world.getMoonBrightness() && world.getMaxLocalRawBrightness(pos) <= random.nextInt(8)) { ++ // Paper end - Replace rules for Height in Swamp Biome + return checkMobSpawnRules(type, world, spawnReason, pos, random); + } + +@@ -351,7 +355,10 @@ public class Slime extends Mob implements Enemy { + ChunkPos chunkcoordintpair = new ChunkPos(pos); + boolean flag = world.getMinecraftWorld().paperConfig().entities.spawning.allChunksAreSlimeChunks || WorldgenRandom.seedSlimeChunk(chunkcoordintpair.x, chunkcoordintpair.z, ((WorldGenLevel) world).getSeed(), world.getMinecraftWorld().spigotConfig.slimeSeed).nextInt(10) == 0; // Spigot // Paper + +- if (random.nextInt(10) == 0 && flag && pos.getY() < 40) { ++ // Paper start - Replace rules for Height in Slime Chunks ++ final double maxHeightSlimeChunk = world.getMinecraftWorld().paperConfig().entities.spawning.slimeSpawnHeight.slimeChunk.maximum; ++ if (random.nextInt(10) == 0 && flag && pos.getY() < maxHeightSlimeChunk) { ++ // Paper end - Replace rules for Height in Slime Chunks + return checkMobSpawnRules(type, world, spawnReason, pos, random); + } + } diff --git a/patches/server/0643-Fix-xp-reward-for-baby-zombies.patch b/patches/server/0643-Fix-xp-reward-for-baby-zombies.patch new file mode 100644 index 0000000000..3ec320760f --- /dev/null +++ b/patches/server/0643-Fix-xp-reward-for-baby-zombies.patch @@ -0,0 +1,32 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sun, 16 Jan 2022 10:34:02 -0800 +Subject: [PATCH] Fix xp reward for baby zombies + +The field that tracks the xpReward was not +getting reset if the death was cancelled +so this resets it after each call to +Zombie#getExperienceReward + +diff --git a/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java +index a835ec6e063dd247a008da84446f8647f38d89d4..94b3ba2688676e92d9d093b63d92cab39d5d2f02 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java ++++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java +@@ -174,11 +174,16 @@ public class Zombie extends Monster { + + @Override + protected int getBaseExperienceReward(ServerLevel world) { ++ final int previousReward = this.xpReward; // Paper - store previous value to reset after calculating XP reward + if (this.isBaby()) { + this.xpReward = (int) ((double) this.xpReward * 2.5D); + } + +- return super.getBaseExperienceReward(world); ++ // Paper start - store previous value to reset after calculating XP reward ++ int reward = super.getBaseExperienceReward(world); ++ this.xpReward = previousReward; ++ return reward; ++ // Paper end - store previous value to reset after calculating XP reward + } + + @Override diff --git a/patches/server/0644-Multi-Block-Change-API-Implementation.patch b/patches/server/0644-Multi-Block-Change-API-Implementation.patch new file mode 100644 index 0000000000..ae2c0b83d4 --- /dev/null +++ b/patches/server/0644-Multi-Block-Change-API-Implementation.patch @@ -0,0 +1,62 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Brody Beckwith <[email protected]> +Date: Fri, 14 Jan 2022 00:41:11 -0500 +Subject: [PATCH] Multi Block Change API Implementation + + +diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java +index 926ff9be3d9e3f5d620e4c7ccb22b9f64865ff8c..1a37654aff9a9c86c9f7af10a1cf721371f0c5ec 100644 +--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java ++++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket.java +@@ -62,6 +62,14 @@ public class ClientboundSectionBlocksUpdatePacket implements Packet<ClientGamePa + + } + ++ // Paper start - Multi Block Change API ++ public ClientboundSectionBlocksUpdatePacket(SectionPos sectionPos, it.unimi.dsi.fastutil.shorts.Short2ObjectMap<BlockState> blockChanges) { ++ this.sectionPos = sectionPos; ++ this.positions = blockChanges.keySet().toShortArray(); ++ this.states = blockChanges.values().toArray(new BlockState[0]); ++ } ++ // Paper end - Multi Block Change API ++ + private void write(FriendlyByteBuf buf) { + buf.writeLong(this.sectionPos.asLong()); + buf.writeVarInt(this.positions.length); +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +index f363f885b3dc1852b09914f034740794e3025d3d..eb48efa038043dacf539811de9e0f0faa1ec6b42 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -937,6 +937,32 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + this.getHandle().connection.send(packet); + } + ++ // Paper start ++ @Override ++ public void sendMultiBlockChange(final Map<? extends io.papermc.paper.math.Position, BlockData> blockChanges) { ++ if (this.getHandle().connection == null) return; ++ ++ Map<SectionPos, it.unimi.dsi.fastutil.shorts.Short2ObjectMap<net.minecraft.world.level.block.state.BlockState>> sectionMap = new HashMap<>(); ++ ++ for (Map.Entry<? extends io.papermc.paper.math.Position, BlockData> entry : blockChanges.entrySet()) { ++ BlockData blockData = entry.getValue(); ++ BlockPos blockPos = io.papermc.paper.util.MCUtil.toBlockPos(entry.getKey()); ++ SectionPos sectionPos = SectionPos.of(blockPos); ++ ++ it.unimi.dsi.fastutil.shorts.Short2ObjectMap<net.minecraft.world.level.block.state.BlockState> sectionData = sectionMap.computeIfAbsent(sectionPos, key -> new it.unimi.dsi.fastutil.shorts.Short2ObjectArrayMap<>()); ++ sectionData.put(SectionPos.sectionRelativePos(blockPos), ((CraftBlockData) blockData).getState()); ++ } ++ ++ for (Map.Entry<SectionPos, it.unimi.dsi.fastutil.shorts.Short2ObjectMap<net.minecraft.world.level.block.state.BlockState>> entry : sectionMap.entrySet()) { ++ SectionPos sectionPos = entry.getKey(); ++ it.unimi.dsi.fastutil.shorts.Short2ObjectMap<net.minecraft.world.level.block.state.BlockState> blockData = entry.getValue(); ++ ++ net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket packet = new net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket(sectionPos, blockData); ++ this.getHandle().connection.send(packet); ++ } ++ } ++ // Paper end ++ + @Override + public void sendBlockChanges(Collection<BlockState> blocks) { + Preconditions.checkArgument(blocks != null, "blocks must not be null"); diff --git a/patches/server/0645-Fix-NotePlayEvent.patch b/patches/server/0645-Fix-NotePlayEvent.patch new file mode 100644 index 0000000000..cdffcd6f0f --- /dev/null +++ b/patches/server/0645-Fix-NotePlayEvent.patch @@ -0,0 +1,54 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Kieran Wallbanks <[email protected]> +Date: Mon, 21 Jun 2021 14:23:50 +0100 +Subject: [PATCH] Fix NotePlayEvent + +== AT == +public org.bukkit.craftbukkit.block.data.CraftBlockData toNMS(Ljava/lang/Enum;Ljava/lang/Class;)Ljava/lang/Enum; + +diff --git a/src/main/java/net/minecraft/world/level/block/NoteBlock.java b/src/main/java/net/minecraft/world/level/block/NoteBlock.java +index 57e13269367a82ec39c2298b20d7595f61326f47..71fd7a467a4cb89cad8d2541366fd4add9115e04 100644 +--- a/src/main/java/net/minecraft/world/level/block/NoteBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/NoteBlock.java +@@ -96,11 +96,12 @@ public class NoteBlock extends Block { + private void playNote(@Nullable Entity entity, BlockState state, Level world, BlockPos pos) { + if (((NoteBlockInstrument) state.getValue(NoteBlock.INSTRUMENT)).worksAboveNoteBlock() || world.getBlockState(pos.above()).isAir()) { + // CraftBukkit start +- org.bukkit.event.block.NotePlayEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNotePlayEvent(world, pos, state.getValue(NoteBlock.INSTRUMENT), state.getValue(NoteBlock.NOTE)); +- if (event.isCancelled()) { +- return; +- } ++ // org.bukkit.event.block.NotePlayEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNotePlayEvent(world, pos, state.getValue(NoteBlock.INSTRUMENT), state.getValue(NoteBlock.NOTE)); ++ // if (event.isCancelled()) { ++ // return; ++ // } + // CraftBukkit end ++ // Paper - move NotePlayEvent call to fix instrument/note changes; TODO any way to cancel the game event? + world.blockEvent(pos, this, 0, 0); + world.gameEvent(entity, (Holder) GameEvent.NOTE_BLOCK_PLAY, pos); + } +@@ -139,10 +140,14 @@ public class NoteBlock extends Block { + @Override + protected boolean triggerEvent(BlockState state, Level world, BlockPos pos, int type, int data) { + NoteBlockInstrument blockpropertyinstrument = (NoteBlockInstrument) state.getValue(NoteBlock.INSTRUMENT); ++ // Paper start - move NotePlayEvent call to fix instrument/note changes ++ org.bukkit.event.block.NotePlayEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNotePlayEvent(world, pos, blockpropertyinstrument, state.getValue(NOTE)); ++ if (event.isCancelled()) return false; ++ // Paper end - move NotePlayEvent call to fix instrument/note changes + float f; + + if (blockpropertyinstrument.isTunable()) { +- int k = (Integer) state.getValue(NoteBlock.NOTE); ++ int k = event.getNote().getId(); // Paper - move NotePlayEvent call to fix instrument/note changes + + f = NoteBlock.getPitchFromNote(k); + world.addParticle(ParticleTypes.NOTE, (double) pos.getX() + 0.5D, (double) pos.getY() + 1.2D, (double) pos.getZ() + 0.5D, (double) k / 24.0D, 0.0D, 0.0D); +@@ -161,7 +166,7 @@ public class NoteBlock extends Block { + + holder = Holder.direct(SoundEvent.createVariableRangeEvent(minecraftkey)); + } else { +- holder = blockpropertyinstrument.getSoundEvent(); ++ holder = org.bukkit.craftbukkit.block.data.CraftBlockData.toNMS(event.getInstrument(), NoteBlockInstrument.class).getSoundEvent(); // Paper - move NotePlayEvent call to fix instrument/note changes + } + + world.playSeededSound((Player) null, (double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, holder, SoundSource.RECORDS, 3.0F, f, world.random.nextLong()); diff --git a/patches/server/0646-Freeze-Tick-Lock-API.patch b/patches/server/0646-Freeze-Tick-Lock-API.patch new file mode 100644 index 0000000000..74446f0546 --- /dev/null +++ b/patches/server/0646-Freeze-Tick-Lock-API.patch @@ -0,0 +1,82 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Owen1212055 <[email protected]> +Date: Sun, 26 Dec 2021 20:27:43 -0500 +Subject: [PATCH] Freeze Tick Lock API + + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 17c8a369e5a09276a3918dfb2bb004441e35a8e0..4784bfb23a7e0004287f89213d50bcfaaaed9e38 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -415,6 +415,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + private org.bukkit.util.Vector origin; + @javax.annotation.Nullable + private UUID originWorld; ++ public boolean freezeLocked = false; // Paper - Freeze Tick Lock API + + public void setOrigin(@javax.annotation.Nonnull Location location) { + this.origin = location.toVector(); +@@ -764,7 +765,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + this.setRemainingFireTicks(this.remainingFireTicks - 1); + } + +- if (this.getTicksFrozen() > 0) { ++ if (this.getTicksFrozen() > 0 && !freezeLocked) { // Paper - Freeze Tick Lock API + this.setTicksFrozen(0); + this.level().levelEvent((Player) null, 1009, this.blockPosition, 1); + } +@@ -2452,6 +2453,9 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + if (fromNetherPortal) { + nbttagcompound.putBoolean("Paper.FromNetherPortal", true); + } ++ if (freezeLocked) { ++ nbttagcompound.putBoolean("Paper.FreezeLock", true); ++ } + // Paper end + return nbttagcompound; + } catch (Throwable throwable) { +@@ -2599,6 +2603,9 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + if (spawnReason == null) { + spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT; + } ++ if (nbt.contains("Paper.FreezeLock")) { ++ freezeLocked = nbt.getBoolean("Paper.FreezeLock"); ++ } + // Paper end + + } catch (Throwable throwable) { +diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java +index dfa1e3548f9b48c20f28712ecb06310fe9c856f5..bb96b962439aa62954352f193b44f97d2e826373 100644 +--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java ++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java +@@ -3621,7 +3621,7 @@ public abstract class LivingEntity extends Entity implements Attackable { + this.calculateEntityAnimation(this instanceof FlyingAnimal); + gameprofilerfiller.pop(); + gameprofilerfiller.push("freezing"); +- if (!this.level().isClientSide && !this.isDeadOrDying()) { ++ if (!this.level().isClientSide && !this.isDeadOrDying() && !this.freezeLocked) { // Paper - Freeze Tick Lock API + int i = this.getTicksFrozen(); + + if (this.isInPowderSnow && this.canFreeze()) { +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +index 442b5f13e976dd63bf1dccc12eb8c3f16314c581..10fb64df10820974d11f142c102a11a5bd0f317c 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +@@ -324,6 +324,17 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { + return this.getHandle().isFullyFrozen(); + } + ++ // Paper start - Freeze Tick Lock API ++ @Override ++ public boolean isFreezeTickingLocked() { ++ return this.entity.freezeLocked; ++ } ++ ++ @Override ++ public void lockFreezeTicks(boolean locked) { ++ this.entity.freezeLocked = locked; ++ } ++ // Paper end - Freeze Tick Lock API + @Override + public void remove() { + this.entity.pluginRemoved = true; diff --git a/patches/server/0647-More-PotionEffectType-API.patch b/patches/server/0647-More-PotionEffectType-API.patch new file mode 100644 index 0000000000..2773c98338 --- /dev/null +++ b/patches/server/0647-More-PotionEffectType-API.patch @@ -0,0 +1,98 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Thu, 27 May 2021 21:58:24 -0700 +Subject: [PATCH] More PotionEffectType API + +== AT == +public net.minecraft.world.effect.MobEffect attributeModifiers +public net.minecraft.world.effect.MobEffect$AttributeTemplate + +diff --git a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java +index 21d4224c8993f521d6004d708ecbf71fa6d09306..6cf790c9fa23ea313423fdaeb7c181bf530828c6 100644 +--- a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java ++++ b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java +@@ -129,6 +129,48 @@ public class CraftPotionEffectType extends PotionEffectType implements Handleabl + return this.handle.getDescriptionId(); + } + ++ // Paper start ++ @Override ++ public java.util.Map<org.bukkit.attribute.Attribute, org.bukkit.attribute.AttributeModifier> getEffectAttributes() { ++ // re-create map each time because a nms MobEffect can have its attributes modified ++ final java.util.Map<org.bukkit.attribute.Attribute, org.bukkit.attribute.AttributeModifier> attributeMap = new java.util.HashMap<>(); ++ this.handle.attributeModifiers.forEach((attribute, attributeModifier) -> { ++ attributeMap.put( ++ org.bukkit.craftbukkit.attribute.CraftAttribute.minecraftHolderToBukkit(attribute), ++ // use zero as amplifier to get the base amount, as it is amount = base * (amplifier + 1) ++ org.bukkit.craftbukkit.attribute.CraftAttributeInstance.convert(attributeModifier.create(0)) ++ ); ++ }); ++ return java.util.Map.copyOf(attributeMap); ++ } ++ ++ @Override ++ public double getAttributeModifierAmount(org.bukkit.attribute.Attribute attribute, int effectAmplifier) { ++ com.google.common.base.Preconditions.checkArgument(effectAmplifier >= 0, "effectAmplifier must be greater than or equal to 0"); ++ Holder<net.minecraft.world.entity.ai.attributes.Attribute> nmsAttribute = org.bukkit.craftbukkit.attribute.CraftAttribute.bukkitToMinecraftHolder(attribute); ++ com.google.common.base.Preconditions.checkArgument(this.handle.attributeModifiers.containsKey(nmsAttribute), attribute + " is not present on " + this.getKey()); ++ return this.handle.attributeModifiers.get(nmsAttribute).create(effectAmplifier).amount(); ++ } ++ ++ @Override ++ public PotionEffectType.Category getEffectCategory() { ++ return fromNMS(handle.getCategory()); ++ } ++ ++ @Override ++ public String translationKey() { ++ return this.handle.getDescriptionId(); ++ } ++ ++ public static PotionEffectType.Category fromNMS(net.minecraft.world.effect.MobEffectCategory mobEffectInfo) { ++ return switch (mobEffectInfo) { ++ case BENEFICIAL -> PotionEffectType.Category.BENEFICIAL; ++ case HARMFUL -> PotionEffectType.Category.HARMFUL; ++ case NEUTRAL -> PotionEffectType.Category.NEUTRAL; ++ }; ++ } ++ // Paper end ++ + @Override + public boolean equals(Object other) { + if (this == other) { +diff --git a/src/test/java/io/papermc/paper/effects/EffectCategoryTest.java b/src/test/java/io/papermc/paper/effects/EffectCategoryTest.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a57e8fdc35efc7e0353d4f36c91578390ee4572e +--- /dev/null ++++ b/src/test/java/io/papermc/paper/effects/EffectCategoryTest.java +@@ -0,0 +1,30 @@ ++package io.papermc.paper.effects; ++ ++import io.papermc.paper.adventure.PaperAdventure; ++import net.minecraft.world.effect.MobEffectCategory; ++import org.bukkit.craftbukkit.potion.CraftPotionEffectType; ++import org.bukkit.potion.PotionEffectType; ++import org.bukkit.support.environment.AllFeatures; ++import org.junit.jupiter.api.Test; ++ ++import static org.junit.jupiter.api.Assertions.assertEquals; ++import static org.junit.jupiter.api.Assertions.assertNotNull; ++ ++@AllFeatures ++public class EffectCategoryTest { ++ ++ @Test ++ public void testEffectCategoriesExist() { ++ for (MobEffectCategory mobEffectInfo : MobEffectCategory.values()) { ++ assertNotNull(CraftPotionEffectType.fromNMS(mobEffectInfo), mobEffectInfo + " is missing a bukkit equivalent"); ++ } ++ } ++ ++ @Test ++ public void testCategoryHasEquivalentColors() { ++ for (MobEffectCategory mobEffectInfo : MobEffectCategory.values()) { ++ PotionEffectType.Category bukkitEffectCategory = CraftPotionEffectType.fromNMS(mobEffectInfo); ++ assertEquals(bukkitEffectCategory.getColor(), PaperAdventure.asAdventure(mobEffectInfo.getTooltipFormatting()), mobEffectInfo.getTooltipFormatting().name() + " doesn't equal " + bukkitEffectCategory.getColor()); ++ } ++ } ++} diff --git a/patches/server/0648-Use-a-CHM-for-StructureTemplate.Pallete-cache.patch b/patches/server/0648-Use-a-CHM-for-StructureTemplate.Pallete-cache.patch new file mode 100644 index 0000000000..027285c2b1 --- /dev/null +++ b/patches/server/0648-Use-a-CHM-for-StructureTemplate.Pallete-cache.patch @@ -0,0 +1,20 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Shane Freeder <[email protected]> +Date: Mon, 12 Jul 2021 12:28:29 +0100 +Subject: [PATCH] Use a CHM for StructureTemplate.Pallete cache + +fixes a CME due to this collection being shared across threads + +diff --git a/src/main/java/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java b/src/main/java/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java +index e7900e7e8263cae131dad2427fb2bfdadf5b8d14..b120949667ae0169a667b329b3cabbd79a0a5bda 100644 +--- a/src/main/java/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java ++++ b/src/main/java/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java +@@ -889,7 +889,7 @@ public class StructureTemplate { + public static final class Palette { + + private final List<StructureTemplate.StructureBlockInfo> blocks; +- private final Map<Block, List<StructureTemplate.StructureBlockInfo>> cache = Maps.newHashMap(); ++ private final Map<Block, List<StructureTemplate.StructureBlockInfo>> cache = Maps.newConcurrentMap(); // Paper - Fix CME due to this collection being shared across threads + @Nullable + private List<StructureTemplate.JigsawBlockInfo> cachedJigsaws; + diff --git a/patches/server/0649-API-for-creating-command-sender-which-forwards-feedb.patch b/patches/server/0649-API-for-creating-command-sender-which-forwards-feedb.patch new file mode 100644 index 0000000000..f86274766d --- /dev/null +++ b/patches/server/0649-API-for-creating-command-sender-which-forwards-feedb.patch @@ -0,0 +1,170 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jason Penilla <[email protected]> +Date: Tue, 1 Feb 2022 15:51:55 -0700 +Subject: [PATCH] API for creating command sender which forwards feedback + + +diff --git a/src/main/java/io/papermc/paper/commands/FeedbackForwardingSender.java b/src/main/java/io/papermc/paper/commands/FeedbackForwardingSender.java +new file mode 100644 +index 0000000000000000000000000000000000000000..e3a5f1ec376319bdfda87fa27ae217bff3914292 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/commands/FeedbackForwardingSender.java +@@ -0,0 +1,111 @@ ++package io.papermc.paper.commands; ++ ++import io.papermc.paper.adventure.PaperAdventure; ++import java.util.UUID; ++import java.util.function.Consumer; ++import net.kyori.adventure.audience.MessageType; ++import net.kyori.adventure.identity.Identity; ++import net.kyori.adventure.text.Component; ++import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; ++import net.minecraft.commands.CommandSource; ++import net.minecraft.commands.CommandSourceStack; ++import net.minecraft.server.level.ServerLevel; ++import net.minecraft.world.phys.Vec2; ++import net.minecraft.world.phys.Vec3; ++import org.bukkit.command.CommandSender; ++import org.bukkit.craftbukkit.CraftServer; ++import org.bukkit.craftbukkit.command.ServerCommandSender; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.checker.nullness.qual.Nullable; ++import org.checkerframework.framework.qual.DefaultQualifier; ++ ++@DefaultQualifier(NonNull.class) ++public final class FeedbackForwardingSender extends ServerCommandSender { ++ private final Consumer<? super Component> feedback; ++ private final CraftServer server; ++ ++ public FeedbackForwardingSender(final Consumer<? super Component> feedback, final CraftServer server) { ++ super(((ServerCommandSender) server.getConsoleSender()).perm); ++ this.server = server; ++ this.feedback = feedback; ++ } ++ ++ @Override ++ public void sendMessage(final String message) { ++ this.sendMessage(LegacyComponentSerializer.legacySection().deserialize(message)); ++ } ++ ++ @Override ++ public void sendMessage(final String... messages) { ++ for (final String message : messages) { ++ this.sendMessage(message); ++ } ++ } ++ ++ @Override ++ public void sendMessage(final Identity identity, final Component message, final MessageType type) { ++ this.feedback.accept(message); ++ } ++ ++ @Override ++ public String getName() { ++ return "FeedbackForwardingSender"; ++ } ++ ++ @Override ++ public Component name() { ++ return Component.text(this.getName()); ++ } ++ ++ @Override ++ public boolean isOp() { ++ return true; ++ } ++ ++ @Override ++ public void setOp(final boolean value) { ++ throw new UnsupportedOperationException("Cannot change operator status of " + this.getClass().getName()); ++ } ++ ++ public CommandSourceStack asVanilla() { ++ final @Nullable ServerLevel overworld = this.server.getServer().overworld(); ++ return new CommandSourceStack( ++ new Source(this), ++ overworld == null ? Vec3.ZERO : Vec3.atLowerCornerOf(overworld.getSharedSpawnPos()), ++ Vec2.ZERO, ++ overworld, ++ 4, ++ this.getName(), ++ net.minecraft.network.chat.Component.literal(this.getName()), ++ this.server.getServer(), ++ null ++ ); ++ } ++ ++ private record Source(FeedbackForwardingSender sender) implements CommandSource { ++ @Override ++ public void sendSystemMessage(final net.minecraft.network.chat.Component message) { ++ this.sender.sendMessage(Identity.nil(), PaperAdventure.asAdventure(message)); ++ } ++ ++ @Override ++ public boolean acceptsSuccess() { ++ return true; ++ } ++ ++ @Override ++ public boolean acceptsFailure() { ++ return true; ++ } ++ ++ @Override ++ public boolean shouldInformAdmins() { ++ return false; ++ } ++ ++ @Override ++ public CommandSender getBukkitSender(final CommandSourceStack stack) { ++ return this.sender; ++ } ++ } ++} +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index 62ab88e022230d25ffb359981ce7da4e64a9be5a..3b01907bc119853e0676e912e9a29b05d9aa5763 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -2165,6 +2165,13 @@ public final class CraftServer implements Server { + return this.console.console; + } + ++ // Paper start ++ @Override ++ public CommandSender createCommandSender(final java.util.function.Consumer<? super net.kyori.adventure.text.Component> feedback) { ++ return new io.papermc.paper.commands.FeedbackForwardingSender(feedback, this); ++ } ++ // Paper end ++ + public EntityMetadataStore getEntityMetadata() { + return this.entityMetadata; + } +diff --git a/src/main/java/org/bukkit/craftbukkit/command/ServerCommandSender.java b/src/main/java/org/bukkit/craftbukkit/command/ServerCommandSender.java +index 7f22950ae61436e91a59cd29a345809c42bbe739..1e3091687735b461d3b6a313ab8761127981d3e8 100644 +--- a/src/main/java/org/bukkit/craftbukkit/command/ServerCommandSender.java ++++ b/src/main/java/org/bukkit/craftbukkit/command/ServerCommandSender.java +@@ -12,7 +12,7 @@ import org.bukkit.permissions.PermissionAttachmentInfo; + import org.bukkit.plugin.Plugin; + + public abstract class ServerCommandSender implements CommandSender { +- private final PermissibleBase perm; ++ public final PermissibleBase perm; // Paper + private net.kyori.adventure.pointer.Pointers adventure$pointers; // Paper - implement pointers + + protected ServerCommandSender() { +diff --git a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java +index 35b05f9321ddbbbdf62f4bf726b58cf1205f0cfb..ce8683eff5b8ade57a2fcb77027cfe4b26986bc7 100644 +--- a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java ++++ b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java +@@ -86,6 +86,11 @@ public final class VanillaCommandWrapper extends BukkitCommand { + if (sender instanceof ProxiedCommandSender) { + return ((ProxiedNativeCommandSender) sender).getHandle(); + } ++ // Paper start ++ if (sender instanceof io.papermc.paper.commands.FeedbackForwardingSender feedback) { ++ return feedback.asVanilla(); ++ } ++ // Paper end + + throw new IllegalArgumentException("Cannot make " + sender + " a vanilla command listener"); + } diff --git a/patches/server/0650-Add-missing-structure-set-seed-configs.patch b/patches/server/0650-Add-missing-structure-set-seed-configs.patch new file mode 100644 index 0000000000..e2dfe31280 --- /dev/null +++ b/patches/server/0650-Add-missing-structure-set-seed-configs.patch @@ -0,0 +1,399 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Thu, 13 Jan 2022 23:05:53 -0800 +Subject: [PATCH] Add missing structure set seed configs + +The 4 missing structure set seed configs are strongholds, mineshafts, +buried treasure, and ancient cities. + +Strongholds use a ring placement scheme which isn't random so they +utilize the world seed by default, this adds a config to override it +for just generating the ring positions. + +Mineshafts and Buried Treasure structure sets are special cases +where the "salt" that can be defined for them via datapacks has 0 +effect because the difference between the spacing and separation is 1 +which is used as the upper bound in the random with salt. So the random +always returns the same int (0) so the salt has no effect. This adds +seeds/salts to the frequency reducer which has a similar effect. + +Co-authored-by: William Blake Galbreath <[email protected]> + +diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java +index fde17b4e2607fc443a33aea3a631aae6ccb71e2c..c64389acf0764c8d048bea2d99a21a0da832150d 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java ++++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java +@@ -574,7 +574,7 @@ public abstract class ChunkGenerator { + } + } + +- if (structureplacement.isStructureChunk(placementCalculator, chunkcoordintpair.x, chunkcoordintpair.z)) { ++ if (structureplacement.isStructureChunk(placementCalculator, chunkcoordintpair.x, chunkcoordintpair.z, structureplacement instanceof net.minecraft.world.level.chunk.ChunkGeneratorStructureState.KeyedRandomSpreadStructurePlacement keyed ? keyed.key : null)) { // Paper - Add missing structure set seed configs + if (list.size() == 1) { + this.tryGenerateStructure((StructureSet.StructureSelectionEntry) list.get(0), structureAccessor, registryManager, randomstate, structureTemplateManager, placementCalculator.getLevelSeed(), chunk, chunkcoordintpair, sectionposition, dimension); + } else { +diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java b/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java +index ed779a03e2ce7684428600dac4f2e92ab002f29f..a20520a6bd28bae1cee82258ac49d9753faba2bd 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java ++++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java +@@ -50,13 +50,14 @@ public class ChunkGeneratorStructureState { + private final Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> ringPositions = new Object2ObjectArrayMap(); + private boolean hasGeneratedPositions; + private final List<Holder<StructureSet>> possibleStructureSets; ++ public final SpigotWorldConfig conf; // Paper - Add missing structure set seed configs + + public static ChunkGeneratorStructureState createForFlat(RandomState randomstate, long i, BiomeSource worldchunkmanager, Stream<Holder<StructureSet>> stream, SpigotWorldConfig conf) { // Spigot + List<Holder<StructureSet>> list = stream.filter((holder) -> { + return ChunkGeneratorStructureState.hasBiomesForStructureSet((StructureSet) holder.value(), worldchunkmanager); + }).toList(); + +- return new ChunkGeneratorStructureState(randomstate, worldchunkmanager, i, 0L, ChunkGeneratorStructureState.injectSpigot(list, conf)); // Spigot ++ return new ChunkGeneratorStructureState(randomstate, worldchunkmanager, i, 0L, ChunkGeneratorStructureState.injectSpigot(list, conf), conf); // Spigot + } + + public static ChunkGeneratorStructureState createForNormal(RandomState randomstate, long i, BiomeSource worldchunkmanager, HolderLookup<StructureSet> holderlookup, SpigotWorldConfig conf) { // Spigot +@@ -64,14 +65,24 @@ public class ChunkGeneratorStructureState { + return ChunkGeneratorStructureState.hasBiomesForStructureSet((StructureSet) holder_c.value(), worldchunkmanager); + }).collect(Collectors.toUnmodifiableList()); + +- return new ChunkGeneratorStructureState(randomstate, worldchunkmanager, i, i, ChunkGeneratorStructureState.injectSpigot(list, conf)); // Spigot ++ return new ChunkGeneratorStructureState(randomstate, worldchunkmanager, i, i, ChunkGeneratorStructureState.injectSpigot(list, conf), conf); // Spigot + } ++ // Paper start - Add missing structure set seed configs; horrible hack because spigot creates a ton of direct Holders which lose track of the identifying key ++ public static final class KeyedRandomSpreadStructurePlacement extends RandomSpreadStructurePlacement { ++ public final net.minecraft.resources.ResourceKey<StructureSet> key; ++ public KeyedRandomSpreadStructurePlacement(net.minecraft.resources.ResourceKey<StructureSet> key, net.minecraft.core.Vec3i locateOffset, FrequencyReductionMethod frequencyReductionMethod, float frequency, int salt, java.util.Optional<StructurePlacement.ExclusionZone> exclusionZone, int spacing, int separation, net.minecraft.world.level.levelgen.structure.placement.RandomSpreadType spreadType) { ++ super(locateOffset, frequencyReductionMethod, frequency, salt, exclusionZone, spacing, separation, spreadType); ++ this.key = key; ++ } ++ } ++ // Paper end - Add missing structure set seed configs + + // Spigot start + private static List<Holder<StructureSet>> injectSpigot(List<Holder<StructureSet>> list, SpigotWorldConfig conf) { + return list.stream().map((holder) -> { + StructureSet structureset = holder.value(); +- if (structureset.placement() instanceof RandomSpreadStructurePlacement randomConfig) { ++ final Holder<StructureSet> newHolder; // Paper - Add missing structure set seed configs ++ if (structureset.placement() instanceof RandomSpreadStructurePlacement randomConfig && holder.unwrapKey().orElseThrow().location().getNamespace().equals(net.minecraft.resources.ResourceLocation.DEFAULT_NAMESPACE)) { // Paper - Add missing structure set seed configs; check namespace cause datapacks could add structure sets with the same path + String name = holder.unwrapKey().orElseThrow().location().getPath(); + int seed = randomConfig.salt; + +@@ -118,11 +129,27 @@ public class ChunkGeneratorStructureState { + case "villages": + seed = conf.villageSeed; + break; ++ // Paper start - Add missing structure set seed configs ++ case "ancient_cities": ++ seed = conf.ancientCitySeed; ++ break; ++ case "trail_ruins": ++ seed = conf.trailRuinsSeed; ++ break; ++ case "trial_chambers": ++ seed = conf.trialChambersSeed; ++ break; ++ // Paper end - Add missing structure set seed configs + } + +- structureset = new StructureSet(structureset.structures(), new RandomSpreadStructurePlacement(randomConfig.locateOffset, randomConfig.frequencyReductionMethod, randomConfig.frequency, seed, randomConfig.exclusionZone, randomConfig.spacing(), randomConfig.separation(), randomConfig.spreadType())); ++ // Paper start - Add missing structure set seed configs ++ structureset = new StructureSet(structureset.structures(), new KeyedRandomSpreadStructurePlacement(holder.unwrapKey().orElseThrow(), randomConfig.locateOffset, randomConfig.frequencyReductionMethod, randomConfig.frequency, seed, randomConfig.exclusionZone, randomConfig.spacing(), randomConfig.separation(), randomConfig.spreadType())); ++ newHolder = Holder.direct(structureset); // I really wish we didn't have to do this here ++ } else { ++ newHolder = holder; + } +- return Holder.direct(structureset); ++ return newHolder; ++ // Paper end - Add missing structure set seed configs + }).collect(Collectors.toUnmodifiableList()); + } + // Spigot end +@@ -139,12 +166,13 @@ public class ChunkGeneratorStructureState { + return stream.anyMatch(set::contains); + } + +- private ChunkGeneratorStructureState(RandomState noiseConfig, BiomeSource biomeSource, long structureSeed, long concentricRingSeed, List<Holder<StructureSet>> structureSets) { ++ private ChunkGeneratorStructureState(RandomState noiseConfig, BiomeSource biomeSource, long structureSeed, long concentricRingSeed, List<Holder<StructureSet>> structureSets, SpigotWorldConfig conf) { // Paper - Add missing structure set seed configs + this.randomState = noiseConfig; + this.levelSeed = structureSeed; + this.biomeSource = biomeSource; + this.concentricRingsSeed = concentricRingSeed; + this.possibleStructureSets = structureSets; ++ this.conf = conf; // Paper - Add missing structure set seed configs + } + + public List<Holder<StructureSet>> possibleStructureSets() { +@@ -198,7 +226,13 @@ public class ChunkGeneratorStructureState { + HolderSet<Biome> holderset = placement.preferredBiomes(); + RandomSource randomsource = RandomSource.create(); + ++ // Paper start - Add missing structure set seed configs ++ if (this.conf.strongholdSeed != null && structureSetEntry.is(net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.STRONGHOLDS)) { ++ randomsource.setSeed(this.conf.strongholdSeed); ++ } else { ++ // Paper end - Add missing structure set seed configs + randomsource.setSeed(this.concentricRingsSeed); ++ } // Paper - Add missing structure set seed configs + double d0 = randomsource.nextDouble() * Math.PI * 2.0D; + int l = 0; + int i1 = 0; +@@ -275,7 +309,7 @@ public class ChunkGeneratorStructureState { + + for (int l = centerChunkX - chunkCount; l <= centerChunkX + chunkCount; ++l) { + for (int i1 = centerChunkZ - chunkCount; i1 <= centerChunkZ + chunkCount; ++i1) { +- if (structureplacement.isStructureChunk(this, l, i1)) { ++ if (structureplacement.isStructureChunk(this, l, i1, structureplacement instanceof KeyedRandomSpreadStructurePlacement keyed ? keyed.key : null)) { // Paper - Add missing structure set seed configs + return true; + } + } +diff --git a/src/main/java/net/minecraft/world/level/levelgen/structure/StructureCheck.java b/src/main/java/net/minecraft/world/level/levelgen/structure/StructureCheck.java +index 953ab7638f7242b5a11dd1de8786172443a0558c..5f354b333a39b873915bedd57b647355ae5bdf56 100644 +--- a/src/main/java/net/minecraft/world/level/levelgen/structure/StructureCheck.java ++++ b/src/main/java/net/minecraft/world/level/levelgen/structure/StructureCheck.java +@@ -74,6 +74,20 @@ public class StructureCheck { + this.fixerUpper = dataFixer; + } + ++ // Paper start - add missing structure salt configs ++ @Nullable ++ private Integer getSaltOverride(Structure type) { ++ if (this.heightAccessor instanceof net.minecraft.server.level.ServerLevel serverLevel) { ++ if (type instanceof net.minecraft.world.level.levelgen.structure.structures.MineshaftStructure) { ++ return serverLevel.spigotConfig.mineshaftSeed; ++ } else if (type instanceof net.minecraft.world.level.levelgen.structure.structures.BuriedTreasureStructure) { ++ return serverLevel.spigotConfig.buriedTreasureSeed; ++ } ++ } ++ return null; ++ } ++ // Paper end - add missing structure seed configs ++ + public StructureCheckResult checkStart(ChunkPos pos, Structure type, StructurePlacement placement, boolean skipReferencedStructures) { + long l = pos.toLong(); + Object2IntMap<Structure> object2IntMap = this.loadedChunks.get(l); +@@ -83,7 +97,7 @@ public class StructureCheck { + StructureCheckResult structureCheckResult = this.tryLoadFromStorage(pos, type, skipReferencedStructures, l); + if (structureCheckResult != null) { + return structureCheckResult; +- } else if (!placement.applyAdditionalChunkRestrictions(pos.x, pos.z, this.seed)) { ++ } else if (!placement.applyAdditionalChunkRestrictions(pos.x, pos.z, this.seed, this.getSaltOverride(type))) { // Paper - add missing structure seed configs + return StructureCheckResult.START_NOT_PRESENT; + } else { + boolean bl = this.featureChecks +diff --git a/src/main/java/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java b/src/main/java/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java +index a4c34e9415632354d33668a38d06453ada4d3c77..cbf13e4f2da6a27619e9bc9a7cd73bb6e69cad2a 100644 +--- a/src/main/java/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java ++++ b/src/main/java/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java +@@ -79,14 +79,30 @@ public abstract class StructurePlacement { + return this.exclusionZone; + } + ++ @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - Add missing structure set seed configs + public boolean isStructureChunk(ChunkGeneratorStructureState calculator, int chunkX, int chunkZ) { ++ // Paper start - Add missing structure set seed configs ++ return this.isStructureChunk(calculator, chunkX, chunkZ, null); ++ } ++ public boolean isStructureChunk(ChunkGeneratorStructureState calculator, int chunkX, int chunkZ, @org.jetbrains.annotations.Nullable net.minecraft.resources.ResourceKey<StructureSet> structureSetKey) { ++ Integer saltOverride = null; ++ if (structureSetKey != null) { ++ if (structureSetKey == net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.MINESHAFTS) { ++ saltOverride = calculator.conf.mineshaftSeed; ++ } else if (structureSetKey == net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.BURIED_TREASURES) { ++ saltOverride = calculator.conf.buriedTreasureSeed; ++ } ++ } ++ // Paper end - Add missing structure set seed configs + return this.isPlacementChunk(calculator, chunkX, chunkZ) +- && this.applyAdditionalChunkRestrictions(chunkX, chunkZ, calculator.getLevelSeed()) ++ && this.applyAdditionalChunkRestrictions(chunkX, chunkZ, calculator.getLevelSeed(), saltOverride) // Paper - Add missing structure set seed configs + && this.applyInteractionsWithOtherStructures(calculator, chunkX, chunkZ); + } + +- public boolean applyAdditionalChunkRestrictions(int chunkX, int chunkZ, long seed) { +- return !(this.frequency < 1.0F) || this.frequencyReductionMethod.shouldGenerate(seed, this.salt, chunkX, chunkZ, this.frequency); ++ // Paper start - Add missing structure set seed configs ++ public boolean applyAdditionalChunkRestrictions(int chunkX, int chunkZ, long seed, @org.jetbrains.annotations.Nullable Integer saltOverride) { ++ return !(this.frequency < 1.0F) || this.frequencyReductionMethod.shouldGenerate(seed, this.salt, chunkX, chunkZ, this.frequency, saltOverride); ++ // Paper end - Add missing structure set seed configs + } + + public boolean applyInteractionsWithOtherStructures(ChunkGeneratorStructureState calculator, int centerChunkX, int centerChunkZ) { +@@ -101,25 +117,31 @@ public abstract class StructurePlacement { + + public abstract StructurePlacementType<?> type(); + +- private static boolean probabilityReducer(long seed, int salt, int chunkX, int chunkZ, float frequency) { ++ private static boolean probabilityReducer(long seed, int salt, int chunkX, int chunkZ, float frequency, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs; ignore here + WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); + worldgenRandom.setLargeFeatureWithSalt(seed, salt, chunkX, chunkZ); + return worldgenRandom.nextFloat() < frequency; + } + +- private static boolean legacyProbabilityReducerWithDouble(long seed, int salt, int chunkX, int chunkZ, float frequency) { ++ private static boolean legacyProbabilityReducerWithDouble(long seed, int salt, int chunkX, int chunkZ, float frequency, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs + WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); ++ if (saltOverride == null) { // Paper - Add missing structure set seed configs + worldgenRandom.setLargeFeatureSeed(seed, chunkX, chunkZ); ++ // Paper start - Add missing structure set seed configs ++ } else { ++ worldgenRandom.setLargeFeatureWithSalt(seed, chunkX, chunkZ, saltOverride); ++ } ++ // Paper end - Add missing structure set seed configs + return worldgenRandom.nextDouble() < (double)frequency; + } + +- private static boolean legacyArbitrarySaltProbabilityReducer(long seed, int salt, int chunkX, int chunkZ, float frequency) { ++ private static boolean legacyArbitrarySaltProbabilityReducer(long seed, int salt, int chunkX, int chunkZ, float frequency, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs + WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); +- worldgenRandom.setLargeFeatureWithSalt(seed, chunkX, chunkZ, 10387320); ++ worldgenRandom.setLargeFeatureWithSalt(seed, chunkX, chunkZ, saltOverride != null ? saltOverride : HIGHLY_ARBITRARY_RANDOM_SALT); // Paper - Add missing structure set seed configs + return worldgenRandom.nextFloat() < frequency; + } + +- private static boolean legacyPillagerOutpostReducer(long seed, int salt, int chunkX, int chunkZ, float frequency) { ++ private static boolean legacyPillagerOutpostReducer(long seed, int salt, int chunkX, int chunkZ, float frequency, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs; ignore here + int i = chunkX >> 4; + int j = chunkZ >> 4; + WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); +@@ -147,7 +169,7 @@ public abstract class StructurePlacement { + + @FunctionalInterface + public interface FrequencyReducer { +- boolean shouldGenerate(long seed, int salt, int chunkX, int chunkZ, float chance); ++ boolean shouldGenerate(long seed, int salt, int chunkX, int chunkZ, float chance, @org.jetbrains.annotations.Nullable Integer saltOverride); // Paper - Add missing structure set seed configs + } + + public static enum FrequencyReductionMethod implements StringRepresentable { +@@ -167,8 +189,8 @@ public abstract class StructurePlacement { + this.reducer = generationPredicate; + } + +- public boolean shouldGenerate(long seed, int salt, int chunkX, int chunkZ, float chance) { +- return this.reducer.shouldGenerate(seed, salt, chunkX, chunkZ, chance); ++ public boolean shouldGenerate(long seed, int salt, int chunkX, int chunkZ, float chance, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs ++ return this.reducer.shouldGenerate(seed, salt, chunkX, chunkZ, chance, saltOverride); // Paper - Add missing structure set seed configs + } + + @Override +diff --git a/src/main/java/org/spigotmc/SpigotWorldConfig.java b/src/main/java/org/spigotmc/SpigotWorldConfig.java +index e76f96a5c48d1eda2f9bbb3e11dd79f23f9ab75c..2b263246135c85aa225120519e9702a628773935 100644 +--- a/src/main/java/org/spigotmc/SpigotWorldConfig.java ++++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java +@@ -322,6 +322,18 @@ public class SpigotWorldConfig + public int mansionSeed; + public int fossilSeed; + public int portalSeed; ++ // Paper start - add missing structure set configs ++ public int ancientCitySeed; ++ public int trailRuinsSeed; ++ public int trialChambersSeed; ++ public int buriedTreasureSeed; ++ public Integer mineshaftSeed; ++ public Long strongholdSeed; ++ private <N extends Number> N getSeed(String path, java.util.function.Function<String, N> toNumberFunc) { ++ final String value = this.getString(path, "default"); ++ return org.apache.commons.lang3.math.NumberUtils.isParsable(value) ? toNumberFunc.apply(value) : null; ++ } ++ // Paper end + private void initWorldGenSeeds() + { + this.villageSeed = this.getInt( "seed-village", 10387312 ); +@@ -339,6 +351,14 @@ public class SpigotWorldConfig + this.mansionSeed = this.getInt( "seed-mansion", 10387319 ); + this.fossilSeed = this.getInt( "seed-fossil", 14357921 ); + this.portalSeed = this.getInt( "seed-portal", 34222645 ); ++ // Paper start - add missing structure set configs ++ this.ancientCitySeed = this.getInt("seed-ancientcity", 20083232); ++ this.trailRuinsSeed = this.getInt("seed-trailruins", 83469867); ++ this.trialChambersSeed = this.getInt("seed-trialchambers", 94251327); ++ this.buriedTreasureSeed = this.getInt("seed-buriedtreasure", 10387320); // StructurePlacement#HIGHLY_ARBITRARY_RANDOM_SALT ++ this.mineshaftSeed = this.getSeed("seed-mineshaft", Integer::parseInt); ++ this.strongholdSeed = this.getSeed("seed-stronghold", Long::parseLong); ++ // Paper end + this.log( "Custom Map Seeds: Village: " + this.villageSeed + " Desert: " + this.desertSeed + " Igloo: " + this.iglooSeed + " Jungle: " + this.jungleSeed + " Swamp: " + this.swampSeed + " Monument: " + this.monumentSeed + + " Ocean: " + this.oceanSeed + " Shipwreck: " + this.shipwreckSeed + " End City: " + this.endCitySeed + " Slime: " + this.slimeSeed + " Nether: " + this.netherSeed + " Mansion: " + this.mansionSeed + " Fossil: " + this.fossilSeed + " Portal: " + this.portalSeed ); + } +diff --git a/src/test/java/io/papermc/paper/world/structure/StructureSeedConfigTest.java b/src/test/java/io/papermc/paper/world/structure/StructureSeedConfigTest.java +new file mode 100644 +index 0000000000000000000000000000000000000000..cc1fb5ae9e0898735771c6163c9b90658fb61eed +--- /dev/null ++++ b/src/test/java/io/papermc/paper/world/structure/StructureSeedConfigTest.java +@@ -0,0 +1,77 @@ ++package io.papermc.paper.world.structure; ++ ++import io.papermc.paper.configuration.PaperConfigurations; ++import java.io.File; ++import java.lang.reflect.Field; ++import net.minecraft.core.Registry; ++import net.minecraft.core.registries.Registries; ++import net.minecraft.resources.ResourceKey; ++import net.minecraft.resources.ResourceLocation; ++import net.minecraft.world.level.levelgen.structure.BuiltinStructureSets; ++import net.minecraft.world.level.levelgen.structure.StructureSet; ++import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement; ++import org.bukkit.configuration.file.YamlConfiguration; ++import org.bukkit.support.RegistryHelper; ++import org.bukkit.support.environment.AllFeatures; ++import org.jetbrains.annotations.NotNull; ++import org.junit.jupiter.api.Test; ++import org.spigotmc.SpigotConfig; ++import org.spigotmc.SpigotWorldConfig; ++ ++import static org.junit.jupiter.api.Assertions.assertEquals; ++ ++@AllFeatures ++public class StructureSeedConfigTest { ++ ++ @Test ++ public void checkStructureSeedDefaults() throws ReflectiveOperationException { ++ SpigotConfig.config = new YamlConfiguration() { ++ @Override ++ public void save(final @NotNull File file) { ++ // no-op ++ } ++ }; ++ final SpigotWorldConfig config = PaperConfigurations.SPIGOT_WORLD_DEFAULTS.get(); ++ ++ ++ final Registry<StructureSet> structureSets = RegistryHelper.getRegistry().lookupOrThrow(Registries.STRUCTURE_SET); ++ for (final ResourceKey<StructureSet> setKey : structureSets.registryKeySet()) { ++ assertEquals(ResourceLocation.DEFAULT_NAMESPACE, setKey.location().getNamespace()); ++ final StructureSet set = structureSets.getValueOrThrow(setKey); ++ if (setKey == BuiltinStructureSets.STRONGHOLDS) { // special case due to seed matching world seed ++ assertEquals(0, set.placement().salt); ++ continue; ++ } ++ int salt = switch (setKey.location().getPath()) { ++ case "villages" -> config.villageSeed; ++ case "desert_pyramids" -> config.desertSeed; ++ case "igloos" -> config.iglooSeed; ++ case "jungle_temples" -> config.jungleSeed; ++ case "swamp_huts" -> config.swampSeed; ++ case "pillager_outposts" -> config.outpostSeed; ++ case "ocean_monuments" -> config.monumentSeed; ++ case "woodland_mansions" -> config.mansionSeed; ++ case "buried_treasures" -> config.buriedTreasureSeed; ++ case "mineshafts" -> config.mineshaftSeed == null ? 0 : config.mineshaftSeed; // mineshaft seed is set differently ++ case "ruined_portals" -> config.portalSeed; ++ case "shipwrecks" -> config.shipwreckSeed; ++ case "ocean_ruins" -> config.oceanSeed; ++ case "nether_complexes" -> config.netherSeed; ++ case "nether_fossils" -> config.fossilSeed; ++ case "end_cities" -> config.endCitySeed; ++ case "ancient_cities" -> config.ancientCitySeed; ++ case "trail_ruins" -> config.trailRuinsSeed; ++ case "trial_chambers" -> config.trialChambersSeed; ++ default -> throw new AssertionError("Missing structure set seed in SpigotWorldConfig for " + setKey); ++ }; ++ if (setKey == BuiltinStructureSets.BURIED_TREASURES) { ++ final Field field = StructurePlacement.class.getDeclaredField("HIGHLY_ARBITRARY_RANDOM_SALT"); ++ field.trySetAccessible(); ++ assertEquals(0, set.placement().salt); ++ assertEquals(field.get(null), salt, "Mismatched default seed for " + setKey + ". Should be " + field.get(null)); ++ continue; ++ } ++ assertEquals(set.placement().salt, salt, "Mismatched default seed for " + setKey + ". Should be " + set.placement().salt); ++ } ++ } ++} diff --git a/patches/server/0651-Fix-cancelled-powdered-snow-bucket-placement.patch b/patches/server/0651-Fix-cancelled-powdered-snow-bucket-placement.patch new file mode 100644 index 0000000000..f07f56f707 --- /dev/null +++ b/patches/server/0651-Fix-cancelled-powdered-snow-bucket-placement.patch @@ -0,0 +1,31 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Fri, 8 Oct 2021 13:12:58 -0700 +Subject: [PATCH] Fix cancelled powdered snow bucket placement + +Cancelling the placement of powdered snow from the powdered +snow bucket didn't revert grass that became snowy because of the +placement. + +diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java +index e320264c2283c2c09910ea70606413c73f443b1f..18f84d54ec72debec652adb22067e11aa058b238 100644 +--- a/src/main/java/net/minecraft/world/item/ItemStack.java ++++ b/src/main/java/net/minecraft/world/item/ItemStack.java +@@ -430,7 +430,7 @@ public final class ItemStack implements DataComponentHolder { + int oldCount = this.getCount(); + ServerLevel world = (ServerLevel) context.getLevel(); + +- if (!(item instanceof BucketItem || item instanceof SolidBucketItem)) { // if not bucket ++ if (!(item instanceof BucketItem/* || item instanceof SolidBucketItem*/)) { // if not bucket // Paper - Fix cancelled powdered snow bucket placement + world.captureBlockStates = true; + // special case bonemeal + if (item == Items.BONE_MEAL) { +@@ -493,7 +493,7 @@ public final class ItemStack implements DataComponentHolder { + world.capturedBlockStates.clear(); + if (blocks.size() > 1) { + placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockMultiPlaceEvent(world, entityhuman, enumhand, blocks, blockposition.getX(), blockposition.getY(), blockposition.getZ()); +- } else if (blocks.size() == 1) { ++ } else if (blocks.size() == 1 && item != Items.POWDER_SNOW_BUCKET) { // Paper - Fix cancelled powdered snow bucket placement + placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPlaceEvent(world, entityhuman, enumhand, blocks.get(0), blockposition.getX(), blockposition.getY(), blockposition.getZ()); + } + diff --git a/patches/server/0652-Add-missing-Validate-calls-to-CraftServer-getSpawnLi.patch b/patches/server/0652-Add-missing-Validate-calls-to-CraftServer-getSpawnLi.patch new file mode 100644 index 0000000000..d5922672e4 --- /dev/null +++ b/patches/server/0652-Add-missing-Validate-calls-to-CraftServer-getSpawnLi.patch @@ -0,0 +1,20 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jason Penilla <[email protected]> +Date: Sat, 12 Feb 2022 12:40:50 -0700 +Subject: [PATCH] Add missing Validate calls to CraftServer#getSpawnLimit + +Copies appropriate checks from CraftWorld#getSpawnLimit + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index 3b01907bc119853e0676e912e9a29b05d9aa5763..188e3066d6659b5e45cec0b50dcbd5d20659830a 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -2342,6 +2342,8 @@ public final class CraftServer implements Server { + @Override + public int getSpawnLimit(SpawnCategory spawnCategory) { + // Paper start - Add mobcaps commands ++ Preconditions.checkArgument(spawnCategory != null, "SpawnCategory cannot be null"); ++ Preconditions.checkArgument(CraftSpawnCategory.isValidForLimits(spawnCategory), "SpawnCategory." + spawnCategory + " does not have a spawn limit."); + return this.getSpawnLimitUnsafe(spawnCategory); + } + public int getSpawnLimitUnsafe(final SpawnCategory spawnCategory) { diff --git a/patches/server/0653-Add-GameEvent-tags.patch b/patches/server/0653-Add-GameEvent-tags.patch new file mode 100644 index 0000000000..bb674d2f17 --- /dev/null +++ b/patches/server/0653-Add-GameEvent-tags.patch @@ -0,0 +1,81 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sun, 3 Jan 2021 20:03:35 -0800 +Subject: [PATCH] Add GameEvent tags + + +diff --git a/src/main/java/io/papermc/paper/CraftGameEventTag.java b/src/main/java/io/papermc/paper/CraftGameEventTag.java +new file mode 100644 +index 0000000000000000000000000000000000000000..874c420e60b6be09c806d64f40cf63663ffddc07 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/CraftGameEventTag.java +@@ -0,0 +1,35 @@ ++package io.papermc.paper; ++ ++import net.minecraft.core.registries.BuiltInRegistries; ++import net.minecraft.core.registries.Registries; ++import net.minecraft.resources.ResourceKey; ++import net.minecraft.tags.TagKey; ++import org.bukkit.GameEvent; ++import org.bukkit.craftbukkit.tag.CraftTag; ++import org.bukkit.craftbukkit.util.CraftNamespacedKey; ++import org.jetbrains.annotations.NotNull; ++ ++import java.util.Collections; ++import java.util.IdentityHashMap; ++import java.util.Map; ++import java.util.Objects; ++import java.util.Set; ++import java.util.stream.Collectors; ++ ++public class CraftGameEventTag extends CraftTag<net.minecraft.world.level.gameevent.GameEvent, GameEvent> { ++ ++ public CraftGameEventTag(net.minecraft.core.Registry<net.minecraft.world.level.gameevent.GameEvent> registry, TagKey<net.minecraft.world.level.gameevent.GameEvent> tag) { ++ super(registry, tag); ++ } ++ ++ private static final Map<GameEvent, ResourceKey<net.minecraft.world.level.gameevent.GameEvent>> KEY_CACHE = Collections.synchronizedMap(new IdentityHashMap<>()); ++ @Override ++ public boolean isTagged(@NotNull GameEvent gameEvent) { ++ return registry.getOrThrow(KEY_CACHE.computeIfAbsent(gameEvent, event -> ResourceKey.create(Registries.GAME_EVENT, CraftNamespacedKey.toMinecraft(event.getKey())))).is(tag); ++ } ++ ++ @Override ++ public @NotNull Set<GameEvent> getValues() { ++ return getHandle().stream().map((nms) -> Objects.requireNonNull(GameEvent.getByKey(CraftNamespacedKey.fromMinecraft(BuiltInRegistries.GAME_EVENT.getKey(nms.value()))), nms + " is not a recognized game event")).collect(Collectors.toUnmodifiableSet()); ++ } ++} +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index 188e3066d6659b5e45cec0b50dcbd5d20659830a..72f8d3a89396f9289f5b451b24cc181e7ac3222e 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -2709,6 +2709,15 @@ public final class CraftServer implements Server { + return (org.bukkit.Tag<T>) new CraftDamageTag(damageRegistry, damageTagKey); + } + } ++ // Paper start ++ case org.bukkit.Tag.REGISTRY_GAME_EVENTS -> { ++ Preconditions.checkArgument(clazz == org.bukkit.GameEvent.class, "Game Event namespace must have GameEvent type"); ++ TagKey<net.minecraft.world.level.gameevent.GameEvent> gameEventTagKey = TagKey.create(net.minecraft.core.registries.Registries.GAME_EVENT, key); ++ if (net.minecraft.core.registries.BuiltInRegistries.GAME_EVENT.get(gameEventTagKey).isPresent()) { ++ return (org.bukkit.Tag<T>) new io.papermc.paper.CraftGameEventTag(net.minecraft.core.registries.BuiltInRegistries.GAME_EVENT, gameEventTagKey); ++ } ++ } ++ // Paper end + default -> throw new IllegalArgumentException(); + } + +@@ -2746,6 +2755,13 @@ public final class CraftServer implements Server { + net.minecraft.core.Registry<DamageType> damageTags = CraftRegistry.getMinecraftRegistry(Registries.DAMAGE_TYPE); + return damageTags.getTags().map(pair -> (org.bukkit.Tag<T>) new CraftDamageTag(damageTags, pair.key())).collect(ImmutableList.toImmutableList()); + } ++ // Paper start ++ case org.bukkit.Tag.REGISTRY_GAME_EVENTS -> { ++ Preconditions.checkArgument(clazz == org.bukkit.GameEvent.class); ++ net.minecraft.core.Registry<net.minecraft.world.level.gameevent.GameEvent> gameEvents = net.minecraft.core.registries.BuiltInRegistries.GAME_EVENT; ++ return gameEvents.getTags().map(pair -> (org.bukkit.Tag<T>) new io.papermc.paper.CraftGameEventTag(gameEvents, pair.key())).collect(ImmutableList.toImmutableList()); ++ } ++ // Paper end + default -> throw new IllegalArgumentException(); + } + } diff --git a/patches/server/0654-Execute-chunk-tasks-fairly-for-worlds-while-waiting-.patch b/patches/server/0654-Execute-chunk-tasks-fairly-for-worlds-while-waiting-.patch new file mode 100644 index 0000000000..5929e0f042 --- /dev/null +++ b/patches/server/0654-Execute-chunk-tasks-fairly-for-worlds-while-waiting-.patch @@ -0,0 +1,37 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf <[email protected]> +Date: Tue, 28 Dec 2021 07:19:01 -0800 +Subject: [PATCH] Execute chunk tasks fairly for worlds while waiting for next + tick + +Currently, only the first world would have had tasks executed. +This might result in chunks loading far slower in the nether, +for example. + +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index c133a646baf88e0489d358e302d67f21f76b47c3..17700ebf508f3ac7a4d1cdd8d52355afaf5d006f 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -1404,6 +1404,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa + if (super.pollTask()) { + return true; + } else { ++ boolean ret = false; // Paper - force execution of all worlds, do not just bias the first + if (this.tickRateManager.isSprinting() || this.haveTime()) { + Iterator iterator = this.getAllLevels().iterator(); + +@@ -1411,12 +1412,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa + ServerLevel worldserver = (ServerLevel) iterator.next(); + + if (worldserver.getChunkSource().pollTask()) { +- return true; ++ ret = true; // Paper - force execution of all worlds, do not just bias the first + } + } + } + +- return false; ++ return ret; // Paper - force execution of all worlds, do not just bias the first + } + } + diff --git a/patches/server/0655-Furnace-RecipesUsed-API.patch b/patches/server/0655-Furnace-RecipesUsed-API.patch new file mode 100644 index 0000000000..f2b8d8fe65 --- /dev/null +++ b/patches/server/0655-Furnace-RecipesUsed-API.patch @@ -0,0 +1,48 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Thu, 13 Jan 2022 15:20:47 -0800 +Subject: [PATCH] Furnace RecipesUsed API + + +diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftFurnace.java b/src/main/java/org/bukkit/craftbukkit/block/CraftFurnace.java +index fd2f28ee785283bcc979c7a146308827f2ce2ba9..91c10f7d2044d60ba756179d93c5c72d32bba074 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/CraftFurnace.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/CraftFurnace.java +@@ -103,5 +103,37 @@ public abstract class CraftFurnace<T extends AbstractFurnaceBlockEntity> extends + snapshot.cookSpeedMultiplier = multiplier; + snapshot.cookingTotalTime = AbstractFurnaceBlockEntity.getTotalCookTime(this.isPlaced() ? this.world.getHandle() : null, snapshot, snapshot.recipeType, snapshot.cookSpeedMultiplier); // Update the snapshot's current total cook time to scale with the newly set multiplier + } ++ ++ @Override ++ public int getRecipeUsedCount(org.bukkit.NamespacedKey furnaceRecipe) { ++ return this.getSnapshot().recipesUsed.getInt(io.papermc.paper.util.MCUtil.toResourceKey(net.minecraft.core.registries.Registries.RECIPE, furnaceRecipe)); ++ } ++ ++ @Override ++ public boolean hasRecipeUsedCount(org.bukkit.NamespacedKey furnaceRecipe) { ++ return this.getSnapshot().recipesUsed.containsKey(io.papermc.paper.util.MCUtil.toResourceKey(net.minecraft.core.registries.Registries.RECIPE, furnaceRecipe)); ++ } ++ ++ @Override ++ public void setRecipeUsedCount(org.bukkit.inventory.CookingRecipe<?> furnaceRecipe, int count) { ++ final var location = io.papermc.paper.util.MCUtil.toResourceKey(net.minecraft.core.registries.Registries.RECIPE, furnaceRecipe.getKey()); ++ java.util.Optional<net.minecraft.world.item.crafting.RecipeHolder<?>> nmsRecipe = (this.isPlaced() ? this.world.getHandle().recipeAccess() : net.minecraft.server.MinecraftServer.getServer().getRecipeManager()).byKey(location); ++ com.google.common.base.Preconditions.checkArgument(nmsRecipe.isPresent() && nmsRecipe.get().value() instanceof net.minecraft.world.item.crafting.AbstractCookingRecipe, furnaceRecipe.getKey() + " is not recognized as a valid and registered furnace recipe"); ++ if (count > 0) { ++ this.getSnapshot().recipesUsed.put(location, count); ++ } else { ++ this.getSnapshot().recipesUsed.removeInt(location); ++ } ++ } ++ ++ @Override ++ public void setRecipesUsed(java.util.Map<org.bukkit.inventory.CookingRecipe<?>, Integer> recipesUsed) { ++ this.getSnapshot().recipesUsed.clear(); ++ recipesUsed.forEach((recipe, integer) -> { ++ if (integer != null) { ++ this.setRecipeUsedCount(recipe, integer); ++ } ++ }); ++ } + // Paper end + } diff --git a/patches/server/0656-Configurable-sculk-sensor-listener-range.patch b/patches/server/0656-Configurable-sculk-sensor-listener-range.patch new file mode 100644 index 0000000000..3dd1324354 --- /dev/null +++ b/patches/server/0656-Configurable-sculk-sensor-listener-range.patch @@ -0,0 +1,106 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Thu, 19 Aug 2021 18:45:42 -0700 +Subject: [PATCH] Configurable sculk sensor listener range + +== AT == +public-f net.minecraft.world.level.gameevent.vibrations.VibrationListener listenerRange + +diff --git a/src/main/java/net/minecraft/world/level/block/entity/CalibratedSculkSensorBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/CalibratedSculkSensorBlockEntity.java +index 41ccbee5fc7767a7d5e1cdca0ec7d9a17ee80a90..1d28f117965da22694b12018923a5f1347905085 100644 +--- a/src/main/java/net/minecraft/world/level/block/entity/CalibratedSculkSensorBlockEntity.java ++++ b/src/main/java/net/minecraft/world/level/block/entity/CalibratedSculkSensorBlockEntity.java +@@ -20,6 +20,12 @@ public class CalibratedSculkSensorBlockEntity extends SculkSensorBlockEntity { + public VibrationSystem.User createVibrationUser() { + return new CalibratedSculkSensorBlockEntity.VibrationUser(this.getBlockPos()); + } ++ // Paper start - Configurable sculk sensor listener range ++ @Override ++ protected void saveRangeOverride(final net.minecraft.nbt.CompoundTag nbt) { ++ if (this.rangeOverride != null && this.rangeOverride != 16) nbt.putInt(PAPER_LISTENER_RANGE_NBT_KEY, this.rangeOverride); // only save if it's different from the default ++ } ++ // Paper end - Configurable sculk sensor listener range + + protected class VibrationUser extends SculkSensorBlockEntity.VibrationUser { + public VibrationUser(final BlockPos pos) { +@@ -28,6 +34,7 @@ public class CalibratedSculkSensorBlockEntity extends SculkSensorBlockEntity { + + @Override + public int getListenerRadius() { ++ if (CalibratedSculkSensorBlockEntity.this.rangeOverride != null) return CalibratedSculkSensorBlockEntity.this.rangeOverride; // Paper - Configurable sculk sensor listener range + return 16; + } + +diff --git a/src/main/java/net/minecraft/world/level/block/entity/SculkSensorBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/SculkSensorBlockEntity.java +index 070814eb011ab8e02218c91e1cf75be5501c1a0a..28849cf84afcdc0d9fc245fac1a8d769a2db3b68 100644 +--- a/src/main/java/net/minecraft/world/level/block/entity/SculkSensorBlockEntity.java ++++ b/src/main/java/net/minecraft/world/level/block/entity/SculkSensorBlockEntity.java +@@ -26,6 +26,7 @@ public class SculkSensorBlockEntity extends BlockEntity implements GameEventList + private final VibrationSystem.Listener vibrationListener; + private final VibrationSystem.User vibrationUser = this.createVibrationUser(); + public int lastVibrationFrequency; ++ @Nullable public Integer rangeOverride = null; // Paper - Configurable sculk sensor listener range + + protected SculkSensorBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) { + super(type, pos, state); +@@ -52,8 +53,16 @@ public class SculkSensorBlockEntity extends BlockEntity implements GameEventList + .resultOrPartial(string -> LOGGER.error("Failed to parse vibration listener for Sculk Sensor: '{}'", string)) + .ifPresent(listener -> this.vibrationData = listener); + } ++ // Paper start - Configurable sculk sensor listener range ++ if (nbt.contains(PAPER_LISTENER_RANGE_NBT_KEY)) { ++ this.rangeOverride = nbt.getInt(PAPER_LISTENER_RANGE_NBT_KEY); ++ } else { ++ this.rangeOverride = null; ++ } ++ // Paper end - Configurable sculk sensor listener range + } + ++ protected static final String PAPER_LISTENER_RANGE_NBT_KEY = "Paper.ListenerRange"; // Paper - Configurable sculk sensor listener range + @Override + protected void saveAdditional(CompoundTag nbt, HolderLookup.Provider registries) { + super.saveAdditional(nbt, registries); +@@ -63,7 +72,13 @@ public class SculkSensorBlockEntity extends BlockEntity implements GameEventList + .encodeStart(registryOps, this.vibrationData) + .resultOrPartial(string -> LOGGER.error("Failed to encode vibration listener for Sculk Sensor: '{}'", string)) + .ifPresent(listenerNbt -> nbt.put("listener", listenerNbt)); ++ this.saveRangeOverride(nbt); // Paper - Configurable sculk sensor listener range ++ } ++ // Paper start - Configurable sculk sensor listener range ++ protected void saveRangeOverride(CompoundTag nbt) { ++ if (this.rangeOverride != null && this.rangeOverride != VibrationUser.LISTENER_RANGE) nbt.putInt(PAPER_LISTENER_RANGE_NBT_KEY, this.rangeOverride); // only save if it's different from the default + } ++ // Paper end - Configurable sculk sensor listener range + + @Override + public VibrationSystem.Data getVibrationData() { +@@ -100,6 +115,7 @@ public class SculkSensorBlockEntity extends BlockEntity implements GameEventList + + @Override + public int getListenerRadius() { ++ if (SculkSensorBlockEntity.this.rangeOverride != null) return SculkSensorBlockEntity.this.rangeOverride; // Paper - Configurable sculk sensor listener range + return 8; + } + +diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftSculkSensor.java b/src/main/java/org/bukkit/craftbukkit/block/CraftSculkSensor.java +index 70d85dbfcaae7ee632a4f541334302b46615a254..6ba229afb0219ff229fd794c59d7585f010742ee 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/CraftSculkSensor.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/CraftSculkSensor.java +@@ -36,4 +36,17 @@ public class CraftSculkSensor<T extends SculkSensorBlockEntity> extends CraftBlo + public CraftSculkSensor<T> copy(Location location) { + return new CraftSculkSensor<>(this, location); + } ++ ++ // Paper start ++ @Override ++ public int getListenerRange() { ++ return this.getSnapshot().getListener().getListenerRadius(); ++ } ++ ++ @Override ++ public void setListenerRange(int range) { ++ Preconditions.checkArgument(range > 0, "Vibration listener range must be greater than 0"); ++ this.getSnapshot().rangeOverride = range; ++ } ++ // Paper end + } diff --git a/patches/server/0657-Add-missing-block-data-API.patch b/patches/server/0657-Add-missing-block-data-API.patch new file mode 100644 index 0000000000..7c18d0cb0d --- /dev/null +++ b/patches/server/0657-Add-missing-block-data-API.patch @@ -0,0 +1,214 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sat, 16 Oct 2021 22:57:31 -0700 +Subject: [PATCH] Add missing block data API + +General purpose patch adding missing getters/setters to BlockData and +its child types. + +Co-authored-by: SoSeDiK <[email protected]> +Co-authored-by: Fabrizio La Rosa <[email protected]> + +diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftBed.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftBed.java +index 2ccf3fbe3f991b7a014cff3bcd424e6a81bc310a..e5450d3511389bf3bd6461fb6ec65ea82e4ae9f0 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftBed.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftBed.java +@@ -51,4 +51,11 @@ public final class CraftBed extends org.bukkit.craftbukkit.block.data.CraftBlock + public java.util.Set<org.bukkit.block.BlockFace> getFaces() { + return this.getValues(CraftBed.FACING, org.bukkit.block.BlockFace.class); + } ++ ++ // Paper start ++ @Override ++ public void setOccupied(boolean occupied) { ++ set(CraftBed.OCCUPIED, occupied); ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCandle.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCandle.java +index 2230160d5e04e979467a56346600436c1e5dd70c..08436bfeba2f35fb11b16c4f71f76e13c0d44b1a 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCandle.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftCandle.java +@@ -31,6 +31,12 @@ public final class CraftCandle extends org.bukkit.craftbukkit.block.data.CraftBl + public int getMaximumCandles() { + return getMax(CraftCandle.CANDLES); + } ++ // Paper start ++ @Override ++ public int getMinimumCandles() { ++ return getMin(CraftCandle.CANDLES); ++ } ++ // Paper end + + // org.bukkit.craftbukkit.block.data.CraftLightable + +diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftComposter.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftComposter.java +index 7ce2e8b733bcd496dcfccb1ddfcb7c5c1b64052e..5ae27fc8f9d18bae949d335ea53e7e70917f0e80 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftComposter.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftComposter.java +@@ -31,4 +31,11 @@ public final class CraftComposter extends org.bukkit.craftbukkit.block.data.Craf + public int getMaximumLevel() { + return getMax(CraftComposter.LEVEL); + } ++ ++ // Paper start ++ @Override ++ public int getMinimumLevel() { ++ return getMin(CraftComposter.LEVEL); ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftDecoratedPot.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftDecoratedPot.java +index 356230b9b266974e36d0508f8c239714d673504d..b7ea9a6fba6b4fc157dfcc4bee099871b8ad7380 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftDecoratedPot.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftDecoratedPot.java +@@ -45,4 +45,18 @@ public final class CraftDecoratedPot extends org.bukkit.craftbukkit.block.data.C + public void setWaterlogged(boolean waterlogged) { + this.set(CraftDecoratedPot.WATERLOGGED, waterlogged); + } ++ ++ // Paper start - add missing block data api ++ private static final net.minecraft.world.level.block.state.properties.BooleanProperty CRACKED = getBoolean(net.minecraft.world.level.block.DecoratedPotBlock.class, "cracked"); ++ ++ @Override ++ public boolean isCracked() { ++ return this.get(CraftDecoratedPot.CRACKED); ++ } ++ ++ @Override ++ public void setCracked(final boolean cracked) { ++ this.set(CraftDecoratedPot.CRACKED, cracked); ++ } ++ // Paper end - add missing block data api + } +diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftFluids.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftFluids.java +index 70d734fc71a4499bbf569b3908aa5fbbdf19e6a0..1af5fe48c5861077555e6bdeb6312859b7b37eb2 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftFluids.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftFluids.java +@@ -31,4 +31,11 @@ public final class CraftFluids extends org.bukkit.craftbukkit.block.data.CraftBl + public int getMaximumLevel() { + return getMax(CraftFluids.LEVEL); + } ++ ++ // Paper start ++ @Override ++ public int getMinimumLevel() { ++ return getMin(CraftFluids.LEVEL); ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftLayeredCauldron.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftLayeredCauldron.java +index bf0d53f65f8a672c385b2e798b109a9662725f9e..c0e0cbceb0b5c36f4ac4672f217027a5898900a6 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftLayeredCauldron.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftLayeredCauldron.java +@@ -31,4 +31,11 @@ public final class CraftLayeredCauldron extends org.bukkit.craftbukkit.block.dat + public int getMaximumLevel() { + return getMax(CraftLayeredCauldron.LEVEL); + } ++ ++ // Paper start ++ @Override ++ public int getMinimumLevel() { ++ return getMin(CraftLayeredCauldron.LEVEL); ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftLeaves.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftLeaves.java +index 33d9a950ed678595fe2573e9f89a8d1716040503..ab336b400c1937ff86b681b27b1550e4b7f1ab79 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftLeaves.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftLeaves.java +@@ -51,4 +51,16 @@ public final class CraftLeaves extends org.bukkit.craftbukkit.block.data.CraftBl + public void setWaterlogged(boolean waterlogged) { + this.set(CraftLeaves.WATERLOGGED, waterlogged); + } ++ ++ // Paper start ++ @Override ++ public int getMaximumDistance() { ++ return getMax(CraftLeaves.DISTANCE); ++ } ++ ++ @Override ++ public int getMinimumDistance() { ++ return getMin(CraftLeaves.DISTANCE); ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftLight.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftLight.java +index 49f314b1447212a1a5a7623d2302b5960a44ce6e..8c936a95effa84ba0337d2aaf880cc561591fb33 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftLight.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftLight.java +@@ -32,6 +32,13 @@ public final class CraftLight extends org.bukkit.craftbukkit.block.data.CraftBlo + return getMax(CraftLight.LEVEL); + } + ++ // Paper start ++ @Override ++ public int getMinimumLevel() { ++ return getMin(CraftLight.LEVEL); ++ } ++ // Paper end ++ + // org.bukkit.craftbukkit.block.data.CraftWaterlogged + + private static final net.minecraft.world.level.block.state.properties.BooleanProperty WATERLOGGED = getBoolean(net.minecraft.world.level.block.LightBlock.class, "waterlogged"); +diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftMangroveLeaves.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftMangroveLeaves.java +index 7a1f2fd2f7f8f1b46352fe2c4d0cdf23a88020fd..8b621aaeadcf2cc6e2ccdbab92f4ae2b89a6ca08 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftMangroveLeaves.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftMangroveLeaves.java +@@ -51,4 +51,16 @@ public final class CraftMangroveLeaves extends org.bukkit.craftbukkit.block.data + public void setWaterlogged(boolean waterlogged) { + this.set(CraftMangroveLeaves.WATERLOGGED, waterlogged); + } ++ ++ // Paper start ++ @Override ++ public int getMinimumDistance() { ++ return getMin(CraftMangroveLeaves.DISTANCE); ++ } ++ ++ @Override ++ public int getMaximumDistance() { ++ return getMax(CraftMangroveLeaves.DISTANCE); ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftParticleLeaves.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftParticleLeaves.java +index db4849a35d34da4ab42bbe7569c4944ed95d8f2b..e37e84c333a42006b6c32cf5cd71c0bbfa725141 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftParticleLeaves.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftParticleLeaves.java +@@ -51,4 +51,16 @@ public final class CraftParticleLeaves extends org.bukkit.craftbukkit.block.data + public void setWaterlogged(boolean waterlogged) { + this.set(CraftParticleLeaves.WATERLOGGED, waterlogged); + } ++ ++ // Paper start ++ @Override ++ public int getMaximumDistance() { ++ return getMax(DISTANCE); ++ } ++ ++ @Override ++ public int getMinimumDistance() { ++ return getMin(DISTANCE); ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPinkPetals.java b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPinkPetals.java +index 78b220a6f460cd91ad1574c0d32f3e4288eaf431..0f7df1b4c58ba731832958043ba345ec77737e54 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPinkPetals.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/impl/CraftPinkPetals.java +@@ -27,6 +27,13 @@ public final class CraftPinkPetals extends org.bukkit.craftbukkit.block.data.Cra + this.set(CraftPinkPetals.FLOWER_AMOUNT, flower_amount); + } + ++ // Paper start ++ @Override ++ public int getMinimumFlowerAmount() { ++ return getMin(CraftPinkPetals.FLOWER_AMOUNT); ++ } ++ // Paper end ++ + @Override + public int getMaximumFlowerAmount() { + return getMax(CraftPinkPetals.FLOWER_AMOUNT); diff --git a/patches/server/0658-Option-to-have-default-CustomSpawners-in-custom-worl.patch b/patches/server/0658-Option-to-have-default-CustomSpawners-in-custom-worl.patch new file mode 100644 index 0000000000..69da29074a --- /dev/null +++ b/patches/server/0658-Option-to-have-default-CustomSpawners-in-custom-worl.patch @@ -0,0 +1,32 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sat, 19 Feb 2022 20:15:41 -0800 +Subject: [PATCH] Option to have default CustomSpawners in custom worlds + +By default, only LevelStem's that specifically match the ResourceKey for +OVERWORLD will have the 5 (currently) impls of CustomSpawner (for +phantoms, wandering traders, etc.). This adds an option to instead of +just looking at the LevelStem key, look at the DimensionType key which +is one level below that. Defaults to off to keep vanilla behavior. + +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 17700ebf508f3ac7a4d1cdd8d52355afaf5d006f..64dce8d94cf261113d6f8b99020bf710b9762c2b 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -645,7 +645,15 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa + this.commandStorage = new CommandStorage(worldpersistentdata); + } else { + ChunkProgressListener worldloadlistener = this.progressListenerFactory.create(this.worldData.getGameRules().getInt(GameRules.RULE_SPAWN_CHUNK_RADIUS)); +- world = new ServerLevel(this, this.executor, worldSession, iworlddataserver, worldKey, worlddimension, worldloadlistener, flag, j, ImmutableList.of(), true, this.overworld().getRandomSequences(), org.bukkit.World.Environment.getEnvironment(dimension), gen, biomeProvider); ++ // Paper start - option to use the dimension_type to check if spawners should be added. I imagine mojang will add some datapack-y way of managing this in the future. ++ final List<CustomSpawner> spawners; ++ if (io.papermc.paper.configuration.GlobalConfiguration.get().misc.useDimensionTypeForCustomSpawners && this.registryAccess().lookupOrThrow(Registries.DIMENSION_TYPE).getResourceKey(worlddimension.type().value()).orElseThrow() == net.minecraft.world.level.dimension.BuiltinDimensionTypes.OVERWORLD) { ++ spawners = list; ++ } else { ++ spawners = Collections.emptyList(); ++ } ++ world = new ServerLevel(this, this.executor, worldSession, iworlddataserver, worldKey, worlddimension, worldloadlistener, flag, j, spawners, true, this.overworld().getRandomSequences(), org.bukkit.World.Environment.getEnvironment(dimension), gen, biomeProvider); ++ // Paper end - option to use the dimension_type to check if spawners should be added + } + + worlddata.setModdedInfo(this.getServerModName(), this.getModdedStatus().shouldReportAsModified()); diff --git a/patches/server/0659-Put-world-into-worldlist-before-initing-the-world.patch b/patches/server/0659-Put-world-into-worldlist-before-initing-the-world.patch new file mode 100644 index 0000000000..ffe26b0b55 --- /dev/null +++ b/patches/server/0659-Put-world-into-worldlist-before-initing-the-world.patch @@ -0,0 +1,41 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf <[email protected]> +Date: Tue, 22 Feb 2022 14:21:35 -0800 +Subject: [PATCH] Put world into worldlist before initing the world + +Some parts of legacy conversion will need the overworld +to get the legacy structure data storage + +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 64dce8d94cf261113d6f8b99020bf710b9762c2b..03ee72a20330dd9ba6ff2808c1252454a6c217bc 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -657,9 +657,10 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa + } + + worlddata.setModdedInfo(this.getServerModName(), this.getModdedStatus().shouldReportAsModified()); ++ this.addLevel(world); // Paper - Put world into worldlist before initing the world; move up + this.initWorld(world, worlddata, this.worldData, worldoptions); + +- this.addLevel(world); ++ // Paper - Put world into worldlist before initing the world; move up + this.getPlayerList().addWorldborderListener(world); + + if (worlddata.getCustomBossEvents() != null) { +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index 72f8d3a89396f9289f5b451b24cc181e7ac3222e..75bd9617164c63a641602bf772a5e0f15a322c7e 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -1336,10 +1336,11 @@ public final class CraftServer implements Server { + return null; + } + ++ this.console.addLevel(internal); // Paper - Put world into worldlist before initing the world; move up + this.console.initWorld(internal, worlddata, worlddata, worlddata.worldGenOptions()); + + internal.setSpawnSettings(true); +- this.console.addLevel(internal); ++ // Paper - Put world into worldlist before initing the world; move up + + this.getServer().prepareLevels(internal.getChunkSource().chunkMap.progressListener, internal); + internal.entityManager.tick(); // SPIGOT-6526: Load pending entities so they are available to the API diff --git a/patches/server/0660-Custom-Potion-Mixes.patch b/patches/server/0660-Custom-Potion-Mixes.patch new file mode 100644 index 0000000000..162f383a75 --- /dev/null +++ b/patches/server/0660-Custom-Potion-Mixes.patch @@ -0,0 +1,329 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Thu, 7 Oct 2021 14:34:55 -0700 +Subject: [PATCH] Custom Potion Mixes + +== AT == +public-f net.minecraft.server.MinecraftServer potionBrewing + +diff --git a/src/main/java/io/papermc/paper/potion/PaperPotionBrewer.java b/src/main/java/io/papermc/paper/potion/PaperPotionBrewer.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d9390227a2bba4e03aa9ee592ca157127633c41b +--- /dev/null ++++ b/src/main/java/io/papermc/paper/potion/PaperPotionBrewer.java +@@ -0,0 +1,56 @@ ++package io.papermc.paper.potion; ++ ++import com.google.common.base.Preconditions; ++import java.util.Collection; ++import net.minecraft.server.MinecraftServer; ++import org.bukkit.NamespacedKey; ++import org.bukkit.potion.PotionBrewer; ++import org.bukkit.potion.PotionEffect; ++import org.bukkit.potion.PotionType; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.framework.qual.DefaultQualifier; ++ ++@DefaultQualifier(NonNull.class) ++public class PaperPotionBrewer implements PotionBrewer { ++ ++ private final MinecraftServer minecraftServer; ++ ++ public PaperPotionBrewer(final MinecraftServer minecraftServer) { ++ this.minecraftServer = minecraftServer; ++ } ++ ++ @Override ++ @Deprecated(forRemoval = true) ++ public Collection<PotionEffect> getEffects(PotionType type, boolean upgraded, boolean extended) { ++ final org.bukkit.NamespacedKey key = type.getKey(); ++ ++ Preconditions.checkArgument(!key.getKey().startsWith("strong_"), "Strong potion type cannot be used directly, got %s", key); ++ Preconditions.checkArgument(!key.getKey().startsWith("long_"), "Extended potion type cannot be used directly, got %s", key); ++ ++ org.bukkit.NamespacedKey effectiveKey = key; ++ if (upgraded) { ++ effectiveKey = new org.bukkit.NamespacedKey(key.namespace(), "strong_" + key.key()); ++ } else if (extended) { ++ effectiveKey = new org.bukkit.NamespacedKey(key.namespace(), "long_" + key.key()); ++ } ++ ++ final org.bukkit.potion.PotionType effectivePotionType = org.bukkit.Registry.POTION.get(effectiveKey); ++ Preconditions.checkNotNull(type, "Unknown potion type from data " + effectiveKey.asMinimalString()); // Legacy error message in 1.20.4 ++ return effectivePotionType.getPotionEffects(); ++ } ++ ++ @Override ++ public void addPotionMix(final PotionMix potionMix) { ++ this.minecraftServer.potionBrewing().addPotionMix(potionMix); ++ } ++ ++ @Override ++ public void removePotionMix(final NamespacedKey key) { ++ this.minecraftServer.potionBrewing.removePotionMix(key); ++ } ++ ++ @Override ++ public void resetPotionMixes() { ++ this.minecraftServer.potionBrewing = this.minecraftServer.potionBrewing().reload(this.minecraftServer.getWorldData().enabledFeatures()); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/potion/PaperPotionMix.java b/src/main/java/io/papermc/paper/potion/PaperPotionMix.java +new file mode 100644 +index 0000000000000000000000000000000000000000..7ea357ac2f3a93db4ebdf24b5072be7d1cad3e33 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/potion/PaperPotionMix.java +@@ -0,0 +1,21 @@ ++package io.papermc.paper.potion; ++ ++import java.util.function.Predicate; ++import net.minecraft.world.item.ItemStack; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.craftbukkit.inventory.CraftRecipe; ++import org.bukkit.inventory.RecipeChoice; ++ ++public record PaperPotionMix(ItemStack result, Predicate<ItemStack> input, Predicate<ItemStack> ingredient) { ++ ++ public PaperPotionMix(PotionMix potionMix) { ++ this(CraftItemStack.asNMSCopy(potionMix.getResult()), convert(potionMix.getInput()), convert(potionMix.getIngredient())); ++ } ++ ++ static Predicate<ItemStack> convert(final RecipeChoice choice) { ++ if (choice instanceof PredicateRecipeChoice predicateRecipeChoice) { ++ return stack -> predicateRecipeChoice.test(CraftItemStack.asBukkitCopy(stack)); ++ } ++ return CraftRecipe.toIngredient(choice, true); ++ } ++} +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 03ee72a20330dd9ba6ff2808c1252454a6c217bc..fcc00d5b0be3d2adb92c8243ccca8d0190fcc413 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -2196,6 +2196,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa + this.worldData.setDataConfiguration(worlddataconfiguration); + this.resources.managers.updateStaticRegistryTags(); + this.resources.managers.getRecipeManager().finalizeRecipeLoading(this.worldData.enabledFeatures()); ++ this.potionBrewing = this.potionBrewing.reload(this.worldData.enabledFeatures()); // Paper - Custom Potion Mixes + this.getPlayerList().saveAll(); + this.getPlayerList().reloadResources(); + this.functionManager.replaceLibrary(this.resources.managers.getFunctionLibrary()); +diff --git a/src/main/java/net/minecraft/world/inventory/BrewingStandMenu.java b/src/main/java/net/minecraft/world/inventory/BrewingStandMenu.java +index 6acc06ab49549ebd80e9871dce7f3fe6ebde9498..d2688ca4430e2e45b35b97b0b3b7c79b5aac23a4 100644 +--- a/src/main/java/net/minecraft/world/inventory/BrewingStandMenu.java ++++ b/src/main/java/net/minecraft/world/inventory/BrewingStandMenu.java +@@ -57,9 +57,11 @@ public class BrewingStandMenu extends AbstractContainerMenu { + this.brewingStandData = propertyDelegate; + PotionBrewing potionbrewer = playerInventory.player.level().potionBrewing(); + +- this.addSlot(new BrewingStandMenu.PotionSlot(inventory, 0, 56, 51)); +- this.addSlot(new BrewingStandMenu.PotionSlot(inventory, 1, 79, 58)); +- this.addSlot(new BrewingStandMenu.PotionSlot(inventory, 2, 102, 51)); ++ // Paper start - custom potion mixes ++ this.addSlot(new BrewingStandMenu.PotionSlot(inventory, 0, 56, 51, potionbrewer)); ++ this.addSlot(new BrewingStandMenu.PotionSlot(inventory, 1, 79, 58, potionbrewer)); ++ this.addSlot(new BrewingStandMenu.PotionSlot(inventory, 2, 102, 51, potionbrewer)); ++ // Paper end - custom potion mixes + this.ingredientSlot = this.addSlot(new BrewingStandMenu.IngredientsSlot(potionbrewer, inventory, 3, 79, 17)); + this.addSlot(new BrewingStandMenu.FuelSlot(inventory, 4, 17, 17)); + this.addDataSlots(propertyDelegate); +@@ -90,7 +92,7 @@ public class BrewingStandMenu extends AbstractContainerMenu { + if (!this.moveItemStackTo(itemstack1, 3, 4, false)) { + return ItemStack.EMPTY; + } +- } else if (BrewingStandMenu.PotionSlot.mayPlaceItem(itemstack)) { ++ } else if (BrewingStandMenu.PotionSlot.mayPlaceItem(itemstack, this.player.player.level().potionBrewing())) { // Paper - custom potion mixes + if (!this.moveItemStackTo(itemstack1, 0, 3, false)) { + return ItemStack.EMPTY; + } +@@ -139,13 +141,15 @@ public class BrewingStandMenu extends AbstractContainerMenu { + + private static class PotionSlot extends Slot { + +- public PotionSlot(Container inventory, int index, int x, int y) { ++ private final PotionBrewing potionBrewing; // Paper - custom potion mixes ++ public PotionSlot(Container inventory, int index, int x, int y, PotionBrewing potionBrewing) { // Paper - custom potion mixes + super(inventory, index, x, y); ++ this.potionBrewing = potionBrewing; // Paper - custom potion mixes + } + + @Override + public boolean mayPlace(ItemStack stack) { +- return PotionSlot.mayPlaceItem(stack); ++ return PotionSlot.mayPlaceItem(stack, this.potionBrewing); // Paper - custom potion mixes + } + + @Override +@@ -164,8 +168,8 @@ public class BrewingStandMenu extends AbstractContainerMenu { + super.onTake(player, stack); + } + +- public static boolean mayPlaceItem(ItemStack stack) { +- return stack.is(Items.POTION) || stack.is(Items.SPLASH_POTION) || stack.is(Items.LINGERING_POTION) || stack.is(Items.GLASS_BOTTLE); ++ public static boolean mayPlaceItem(ItemStack stack, PotionBrewing potionBrewing) { // Paper - custom potion mixes ++ return stack.is(Items.POTION) || stack.is(Items.SPLASH_POTION) || stack.is(Items.LINGERING_POTION) || stack.is(Items.GLASS_BOTTLE) || potionBrewing.isCustomInput(stack); // Paper - Custom Potion Mixes + } + + @Override +diff --git a/src/main/java/net/minecraft/world/item/alchemy/PotionBrewing.java b/src/main/java/net/minecraft/world/item/alchemy/PotionBrewing.java +index 50e81e3babd331077eda8daa769eb2b3f99e8ca2..ca01f3344005b295c7ae98f6d5b03f79513b12a4 100644 +--- a/src/main/java/net/minecraft/world/item/alchemy/PotionBrewing.java ++++ b/src/main/java/net/minecraft/world/item/alchemy/PotionBrewing.java +@@ -19,6 +19,7 @@ public class PotionBrewing { + private final List<Ingredient> containers; + private final List<PotionBrewing.Mix<Potion>> potionMixes; + private final List<PotionBrewing.Mix<Item>> containerMixes; ++ private final it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap<org.bukkit.NamespacedKey, io.papermc.paper.potion.PaperPotionMix> customMixes = new it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap<>(); // Paper - Custom Potion Mixes + + PotionBrewing(List<Ingredient> potionTypes, List<PotionBrewing.Mix<Potion>> potionRecipes, List<PotionBrewing.Mix<Item>> itemRecipes) { + this.containers = potionTypes; +@@ -27,7 +28,7 @@ public class PotionBrewing { + } + + public boolean isIngredient(ItemStack stack) { +- return this.isContainerIngredient(stack) || this.isPotionIngredient(stack); ++ return this.isContainerIngredient(stack) || this.isPotionIngredient(stack) || this.isCustomIngredient(stack); // Paper - Custom Potion Mixes + } + + private boolean isContainer(ItemStack stack) { +@@ -71,6 +72,11 @@ public class PotionBrewing { + } + + public boolean hasMix(ItemStack input, ItemStack ingredient) { ++ // Paper start - Custom Potion Mixes ++ if (this.hasCustomMix(input, ingredient)) { ++ return true; ++ } ++ // Paper end - Custom Potion Mixes + return this.isContainer(input) && (this.hasContainerMix(input, ingredient) || this.hasPotionMix(input, ingredient)); + } + +@@ -103,6 +109,13 @@ public class PotionBrewing { + if (input.isEmpty()) { + return input; + } else { ++ // Paper start - Custom Potion Mixes ++ for (io.papermc.paper.potion.PaperPotionMix mix : this.customMixes.values()) { ++ if (mix.input().test(input) && mix.ingredient().test(ingredient)) { ++ return mix.result().copy(); ++ } ++ } ++ // Paper end - Custom Potion Mixes + Optional<Holder<Potion>> optional = input.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY).potion(); + if (optional.isEmpty()) { + return input; +@@ -190,6 +203,50 @@ public class PotionBrewing { + builder.addMix(Potions.SLOW_FALLING, Items.REDSTONE, Potions.LONG_SLOW_FALLING); + } + ++ // Paper start - Custom Potion Mixes ++ public boolean isCustomIngredient(ItemStack stack) { ++ for (io.papermc.paper.potion.PaperPotionMix mix : this.customMixes.values()) { ++ if (mix.ingredient().test(stack)) { ++ return true; ++ } ++ } ++ return false; ++ } ++ ++ public boolean isCustomInput(ItemStack stack) { ++ for (io.papermc.paper.potion.PaperPotionMix mix : this.customMixes.values()) { ++ if (mix.input().test(stack)) { ++ return true; ++ } ++ } ++ return false; ++ } ++ ++ private boolean hasCustomMix(ItemStack input, ItemStack ingredient) { ++ for (io.papermc.paper.potion.PaperPotionMix mix : this.customMixes.values()) { ++ if (mix.input().test(input) && mix.ingredient().test(ingredient)) { ++ return true; ++ } ++ } ++ return false; ++ } ++ ++ public void addPotionMix(io.papermc.paper.potion.PotionMix mix) { ++ if (this.customMixes.containsKey(mix.getKey())) { ++ throw new IllegalArgumentException("Duplicate recipe ignored with ID " + mix.getKey()); ++ } ++ this.customMixes.putAndMoveToFirst(mix.getKey(), new io.papermc.paper.potion.PaperPotionMix(mix)); ++ } ++ ++ public boolean removePotionMix(org.bukkit.NamespacedKey key) { ++ return this.customMixes.remove(key) != null; ++ } ++ ++ public PotionBrewing reload(FeatureFlagSet flags) { ++ return bootstrap(flags); ++ } ++ // Paper end - Custom Potion Mixes ++ + public static class Builder { + private final List<Ingredient> containers = new ArrayList<>(); + private final List<PotionBrewing.Mix<Potion>> potionMixes = new ArrayList<>(); +diff --git a/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java +index e167c2834f1b7899a7d11cef782940deeb739a9c..2bafacd7bc56186d9105d2031180f8c4a6940018 100644 +--- a/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java ++++ b/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java +@@ -316,12 +316,12 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements + + @Override + public boolean canPlaceItem(int slot, ItemStack stack) { ++ PotionBrewing potionbrewer = this.level != null ? this.level.potionBrewing() : PotionBrewing.EMPTY; // Paper - move up + if (slot == 3) { +- PotionBrewing potionbrewer = this.level != null ? this.level.potionBrewing() : PotionBrewing.EMPTY; + + return potionbrewer.isIngredient(stack); + } else { +- return slot == 4 ? stack.is(ItemTags.BREWING_FUEL) : (stack.is(Items.POTION) || stack.is(Items.SPLASH_POTION) || stack.is(Items.LINGERING_POTION) || stack.is(Items.GLASS_BOTTLE)) && this.getItem(slot).isEmpty(); ++ return slot == 4 ? stack.is(ItemTags.BREWING_FUEL) : (stack.is(Items.POTION) || stack.is(Items.SPLASH_POTION) || stack.is(Items.LINGERING_POTION) || stack.is(Items.GLASS_BOTTLE) || potionbrewer.isCustomInput(stack)) && this.getItem(slot).isEmpty(); // Paper - Custom Potion Mixes + } + } + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index 75bd9617164c63a641602bf772a5e0f15a322c7e..82e1c4713e043c4903b1f0154609da4558f90aef 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -311,6 +311,7 @@ public final class CraftServer implements Server { + private final io.papermc.paper.datapack.PaperDatapackManager datapackManager; // Paper + public static Exception excessiveVelEx; // Paper - Velocity warnings + private final io.papermc.paper.logging.SysoutCatcher sysoutCatcher = new io.papermc.paper.logging.SysoutCatcher(); // Paper ++ private final io.papermc.paper.potion.PaperPotionBrewer potionBrewer; // Paper - Custom Potion Mixes + + static { + ConfigurationSerialization.registerClass(CraftOfflinePlayer.class); +@@ -394,6 +395,7 @@ public final class CraftServer implements Server { + if (this.configuration.getBoolean("settings.use-map-color-cache")) { + MapPalette.setMapColorCache(new CraftMapColorCache(this.logger)); + } ++ this.potionBrewer = new io.papermc.paper.potion.PaperPotionBrewer(console); // Paper - custom potion mixes + datapackManager = new io.papermc.paper.datapack.PaperDatapackManager(console.getPackRepository()); // Paper + } + +@@ -3076,5 +3078,9 @@ public final class CraftServer implements Server { + return datapackManager; + } + ++ @Override ++ public io.papermc.paper.potion.PaperPotionBrewer getPotionBrewer() { ++ return this.potionBrewer; ++ } + // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java +index fa47232eeb36ed92b5a526bb00398f08b6c0ab41..5b176cd5f80a49e3a2afbcbf4fe2958143719bce 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java +@@ -23,6 +23,11 @@ public interface CraftRecipe extends Recipe { + } + + default Ingredient toNMS(RecipeChoice bukkit, boolean requireNotEmpty) { ++ // Paper start ++ return toIngredient(bukkit, requireNotEmpty); ++ } ++ static Ingredient toIngredient(RecipeChoice bukkit, boolean requireNotEmpty) { ++ // Paper end + Ingredient stack; + + if (bukkit == null) { diff --git a/patches/server/0661-Force-close-world-loading-screen.patch b/patches/server/0661-Force-close-world-loading-screen.patch new file mode 100644 index 0000000000..820a46bebe --- /dev/null +++ b/patches/server/0661-Force-close-world-loading-screen.patch @@ -0,0 +1,32 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Nassim Jahnke <[email protected]> +Date: Wed, 2 Mar 2022 09:45:56 +0100 +Subject: [PATCH] Force close world loading screen + +Dead players would be stuck in the world loading screen and other players may +miss messages and similar sent in the join event if chunk loading is slow. +Paper already circumvents falling through the world before chunks are loaded, +so we do not need that. The client only needs the chunk it is currently in to +be loaded to close the loading screen, so we just send an empty one. + +diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java +index 3a0e0196f5bfa554b23fff9ff1a18a189b36452e..c8bb797a322220647d5839cc5d20b3ac7c01b8ba 100644 +--- a/src/main/java/net/minecraft/server/players/PlayerList.java ++++ b/src/main/java/net/minecraft/server/players/PlayerList.java +@@ -406,6 +406,16 @@ public abstract class PlayerList { + } + // Paper end - Configurable player collision + PlayerList.LOGGER.info("{}[{}] logged in with entity id {} at ([{}]{}, {}, {})", player.getName().getString(), s1, player.getId(), worldserver1.serverLevelData.getLevelName(), player.getX(), player.getY(), player.getZ()); ++ // Paper start - Send empty chunk, so players aren't stuck in the world loading screen with our chunk system not sending chunks when dead ++ if (player.isDeadOrDying()) { ++ net.minecraft.core.Holder<net.minecraft.world.level.biome.Biome> plains = worldserver1.registryAccess().lookupOrThrow(net.minecraft.core.registries.Registries.BIOME) ++ .getOrThrow(net.minecraft.world.level.biome.Biomes.PLAINS); ++ player.connection.send(new net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket( ++ new net.minecraft.world.level.chunk.EmptyLevelChunk(worldserver1, player.chunkPosition(), plains), ++ worldserver1.getLightEngine(), (java.util.BitSet)null, (java.util.BitSet) null) ++ ); ++ } ++ // Paper end - Send empty chunk + } + + public void updateEntireScoreboard(ServerScoreboard scoreboard, ServerPlayer player) { diff --git a/patches/server/0662-Fix-falling-block-spawn-methods.patch b/patches/server/0662-Fix-falling-block-spawn-methods.patch new file mode 100644 index 0000000000..9703a17436 --- /dev/null +++ b/patches/server/0662-Fix-falling-block-spawn-methods.patch @@ -0,0 +1,57 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Nassim Jahnke <[email protected]> +Date: Fri, 4 Mar 2022 20:35:19 +0100 +Subject: [PATCH] Fix falling block spawn methods + +Restores the API behavior from previous versions of the server +- Do not call API events +- Do not replace the existing block in the world + +== AT == +public net.minecraft.world.entity.item.FallingBlockEntity <init>(Lnet/minecraft/world/level/Level;DDDLnet/minecraft/world/level/block/state/BlockState;)V + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index 66778ebd82563823f692c7151f40a373e8d7427a..827cf8b2e241e49dac961b103f32546a04f8a2f9 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -1386,7 +1386,12 @@ public class CraftWorld extends CraftRegionAccessor implements World { + Preconditions.checkArgument(material != null, "Material cannot be null"); + Preconditions.checkArgument(material.isBlock(), "Material.%s must be a block", material); + +- FallingBlockEntity entity = FallingBlockEntity.fall(this.world, BlockPos.containing(location.getX(), location.getY(), location.getZ()), CraftBlockType.bukkitToMinecraft(material).defaultBlockState(), SpawnReason.CUSTOM); ++ // Paper start - restore API behavior for spawning falling blocks ++ FallingBlockEntity entity = new FallingBlockEntity(this.world, location.getX(), location.getY(), location.getZ(), CraftBlockType.bukkitToMinecraft(material).defaultBlockState()); // Paper ++ entity.time = 1; ++ ++ this.world.addFreshEntity(entity, SpawnReason.CUSTOM); ++ // Paper end - restore API behavior for spawning falling blocks + return (FallingBlock) entity.getBukkitEntity(); + } + +@@ -1395,7 +1400,12 @@ public class CraftWorld extends CraftRegionAccessor implements World { + Preconditions.checkArgument(location != null, "Location cannot be null"); + Preconditions.checkArgument(data != null, "BlockData cannot be null"); + +- FallingBlockEntity entity = FallingBlockEntity.fall(this.world, BlockPos.containing(location.getX(), location.getY(), location.getZ()), ((CraftBlockData) data).getState(), SpawnReason.CUSTOM); ++ // Paper start - restore API behavior for spawning falling blocks ++ FallingBlockEntity entity = new FallingBlockEntity(this.world, location.getX(), location.getY(), location.getZ(), ((CraftBlockData) data).getState()); ++ entity.time = 1; ++ ++ this.world.addFreshEntity(entity, SpawnReason.CUSTOM); ++ // Paper end - restore API behavior for spawning falling blocks + return (FallingBlock) entity.getBukkitEntity(); + } + +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java +index 95c2ef53e6c5574e69614d8058eb3ed94ab6b0d6..f3ad9c57ce2fc06b90b7ab7e1f31ef0e1b564c9f 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java +@@ -438,7 +438,7 @@ public final class CraftEntityTypes { + register(new EntityTypeData<>(EntityType.TNT, TNTPrimed.class, CraftTNTPrimed::new, spawnData -> new PrimedTnt(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z(), null))); + register(new EntityTypeData<>(EntityType.FALLING_BLOCK, FallingBlock.class, CraftFallingBlock::new, spawnData -> { + BlockPos pos = BlockPos.containing(spawnData.x(), spawnData.y(), spawnData.z()); +- return FallingBlockEntity.fall(spawnData.minecraftWorld(), pos, spawnData.world().getBlockState(pos)); ++ return new FallingBlockEntity(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z(), spawnData.world().getBlockState(pos)); // Paper - create falling block entities correctly + })); + register(new EntityTypeData<>(EntityType.FIREWORK_ROCKET, Firework.class, CraftFirework::new, spawnData -> new FireworkRocketEntity(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z(), net.minecraft.world.item.ItemStack.EMPTY))); + register(new EntityTypeData<>(EntityType.EVOKER_FANGS, EvokerFangs.class, CraftEvokerFangs::new, spawnData -> new net.minecraft.world.entity.projectile.EvokerFangs(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z(), (float) Math.toRadians(spawnData.yaw()), 0, null))); diff --git a/patches/server/0663-Expose-furnace-minecart-push-values.patch b/patches/server/0663-Expose-furnace-minecart-push-values.patch new file mode 100644 index 0000000000..fb48aabb5f --- /dev/null +++ b/patches/server/0663-Expose-furnace-minecart-push-values.patch @@ -0,0 +1,42 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: EpicKnarvik97 <[email protected]> +Date: Sat, 5 Mar 2022 20:58:46 +0100 +Subject: [PATCH] Expose furnace minecart push values + +Adds methods for getting and setting a furnace minecart's push values + +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartFurnace.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartFurnace.java +index 53042b75b45093535d6572239b34c3ff9a72f648..1be1f6d23f2224d4d8720d40f2e530736b1bae81 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartFurnace.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartFurnace.java +@@ -27,6 +27,30 @@ public class CraftMinecartFurnace extends CraftMinecart implements PoweredMineca + this.getHandle().fuel = fuel; + } + ++ // Paper start ++ @Override ++ public double getPushX() { ++ return getHandle().push.x; ++ } ++ ++ @Override ++ public double getPushZ() { ++ return getHandle().push.z; ++ } ++ ++ @Override ++ public void setPushX(double xPush) { ++ final net.minecraft.world.phys.Vec3 push = getHandle().push; ++ getHandle().push = new net.minecraft.world.phys.Vec3(xPush, push.y, push.z); ++ } ++ ++ @Override ++ public void setPushZ(double zPush) { ++ final net.minecraft.world.phys.Vec3 push = getHandle().push; ++ getHandle().push = new net.minecraft.world.phys.Vec3(push.x, push.y, zPush); ++ } ++ // Paper end ++ + @Override + public String toString() { + return "CraftMinecartFurnace"; diff --git a/patches/server/0664-Fix-cancelling-ProjectileHitEvent-for-piercing-arrow.patch b/patches/server/0664-Fix-cancelling-ProjectileHitEvent-for-piercing-arrow.patch new file mode 100644 index 0000000000..c98e7fd0f3 --- /dev/null +++ b/patches/server/0664-Fix-cancelling-ProjectileHitEvent-for-piercing-arrow.patch @@ -0,0 +1,40 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sat, 19 Feb 2022 19:05:59 -0800 +Subject: [PATCH] Fix cancelling ProjectileHitEvent for piercing arrows + +Piercing arrows search for multiple entities inside a while +loop that is checking the projectile entity's removed state. +If the hit event is cancelled on the first entity, the event will +be called over and over again inside that while loop until the event +is not cancelled. The solution here, is to make use of an +already-existing field on AbstractArrow for tracking entities hit by +piercing arrows to avoid duplicate damage being applied. + +== AT == +protected net.minecraft.world.entity.projectile.Projectile hitCancelled + +diff --git a/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java b/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java +index aab7d546317d93876ccd0e02c0631ccc7c8f9bf5..0abe2fe6d7cf0a2084b7219c3ab0c5118586a8fe 100644 +--- a/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java ++++ b/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java +@@ -345,6 +345,19 @@ public abstract class AbstractArrow extends Projectile { + + } + ++ // Paper start - Fix cancelling ProjectileHitEvent for piercing arrows ++ @Override ++ public ProjectileDeflection preHitTargetOrDeflectSelf(HitResult hitResult) { ++ if (hitResult instanceof EntityHitResult entityHitResult && this.hitCancelled && this.getPierceLevel() > 0) { ++ if (this.piercingIgnoreEntityIds == null) { ++ this.piercingIgnoreEntityIds = new IntOpenHashSet(5); ++ } ++ this.piercingIgnoreEntityIds.add(entityHitResult.getEntity().getId()); ++ } ++ return super.preHitTargetOrDeflectSelf(hitResult); ++ } ++ // Paper end - Fix cancelling ProjectileHitEvent for piercing arrows ++ + @Override + protected double getDefaultGravity() { + return 0.05D; diff --git a/patches/server/0665-More-Projectile-API.patch b/patches/server/0665-More-Projectile-API.patch new file mode 100644 index 0000000000..e5cadb6487 --- /dev/null +++ b/patches/server/0665-More-Projectile-API.patch @@ -0,0 +1,932 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Owen1212055 <[email protected]> +Date: Tue, 22 Jun 2021 23:41:11 -0400 +Subject: [PATCH] More Projectile API + +== AT == +public net.minecraft.world.entity.projectile.FishingHook timeUntilLured +public net.minecraft.world.entity.projectile.FishingHook fishAngle +public net.minecraft.world.entity.projectile.ShulkerBullet targetDeltaX +public net.minecraft.world.entity.projectile.ShulkerBullet targetDeltaY +public net.minecraft.world.entity.projectile.ShulkerBullet targetDeltaZ +public net.minecraft.world.entity.projectile.ShulkerBullet currentMoveDirection +public net.minecraft.world.entity.projectile.ShulkerBullet flightSteps +public net.minecraft.world.entity.projectile.AbstractArrow soundEvent +public net.minecraft.world.entity.projectile.AbstractArrow setPickupItemStack(Lnet/minecraft/world/item/ItemStack;)V +public net.minecraft.world.entity.projectile.ThrownTrident dealtDamage +public net.minecraft.world.entity.projectile.Arrow NO_EFFECT_COLOR +public net.minecraft.world.entity.projectile.Projectile hasBeenShot +public net.minecraft.world.entity.projectile.Projectile leftOwner +public net.minecraft.world.entity.projectile.Projectile ownerUUID +public net.minecraft.world.entity.projectile.Projectile preOnHit(Lnet/minecraft/world/phys/HitResult;)V +public net.minecraft.world.entity.projectile.Projectile canHitEntity(Lnet/minecraft/world/entity/Entity;)Z +public net.minecraft.world.entity.projectile.FireworkRocketEntity getDefaultItem()Lnet/minecraft/world/item/ItemStack; +public net.minecraft.world.item.CrossbowItem FIREWORK_POWER + +Co-authored-by: Nassim Jahnke <[email protected]> +Co-authored-by: SoSeDiK <[email protected]> +Co-authored-by: MelnCat <[email protected]> +Co-authored-by: Lulu13022002 <[email protected]> + +diff --git a/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java b/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java +index 966ba1a9c2e7923a970ce3799a5e2ef6ca36bf84..a2487ca0d7794e58d9ede35d9be5a05a7ea7b03c 100644 +--- a/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java ++++ b/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java +@@ -419,13 +419,18 @@ public class FishingHook extends Projectile { + } + } else { + // CraftBukkit start - logic to modify fishing wait time +- this.timeUntilLured = Mth.nextInt(this.random, this.minWaitTime, this.maxWaitTime); +- this.timeUntilLured -= (this.applyLure) ? (this.lureSpeed >= this.maxWaitTime ? this.timeUntilLured - 1 : this.lureSpeed ) : 0; // Paper - Fix Lure infinite loop ++ this.resetTimeUntilLured(); // Paper - more projectile api - extract time until lured reset logic + // CraftBukkit end + } + } + + } ++ // Paper start - more projectile api - extract time until lured reset logic ++ public void resetTimeUntilLured() { ++ this.timeUntilLured = Mth.nextInt(this.random, this.minWaitTime, this.maxWaitTime); ++ this.timeUntilLured -= (this.applyLure) ? (this.lureSpeed >= this.maxWaitTime ? this.timeUntilLured - 1 : this.lureSpeed ) : 0; // Paper - Fix Lure infinite loop ++ } ++ // Paper end - more projectile api - extract time until lured reset logic + + public boolean calculateOpenWater(BlockPos pos) { + FishingHook.OpenWaterType entityfishinghook_waterposition = FishingHook.OpenWaterType.INVALID; +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 5cff58ab33657e7fb2642928e42b728e7b0b4689..f3781e37358f970b56e5b8de77ef224151270f1c 100644 +--- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java ++++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java +@@ -288,7 +288,7 @@ public abstract class Projectile extends Entity implements TraceableEntity { + } + + // CraftBukkit start - call projectile hit event +- protected ProjectileDeflection preHitTargetOrDeflectSelf(HitResult movingobjectposition) { ++ public ProjectileDeflection preHitTargetOrDeflectSelf(HitResult movingobjectposition) { // Paper - protected -> public + org.bukkit.event.entity.ProjectileHitEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileHitEvent(this, movingobjectposition); + this.hitCancelled = event != null && event.isCancelled(); + if (movingobjectposition.getType() == HitResult.Type.BLOCK || !this.hitCancelled) { +diff --git a/src/main/java/net/minecraft/world/entity/projectile/ThrownPotion.java b/src/main/java/net/minecraft/world/entity/projectile/ThrownPotion.java +index d6ac07d9d5ee0430a1d91b7084b378aac1d047e5..a486466040a646b8a5a5ff2430cdd25b95b7e20f 100644 +--- a/src/main/java/net/minecraft/world/entity/projectile/ThrownPotion.java ++++ b/src/main/java/net/minecraft/world/entity/projectile/ThrownPotion.java +@@ -103,8 +103,12 @@ public class ThrownPotion extends ThrowableItemProjectile { + @Override + protected void onHit(HitResult hitResult) { + super.onHit(hitResult); ++ // Paper start - More projectile API ++ this.splash(hitResult); ++ } ++ public void splash(@Nullable HitResult hitResult) { ++ // Paper end - More projectile API + Level world = this.level(); +- + if (world instanceof ServerLevel worldserver) { + ItemStack itemstack = this.getItem(); + PotionContents potioncontents = (PotionContents) itemstack.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY); +@@ -116,7 +120,7 @@ public class ThrownPotion extends ThrowableItemProjectile { + if (this.isLingering()) { + showParticles = this.makeAreaOfEffectCloud(potioncontents, hitResult); // CraftBukkit - Pass MovingObjectPosition // Paper + } else { +- showParticles = this.applySplash(worldserver, potioncontents.getAllEffects(), hitResult.getType() == HitResult.Type.ENTITY ? ((EntityHitResult) hitResult).getEntity() : null, hitResult); // CraftBukkit - Pass MovingObjectPosition // Paper ++ showParticles = this.applySplash(worldserver, potioncontents.getAllEffects(), hitResult != null && hitResult.getType() == HitResult.Type.ENTITY ? ((EntityHitResult) hitResult).getEntity() : null, hitResult); // CraftBukkit - Pass MovingObjectPosition // Paper - More projectile API + } + } + +@@ -178,7 +182,7 @@ public class ThrownPotion extends ThrowableItemProjectile { + + } + +- private boolean applySplash(ServerLevel worldserver, Iterable<MobEffectInstance> iterable, @Nullable Entity entity, HitResult position) { // CraftBukkit - Pass MovingObjectPosition // Paper - Fix potions splash events ++ private boolean applySplash(ServerLevel worldserver, Iterable<MobEffectInstance> iterable, @Nullable Entity entity, @Nullable HitResult position) { // CraftBukkit - Pass MovingObjectPosition // Paper - Fix potions splash events & More projectile API + AABB axisalignedbb = this.getBoundingBox().inflate(4.0D, 2.0D, 4.0D); + List<net.minecraft.world.entity.LivingEntity> list = worldserver.getEntitiesOfClass(net.minecraft.world.entity.LivingEntity.class, axisalignedbb); + Map<LivingEntity, Double> affected = new HashMap<LivingEntity, Double>(); // CraftBukkit +@@ -256,7 +260,7 @@ public class ThrownPotion extends ThrowableItemProjectile { + + } + +- private boolean makeAreaOfEffectCloud(PotionContents potioncontents, HitResult position) { // CraftBukkit - Pass MovingObjectPosition ++ private boolean makeAreaOfEffectCloud(PotionContents potioncontents, @Nullable HitResult position) { // CraftBukkit - Pass MovingObjectPosition // Paper - More projectile API + AreaEffectCloud entityareaeffectcloud = new AreaEffectCloud(this.level(), this.getX(), this.getY(), this.getZ()); + Entity entity = this.getOwner(); + +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java b/src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java +index 91c2d0b40d3fca86938cd454e1415a4eea3df7c7..de4fb2654c7895cfd83ad694455ee56cb708c2f2 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/AbstractProjectile.java +@@ -17,4 +17,65 @@ public abstract class AbstractProjectile extends CraftEntity implements Projecti + @Override + public void setBounce(boolean doesBounce) {} + ++ // Paper start - More projectile API ++ @Override ++ public boolean hasLeftShooter() { ++ return this.getHandle().leftOwner; ++ } ++ ++ @Override ++ public void setHasLeftShooter(boolean leftShooter) { ++ this.getHandle().leftOwner = leftShooter; ++ } ++ ++ @Override ++ public boolean hasBeenShot() { ++ return this.getHandle().hasBeenShot; ++ } ++ ++ @Override ++ public void setHasBeenShot(boolean beenShot) { ++ this.getHandle().hasBeenShot = beenShot; ++ } ++ ++ @Override ++ public boolean canHitEntity(org.bukkit.entity.Entity entity) { ++ return this.getHandle().canHitEntity(((CraftEntity) entity).getHandle()); ++ } ++ ++ @Override ++ public void hitEntity(org.bukkit.entity.Entity entity) { ++ this.getHandle().preHitTargetOrDeflectSelf(new net.minecraft.world.phys.EntityHitResult(((CraftEntity) entity).getHandle())); ++ } ++ ++ @Override ++ public void hitEntity(org.bukkit.entity.Entity entity, org.bukkit.util.Vector vector) { ++ this.getHandle().preHitTargetOrDeflectSelf(new net.minecraft.world.phys.EntityHitResult(((CraftEntity) entity).getHandle(), new net.minecraft.world.phys.Vec3(vector.getX(), vector.getY(), vector.getZ()))); ++ } ++ ++ @Override ++ public net.minecraft.world.entity.projectile.Projectile getHandle() { ++ return (net.minecraft.world.entity.projectile.Projectile) entity; ++ } ++ ++ @Override ++ public final org.bukkit.projectiles.ProjectileSource getShooter() { ++ return this.getHandle().projectileSource; ++ } ++ ++ @Override ++ public final void setShooter(org.bukkit.projectiles.ProjectileSource shooter) { ++ if (shooter instanceof CraftEntity craftEntity) { ++ this.getHandle().setOwner(craftEntity.getHandle()); ++ } else { ++ this.getHandle().setOwner(null); ++ } ++ this.getHandle().projectileSource = shooter; ++ } ++ ++ @Override ++ public java.util.UUID getOwnerUniqueId() { ++ return this.getHandle().ownerUUID; ++ } ++ // Paper end - More projectile API + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractArrow.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractArrow.java +index 0f85c1f991469b277bba8b40b087f7224b4b3a85..1f30109abd86b76af343eb5eb75ec3db83ef9417 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractArrow.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractArrow.java +@@ -59,20 +59,7 @@ public class CraftAbstractArrow extends AbstractProjectile implements AbstractAr + this.getHandle().setCritArrow(critical); + } + +- @Override +- public ProjectileSource getShooter() { +- return this.getHandle().projectileSource; +- } +- +- @Override +- public void setShooter(ProjectileSource shooter) { +- if (shooter instanceof Entity) { +- this.getHandle().setOwner(((CraftEntity) shooter).getHandle()); +- } else { +- this.getHandle().setOwner(null); +- } +- this.getHandle().projectileSource = shooter; +- } ++ // Paper - moved to AbstractProjectile + + @Override + public boolean isInBlock() { +@@ -133,6 +120,7 @@ public class CraftAbstractArrow extends AbstractProjectile implements AbstractAr + + @Override + public ItemStack getWeapon() { ++ if (this.getHandle().getWeaponItem() == null) return null; // Paper - fix NPE + return CraftItemStack.asBukkitCopy(this.getHandle().getWeaponItem()); + } + +@@ -152,4 +140,37 @@ public class CraftAbstractArrow extends AbstractProjectile implements AbstractAr + public String toString() { + return "CraftArrow"; + } ++ ++ // Paper start ++ @Override ++ public CraftItemStack getItemStack() { ++ return CraftItemStack.asCraftMirror(this.getHandle().getPickupItem()); ++ } ++ ++ @Override ++ public void setItemStack(final ItemStack stack) { ++ Preconditions.checkArgument(stack != null, "ItemStack cannot be null"); ++ this.getHandle().setPickupItemStack(CraftItemStack.asNMSCopy(stack)); ++ } ++ ++ @Override ++ public void setLifetimeTicks(int ticks) { ++ this.getHandle().life = ticks; ++ } ++ ++ @Override ++ public int getLifetimeTicks() { ++ return this.getHandle().life; ++ } ++ ++ @Override ++ public org.bukkit.Sound getHitSound() { ++ return org.bukkit.craftbukkit.CraftSound.minecraftToBukkit(this.getHandle().soundEvent); ++ } ++ ++ @Override ++ public void setHitSound(org.bukkit.Sound sound) { ++ this.getHandle().setSoundEvent(org.bukkit.craftbukkit.CraftSound.bukkitToMinecraft(sound)); ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftAreaEffectCloud.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftAreaEffectCloud.java +index 6591513bb62226b6f85fd2ef9f6ebe376f0f7362..f9c113dc018702159345240d6d0de85767afa0c3 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftAreaEffectCloud.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftAreaEffectCloud.java +@@ -125,7 +125,7 @@ public class CraftAreaEffectCloud extends CraftEntity implements AreaEffectCloud + + @Override + public Color getColor() { +- return Color.fromRGB(this.getHandle().potionContents.getColor()); ++ return Color.fromRGB(this.getHandle().potionContents.getColor() & 0x00FFFFFF); // Paper - skip alpha channel + } + + @Override +@@ -143,7 +143,7 @@ public class CraftAreaEffectCloud extends CraftEntity implements AreaEffectCloud + this.removeCustomEffect(effect.getType()); + } + this.getHandle().addEffect(CraftPotionUtil.fromBukkit(effect)); +- this.getHandle().updateColor(); ++ // this.getHandle().updateColor(); // Paper - already done above + return true; + } + +@@ -151,7 +151,7 @@ public class CraftAreaEffectCloud extends CraftEntity implements AreaEffectCloud + public void clearCustomEffects() { + PotionContents old = this.getHandle().potionContents; + this.getHandle().setPotionContents(new PotionContents(old.potion(), old.customColor(), List.of(), old.customName())); +- this.getHandle().updateColor(); ++ // this.getHandle().updateColor(); // Paper - already done above + } + + @Override +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java +index 199d5836dc787cca54c6b653a4e67573f2f758a2..15d50a284cafc2eb59239ca00926836526f09e06 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java +@@ -43,7 +43,7 @@ public class CraftArrow extends CraftAbstractArrow implements Arrow { + this.removeCustomEffect(effect.getType()); + } + this.getHandle().addEffect(CraftPotionUtil.fromBukkit(effect)); +- this.getHandle().updateColor(); ++ // this.getHandle().updateColor(); // Paper - already done above + return true; + } + +@@ -51,7 +51,7 @@ public class CraftArrow extends CraftAbstractArrow implements Arrow { + public void clearCustomEffects() { + PotionContents old = this.getHandle().getPotionContents(); + this.getHandle().setPotionContents(new PotionContents(old.potion(), old.customColor(), List.of(), old.customName())); +- this.getHandle().updateColor(); ++ // this.getHandle().updateColor(); // Paper - already done above + } + + @Override +@@ -117,16 +117,17 @@ public class CraftArrow extends CraftAbstractArrow implements Arrow { + + @Override + public void setColor(Color color) { +- int colorRGB = (color == null) ? -1 : color.asRGB(); ++ int colorRGB = (color == null) ? net.minecraft.world.entity.projectile.Arrow.NO_EFFECT_COLOR : color.asARGB(); // Paper + PotionContents old = this.getHandle().getPotionContents(); + this.getHandle().setPotionContents(new PotionContents(old.potion(), Optional.of(colorRGB), old.customEffects(), old.customName())); + } + + @Override + public Color getColor() { +- if (this.getHandle().getColor() <= -1) { ++ int color = this.getHandle().getColor(); // Paper ++ if (color == net.minecraft.world.entity.projectile.Arrow.NO_EFFECT_COLOR) { // Paper + return null; + } +- return Color.fromRGB(this.getHandle().getColor()); ++ return Color.fromARGB(color); // Paper + } + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java +index f3ad9c57ce2fc06b90b7ab7e1f31ef0e1b564c9f..2a32f2aa3cb7395900cf2d06cc2dcd4ddacd4145 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityTypes.java +@@ -440,7 +440,7 @@ public final class CraftEntityTypes { + BlockPos pos = BlockPos.containing(spawnData.x(), spawnData.y(), spawnData.z()); + return new FallingBlockEntity(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z(), spawnData.world().getBlockState(pos)); // Paper - create falling block entities correctly + })); +- register(new EntityTypeData<>(EntityType.FIREWORK_ROCKET, Firework.class, CraftFirework::new, spawnData -> new FireworkRocketEntity(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z(), net.minecraft.world.item.ItemStack.EMPTY))); ++ register(new EntityTypeData<>(EntityType.FIREWORK_ROCKET, Firework.class, CraftFirework::new, spawnData -> new FireworkRocketEntity(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z(), FireworkRocketEntity.getDefaultItem()))); // Paper - pass correct default to rocket for data storage + register(new EntityTypeData<>(EntityType.EVOKER_FANGS, EvokerFangs.class, CraftEvokerFangs::new, spawnData -> new net.minecraft.world.entity.projectile.EvokerFangs(spawnData.minecraftWorld(), spawnData.x(), spawnData.y(), spawnData.z(), (float) Math.toRadians(spawnData.yaw()), 0, null))); + register(new EntityTypeData<>(EntityType.COMMAND_BLOCK_MINECART, CommandMinecart.class, CraftMinecartCommand::new, createMinecart(net.minecraft.world.entity.EntityType.COMMAND_BLOCK_MINECART))); + register(new EntityTypeData<>(EntityType.MINECART, RideableMinecart.class, CraftMinecartRideable::new, createMinecart(net.minecraft.world.entity.EntityType.MINECART))); +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java +index 1b084d63bdbb24dad45d28eed1693eb6e26e24dc..43d7bea201a52cfeacf60c75caa28dfd2c4ff164 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftFireball.java +@@ -34,20 +34,7 @@ public class CraftFireball extends AbstractProjectile implements Fireball { + this.getHandle().bukkitYield = yield; + } + +- @Override +- public ProjectileSource getShooter() { +- return this.getHandle().projectileSource; +- } +- +- @Override +- public void setShooter(ProjectileSource shooter) { +- if (shooter instanceof CraftLivingEntity) { +- this.getHandle().setOwner(((CraftLivingEntity) shooter).getHandle()); +- } else { +- this.getHandle().setOwner(null); +- } +- this.getHandle().projectileSource = shooter; +- } ++ // Paper - moved to AbstractProjectile + + @Override + public Vector getDirection() { +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java +index c9e15a9d82dee935293b2e7e233f5b9b2d822448..2d54cf6f3d9696c55335f0a2057025e2034d4e13 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftFirework.java +@@ -15,24 +15,26 @@ import org.bukkit.inventory.meta.FireworkMeta; + public class CraftFirework extends CraftProjectile implements Firework { + + private final Random random = new Random(); +- private final CraftItemStack item; ++ //private CraftItemStack item; // Paper - Remove usage, not accurate representation of current item. + + public CraftFirework(CraftServer server, FireworkRocketEntity entity) { + super(server, entity); + +- ItemStack item = this.getHandle().getEntityData().get(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM); +- +- if (item.isEmpty()) { +- item = new ItemStack(Items.FIREWORK_ROCKET); +- this.getHandle().getEntityData().set(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM, item); +- } +- +- this.item = CraftItemStack.asCraftMirror(item); +- +- // Ensure the item is a firework... +- if (this.item.getType() != Material.FIREWORK_ROCKET) { +- this.item.setType(Material.FIREWORK_ROCKET); +- } ++ // Paper start - Expose firework item directly ++// ItemStack item = this.getHandle().getEntityData().get(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM); ++// ++// if (item.isEmpty()) { ++// item = new ItemStack(Items.FIREWORK_ROCKET); ++// this.getHandle().getEntityData().set(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM, item); ++// } ++// ++// this.item = CraftItemStack.asCraftMirror(item); ++// ++// // Ensure the item is a firework... ++// if (this.item.getType() != Material.FIREWORK_ROCKET) { ++// this.item.setType(Material.FIREWORK_ROCKET); ++// } ++ // Paper end - Expose firework item directly + } + + @Override +@@ -47,12 +49,12 @@ public class CraftFirework extends CraftProjectile implements Firework { + + @Override + public FireworkMeta getFireworkMeta() { +- return (FireworkMeta) this.item.getItemMeta(); ++ return (FireworkMeta) CraftItemStack.getItemMeta(this.getHandle().getEntityData().get(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM), org.bukkit.inventory.ItemType.FIREWORK_ROCKET); // Paper - Expose firework item directly + } + + @Override + public void setFireworkMeta(FireworkMeta meta) { +- this.item.setItemMeta(meta); ++ applyFireworkEffect(meta); // Paper - Expose firework item directly + + // Copied from EntityFireworks constructor, update firework lifetime/power + this.getHandle().lifetime = 10 * (1 + meta.getPower()) + this.random.nextInt(6) + this.random.nextInt(7); +@@ -136,4 +138,46 @@ public class CraftFirework extends CraftProjectile implements Firework { + return getHandle().spawningEntity; + } + // Paper end ++ // Paper start - Expose firework item directly + manually setting flight ++ @Override ++ public org.bukkit.inventory.ItemStack getItem() { ++ return CraftItemStack.asBukkitCopy(this.getHandle().getItem()); ++ } ++ ++ @Override ++ public void setItem(org.bukkit.inventory.ItemStack itemStack) { ++ FireworkMeta meta = getFireworkMeta(); ++ ItemStack nmsItem = itemStack == null ? FireworkRocketEntity.getDefaultItem() : CraftItemStack.asNMSCopy(itemStack); ++ this.getHandle().getEntityData().set(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM, nmsItem); ++ ++ applyFireworkEffect(meta); ++ } ++ ++ @Override ++ public int getTicksFlown() { ++ return this.getHandle().life; ++ } ++ ++ @Override ++ public void setTicksFlown(int ticks) { ++ this.getHandle().life = ticks; ++ } ++ ++ @Override ++ public int getTicksToDetonate() { ++ return this.getHandle().lifetime; ++ } ++ ++ @Override ++ public void setTicksToDetonate(int ticks) { ++ this.getHandle().lifetime = ticks; ++ } ++ ++ void applyFireworkEffect(FireworkMeta meta) { ++ ItemStack item = this.getHandle().getItem(); ++ CraftItemStack.applyMetaToItem(item, meta); ++ ++ this.getHandle().getEntityData().set(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM, item); ++ } ++ // Paper end - Expose firework item directly + manually setting flight + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftFishHook.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftFishHook.java +index 6e2f91423371ead9890095cf4b1e2299c4dcba28..9d8f4b7176e60180565e3134a14ecf19060f2621 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftFishHook.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftFishHook.java +@@ -196,4 +196,42 @@ public class CraftFishHook extends CraftProjectile implements FishHook { + public HookState getState() { + return HookState.values()[this.getHandle().currentState.ordinal()]; + } ++ // Paper start - More FishHook API ++ @Override ++ public int getWaitTime() { ++ return this.getHandle().timeUntilLured; ++ } ++ ++ @Override ++ public void setWaitTime(int ticks) { ++ this.getHandle().timeUntilLured = ticks; ++ } ++ ++ @Override ++ public int getTimeUntilBite() { ++ return this.getHandle().timeUntilHooked; ++ } ++ ++ @Override ++ public void setTimeUntilBite(final int ticks) { ++ com.google.common.base.Preconditions.checkArgument(ticks >= 1, "Cannot set time until bite to less than 1 (%s<1)", ticks); ++ final FishingHook hook = this.getHandle(); ++ ++ // Reset the fish angle hook only when this call "enters" the fish into the lure stage. ++ final boolean alreadyInLuringPhase = hook.timeUntilHooked > 0 && hook.timeUntilLured <= 0; ++ if (!alreadyInLuringPhase) { ++ hook.fishAngle = net.minecraft.util.Mth.nextFloat(hook.random, hook.minLureAngle, hook.maxLureAngle); ++ hook.timeUntilLured = 0; ++ } ++ ++ hook.timeUntilHooked = ticks; ++ } ++ ++ @Override ++ public void resetFishingState() { ++ final FishingHook hook = this.getHandle(); ++ hook.resetTimeUntilLured(); ++ hook.timeUntilHooked = 0; // Reset time until hooked, will be repopulated once lured time is ticked down. ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +index 10a95c1a40597867ffd2974037bfed86dd6deda4..2028f9874e96a3b274528858d4149b04a075f256 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +@@ -579,8 +579,15 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { + } + + @Override +- @SuppressWarnings("unchecked") + public <T extends Projectile> T launchProjectile(Class<? extends T> projectile, Vector velocity) { ++ // Paper start - launchProjectile consumer ++ return this.launchProjectile(projectile, velocity, null); ++ } ++ ++ @Override ++ @SuppressWarnings("unchecked") ++ public <T extends Projectile> T launchProjectile(Class<? extends T> projectile, Vector velocity, java.util.function.Consumer<? super T> function) { ++ // Paper end - launchProjectile consumer + Preconditions.checkState(!this.getHandle().generation, "Cannot launch projectile during world generation"); + + net.minecraft.world.level.Level world = ((CraftWorld) this.getWorld()).getHandle(); +@@ -606,7 +613,7 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { + } else { + launch = new net.minecraft.world.entity.projectile.Arrow(world, this.getHandle(), new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.ARROW), null); + } +- ((net.minecraft.world.entity.projectile.AbstractArrow) launch).shootFromRotation(this.getHandle(), this.getHandle().getXRot(), this.getHandle().getYRot(), 0.0F, 3.0F, 1.0F); // ItemBow ++ ((net.minecraft.world.entity.projectile.AbstractArrow) launch).shootFromRotation(this.getHandle(), this.getHandle().getXRot(), this.getHandle().getYRot(), 0.0F, Trident.class.isAssignableFrom(projectile) ? net.minecraft.world.item.TridentItem.SHOOT_POWER : 3.0F, 1.0F); // ItemBow // Paper - see TridentItem + } else if (ThrownPotion.class.isAssignableFrom(projectile)) { + if (LingeringPotion.class.isAssignableFrom(projectile)) { + launch = new net.minecraft.world.entity.projectile.ThrownPotion(world, this.getHandle(), new net.minecraft.world.item.ItemStack(Items.LINGERING_POTION)); +@@ -663,8 +670,26 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { + } else if (Firework.class.isAssignableFrom(projectile)) { + Location location = this.getEyeLocation(); + +- launch = new FireworkRocketEntity(world, net.minecraft.world.item.ItemStack.EMPTY, this.getHandle()); +- launch.moveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); ++ // Paper start - see CrossbowItem ++ launch = new FireworkRocketEntity(world, FireworkRocketEntity.getDefaultItem(), this.getHandle(), location.getX(), location.getY() - 0.15F, location.getZ(), true); // Paper - pass correct default to rocket for data storage & see CrossbowItem for regular launch without elytra boost ++ ++ // Lifted from net.minecraft.world.item.ProjectileWeaponItem.shoot ++ float f2 = /* net.minecraft.world.item.enchantment.EnchantmentHelper.processProjectileSpread((ServerLevel) world, new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.CROSSBOW), this.getHandle(), 0.0F); */ 0; // Just shortcut this to 0, no need to do any calculations on a non existing stack ++ int projectileSize = 1; ++ int i = 0; ++ ++ float f3 = projectileSize == 1 ? 0.0F : 2.0F * f2 / (float) (projectileSize - 1); ++ float f4 = (float) ((projectileSize - 1) % 2) * f3 / 2.0F; ++ float f5 = 1.0F; ++ float yaw = f4 + f5 * (float) ((i + 1) / 2) * f3; ++ ++ // Lifted from net.minecraft.world.item.CrossbowItem.shootProjectile ++ Vec3 vec3 = this.getHandle().getUpVector(1.0F); ++ org.joml.Quaternionf quaternionf = new org.joml.Quaternionf().setAngleAxis((double)(yaw * (float) (Math.PI / 180.0)), vec3.x, vec3.y, vec3.z); ++ Vec3 vec32 = this.getHandle().getViewVector(1.0F); ++ org.joml.Vector3f vector3f = vec32.toVector3f().rotate(quaternionf); ++ ((FireworkRocketEntity) launch).shoot((double)vector3f.x(), (double)vector3f.y(), (double)vector3f.z(), net.minecraft.world.item.CrossbowItem.FIREWORK_POWER, 1.0F); ++ // Paper end + } + + Preconditions.checkArgument(launch != null, "Projectile (%s) not supported", projectile.getName()); +@@ -672,6 +697,11 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { + if (velocity != null) { + ((T) launch.getBukkitEntity()).setVelocity(velocity); + } ++ // Paper start - launchProjectile consumer ++ if (function != null) { ++ function.accept((T) launch.getBukkitEntity()); ++ } ++ // Paper end - launchProjectile consumer + + world.addFreshEntity(launch); + return (T) launch.getBukkitEntity(); +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLlamaSpit.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLlamaSpit.java +index 70cbc6c668c60e9d608ca7013b72f9b916c05c2d..47633f05b4fab1dcabc2117e7645fe6d6949622a 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLlamaSpit.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLlamaSpit.java +@@ -20,13 +20,5 @@ public class CraftLlamaSpit extends AbstractProjectile implements LlamaSpit { + return "CraftLlamaSpit"; + } + +- @Override +- public ProjectileSource getShooter() { +- return (this.getHandle().getOwner() != null) ? (ProjectileSource) this.getHandle().getOwner().getBukkitEntity() : null; +- } +- +- @Override +- public void setShooter(ProjectileSource source) { +- this.getHandle().setOwner((source != null) ? ((CraftLivingEntity) source).getHandle() : null); +- } ++ // Paper - moved to AbstractProjectile + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftProjectile.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftProjectile.java +index 696fdfa723aa896a67946f862d7c6890f7f7ab17..4f1fa7dec78970bdfc184d3c1f1632dc9d75a574 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftProjectile.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftProjectile.java +@@ -10,20 +10,7 @@ public abstract class CraftProjectile extends AbstractProjectile implements Proj + super(server, entity); + } + +- @Override +- public ProjectileSource getShooter() { +- return this.getHandle().projectileSource; +- } +- +- @Override +- public void setShooter(ProjectileSource shooter) { +- if (shooter instanceof CraftLivingEntity) { +- this.getHandle().setOwner((LivingEntity) ((CraftLivingEntity) shooter).entity); +- } else { +- this.getHandle().setOwner(null); +- } +- this.getHandle().projectileSource = shooter; +- } ++ // Paper - moved to AbstractProjectile + + @Override + public net.minecraft.world.entity.projectile.Projectile getHandle() { +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftShulkerBullet.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftShulkerBullet.java +index d685d09cae5f862c0004f148298c800736d2139e..b3797a43eeee11cb7ae0774d61bd5f195d0aa3ad 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftShulkerBullet.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftShulkerBullet.java +@@ -12,31 +12,56 @@ public class CraftShulkerBullet extends AbstractProjectile implements ShulkerBul + super(server, entity); + } + ++ // Paper - moved to AbstractProjectile ++ ++ @Override ++ public org.bukkit.entity.Entity getTarget() { ++ return this.getHandle().getTarget() != null ? this.getHandle().getTarget().getBukkitEntity() : null; ++ } ++ + @Override +- public ProjectileSource getShooter() { +- return this.getHandle().projectileSource; ++ public void setTarget(org.bukkit.entity.Entity target) { ++ Preconditions.checkState(!this.getHandle().generation, "Cannot set target during world generation"); ++ ++ this.getHandle().setTarget(target == null ? null : ((CraftEntity) target).getHandle()); + } + + @Override +- public void setShooter(ProjectileSource shooter) { +- if (shooter instanceof Entity) { +- this.getHandle().setOwner(((CraftEntity) shooter).getHandle()); +- } else { +- this.getHandle().setOwner(null); ++ public org.bukkit.util.Vector getTargetDelta() { ++ net.minecraft.world.entity.projectile.ShulkerBullet bullet = this.getHandle(); ++ return new org.bukkit.util.Vector(bullet.targetDeltaX, bullet.targetDeltaY, bullet.targetDeltaZ); ++ } ++ ++ @Override ++ public void setTargetDelta(org.bukkit.util.Vector vector) { ++ net.minecraft.world.entity.projectile.ShulkerBullet bullet = this.getHandle(); ++ bullet.targetDeltaX = vector.getX(); ++ bullet.targetDeltaY = vector.getY(); ++ bullet.targetDeltaZ = vector.getZ(); ++ } ++ ++ @Override ++ public org.bukkit.block.BlockFace getCurrentMovementDirection() { ++ net.minecraft.core.Direction dir = this.getHandle().currentMoveDirection; ++ if (dir == null) { ++ return null; // random dir + } +- this.getHandle().projectileSource = shooter; ++ return org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(dir); + } + + @Override +- public org.bukkit.entity.Entity getTarget() { +- return this.getHandle().getTarget() != null ? this.getHandle().getTarget().getBukkitEntity() : null; ++ public void setCurrentMovementDirection(org.bukkit.block.BlockFace movementDirection) { ++ this.getHandle().currentMoveDirection = org.bukkit.craftbukkit.block.CraftBlock.blockFaceToNotch(movementDirection); + } + + @Override +- public void setTarget(org.bukkit.entity.Entity target) { +- Preconditions.checkState(!this.getHandle().generation, "Cannot set target during world generation"); ++ public int getFlightSteps() { ++ return this.getHandle().flightSteps; ++ } + +- this.getHandle().setTarget(target == null ? null : ((CraftEntity) target).getHandle()); ++ @Override ++ public void setFlightSteps(int steps) { ++ this.getHandle().flightSteps = steps; + } + + @Override +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java +index d67a80161b3e7c1fe02a6ed9d341c00dc7c2847a..f6fa6f1ac50b757dd3bc9a8dee9f6085446182c8 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java +@@ -36,11 +36,31 @@ public class CraftThrownPotion extends CraftThrowableProjectile implements Throw + @Override + public void setItem(ItemStack item) { + Preconditions.checkArgument(item != null, "ItemStack cannot be null"); +- Preconditions.checkArgument(item.getType() == Material.LINGERING_POTION || item.getType() == Material.SPLASH_POTION, "ItemStack material must be Material.LINGERING_POTION or Material.SPLASH_POTION but was Material.%s", item.getType()); ++ // Preconditions.checkArgument(item.getType() == Material.LINGERING_POTION || item.getType() == Material.SPLASH_POTION, "ItemStack material must be Material.LINGERING_POTION or Material.SPLASH_POTION but was Material.%s", item.getType()); // Paper - Projectile API ++ org.bukkit.inventory.meta.PotionMeta meta = (item.getType() == Material.LINGERING_POTION || item.getType() == Material.SPLASH_POTION) ? null : this.getPotionMeta(); // Paper - Projectile API + + this.getHandle().setItem(CraftItemStack.asNMSCopy(item)); ++ if (meta != null) this.setPotionMeta(meta); // Paper - Projectile API + } + ++ // Paper start - Projectile API ++ @Override ++ public org.bukkit.inventory.meta.PotionMeta getPotionMeta() { ++ return (org.bukkit.inventory.meta.PotionMeta) CraftItemStack.getItemMeta(this.getHandle().getItem(), org.bukkit.inventory.ItemType.SPLASH_POTION); ++ } ++ ++ @Override ++ public void setPotionMeta(org.bukkit.inventory.meta.PotionMeta meta) { ++ net.minecraft.world.item.ItemStack item = this.getHandle().getItem(); ++ CraftItemStack.applyMetaToItem(item, meta); ++ this.getHandle().setItem(item); // Reset item ++ } ++ ++ @Override ++ public void splash() { ++ this.getHandle().splash(null); ++ } ++ // Paper end + @Override + public net.minecraft.world.entity.projectile.ThrownPotion getHandle() { + return (net.minecraft.world.entity.projectile.ThrownPotion) this.entity; +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftTrident.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftTrident.java +index e374b9f40eddca13b30855d25a2030f8df98138f..4fc893378fb0568ddcffc7593d66df6bfe23f659 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftTrident.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftTrident.java +@@ -53,5 +53,15 @@ public class CraftTrident extends CraftAbstractArrow implements Trident { + com.google.common.base.Preconditions.checkArgument(loyaltyLevel >= 0 && loyaltyLevel <= 127, "The loyalty level has to be between 0 and 127"); + this.getHandle().setLoyalty((byte) loyaltyLevel); + } ++ ++ @Override ++ public boolean hasDealtDamage() { ++ return this.getHandle().dealtDamage; ++ } ++ ++ @Override ++ public void setHasDealtDamage(boolean hasDealtDamage) { ++ this.getHandle().dealtDamage = hasDealtDamage; ++ } + // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +index a5285a8952b2d99bfbb928b1bb31d3ddcf8ed57b..ca094e393de32b64db59c9fe906433761d70d29b 100644 +--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java ++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +@@ -841,19 +841,19 @@ public class CraftEventFactory { + /** + * PotionSplashEvent + */ +- public static PotionSplashEvent callPotionSplashEvent(net.minecraft.world.entity.projectile.ThrownPotion potion, HitResult position, Map<LivingEntity, Double> affectedEntities) { ++ public static PotionSplashEvent callPotionSplashEvent(net.minecraft.world.entity.projectile.ThrownPotion potion, @Nullable HitResult position, Map<LivingEntity, Double> affectedEntities) { // Paper - nullable hitResult + ThrownPotion thrownPotion = (ThrownPotion) potion.getBukkitEntity(); + + Block hitBlock = null; + BlockFace hitFace = null; +- if (position.getType() == HitResult.Type.BLOCK) { ++ if (position != null && position.getType() == HitResult.Type.BLOCK) { // Paper - nullable hitResult + BlockHitResult positionBlock = (BlockHitResult) position; + hitBlock = CraftBlock.at(potion.level(), positionBlock.getBlockPos()); + hitFace = CraftBlock.notchToBlockFace(positionBlock.getDirection()); + } + + org.bukkit.entity.Entity hitEntity = null; +- if (position.getType() == HitResult.Type.ENTITY) { ++ if (position != null && position.getType() == HitResult.Type.ENTITY) { // Paper - nullable hitResult + hitEntity = ((EntityHitResult) position).getEntity().getBukkitEntity(); + } + +@@ -862,20 +862,20 @@ public class CraftEventFactory { + return event; + } + +- public static LingeringPotionSplashEvent callLingeringPotionSplashEvent(net.minecraft.world.entity.projectile.ThrownPotion potion, HitResult position, net.minecraft.world.entity.AreaEffectCloud cloud) { ++ public static LingeringPotionSplashEvent callLingeringPotionSplashEvent(net.minecraft.world.entity.projectile.ThrownPotion potion, @Nullable HitResult position, net.minecraft.world.entity.AreaEffectCloud cloud) { // Paper - nullable hitResult + ThrownPotion thrownPotion = (ThrownPotion) potion.getBukkitEntity(); + AreaEffectCloud effectCloud = (AreaEffectCloud) cloud.getBukkitEntity(); + + Block hitBlock = null; + BlockFace hitFace = null; +- if (position.getType() == HitResult.Type.BLOCK) { ++ if (position != null && position.getType() == HitResult.Type.BLOCK) { // Paper + BlockHitResult positionBlock = (BlockHitResult) position; + hitBlock = CraftBlock.at(potion.level(), positionBlock.getBlockPos()); + hitFace = CraftBlock.notchToBlockFace(positionBlock.getDirection()); + } + + org.bukkit.entity.Entity hitEntity = null; +- if (position.getType() == HitResult.Type.ENTITY) { ++ if (position != null && position.getType() == HitResult.Type.ENTITY) { // Paper + hitEntity = ((EntityHitResult) position).getEntity().getBukkitEntity(); + } + +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java +index 60062ea5b18b95a14c459f2f3a0743c1e1ac0f12..502be683e8b04a9966043c9bee9d9fe793b12ef5 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java +@@ -341,12 +341,23 @@ public final class CraftItemStack extends ItemStack { + public ItemMeta getItemMeta() { + return CraftItemStack.getItemMeta(this.handle); + } ++ // Paper start ++ public static void applyMetaToItem(net.minecraft.world.item.ItemStack itemStack, ItemMeta itemMeta) { ++ final CraftMetaItem.Applicator tag = new CraftMetaItem.Applicator(); ++ ((CraftMetaItem) itemMeta).applyToItem(tag); ++ itemStack.applyComponents(tag.build()); ++ } + + public static ItemMeta getItemMeta(net.minecraft.world.item.ItemStack item) { ++ return getItemMeta(item, null); ++ } ++ public static ItemMeta getItemMeta(net.minecraft.world.item.ItemStack item, org.bukkit.inventory.ItemType metaForType) { ++ // Paper end + if (!CraftItemStack.hasItemMeta(item)) { + return CraftItemFactory.instance().getItemMeta(CraftItemStack.getType(item)); + } + ++ if (metaForType != null) { return ((CraftItemType<?>) metaForType).getItemMeta(item); } // Paper + return ((CraftItemType<?>) CraftItemType.minecraftToBukkitNew(item.getItem())).getItemMeta(item); + } + +diff --git a/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java b/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java +index 642f5bf75661eb485558bc227f668e84416f3b5f..76fd4d27730d9139caa67099a6757ea33d681be9 100644 +--- a/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java ++++ b/src/main/java/org/bukkit/craftbukkit/projectiles/CraftBlockProjectileSource.java +@@ -56,7 +56,15 @@ public class CraftBlockProjectileSource implements BlockProjectileSource { + + @Override + public <T extends Projectile> T launchProjectile(Class<? extends T> projectile, Vector velocity) { ++ // Paper start - launchProjectile consumer ++ return this.launchProjectile(projectile, velocity, null); ++ } ++ ++ @Override ++ public <T extends Projectile> T launchProjectile(Class<? extends T> projectile, Vector velocity, java.util.function.Consumer<? super T> function) { ++ // Paper end - launchProjectile consumer + Preconditions.checkArgument(this.getBlock().getType() == Material.DISPENSER, "Block is no longer dispenser"); ++ + // Copied from BlockDispenser.dispense() + BlockSource sourceblock = new BlockSource((ServerLevel) this.dispenserBlock.getLevel(), this.dispenserBlock.getBlockPos(), this.dispenserBlock.getBlockState(), this.dispenserBlock); + // Copied from DispenseBehaviorProjectile +@@ -68,7 +76,7 @@ public class CraftBlockProjectileSource implements BlockProjectileSource { + item = Items.SNOWBALL; + } else if (Egg.class.isAssignableFrom(projectile)) { + item = Items.EGG; +- } else if (EnderPearl.class.isAssignableFrom(projectile)) { ++ } else if (false && EnderPearl.class.isAssignableFrom(projectile)) { // Paper - more projectile API - disallow enderpearl, it is not a projectile item + item = Items.ENDER_PEARL; + } else if (ThrownExpBottle.class.isAssignableFrom(projectile)) { + item = Items.EXPERIENCE_BOTTLE; +@@ -83,20 +91,20 @@ public class CraftBlockProjectileSource implements BlockProjectileSource { + item = Items.TIPPED_ARROW; + } else if (SpectralArrow.class.isAssignableFrom(projectile)) { + item = Items.SPECTRAL_ARROW; +- } else { ++ } else if (org.bukkit.entity.Arrow.class.isAssignableFrom(projectile)) { // Paper - more projectile API - disallow trident + item = Items.ARROW; + } + } else if (Fireball.class.isAssignableFrom(projectile)) { +- if (AbstractWindCharge.class.isAssignableFrom(projectile)) { ++ if (org.bukkit.entity.WindCharge.class.isAssignableFrom(projectile)) { // Paper - more projectile API - only allow wind charge not breeze wind charge + item = Items.WIND_CHARGE; +- } else { ++ } else if (org.bukkit.entity.SmallFireball.class.isAssignableFrom(projectile)) { // Paper - more projectile API - only allow firing fire charges. + item = Items.FIRE_CHARGE; + } + } else if (Firework.class.isAssignableFrom(projectile)) { + item = Items.FIREWORK_ROCKET; + } + +- Preconditions.checkArgument(item instanceof ProjectileItem, "Projectile not supported"); ++ Preconditions.checkArgument(item instanceof ProjectileItem, "Projectile '%s' not supported", projectile.getSimpleName()); // Paper - more projectile API - include simple name in exception + + ItemStack itemstack = new ItemStack(item); + ProjectileItem projectileItem = (ProjectileItem) item; +@@ -105,7 +113,7 @@ public class CraftBlockProjectileSource implements BlockProjectileSource { + Position iposition = dispenseConfig.positionFunction().getDispensePosition(sourceblock, enumdirection); + net.minecraft.world.entity.projectile.Projectile launch = projectileItem.asProjectile(world, iposition, itemstack, enumdirection); + +- if (Fireball.class.isAssignableFrom(projectile)) { ++ if (false && Fireball.class.isAssignableFrom(projectile)) { // Paper - more project API - dispensers cannot launch anything but fire charges. + AbstractHurtingProjectile customFireball = null; + if (WitherSkull.class.isAssignableFrom(projectile)) { + launch = customFireball = EntityType.WITHER_SKULL.create(world, EntitySpawnReason.TRIGGERED); +@@ -130,7 +138,7 @@ public class CraftBlockProjectileSource implements BlockProjectileSource { + } + } + +- if (launch instanceof net.minecraft.world.entity.projectile.AbstractArrow arrow) { ++ if (false && launch instanceof net.minecraft.world.entity.projectile.AbstractArrow arrow) { // Paper - more projectile API - this is set by the respective ArrowItem when constructing the projectile + arrow.pickup = net.minecraft.world.entity.projectile.AbstractArrow.Pickup.ALLOWED; + } + launch.projectileSource = this; +@@ -139,6 +147,11 @@ public class CraftBlockProjectileSource implements BlockProjectileSource { + if (velocity != null) { + ((T) launch.getBukkitEntity()).setVelocity(velocity); + } ++ // Paper start ++ if (function != null) { ++ function.accept((T) launch.getBukkitEntity()); ++ } ++ // Paper end + + world.addFreshEntity(launch); + return (T) launch.getBukkitEntity(); diff --git a/patches/server/0666-Fix-swamp-hut-cat-generation-deadlock.patch b/patches/server/0666-Fix-swamp-hut-cat-generation-deadlock.patch new file mode 100644 index 0000000000..ed87313991 --- /dev/null +++ b/patches/server/0666-Fix-swamp-hut-cat-generation-deadlock.patch @@ -0,0 +1,64 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf <[email protected]> +Date: Sat, 12 Mar 2022 06:31:13 -0800 +Subject: [PATCH] Fix swamp hut cat generation deadlock + +The worldgen thread will attempt to get structure references +via the world's getChunkAt method, which is fine if the gen is +not cancelled - but if the chunk was unloaded, the call will block +indefinitely. Instead of using the world state, we use the already +supplied ServerLevelAccessor which will always have the chunk available. + +diff --git a/src/main/java/net/minecraft/world/entity/animal/Cat.java b/src/main/java/net/minecraft/world/entity/animal/Cat.java +index 93d8d45a9a8108e58f3a96d77dd35c7584133835..629b282cf27806ff37d67f83d44c06a9f32a9185 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/Cat.java ++++ b/src/main/java/net/minecraft/world/entity/animal/Cat.java +@@ -365,7 +365,7 @@ public class Cat extends TamableAnimal implements VariantHolder<Holder<CatVarian + BuiltInRegistries.CAT_VARIANT.getRandomElementOf(tagkey, world.getRandom()).ifPresent(this::setVariant); + ServerLevel worldserver = world.getLevel(); + +- if (worldserver.structureManager().getStructureWithPieceAt(this.blockPosition(), StructureTags.CATS_SPAWN_AS_BLACK).isValid()) { ++ if (worldserver.structureManager().getStructureWithPieceAt(this.blockPosition(), StructureTags.CATS_SPAWN_AS_BLACK, world).isValid()) { // Paper - Fix swamp hut cat generation deadlock + this.setVariant((Holder) BuiltInRegistries.CAT_VARIANT.getOrThrow(CatVariant.ALL_BLACK)); + this.setPersistenceRequired(); + } +diff --git a/src/main/java/net/minecraft/world/level/StructureManager.java b/src/main/java/net/minecraft/world/level/StructureManager.java +index 3d607ac74de4bc051ab68269dbab0bbd3a52dc54..03adb02297911e5a5051edac14453d7e3eeac29c 100644 +--- a/src/main/java/net/minecraft/world/level/StructureManager.java ++++ b/src/main/java/net/minecraft/world/level/StructureManager.java +@@ -48,7 +48,12 @@ public class StructureManager { + } + + public List<StructureStart> startsForStructure(ChunkPos pos, Predicate<Structure> predicate) { +- Map<Structure, LongSet> map = this.level.getChunk(pos.x, pos.z, ChunkStatus.STRUCTURE_REFERENCES).getAllReferences(); ++ // Paper start - Fix swamp hut cat generation deadlock ++ return this.startsForStructure(pos, predicate, null); ++ } ++ public List<StructureStart> startsForStructure(ChunkPos pos, Predicate<Structure> predicate, @Nullable ServerLevelAccessor levelAccessor) { ++ Map<Structure, LongSet> map = (levelAccessor == null ? this.level : levelAccessor).getChunk(pos.x, pos.z, ChunkStatus.STRUCTURE_REFERENCES).getAllReferences(); ++ // Paper end - Fix swamp hut cat generation deadlock + Builder<StructureStart> builder = ImmutableList.builder(); + + for (Entry<Structure, LongSet> entry : map.entrySet()) { +@@ -116,10 +121,20 @@ public class StructureManager { + } + + public StructureStart getStructureWithPieceAt(BlockPos pos, Predicate<Holder<Structure>> predicate) { ++ // Paper start - Fix swamp hut cat generation deadlock ++ return this.getStructureWithPieceAt(pos, predicate, null); ++ } ++ ++ public StructureStart getStructureWithPieceAt(BlockPos pos, TagKey<Structure> tag, @Nullable ServerLevelAccessor levelAccessor) { ++ return this.getStructureWithPieceAt(pos, structure -> structure.is(tag), levelAccessor); ++ } ++ ++ public StructureStart getStructureWithPieceAt(BlockPos pos, Predicate<Holder<Structure>> predicate, @Nullable ServerLevelAccessor levelAccessor) { ++ // Paper end - Fix swamp hut cat generation deadlock + Registry<Structure> registry = this.registryAccess().lookupOrThrow(Registries.STRUCTURE); + + for (StructureStart structureStart : this.startsForStructure( +- new ChunkPos(pos), structure -> registry.get(registry.getId(structure)).map(predicate::test).orElse(false) ++ new ChunkPos(pos), structure -> registry.get(registry.getId(structure)).map(predicate::test).orElse(false), levelAccessor // Paper - Fix swamp hut cat generation deadlock + )) { + if (this.structureHasPieceAt(pos, structureStart)) { + return structureStart; diff --git a/patches/server/0667-Don-t-allow-vehicle-movement-from-players-while-tele.patch b/patches/server/0667-Don-t-allow-vehicle-movement-from-players-while-tele.patch new file mode 100644 index 0000000000..75b88b14da --- /dev/null +++ b/patches/server/0667-Don-t-allow-vehicle-movement-from-players-while-tele.patch @@ -0,0 +1,24 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf <[email protected]> +Date: Mon, 14 Mar 2022 12:35:37 -0700 +Subject: [PATCH] Don't allow vehicle movement from players while teleporting + +Bring the vehicle move packet behavior in line with the +regular player move packet. + +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index d76df6ed588b1109cdebf239884d4c9ce5de47a6..f259e32509b0ac6a62ab203911f93cac6150d71d 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -491,6 +491,11 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + this.disconnect((Component) Component.translatable("multiplayer.disconnect.invalid_vehicle_movement"), org.bukkit.event.player.PlayerKickEvent.Cause.INVALID_VEHICLE_MOVEMENT); // Paper - kick event cause + } else if (!this.updateAwaitingTeleport() && this.player.hasClientLoaded()) { + Entity entity = this.player.getRootVehicle(); ++ // Paper start - Don't allow vehicle movement from players while teleporting ++ if (this.awaitingPositionFromClient != null || this.player.isImmobile() || entity.isRemoved()) { ++ return; ++ } ++ // Paper end - Don't allow vehicle movement from players while teleporting + + if (entity != this.player && entity.getControllingPassenger() == this.player && entity == this.lastVehicle) { + ServerLevel worldserver = this.player.serverLevel(); diff --git a/patches/server/0668-Implement-getComputedBiome-API.patch b/patches/server/0668-Implement-getComputedBiome-API.patch new file mode 100644 index 0000000000..dab55a6218 --- /dev/null +++ b/patches/server/0668-Implement-getComputedBiome-API.patch @@ -0,0 +1,61 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jason Penilla <[email protected]> +Date: Mon, 14 Mar 2022 22:46:05 -0700 +Subject: [PATCH] Implement getComputedBiome API + + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java b/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java +index ca35e93239eea09b3d0dc6ef18f58743e633996b..a7748f4b7c5a1630937c702b3fd5fded93793d64 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java +@@ -77,6 +77,13 @@ public abstract class CraftRegionAccessor implements RegionAccessor { + return CraftBiome.minecraftHolderToBukkit(this.getHandle().getNoiseBiome(x >> 2, y >> 2, z >> 2)); + } + ++ // Paper start ++ @Override ++ public Biome getComputedBiome(int x, int y, int z) { ++ return CraftBiome.minecraftHolderToBukkit(this.getHandle().getBiome(new BlockPos(x, y, z))); ++ } ++ // Paper end ++ + @Override + public void setBiome(Location location, Biome biome) { + this.setBiome(location.getBlockX(), location.getBlockY(), location.getBlockZ(), biome); +diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java +index d5b495b5a3ca7f4411d1a700f7149042a16509f1..68fcec085334383808b2117a49220f4d8239220b 100644 +--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java ++++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java +@@ -340,6 +340,13 @@ public class CraftBlock implements Block { + return this.getWorld().getBiome(this.getX(), this.getY(), this.getZ()); + } + ++ // Paper start ++ @Override ++ public Biome getComputedBiome() { ++ return this.getWorld().getComputedBiome(this.getX(), this.getY(), this.getZ()); ++ } ++ // Paper end ++ + @Override + public void setBiome(Biome bio) { + this.getWorld().setBiome(this.getX(), this.getY(), this.getZ(), bio); +diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java b/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java +index 2c7b64bd7071cb803a042152d497982d753e0b5d..a23269e3bdb83f85a1d08d5f7b54742025223ada 100644 +--- a/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java ++++ b/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java +@@ -166,6 +166,14 @@ public class CraftLimitedRegion extends CraftRegionAccessor implements LimitedRe + return super.getBiome(x, y, z); + } + ++ // Paper start ++ @Override ++ public Biome getComputedBiome(int x, int y, int z) { ++ Preconditions.checkArgument(this.isInRegion(x, y, z), "Coordinates %s, %s, %s are not in the region", x, y, z); ++ return super.getComputedBiome(x, y, z); ++ } ++ // Paper end ++ + @Override + public void setBiome(int x, int y, int z, Holder<net.minecraft.world.level.biome.Biome> biomeBase) { + Preconditions.checkArgument(this.isInRegion(x, y, z), "Coordinates %s, %s, %s are not in the region", x, y, z); diff --git a/patches/server/0669-Make-some-itemstacks-nonnull.patch b/patches/server/0669-Make-some-itemstacks-nonnull.patch new file mode 100644 index 0000000000..20944d9088 --- /dev/null +++ b/patches/server/0669-Make-some-itemstacks-nonnull.patch @@ -0,0 +1,28 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Tue, 15 Mar 2022 01:38:15 -0700 +Subject: [PATCH] Make some itemstacks nonnull + + +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java +index 107fa1d4bd977d17dc062da280dda46eb3c5f81c..e62baea16df017f1e394e3c706157e158066eb93 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java +@@ -155,13 +155,13 @@ public class CraftInventoryPlayer extends CraftInventory implements org.bukkit.i + case OFF_HAND: + return this.getItemInOffHand(); + case FEET: +- return this.getBoots(); ++ return java.util.Objects.requireNonNullElseGet(this.getBoots(), () -> new ItemStack(org.bukkit.Material.AIR)); // Paper - make nonnull + case LEGS: +- return this.getLeggings(); ++ return java.util.Objects.requireNonNullElseGet(this.getLeggings(), () -> new ItemStack(org.bukkit.Material.AIR)); // Paper - make nonnull + case CHEST: +- return this.getChestplate(); ++ return java.util.Objects.requireNonNullElseGet(this.getChestplate(), () -> new ItemStack(org.bukkit.Material.AIR)); // Paper - make nonnull + case HEAD: +- return this.getHelmet(); ++ return java.util.Objects.requireNonNullElseGet(this.getHelmet(), () -> new ItemStack(org.bukkit.Material.AIR)); // Paper - make nonnull + default: + throw new IllegalArgumentException("Could not get slot " + slot + " - not a valid slot for PlayerInventory"); + } diff --git a/patches/server/0670-Implement-enchantWithLevels-API.patch b/patches/server/0670-Implement-enchantWithLevels-API.patch new file mode 100644 index 0000000000..008d6aaf29 --- /dev/null +++ b/patches/server/0670-Implement-enchantWithLevels-API.patch @@ -0,0 +1,58 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jason Penilla <[email protected]> +Date: Wed, 16 Mar 2022 20:35:21 -0700 +Subject: [PATCH] Implement enchantWithLevels API + + +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java +index a04840b77e2d84e754c9cfa79bc593608bc22c90..6d228d643342588877e8385acdc202de57d455f1 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java +@@ -307,4 +307,47 @@ public final class CraftItemFactory implements ItemFactory { + return eggItem == null ? null : new net.minecraft.world.item.ItemStack(eggItem).asBukkitMirror(); + } + // Paper end - old getSpawnEgg API ++ // Paper start - enchantWithLevels API ++ @Override ++ public ItemStack enchantWithLevels(ItemStack itemStack, int levels, boolean allowTreasure, java.util.Random random) { ++ return enchantWithLevels( ++ itemStack, ++ levels, ++ allowTreasure ++ ? Optional.empty() ++ // While IN_ENCHANTING_TABLE is not logically the same as all but TREASURE, the tag is defined as ++ // NON_TREASURE, which does contain all enchantments not in the treasure tag. ++ // Additionally, the allowTreasure boolean is more intended to configure this method to behave like ++ // an enchanting table. ++ : net.minecraft.server.MinecraftServer.getServer().registryAccess().lookupOrThrow(Registries.ENCHANTMENT).get(EnchantmentTags.IN_ENCHANTING_TABLE), ++ random ++ ); ++ } ++ ++ @SuppressWarnings("OptionalUsedAsFieldOrParameterType") ++ private ItemStack enchantWithLevels( ++ ItemStack itemStack, ++ int levels, ++ Optional<? extends net.minecraft.core.HolderSet<net.minecraft.world.item.enchantment.Enchantment>> possibleEnchantments, ++ java.util.Random random ++ ) { ++ Preconditions.checkArgument(itemStack != null, "Argument 'itemStack' must not be null"); ++ Preconditions.checkArgument(!itemStack.isEmpty(), "Argument 'itemStack' cannot be empty"); ++ Preconditions.checkArgument(levels > 0 && levels <= 30, "Argument 'levels' must be in range [1, 30] (attempted " + levels + ")"); ++ Preconditions.checkArgument(random != null, "Argument 'random' must not be null"); ++ final net.minecraft.world.item.ItemStack internalStack = CraftItemStack.asNMSCopy(itemStack); ++ if (internalStack.isEnchanted()) { ++ internalStack.set(net.minecraft.core.component.DataComponents.ENCHANTMENTS, net.minecraft.world.item.enchantment.ItemEnchantments.EMPTY); ++ } ++ final net.minecraft.core.RegistryAccess registryAccess = net.minecraft.server.MinecraftServer.getServer().registryAccess(); ++ final net.minecraft.world.item.ItemStack enchanted = net.minecraft.world.item.enchantment.EnchantmentHelper.enchantItem( ++ new org.bukkit.craftbukkit.util.RandomSourceWrapper(random), ++ internalStack, ++ levels, ++ registryAccess, ++ possibleEnchantments ++ ); ++ return CraftItemStack.asCraftMirror(enchanted); ++ } ++ // Paper end - enchantWithLevels API + } diff --git a/patches/server/0671-Fix-saving-in-unloadWorld.patch b/patches/server/0671-Fix-saving-in-unloadWorld.patch new file mode 100644 index 0000000000..540896076f --- /dev/null +++ b/patches/server/0671-Fix-saving-in-unloadWorld.patch @@ -0,0 +1,20 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Philip Kelley <[email protected]> +Date: Wed, 16 Mar 2022 12:05:59 +0000 +Subject: [PATCH] Fix saving in unloadWorld + +Change savingDisabled to false to ensure ServerLevel's saving logic gets called when unloadWorld is called with save = true + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index 82e1c4713e043c4903b1f0154609da4558f90aef..430b35c35c7aa17d590031515063f2d24eeffe5c 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -1385,7 +1385,7 @@ public final class CraftServer implements Server { + + try { + if (save) { +- handle.save(null, true, true); ++ handle.save(null, true, false); // Paper - Fix saving in unloadWorld + } + + handle.getChunkSource().close(save); diff --git a/patches/server/0672-Buffer-OOB-setBlock-calls.patch b/patches/server/0672-Buffer-OOB-setBlock-calls.patch new file mode 100644 index 0000000000..1b7ba291c4 --- /dev/null +++ b/patches/server/0672-Buffer-OOB-setBlock-calls.patch @@ -0,0 +1,42 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Shane Freeder <[email protected]> +Date: Sat, 19 Mar 2022 12:12:22 +0000 +Subject: [PATCH] Buffer OOB setBlock calls + +lets debug mode throw a trace in order to potentially see where +such calls are cascading from easier, but, generally, if you see one setBlock +call, you're gonna see more, and this just potentially causes a flood of logs +which can cause issues for slower terminals, etc. + +We can limit the flood by just allowing one for a single gen region, +we'll also only gen a trace for the first one, I see no real pressing need +to generate more, given that that would *massively* negate this patch otherwise + +diff --git a/src/main/java/net/minecraft/server/level/WorldGenRegion.java b/src/main/java/net/minecraft/server/level/WorldGenRegion.java +index f1725ef766c35aa623ace58fe8bf31fc9b2bb6b3..5bf438bb58833c1df3620e82d3d2b90207366372 100644 +--- a/src/main/java/net/minecraft/server/level/WorldGenRegion.java ++++ b/src/main/java/net/minecraft/server/level/WorldGenRegion.java +@@ -284,6 +284,7 @@ public class WorldGenRegion implements WorldGenLevel { + } + } + ++ private boolean hasSetFarWarned = false; // Paper - Buffer OOB setBlock calls + @Override + public boolean ensureCanWrite(BlockPos pos) { + int i = SectionPos.blockToSectionCoord(pos.getX()); +@@ -303,7 +304,15 @@ public class WorldGenRegion implements WorldGenLevel { + + return true; + } else { ++ // Paper start - Buffer OOB setBlock calls ++ if (!hasSetFarWarned) { + Util.logAndPauseIfInIde("Detected setBlock in a far chunk [" + i + ", " + j + "], pos: " + String.valueOf(pos) + ", status: " + String.valueOf(this.generatingStep.targetStatus()) + (this.currentlyGenerating == null ? "" : ", currently generating: " + (String) this.currentlyGenerating.get())); ++ hasSetFarWarned = true; ++ if (this.getServer() != null && this.getServer().isDebugging()) { ++ io.papermc.paper.util.TraceUtil.dumpTraceForThread("far setBlock call"); ++ } ++ } ++ // Paper end - Buffer OOB setBlock calls + return false; + } + } diff --git a/patches/server/0673-Add-TameableDeathMessageEvent.patch b/patches/server/0673-Add-TameableDeathMessageEvent.patch new file mode 100644 index 0000000000..85016be93d --- /dev/null +++ b/patches/server/0673-Add-TameableDeathMessageEvent.patch @@ -0,0 +1,24 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Owen1212055 <[email protected]> +Date: Mon, 21 Jun 2021 21:24:45 -0400 +Subject: [PATCH] Add TameableDeathMessageEvent + + +diff --git a/src/main/java/net/minecraft/world/entity/TamableAnimal.java b/src/main/java/net/minecraft/world/entity/TamableAnimal.java +index 45a340fddcec48dfb796f4515a500452adb30c59..1c195c928fb5c165981665393424a64004331c93 100644 +--- a/src/main/java/net/minecraft/world/entity/TamableAnimal.java ++++ b/src/main/java/net/minecraft/world/entity/TamableAnimal.java +@@ -257,7 +257,12 @@ public abstract class TamableAnimal extends Animal implements OwnableEntity { + if (entityliving instanceof ServerPlayer) { + ServerPlayer entityplayer = (ServerPlayer) entityliving; + +- entityplayer.sendSystemMessage(this.getCombatTracker().getDeathMessage()); ++ // Paper start - Add TameableDeathMessageEvent ++ io.papermc.paper.event.entity.TameableDeathMessageEvent event = new io.papermc.paper.event.entity.TameableDeathMessageEvent((org.bukkit.entity.Tameable) getBukkitEntity(), io.papermc.paper.adventure.PaperAdventure.asAdventure(this.getCombatTracker().getDeathMessage())); ++ if (event.callEvent()) { ++ entityplayer.sendSystemMessage(io.papermc.paper.adventure.PaperAdventure.asVanilla(event.deathMessage())); ++ } ++ // Paper end - Add TameableDeathMessageEvent + } + } + } diff --git a/patches/server/0674-Fix-new-block-data-for-EntityChangeBlockEvent.patch b/patches/server/0674-Fix-new-block-data-for-EntityChangeBlockEvent.patch new file mode 100644 index 0000000000..fd2ea22a5e --- /dev/null +++ b/patches/server/0674-Fix-new-block-data-for-EntityChangeBlockEvent.patch @@ -0,0 +1,215 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: SoSeDiK <[email protected]> +Date: Mon, 21 Mar 2022 20:00:53 +0200 +Subject: [PATCH] Fix new block data for EntityChangeBlockEvent + +Also standardizes how to handle EntityChangeBlockEvent before a removeBlock or destroyBlock +call. Always use 'state.getFluidState().createLegacyBlock()' to get the new state instead of +just using the 'air' state. + +Also fixes the new block data for EntityBreakDoorEvent (a sub-event from +EntityChangeBlockEvent) + +Co-authored-by: Lulu13022002 <[email protected]> +Co-authored-by: Jake Potrebic <[email protected]> + +diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java b/src/main/java/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java +index 6a7052cd6ec88ed4aaea2fef0ebc750060d1dec0..2ade08d1466660ee1787fa97908002ef56389712 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java ++++ b/src/main/java/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java +@@ -108,7 +108,7 @@ public class HarvestFarmland extends Behavior<Villager> { + Block block1 = world.getBlockState(this.aboveFarmlandPos.below()).getBlock(); + + if (block instanceof CropBlock && ((CropBlock) block).isMaxAge(iblockdata)) { +- if (CraftEventFactory.callEntityChangeBlockEvent(entity, this.aboveFarmlandPos, Blocks.AIR.defaultBlockState())) { // CraftBukkit ++ if (CraftEventFactory.callEntityChangeBlockEvent(entity, this.aboveFarmlandPos, iblockdata.getFluidState().createLegacyBlock())) { // CraftBukkit // Paper - fix wrong block state + world.destroyBlock(this.aboveFarmlandPos, true, entity); + } // CraftBukkit + } +diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/BreakDoorGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/BreakDoorGoal.java +index e2aa6d46375cbc4f4b694888c4f9750eb26e4940..6827426e6e9706909265f84bf97b5fa7105a7fea 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/goal/BreakDoorGoal.java ++++ b/src/main/java/net/minecraft/world/entity/ai/goal/BreakDoorGoal.java +@@ -73,7 +73,7 @@ public class BreakDoorGoal extends DoorInteractGoal { + + if (this.breakTime == this.getDoorBreakTime() && this.isValidDifficulty(this.mob.level().getDifficulty())) { + // CraftBukkit start +- if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityBreakDoorEvent(this.mob, this.doorPos).isCancelled()) { ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityBreakDoorEvent(this.mob, this.doorPos, this.mob.level().getFluidState(this.doorPos).createLegacyBlock()).isCancelled()) { // Paper - fix wrong block state + this.start(); + return; + } +diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/EatBlockGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/EatBlockGoal.java +index dc3602002e60d64cbbe9504c22282a9e65da2c9d..9e6f946e6d2878aa3fa8abe0f6fa4770d18676d3 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/goal/EatBlockGoal.java ++++ b/src/main/java/net/minecraft/world/entity/ai/goal/EatBlockGoal.java +@@ -67,8 +67,9 @@ public class EatBlockGoal extends Goal { + if (this.eatAnimationTick == this.adjustedTickDelay(4)) { + BlockPos blockposition = this.mob.blockPosition(); + +- if (EatBlockGoal.IS_TALL_GRASS.test(this.level.getBlockState(blockposition))) { +- if (CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockposition, Blocks.AIR.defaultBlockState(), !getServerLevel(this.level).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit ++ final BlockState blockState = this.level.getBlockState(blockposition); // Paper - fix wrong block state ++ if (EatBlockGoal.IS_TALL_GRASS.test(blockState)) { // Paper - fix wrong block state ++ if (CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockposition, blockState.getFluidState().createLegacyBlock(), !getServerLevel(this.level).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit // Paper - fix wrong block state + this.level.destroyBlock(blockposition, false); + } + +@@ -77,7 +78,7 @@ public class EatBlockGoal extends Goal { + BlockPos blockposition1 = blockposition.below(); + + if (this.level.getBlockState(blockposition1).is(Blocks.GRASS_BLOCK)) { +- if (CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockposition1, Blocks.AIR.defaultBlockState(), !getServerLevel(this.level).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit ++ if (CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockposition1, Blocks.DIRT.defaultBlockState(), !getServerLevel(this.level).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING))) { // CraftBukkit // Paper - Fix wrong block state + this.level.levelEvent(2001, blockposition1, Block.getId(Blocks.GRASS_BLOCK.defaultBlockState())); + this.level.setBlock(blockposition1, Blocks.DIRT.defaultBlockState(), 2); + } +diff --git a/src/main/java/net/minecraft/world/entity/animal/Rabbit.java b/src/main/java/net/minecraft/world/entity/animal/Rabbit.java +index cfda434358a2e14ebd6cc10609c599f6d65bb6ef..53d60d62686f9b6bc98b6b25e4315b848600a99d 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/Rabbit.java ++++ b/src/main/java/net/minecraft/world/entity/animal/Rabbit.java +@@ -580,7 +580,7 @@ public class Rabbit extends Animal implements VariantHolder<Rabbit.Variant> { + + if (i == 0) { + // CraftBukkit start +- if (!CraftEventFactory.callEntityChangeBlockEvent(this.rabbit, blockposition, Blocks.AIR.defaultBlockState())) { ++ if (!CraftEventFactory.callEntityChangeBlockEvent(this.rabbit, blockposition, iblockdata.getFluidState().createLegacyBlock())) { // Paper - fix wrong block state + return; + } + // CraftBukkit end +diff --git a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java +index 8415f7e1f8c391961dc5b0669da1ab4f8ae4950d..fa8c7a7f71621437bec2ce69c3ad24b495ceed1d 100644 +--- a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java ++++ b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java +@@ -374,7 +374,7 @@ public class WitherBoss extends Monster implements RangedAttackMob { + + if (WitherBoss.canDestroy(iblockdata)) { + // CraftBukkit start +- if (!CraftEventFactory.callEntityChangeBlockEvent(this, blockposition, Blocks.AIR.defaultBlockState())) { ++ if (!CraftEventFactory.callEntityChangeBlockEvent(this, blockposition, iblockdata.getFluidState().createLegacyBlock())) { // Paper - fix wrong block state + continue; + } + // CraftBukkit end +diff --git a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java +index 4b798695af365dc97cbbbd09f370b8fc425f9ed6..2a394381a4ad46359359ba402b65c62b331480b4 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java ++++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java +@@ -551,7 +551,7 @@ public class EnderMan extends Monster implements NeutralMob { + boolean flag = movingobjectpositionblock.getBlockPos().equals(blockposition); + + if (iblockdata.is(BlockTags.ENDERMAN_HOLDABLE) && flag) { +- if (CraftEventFactory.callEntityChangeBlockEvent(this.enderman, blockposition, Blocks.AIR.defaultBlockState())) { // CraftBukkit - Place event ++ if (CraftEventFactory.callEntityChangeBlockEvent(this.enderman, blockposition, iblockdata.getFluidState().createLegacyBlock())) { // CraftBukkit - Place event // Paper - fix wrong block state + world.removeBlock(blockposition, false); + world.gameEvent((Holder) GameEvent.BLOCK_DESTROY, blockposition, GameEvent.Context.of(this.enderman, iblockdata)); + this.enderman.setCarriedBlock(iblockdata.getBlock().defaultBlockState()); +diff --git a/src/main/java/net/minecraft/world/entity/monster/Ravager.java b/src/main/java/net/minecraft/world/entity/monster/Ravager.java +index 772476d44ee72aed1ba35d10fe51f0ffda34d3f8..cfc28828a5b81563a826ae6045553e7350f67986 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/Ravager.java ++++ b/src/main/java/net/minecraft/world/entity/monster/Ravager.java +@@ -162,7 +162,7 @@ public class Ravager extends Raider { + + if (block instanceof LeavesBlock) { + // CraftBukkit start +- if (!CraftEventFactory.callEntityChangeBlockEvent(this, blockposition, net.minecraft.world.level.block.Blocks.AIR.defaultBlockState())) { ++ if (!CraftEventFactory.callEntityChangeBlockEvent(this, blockposition, iblockdata.getFluidState().createLegacyBlock())) { // Paper - fix wrong block state + continue; + } + // CraftBukkit end +diff --git a/src/main/java/net/minecraft/world/entity/monster/Silverfish.java b/src/main/java/net/minecraft/world/entity/monster/Silverfish.java +index 52d8ea3e40cdb01eab428f5d3d945c0c9f6088ce..ff65cb8ea5233f2dd159f42ad53bc9d300cd604f 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/Silverfish.java ++++ b/src/main/java/net/minecraft/world/entity/monster/Silverfish.java +@@ -165,7 +165,8 @@ public class Silverfish extends Monster { + + if (block instanceof InfestedBlock) { + // CraftBukkit start +- if (!CraftEventFactory.callEntityChangeBlockEvent(this.silverfish, blockposition1, net.minecraft.world.level.block.Blocks.AIR.defaultBlockState())) { ++ BlockState afterState = getServerLevel(world).getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? iblockdata.getFluidState().createLegacyBlock() : ((InfestedBlock) block).hostStateByInfested(world.getBlockState(blockposition1)); // Paper - fix wrong block state ++ if (!CraftEventFactory.callEntityChangeBlockEvent(this.silverfish, blockposition1, afterState)) { // Paper - fix wrong block state + continue; + } + // CraftBukkit end +diff --git a/src/main/java/net/minecraft/world/entity/projectile/ThrownPotion.java b/src/main/java/net/minecraft/world/entity/projectile/ThrownPotion.java +index a486466040a646b8a5a5ff2430cdd25b95b7e20f..e78eef3b6fbcd657f9dd180df4cb2eeb55d0814f 100644 +--- a/src/main/java/net/minecraft/world/entity/projectile/ThrownPotion.java ++++ b/src/main/java/net/minecraft/world/entity/projectile/ThrownPotion.java +@@ -294,7 +294,7 @@ public class ThrownPotion extends ThrowableItemProjectile { + + if (iblockdata.is(BlockTags.FIRE)) { + // CraftBukkit start +- if (CraftEventFactory.callEntityChangeBlockEvent(this, pos, Blocks.AIR.defaultBlockState())) { ++ if (CraftEventFactory.callEntityChangeBlockEvent(this, pos, iblockdata.getFluidState().createLegacyBlock())) { // Paper - fix wrong block state + this.level().destroyBlock(pos, false, this); + } + // CraftBukkit end +diff --git a/src/main/java/net/minecraft/world/level/block/ChorusFlowerBlock.java b/src/main/java/net/minecraft/world/level/block/ChorusFlowerBlock.java +index fae574f8617bada96469a2eb1349eaa061f0450f..6d0d13e70a82c4db7848e1007f5b6d670dd5acad 100644 +--- a/src/main/java/net/minecraft/world/level/block/ChorusFlowerBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/ChorusFlowerBlock.java +@@ -286,7 +286,7 @@ public class ChorusFlowerBlock extends Block { + if (world instanceof ServerLevel worldserver) { + if (projectile.mayInteract(worldserver, blockposition) && projectile.mayBreak(worldserver)) { + // CraftBukkit +- if (!CraftEventFactory.callEntityChangeBlockEvent(projectile, blockposition, Blocks.AIR.defaultBlockState())) { ++ if (!CraftEventFactory.callEntityChangeBlockEvent(projectile, blockposition, state.getFluidState().createLegacyBlock())) { // Paper - fix wrong block state + return; + } + // CraftBukkit end +diff --git a/src/main/java/net/minecraft/world/level/block/PointedDripstoneBlock.java b/src/main/java/net/minecraft/world/level/block/PointedDripstoneBlock.java +index 48503f5ba8bccb42ae3b5324a5f65f6c23a8e479..bd38a0a73d543a85bb5c6d50219f5438ce194df3 100644 +--- a/src/main/java/net/minecraft/world/level/block/PointedDripstoneBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/PointedDripstoneBlock.java +@@ -137,7 +137,7 @@ public class PointedDripstoneBlock extends Block implements Fallable, SimpleWate + + if (projectile.mayInteract(worldserver, blockposition) && projectile.mayBreak(worldserver) && projectile instanceof ThrownTrident && projectile.getDeltaMovement().length() > 0.6D) { + // CraftBukkit start +- if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(projectile, blockposition, Blocks.AIR.defaultBlockState())) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(projectile, blockposition, state.getFluidState().createLegacyBlock())) { // Paper - fix wrong block state + return; + } + // CraftBukkit end +diff --git a/src/main/java/net/minecraft/world/level/block/TntBlock.java b/src/main/java/net/minecraft/world/level/block/TntBlock.java +index d256b0f3998028709334dd6c394d184f2c36efce..2cbe2053dd5d0bcdcd89de69762c77b400b8697a 100644 +--- a/src/main/java/net/minecraft/world/level/block/TntBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/TntBlock.java +@@ -158,7 +158,7 @@ public class TntBlock extends Block { + + if (projectile.isOnFire() && projectile.mayInteract(worldserver, blockposition)) { + // CraftBukkit start +- if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(projectile, blockposition, Blocks.AIR.defaultBlockState()) || !CraftEventFactory.callTNTPrimeEvent(world, blockposition, PrimeCause.PROJECTILE, projectile, null)) { ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(projectile, blockposition, state.getFluidState().createLegacyBlock()) || !CraftEventFactory.callTNTPrimeEvent(world, blockposition, PrimeCause.PROJECTILE, projectile, null)) { // Paper - fix wrong block state + return; + } + // CraftBukkit end +diff --git a/src/main/java/net/minecraft/world/level/block/WaterlilyBlock.java b/src/main/java/net/minecraft/world/level/block/WaterlilyBlock.java +index 674d710ff88db5eced9e017284d1b7ec7a4fe7cd..72320c6099a4b26235bab68570e7b7efad84740f 100644 +--- a/src/main/java/net/minecraft/world/level/block/WaterlilyBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/WaterlilyBlock.java +@@ -37,7 +37,7 @@ public class WaterlilyBlock extends BushBlock { + super.entityInside(state, world, pos, entity); + if (world instanceof ServerLevel && entity instanceof AbstractBoat) { + // CraftBukkit start +- if (!CraftEventFactory.callEntityChangeBlockEvent(entity, pos, Blocks.AIR.defaultBlockState())) { ++ if (!CraftEventFactory.callEntityChangeBlockEvent(entity, pos, state.getFluidState().createLegacyBlock())) { // Paper - fix wrong block state + return; + } + // CraftBukkit end +diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +index ca094e393de32b64db59c9fe906433761d70d29b..fcc9fe8f5f57e9f6c28d762aa6585942e2b2698b 100644 +--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java ++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +@@ -1378,11 +1378,11 @@ public class CraftEventFactory { + return event; + } + +- public static EntityBreakDoorEvent callEntityBreakDoorEvent(Entity entity, BlockPos pos) { ++ public static EntityBreakDoorEvent callEntityBreakDoorEvent(Entity entity, BlockPos pos, net.minecraft.world.level.block.state.BlockState newState) { // Paper + org.bukkit.entity.Entity entity1 = entity.getBukkitEntity(); + Block block = CraftBlock.at(entity.level(), pos); + +- EntityBreakDoorEvent event = new EntityBreakDoorEvent((LivingEntity) entity1, block); ++ EntityBreakDoorEvent event = new EntityBreakDoorEvent((LivingEntity) entity1, block, newState.createCraftBlockData()); // Paper + entity1.getServer().getPluginManager().callEvent(event); + + return event; diff --git a/patches/server/0675-fix-player-loottables-running-when-mob-loot-gamerule.patch b/patches/server/0675-fix-player-loottables-running-when-mob-loot-gamerule.patch new file mode 100644 index 0000000000..2bc0c5993d --- /dev/null +++ b/patches/server/0675-fix-player-loottables-running-when-mob-loot-gamerule.patch @@ -0,0 +1,25 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Tue, 22 Mar 2022 09:50:40 -0700 +Subject: [PATCH] fix player loottables running when mob loot gamerule is false + + +diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java +index e2a8e0291a5c0c00d7d6135a0d34fb8bab7b4cd5..15158bec7e2ca90b45347ba09cf17f25e919f9cf 100644 +--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java ++++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java +@@ -1215,12 +1215,14 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player { + } + } + } ++ if (this.shouldDropLoot() && this.serverLevel().getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) { // Paper - fix player loottables running when mob loot gamerule is false + // SPIGOT-5071: manually add player loot tables (SPIGOT-5195 - ignores keepInventory rule) + this.dropFromLootTable(this.serverLevel(), damageSource, this.lastHurtByPlayerTime > 0); + this.dropCustomDeathLoot(this.serverLevel(), damageSource, flag); + + loot.addAll(this.drops); + this.drops.clear(); // SPIGOT-5188: make sure to clear ++ } // Paper - fix player loottables running when mob loot gamerule is false + + Component defaultMessage = this.getCombatTracker().getDeathMessage(); + diff --git a/patches/server/0676-Ensure-entity-passenger-world-matches-ridden-entity.patch b/patches/server/0676-Ensure-entity-passenger-world-matches-ridden-entity.patch new file mode 100644 index 0000000000..df6388cd5f --- /dev/null +++ b/patches/server/0676-Ensure-entity-passenger-world-matches-ridden-entity.patch @@ -0,0 +1,20 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf <[email protected]> +Date: Thu, 31 Mar 2022 05:11:37 -0700 +Subject: [PATCH] Ensure entity passenger world matches ridden entity + +Bad plugins doing this would cause some obvious problems... + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 4784bfb23a7e0004287f89213d50bcfaaaed9e38..f2572fec8989911803cbcf1110e670b005ae29b0 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -2833,7 +2833,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + + public boolean startRiding(Entity entity, boolean force) { +- if (entity == this.vehicle) { ++ if (entity == this.vehicle || entity.level != this.level) { // Paper - Ensure entity passenger world matches ridden entity (bad plugins) + return false; + } else if (!entity.couldAcceptPassenger()) { + return false; diff --git a/patches/server/0677-Cache-resource-keys-and-optimize-reference-Holder-ta.patch b/patches/server/0677-Cache-resource-keys-and-optimize-reference-Holder-ta.patch new file mode 100644 index 0000000000..7356aca21a --- /dev/null +++ b/patches/server/0677-Cache-resource-keys-and-optimize-reference-Holder-ta.patch @@ -0,0 +1,38 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sun, 20 Mar 2022 22:06:47 -0700 +Subject: [PATCH] Cache resource keys and optimize reference Holder tags set + +TagKeys are always interned, so we can use a reference hash set for them + +diff --git a/src/main/java/net/minecraft/core/Holder.java b/src/main/java/net/minecraft/core/Holder.java +index 94671ea227b59a8f820425c401712e6aeb8b2b10..e91c4e26c25980645941ca8fbdcc3a9d02e31063 100644 +--- a/src/main/java/net/minecraft/core/Holder.java ++++ b/src/main/java/net/minecraft/core/Holder.java +@@ -230,7 +230,7 @@ public interface Holder<T> { + } + + void bindTags(Collection<TagKey<T>> tags) { +- this.tags = Set.copyOf(tags); ++ this.tags = java.util.Collections.unmodifiableSet(new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(tags)); // Paper + } + + @Override +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityType.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityType.java +index 6cf8af0c85231de9955282d4abaa0607ec9a195c..d230cbc26f61d8ac5880825aca4dfab197c20401 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityType.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntityType.java +@@ -25,11 +25,11 @@ public class CraftEntityType { + return bukkit; + } + ++ private static final java.util.Map<EntityType, net.minecraft.resources.ResourceKey<net.minecraft.world.entity.EntityType<?>>> KEY_CACHE = java.util.Collections.synchronizedMap(new java.util.EnumMap<>(EntityType.class)); // Paper + public static net.minecraft.world.entity.EntityType<?> bukkitToMinecraft(EntityType bukkit) { + Preconditions.checkArgument(bukkit != null); +- + return CraftRegistry.getMinecraftRegistry(Registries.ENTITY_TYPE) +- .getOptional(CraftNamespacedKey.toMinecraft(bukkit.getKey())).orElseThrow(); ++ .getOptional(KEY_CACHE.computeIfAbsent(bukkit, type -> net.minecraft.resources.ResourceKey.create(Registries.ENTITY_TYPE, CraftNamespacedKey.toMinecraft(type.getKey())))).orElseThrow(); + } + + public static Holder<net.minecraft.world.entity.EntityType<?>> bukkitToMinecraftHolder(EntityType bukkit) { diff --git a/patches/server/0678-Allow-changing-the-EnderDragon-podium.patch b/patches/server/0678-Allow-changing-the-EnderDragon-podium.patch new file mode 100644 index 0000000000..0676f66fd1 --- /dev/null +++ b/patches/server/0678-Allow-changing-the-EnderDragon-podium.patch @@ -0,0 +1,142 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Doc <[email protected]> +Date: Sun, 3 Apr 2022 11:31:42 -0400 +Subject: [PATCH] Allow changing the EnderDragon podium + + +diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java +index f5aee15f80e226c8d59e66b1a53f529e2fb524a5..037b261a40a800f6d5e9ecd22e230d030b797958 100644 +--- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java ++++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java +@@ -104,6 +104,10 @@ public class EnderDragon extends Mob implements Enemy { + private final int[] nodeAdjacency; + private final BinaryHeap openSet; + private final Explosion explosionSource; // CraftBukkit - reusable source for CraftTNTPrimed.getSource() ++ // Paper start - Allow changing the EnderDragon podium ++ @Nullable ++ private BlockPos podium; ++ // Paper end - Allow changing the EnderDragon podium + + public EnderDragon(EntityType<? extends EnderDragon> entitytypes, Level world) { + super(EntityType.ENDER_DRAGON, world); +@@ -143,6 +147,19 @@ public class EnderDragon extends Mob implements Enemy { + return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 200.0D); + } + ++ // Paper start - Allow changing the EnderDragon podium ++ public BlockPos getPodium() { ++ if (this.podium == null) { ++ return EndPodiumFeature.getLocation(this.getFightOrigin()); ++ } ++ return this.podium; ++ } ++ ++ public void setPodium(@Nullable BlockPos blockPos) { ++ this.podium = blockPos; ++ } ++ // Paper end - Allow changing the EnderDragon podium ++ + @Override + public boolean isFlapping() { + float f = Mth.cos(this.flapTime * 6.2831855F); +@@ -1002,7 +1019,7 @@ public class EnderDragon extends Mob implements Enemy { + vec3d = this.getViewVector(tickDelta); + } + } else { +- BlockPos blockposition = this.level().getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, EndPodiumFeature.getLocation(this.fightOrigin)); ++ BlockPos blockposition = this.level().getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, this.getPodium()); // Paper - Allow changing the EnderDragon podium + + f1 = Math.max((float) Math.sqrt(blockposition.distToCenterSqr(this.position())) / 4.0F, 1.0F); + float f3 = 6.0F / f1; +diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/phases/DragonDeathPhase.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/phases/DragonDeathPhase.java +index a897c994423d7d624df6ff3a67789cc2436f0417..29f4acc2943ce009088c61bb32aed330f6e2c051 100644 +--- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/phases/DragonDeathPhase.java ++++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/phases/DragonDeathPhase.java +@@ -42,7 +42,7 @@ public class DragonDeathPhase extends AbstractDragonPhaseInstance { + public void doServerTick(ServerLevel world) { + this.time++; + if (this.targetLocation == null) { +- BlockPos blockPos = world.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, EndPodiumFeature.getLocation(this.dragon.getFightOrigin())); ++ BlockPos blockPos = world.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, this.dragon.getPodium()); // Paper - Allow changing the EnderDragon podium + this.targetLocation = Vec3.atBottomCenterOf(blockPos); + } + +diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/phases/DragonHoldingPatternPhase.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/phases/DragonHoldingPatternPhase.java +index 2db996f3528c65f5d719cbcfb8ae587ff59c14c1..8e39cf181993ff284a2b0429577de0c307f3ef16 100644 +--- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/phases/DragonHoldingPatternPhase.java ++++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/phases/DragonHoldingPatternPhase.java +@@ -53,7 +53,7 @@ public class DragonHoldingPatternPhase extends AbstractDragonPhaseInstance { + + private void findNewTarget(ServerLevel world) { + if (this.currentPath != null && this.currentPath.isDone()) { +- BlockPos blockPos = world.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, EndPodiumFeature.getLocation(this.dragon.getFightOrigin())); ++ BlockPos blockPos = world.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, this.dragon.getPodium()); // Paper - Allow changing the EnderDragon podium + int i = this.dragon.getDragonFight() == null ? 0 : this.dragon.getDragonFight().getCrystalsAlive(); + if (this.dragon.getRandom().nextInt(i + 3) == 0) { + this.dragon.getPhaseManager().setPhase(EnderDragonPhase.LANDING_APPROACH); +diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/phases/DragonLandingApproachPhase.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/phases/DragonLandingApproachPhase.java +index 4c28d3c3d601b5316d79c8474d106800abc8e95b..1c4250cc61b770707ad25c0e93caeacbcb0a80b0 100644 +--- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/phases/DragonLandingApproachPhase.java ++++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/phases/DragonLandingApproachPhase.java +@@ -52,7 +52,7 @@ public class DragonLandingApproachPhase extends AbstractDragonPhaseInstance { + private void findNewTarget(ServerLevel world) { + if (this.currentPath == null || this.currentPath.isDone()) { + int i = this.dragon.findClosestNode(); +- BlockPos blockPos = world.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, EndPodiumFeature.getLocation(this.dragon.getFightOrigin())); ++ BlockPos blockPos = world.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, this.dragon.getPodium()); // Paper - Allow changing the EnderDragon podium + Player player = world.getNearestPlayer(NEAR_EGG_TARGETING, this.dragon, (double)blockPos.getX(), (double)blockPos.getY(), (double)blockPos.getZ()); + int j; + if (player != null) { +diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/phases/DragonLandingPhase.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/phases/DragonLandingPhase.java +index 789df0e05d0cf0df0f66bffc98f587a31770ebea..cfcd3f91517832d26c1177c3715cfa8ebe675193 100644 +--- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/phases/DragonLandingPhase.java ++++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/phases/DragonLandingPhase.java +@@ -42,7 +42,7 @@ public class DragonLandingPhase extends AbstractDragonPhaseInstance { + public void doServerTick(ServerLevel world) { + if (this.targetLocation == null) { + this.targetLocation = Vec3.atBottomCenterOf( +- world.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, EndPodiumFeature.getLocation(this.dragon.getFightOrigin())) ++ world.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, this.dragon.getPodium()) // Paper - Allow changing the EnderDragon podium + ); + } + +diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/phases/DragonTakeoffPhase.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/phases/DragonTakeoffPhase.java +index f942ea2ad87d4ae12921578f7b869f064c8db7b0..5c5b03f612621ec4efd92b78601032b84e6f3c28 100644 +--- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/phases/DragonTakeoffPhase.java ++++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/phases/DragonTakeoffPhase.java +@@ -24,7 +24,7 @@ public class DragonTakeoffPhase extends AbstractDragonPhaseInstance { + @Override + public void doServerTick(ServerLevel world) { + if (!this.firstTick && this.currentPath != null) { +- BlockPos blockPos = world.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, EndPodiumFeature.getLocation(this.dragon.getFightOrigin())); ++ BlockPos blockPos = world.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, this.dragon.getPodium()); // Paper - Allow changing the EnderDragon podium + if (!blockPos.closerToCenterThan(this.dragon.position(), 10.0)) { + this.dragon.getPhaseManager().setPhase(EnderDragonPhase.HOLDING_PATTERN); + } +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragon.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragon.java +index 25b3d889a1742c347e60725df8d6f6c1cee264c7..7b7b89e67d53ed70efae714192c5fa32977f3d9c 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragon.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEnderDragon.java +@@ -73,4 +73,22 @@ public class CraftEnderDragon extends CraftMob implements EnderDragon, CraftEnem + public int getDeathAnimationTicks() { + return this.getHandle().dragonDeathTime; + } ++ ++ // Paper start - Allow changing the EnderDragon podium ++ @Override ++ public org.bukkit.Location getPodium() { ++ net.minecraft.core.BlockPos blockPosOrigin = this.getHandle().getPodium(); ++ return new org.bukkit.Location(getWorld(), blockPosOrigin.getX(), blockPosOrigin.getY(), blockPosOrigin.getZ()); ++ } ++ ++ @Override ++ public void setPodium(org.bukkit.Location location) { ++ if (location == null) { ++ this.getHandle().setPodium(null); ++ } else { ++ org.apache.commons.lang.Validate.isTrue(location.getWorld() == null || location.getWorld().equals(getWorld()), "You cannot set a podium in a different world to where the dragon is"); ++ this.getHandle().setPodium(io.papermc.paper.util.MCUtil.toBlockPos(location)); ++ } ++ } ++ // Paper end - Allow changing the EnderDragon podium + } diff --git a/patches/server/0679-Fix-NBT-pieces-overriding-a-block-entity-during-worl.patch b/patches/server/0679-Fix-NBT-pieces-overriding-a-block-entity-during-worl.patch new file mode 100644 index 0000000000..f885dbe8b4 --- /dev/null +++ b/patches/server/0679-Fix-NBT-pieces-overriding-a-block-entity-during-worl.patch @@ -0,0 +1,40 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: etil2jz <[email protected]> +Date: Sat, 2 Apr 2022 23:29:24 +0200 +Subject: [PATCH] Fix NBT pieces overriding a block entity during worldgen + deadlock + +By checking if the world passed into StructureTemplate's placeInWorld +is not a WorldGenRegion, we can bypass the deadlock entirely. +See https://bugs.mojang.com/browse/MC-246262 + +diff --git a/src/main/java/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java b/src/main/java/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java +index b120949667ae0169a667b329b3cabbd79a0a5bda..734f511d197bc6bf2b02588069eb02c0224781f5 100644 +--- a/src/main/java/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java ++++ b/src/main/java/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java +@@ -303,7 +303,11 @@ public class StructureTemplate { + + if (definedstructure_blockinfo.nbt != null) { + tileentity = world.getBlockEntity(blockposition2); +- Clearable.tryClear(tileentity); ++ // Paper start - Fix NBT pieces overriding a block entity during worldgen deadlock ++ if (!(world instanceof net.minecraft.world.level.WorldGenLevel)) { ++ Clearable.tryClear(tileentity); ++ } ++ // Paper end - Fix NBT pieces overriding a block entity during worldgen deadlock + world.setBlock(blockposition2, Blocks.BARRIER.defaultBlockState(), 20); + } + // CraftBukkit start +@@ -430,7 +434,11 @@ public class StructureTemplate { + if (pair1.getSecond() != null) { + tileentity = world.getBlockEntity(blockposition6); + if (tileentity != null) { +- tileentity.setChanged(); ++ // Paper start - Fix NBT pieces overriding a block entity during worldgen deadlock ++ if (!(world instanceof net.minecraft.world.level.WorldGenLevel)) { ++ tileentity.setChanged(); ++ } ++ // Paper end - Fix NBT pieces overriding a block entity during worldgen deadlock + } + } + } diff --git a/patches/server/0680-Use-username-instead-of-display-name-in-PlayerList-g.patch b/patches/server/0680-Use-username-instead-of-display-name-in-PlayerList-g.patch new file mode 100644 index 0000000000..5407679d61 --- /dev/null +++ b/patches/server/0680-Use-username-instead-of-display-name-in-PlayerList-g.patch @@ -0,0 +1,20 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Doc <[email protected]> +Date: Fri, 15 Apr 2022 17:40:30 -0400 +Subject: [PATCH] Use username instead of display name in + PlayerList#getPlayerStats + + +diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java +index c8bb797a322220647d5839cc5d20b3ac7c01b8ba..1456945e8d6e82c59bf09150bfe24bd1ae14a8c3 100644 +--- a/src/main/java/net/minecraft/server/players/PlayerList.java ++++ b/src/main/java/net/minecraft/server/players/PlayerList.java +@@ -1352,7 +1352,7 @@ public abstract class PlayerList { + // CraftBukkit start + public ServerStatsCounter getPlayerStats(ServerPlayer entityhuman) { + ServerStatsCounter serverstatisticmanager = entityhuman.getStats(); +- return serverstatisticmanager == null ? this.getPlayerStats(entityhuman.getUUID(), entityhuman.getDisplayName().getString()) : serverstatisticmanager; ++ return serverstatisticmanager == null ? this.getPlayerStats(entityhuman.getUUID(), entityhuman.getGameProfile().getName()) : serverstatisticmanager; // Paper - use username and not display name + } + + public ServerStatsCounter getPlayerStats(UUID uuid, String displayName) { diff --git a/patches/server/0681-Expand-PlayerItemDamageEvent.patch b/patches/server/0681-Expand-PlayerItemDamageEvent.patch new file mode 100644 index 0000000000..85a05db466 --- /dev/null +++ b/patches/server/0681-Expand-PlayerItemDamageEvent.patch @@ -0,0 +1,23 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: HexedHero <[email protected]> +Date: Sun, 10 Apr 2022 06:26:32 +0100 +Subject: [PATCH] Expand PlayerItemDamageEvent + + +diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java +index 18f84d54ec72debec652adb22067e11aa058b238..679112d1622744680b7d3c7d296acafac019d00a 100644 +--- a/src/main/java/net/minecraft/world/item/ItemStack.java ++++ b/src/main/java/net/minecraft/world/item/ItemStack.java +@@ -698,10 +698,11 @@ public final class ItemStack implements DataComponentHolder { + } + + public void hurtAndBreak(int amount, ServerLevel world, @Nullable LivingEntity player, Consumer<Item> breakCallback) { // Paper - Add EntityDamageItemEvent ++ int originalDamage = amount; // Paper - Expand PlayerItemDamageEvent + int j = this.processDurabilityChange(amount, world, player); + // CraftBukkit start + if (player instanceof final ServerPlayer serverPlayer) { // Paper - Add EntityDamageItemEvent +- PlayerItemDamageEvent event = new PlayerItemDamageEvent(serverPlayer.getBukkitEntity(), CraftItemStack.asCraftMirror(this), j); // Paper - Add EntityDamageItemEvent ++ PlayerItemDamageEvent event = new PlayerItemDamageEvent(serverPlayer.getBukkitEntity(), CraftItemStack.asCraftMirror(this), j, originalDamage); // Paper - Add EntityDamageItemEvent + event.getPlayer().getServer().getPluginManager().callEvent(event); + + if (j != event.getDamage() || event.isCancelled()) { diff --git a/patches/server/0682-WorldCreator-keepSpawnLoaded.patch b/patches/server/0682-WorldCreator-keepSpawnLoaded.patch new file mode 100644 index 0000000000..ab38fc7006 --- /dev/null +++ b/patches/server/0682-WorldCreator-keepSpawnLoaded.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Shane Freeder <[email protected]> +Date: Sat, 3 Jul 2021 21:18:28 +0100 +Subject: [PATCH] WorldCreator#keepSpawnLoaded + + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index 430b35c35c7aa17d590031515063f2d24eeffe5c..4b894223387bf4029bbfa15d9605247935d7f829 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -1328,7 +1328,7 @@ public final class CraftServer implements Server { + } + + // If set to not keep spawn in memory (changed from default) then adjust rule accordingly +- if (!creator.keepSpawnInMemory()) { ++ if (creator.keepSpawnLoaded() == net.kyori.adventure.util.TriState.FALSE) { // Paper + worlddata.getGameRules().getRule(GameRules.RULE_SPAWN_CHUNK_RADIUS).set(0, null); + } + ServerLevel internal = (ServerLevel) new ServerLevel(this.console, this.console.executor, worldSession, worlddata, worldKey, worlddimension, this.getServer().progressListenerFactory.create(worlddata.getGameRules().getInt(GameRules.RULE_SPAWN_CHUNK_RADIUS)), diff --git a/patches/server/0683-Fix-CME-in-CraftPersistentDataTypeRegistry.patch b/patches/server/0683-Fix-CME-in-CraftPersistentDataTypeRegistry.patch new file mode 100644 index 0000000000..ed8adb4503 --- /dev/null +++ b/patches/server/0683-Fix-CME-in-CraftPersistentDataTypeRegistry.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Gero <[email protected]> +Date: Sat, 2 Oct 2021 20:08:30 +0200 +Subject: [PATCH] Fix CME in CraftPersistentDataTypeRegistry + + +diff --git a/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataTypeRegistry.java b/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataTypeRegistry.java +index 71dc4a2285ddc86e7aa65ba1a211997ffa8fcce4..20c53aac0aef180ee12de919a8000e4b4bc619ff 100644 +--- a/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataTypeRegistry.java ++++ b/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataTypeRegistry.java +@@ -121,7 +121,7 @@ public final class CraftPersistentDataTypeRegistry { + } + } + +- private final Map<Class, TagAdapter> adapters = new HashMap<>(); ++ private final Map<Class, TagAdapter> adapters = new java.util.concurrent.ConcurrentHashMap<>(); // Paper - Replace HashMap with ConcurrentHashMap to avoid CME + + /** + * Creates a suitable adapter instance for the primitive class type. diff --git a/patches/server/0684-Trigger-bee_nest_destroyed-trigger-in-the-correct-pl.patch b/patches/server/0684-Trigger-bee_nest_destroyed-trigger-in-the-correct-pl.patch new file mode 100644 index 0000000000..105d2132f3 --- /dev/null +++ b/patches/server/0684-Trigger-bee_nest_destroyed-trigger-in-the-correct-pl.patch @@ -0,0 +1,54 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Wed, 2 Feb 2022 13:50:06 -0800 +Subject: [PATCH] Trigger bee_nest_destroyed trigger in the correct place + + +diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java +index e000a918230187f6841b03b7b0dd73687f3cc15e..5c3e5c348e6fececccd8097355f423b9e7ad982b 100644 +--- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java ++++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java +@@ -427,12 +427,16 @@ public class ServerPlayerGameMode { + block.destroy(this.level, pos, iblockdata1); + } + ++ ItemStack mainHandStack = null; // Paper - Trigger bee_nest_destroyed trigger in the correct place ++ boolean isCorrectTool = false; // Paper - Trigger bee_nest_destroyed trigger in the correct place + if (this.isCreative()) { + // return true; // CraftBukkit + } else { + ItemStack itemstack = this.player.getMainHandItem(); + ItemStack itemstack1 = itemstack.copy(); + boolean flag1 = this.player.hasCorrectToolForDrops(iblockdata1); ++ mainHandStack = itemstack1; // Paper - Trigger bee_nest_destroyed trigger in the correct place ++ isCorrectTool = flag1; // Paper - Trigger bee_nest_destroyed trigger in the correct place + + itemstack.mineBlock(this.level, iblockdata1, pos, this.player); + if (flag && flag1 && event.isDropItems()) { // CraftBukkit - Check if block should drop items +@@ -453,6 +457,13 @@ public class ServerPlayerGameMode { + if (flag && event != null) { + iblockdata.getBlock().popExperience(this.level, pos, event.getExpToDrop(), this.player); // Paper + } ++ // Paper start - Trigger bee_nest_destroyed trigger in the correct place (check impls of block#playerDestroy) ++ if (mainHandStack != null) { ++ if (flag && isCorrectTool && event.isDropItems() && block instanceof net.minecraft.world.level.block.BeehiveBlock && tileentity instanceof net.minecraft.world.level.block.entity.BeehiveBlockEntity beehiveBlockEntity) { // simulates the guard on block#playerDestroy above ++ CriteriaTriggers.BEE_NEST_DESTROYED.trigger(player, iblockdata, mainHandStack, beehiveBlockEntity.getOccupantCount()); ++ } ++ } ++ // Paper end - Trigger bee_nest_destroyed trigger in the correct place + + return true; + // CraftBukkit end +diff --git a/src/main/java/net/minecraft/world/level/block/BeehiveBlock.java b/src/main/java/net/minecraft/world/level/block/BeehiveBlock.java +index 4ec03b15bb8a82d92fcb2b7fef70e3c7087a9b68..3e3951cbb196b720256cfc36d6d3b91d48ce3294 100644 +--- a/src/main/java/net/minecraft/world/level/block/BeehiveBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/BeehiveBlock.java +@@ -103,7 +103,7 @@ public class BeehiveBlock extends BaseEntityBlock { + this.angerNearbyBees(world, pos); + } + +- CriteriaTriggers.BEE_NEST_DESTROYED.trigger((ServerPlayer) player, state, tool, tileentitybeehive.getOccupantCount()); ++ // CriteriaTriggers.BEE_NEST_DESTROYED.trigger((ServerPlayer) player, state, tool, tileentitybeehive.getOccupantCount()); // Paper - Trigger bee_nest_destroyed trigger in the correct place; moved until after items are dropped + } + + } diff --git a/patches/server/0685-Add-EntityDyeEvent-and-CollarColorable-interface.patch b/patches/server/0685-Add-EntityDyeEvent-and-CollarColorable-interface.patch new file mode 100644 index 0000000000..79ada84e8b --- /dev/null +++ b/patches/server/0685-Add-EntityDyeEvent-and-CollarColorable-interface.patch @@ -0,0 +1,43 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Fri, 18 Mar 2022 21:15:55 -0700 +Subject: [PATCH] Add EntityDyeEvent and CollarColorable interface + + +diff --git a/src/main/java/net/minecraft/world/entity/animal/Cat.java b/src/main/java/net/minecraft/world/entity/animal/Cat.java +index 629b282cf27806ff37d67f83d44c06a9f32a9185..9a67dfe214d3eb89d1f4371e716df759651ceb1b 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/Cat.java ++++ b/src/main/java/net/minecraft/world/entity/animal/Cat.java +@@ -386,6 +386,13 @@ public class Cat extends TamableAnimal implements VariantHolder<Holder<CatVarian + DyeColor enumcolor = itemdye.getDyeColor(); + + if (enumcolor != this.getCollarColor()) { ++ // Paper start - Add EntityDyeEvent and CollarColorable interface ++ final io.papermc.paper.event.entity.EntityDyeEvent event = new io.papermc.paper.event.entity.EntityDyeEvent(this.getBukkitEntity(), org.bukkit.DyeColor.getByWoolData((byte) enumcolor.getId()), ((net.minecraft.server.level.ServerPlayer) player).getBukkitEntity()); ++ if (!event.callEvent()) { ++ return InteractionResult.FAIL; ++ } ++ enumcolor = DyeColor.byId(event.getColor().getWoolData()); ++ // Paper end - Add EntityDyeEvent and CollarColorable interface + if (!this.level().isClientSide()) { + this.setCollarColor(enumcolor); + itemstack.consume(1, player); +diff --git a/src/main/java/net/minecraft/world/entity/animal/Wolf.java b/src/main/java/net/minecraft/world/entity/animal/Wolf.java +index 9581ffee6da72859ea55c3275a163818d4dcdefc..4f23e32dd7b21492a6fcf7b8bd9b4d9d9a9297a3 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/Wolf.java ++++ b/src/main/java/net/minecraft/world/entity/animal/Wolf.java +@@ -432,6 +432,14 @@ public class Wolf extends TamableAnimal implements NeutralMob, VariantHolder<Hol + DyeColor enumcolor = itemdye.getDyeColor(); + + if (enumcolor != this.getCollarColor()) { ++ // Paper start - Add EntityDyeEvent and CollarColorable interface ++ final io.papermc.paper.event.entity.EntityDyeEvent event = new io.papermc.paper.event.entity.EntityDyeEvent(this.getBukkitEntity(), org.bukkit.DyeColor.getByWoolData((byte) enumcolor.getId()), ((net.minecraft.server.level.ServerPlayer) player).getBukkitEntity()); ++ if (!event.callEvent()) { ++ return InteractionResult.FAIL; ++ } ++ enumcolor = DyeColor.byId(event.getColor().getWoolData()); ++ // Paper end - Add EntityDyeEvent and CollarColorable interface ++ + this.setCollarColor(enumcolor); + itemstack.consume(1, player); + return InteractionResult.SUCCESS; diff --git a/patches/server/0686-Fire-CauldronLevelChange-on-initial-fill.patch b/patches/server/0686-Fire-CauldronLevelChange-on-initial-fill.patch new file mode 100644 index 0000000000..86520e0647 --- /dev/null +++ b/patches/server/0686-Fire-CauldronLevelChange-on-initial-fill.patch @@ -0,0 +1,122 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Tue, 29 Mar 2022 13:46:23 -0700 +Subject: [PATCH] Fire CauldronLevelChange on initial fill + +Also don't fire level events or game events if stalactite +drip is cancelled + +diff --git a/src/main/java/net/minecraft/core/cauldron/CauldronInteraction.java b/src/main/java/net/minecraft/core/cauldron/CauldronInteraction.java +index 637fef0d3cabc867cf861b507b90221e27a711d1..df76185d42075834a39c79515917e03beb938a06 100644 +--- a/src/main/java/net/minecraft/core/cauldron/CauldronInteraction.java ++++ b/src/main/java/net/minecraft/core/cauldron/CauldronInteraction.java +@@ -74,7 +74,7 @@ public interface CauldronInteraction { + if (potioncontents != null && potioncontents.is(Potions.WATER)) { + if (!world.isClientSide) { + // CraftBukkit start +- if (!LayeredCauldronBlock.changeLevel(iblockdata, world, blockposition, Blocks.WATER_CAULDRON.defaultBlockState(), entityhuman, CauldronLevelChangeEvent.ChangeReason.BOTTLE_EMPTY)) { ++ if (!LayeredCauldronBlock.changeLevel(iblockdata, world, blockposition, Blocks.WATER_CAULDRON.defaultBlockState(), entityhuman, CauldronLevelChangeEvent.ChangeReason.BOTTLE_EMPTY, false)) { // Paper - Call CauldronLevelChangeEvent + return InteractionResult.SUCCESS; + } + // CraftBukkit end +@@ -129,7 +129,7 @@ public interface CauldronInteraction { + if (potioncontents != null && potioncontents.is(Potions.WATER)) { + if (!world.isClientSide) { + // CraftBukkit start +- if (!LayeredCauldronBlock.changeLevel(iblockdata, world, blockposition, iblockdata.cycle(LayeredCauldronBlock.LEVEL), entityhuman, CauldronLevelChangeEvent.ChangeReason.BOTTLE_EMPTY)) { ++ if (!LayeredCauldronBlock.changeLevel(iblockdata, world, blockposition, iblockdata.cycle(LayeredCauldronBlock.LEVEL), entityhuman, CauldronLevelChangeEvent.ChangeReason.BOTTLE_EMPTY, false)) { // Paper - Call CauldronLevelChangeEvent + return InteractionResult.SUCCESS; + } + // CraftBukkit end +@@ -215,7 +215,7 @@ public interface CauldronInteraction { + } else { + if (!world.isClientSide) { + // CraftBukkit start +- if (!LayeredCauldronBlock.changeLevel(state, world, pos, Blocks.CAULDRON.defaultBlockState(), player, CauldronLevelChangeEvent.ChangeReason.BUCKET_FILL)) { ++ if (!LayeredCauldronBlock.changeLevel(state, world, pos, Blocks.CAULDRON.defaultBlockState(), player, CauldronLevelChangeEvent.ChangeReason.BUCKET_FILL, false)) { // Paper - Call CauldronLevelChangeEvent + return InteractionResult.SUCCESS; + } + // CraftBukkit end +@@ -236,7 +236,7 @@ public interface CauldronInteraction { + static InteractionResult emptyBucket(Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, BlockState state, SoundEvent soundEvent) { + if (!world.isClientSide) { + // CraftBukkit start +- if (!LayeredCauldronBlock.changeLevel(state, world, pos, state, player, CauldronLevelChangeEvent.ChangeReason.BUCKET_EMPTY)) { ++ if (!LayeredCauldronBlock.changeLevel(state, world, pos, state, player, CauldronLevelChangeEvent.ChangeReason.BUCKET_EMPTY, false)) { // Paper - Call CauldronLevelChangeEvent + return InteractionResult.SUCCESS; + } + // CraftBukkit end +diff --git a/src/main/java/net/minecraft/world/level/block/CauldronBlock.java b/src/main/java/net/minecraft/world/level/block/CauldronBlock.java +index 8d5ad9848d9652b4fd7179c425c47a683ee169ef..c9968934f4ecaa8d81e545f279b3001c7b1ce545 100644 +--- a/src/main/java/net/minecraft/world/level/block/CauldronBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/CauldronBlock.java +@@ -44,9 +44,19 @@ public class CauldronBlock extends AbstractCauldronBlock { + public void handlePrecipitation(BlockState state, Level world, BlockPos pos, Biome.Precipitation precipitation) { + if (CauldronBlock.shouldHandlePrecipitation(world, precipitation)) { + if (precipitation == Biome.Precipitation.RAIN) { ++ // Paper start - Call CauldronLevelChangeEvent ++ if (!LayeredCauldronBlock.changeLevel(state, world, pos, Blocks.WATER_CAULDRON.defaultBlockState(), null, CauldronLevelChangeEvent.ChangeReason.NATURAL_FILL, false)) { // avoid duplicate game event ++ return; ++ } ++ // Paper end - Call CauldronLevelChangeEvent + world.setBlockAndUpdate(pos, Blocks.WATER_CAULDRON.defaultBlockState()); + world.gameEvent((Entity) null, (Holder) GameEvent.BLOCK_CHANGE, pos); + } else if (precipitation == Biome.Precipitation.SNOW) { ++ // Paper start - Call CauldronLevelChangeEvent ++ if (!LayeredCauldronBlock.changeLevel(state, world, pos, Blocks.POWDER_SNOW_CAULDRON.defaultBlockState(), null, CauldronLevelChangeEvent.ChangeReason.NATURAL_FILL, false)) { // avoid duplicate game event ++ return; ++ } ++ // Paper end - Call CauldronLevelChangeEvent + world.setBlockAndUpdate(pos, Blocks.POWDER_SNOW_CAULDRON.defaultBlockState()); + world.gameEvent((Entity) null, (Holder) GameEvent.BLOCK_CHANGE, pos); + } +@@ -65,11 +75,19 @@ public class CauldronBlock extends AbstractCauldronBlock { + + if (fluid == Fluids.WATER) { + iblockdata1 = Blocks.WATER_CAULDRON.defaultBlockState(); +- LayeredCauldronBlock.changeLevel(state, world, pos, iblockdata1, null, CauldronLevelChangeEvent.ChangeReason.NATURAL_FILL); // CraftBukkit ++ // Paper start - Call CauldronLevelChangeEvent; don't send level event or game event if cancelled ++ if (!LayeredCauldronBlock.changeLevel(state, world, pos, iblockdata1, null, CauldronLevelChangeEvent.ChangeReason.NATURAL_FILL)) { // CraftBukkit ++ return; ++ } ++ // Paper end - Call CauldronLevelChangeEvent + world.levelEvent(1047, pos, 0); + } else if (fluid == Fluids.LAVA) { + iblockdata1 = Blocks.LAVA_CAULDRON.defaultBlockState(); +- LayeredCauldronBlock.changeLevel(state, world, pos, iblockdata1, null, CauldronLevelChangeEvent.ChangeReason.NATURAL_FILL); // CraftBukkit ++ // Paper start - Call CauldronLevelChangeEvent; don't send level event or game event if cancelled ++ if (!LayeredCauldronBlock.changeLevel(state, world, pos, iblockdata1, null, CauldronLevelChangeEvent.ChangeReason.NATURAL_FILL)) { // CraftBukkit ++ return; ++ } ++ // Paper end - Call CauldronLevelChangeEvent + world.levelEvent(1046, pos, 0); + } + +diff --git a/src/main/java/net/minecraft/world/level/block/LayeredCauldronBlock.java b/src/main/java/net/minecraft/world/level/block/LayeredCauldronBlock.java +index 4ab73a083eba2ad3e12526af0a0dbcfba5cf6c14..806d18689126d0a971665a33d7fc91102ec76db5 100644 +--- a/src/main/java/net/minecraft/world/level/block/LayeredCauldronBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/LayeredCauldronBlock.java +@@ -107,7 +107,13 @@ public class LayeredCauldronBlock extends AbstractCauldronBlock { + } + + // CraftBukkit start +- public static boolean changeLevel(BlockState iblockdata, Level world, BlockPos blockposition, BlockState newBlock, Entity entity, CauldronLevelChangeEvent.ChangeReason reason) { ++ // Paper start - Call CauldronLevelChangeEvent ++ public static boolean changeLevel(BlockState iblockdata, Level world, BlockPos blockposition, BlockState newBlock, @javax.annotation.Nullable Entity entity, CauldronLevelChangeEvent.ChangeReason reason) { // Paper - entity is nullable ++ return changeLevel(iblockdata, world, blockposition, newBlock, entity, reason, true); ++ } ++ ++ public static boolean changeLevel(BlockState iblockdata, Level world, BlockPos blockposition, BlockState newBlock, @javax.annotation.Nullable Entity entity, CauldronLevelChangeEvent.ChangeReason reason, boolean sendGameEvent) { // Paper - entity is nullable ++ // Paper end - Call CauldronLevelChangeEvent + CraftBlockState newState = CraftBlockStates.getBlockState(world, blockposition); + newState.setData(newBlock); + +@@ -120,7 +126,7 @@ public class LayeredCauldronBlock extends AbstractCauldronBlock { + return false; + } + newState.update(true); +- world.gameEvent((Holder) GameEvent.BLOCK_CHANGE, blockposition, GameEvent.Context.of(newBlock)); ++ if (sendGameEvent) world.gameEvent((Holder) GameEvent.BLOCK_CHANGE, blockposition, GameEvent.Context.of(newBlock)); // Paper - Call CauldronLevelChangeEvent + return true; + } + // CraftBukkit end diff --git a/patches/server/0687-fix-powder-snow-cauldrons-not-turning-to-water.patch b/patches/server/0687-fix-powder-snow-cauldrons-not-turning-to-water.patch new file mode 100644 index 0000000000..201c323a35 --- /dev/null +++ b/patches/server/0687-fix-powder-snow-cauldrons-not-turning-to-water.patch @@ -0,0 +1,45 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Thu, 30 Dec 2021 14:02:13 -0800 +Subject: [PATCH] fix powder snow cauldrons not turning to water + +Powder snow cauldrons should turn to water when +extinguishing an entity + +diff --git a/src/main/java/net/minecraft/world/level/block/LayeredCauldronBlock.java b/src/main/java/net/minecraft/world/level/block/LayeredCauldronBlock.java +index 806d18689126d0a971665a33d7fc91102ec76db5..7dd6b7c0ea472cfbc7ece55bc64bc5d85be4a6c0 100644 +--- a/src/main/java/net/minecraft/world/level/block/LayeredCauldronBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/LayeredCauldronBlock.java +@@ -73,7 +73,7 @@ public class LayeredCauldronBlock extends AbstractCauldronBlock { + // CraftBukkit start - moved down + // entity.clearFire(); + if (entity.mayInteract(worldserver, pos)) { +- if (this.handleEntityOnFireInside(state, world, pos, entity)) { ++ if (this.handleEntityOnFireInsideWithEvent(state, world, pos, entity)) { // Paper - fix powdered snow cauldron extinguishing entities + entity.clearFire(); + } + // CraftBukkit end +@@ -84,6 +84,7 @@ public class LayeredCauldronBlock extends AbstractCauldronBlock { + } + + // CraftBukkit start ++ @io.papermc.paper.annotation.DoNotUse @Deprecated // Paper - fix powdered snow cauldron extinguishing entities; use #handleEntityOnFireInsideWithEvent + private boolean handleEntityOnFireInside(BlockState iblockdata, Level world, BlockPos blockposition, Entity entity) { + if (this.precipitationType == Biome.Precipitation.SNOW) { + return LayeredCauldronBlock.lowerFillLevel((BlockState) Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, (Integer) iblockdata.getValue(LayeredCauldronBlock.LEVEL)), world, blockposition, entity, CauldronLevelChangeEvent.ChangeReason.EXTINGUISH); +@@ -93,6 +94,15 @@ public class LayeredCauldronBlock extends AbstractCauldronBlock { + } + + } ++ // Paper start - fix powdered snow cauldron extinguishing entities ++ protected boolean handleEntityOnFireInsideWithEvent(BlockState state, Level world, BlockPos pos, Entity entity) { ++ if (this.precipitationType == Biome.Precipitation.SNOW) { ++ return LayeredCauldronBlock.lowerFillLevel((BlockState) Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, (Integer) state.getValue(LayeredCauldronBlock.LEVEL)), world, pos, entity, CauldronLevelChangeEvent.ChangeReason.EXTINGUISH); ++ } else { ++ return LayeredCauldronBlock.lowerFillLevel(state, world, pos, entity, CauldronLevelChangeEvent.ChangeReason.EXTINGUISH); ++ } ++ } ++ // Paper end - fix powdered snow cauldron extinguishing entities + + public static void lowerFillLevel(BlockState state, Level world, BlockPos pos) { + // CraftBukkit start diff --git a/patches/server/0688-Add-PlayerStopUsingItemEvent.patch b/patches/server/0688-Add-PlayerStopUsingItemEvent.patch new file mode 100644 index 0000000000..8d30122457 --- /dev/null +++ b/patches/server/0688-Add-PlayerStopUsingItemEvent.patch @@ -0,0 +1,18 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: u9g <[email protected]> +Date: Tue, 3 May 2022 20:41:37 -0400 +Subject: [PATCH] Add PlayerStopUsingItemEvent + + +diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java +index bb96b962439aa62954352f193b44f97d2e826373..a35ee5cca91512a6b534140fe59df0f8355a097d 100644 +--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java ++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java +@@ -4210,6 +4210,7 @@ public abstract class LivingEntity extends Entity implements Attackable { + + public void releaseUsingItem() { + if (!this.useItem.isEmpty()) { ++ if (this instanceof ServerPlayer) new io.papermc.paper.event.player.PlayerStopUsingItemEvent((Player) getBukkitEntity(), useItem.asBukkitMirror(), getTicksUsingItem()).callEvent(); // Paper - Add PlayerStopUsingItemEvent + this.useItem.releaseUsing(this.level(), this, this.getUseItemRemainingTicks()); + if (this.useItem.useOnRelease()) { + this.updatingUsingItem(); diff --git a/patches/server/0689-Don-t-tick-markers.patch b/patches/server/0689-Don-t-tick-markers.patch new file mode 100644 index 0000000000..8486b3dbe8 --- /dev/null +++ b/patches/server/0689-Don-t-tick-markers.patch @@ -0,0 +1,36 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Noah van der Aa <[email protected]> +Date: Fri, 7 Jan 2022 11:58:26 +0100 +Subject: [PATCH] Don't tick markers + +Fixes https://github.com/PaperMC/Paper/issues/7276 and https://github.com/PaperMC/Paper/issues/8118 +by using a config option that, when set to false, does not add markers to the entity +tick list at all and ignores them in Spigot's activation range checks. The entity tick +list is only used in the tick and tickPassenger methods, so we can safely not add the +markers to it. When the config option is set to true, markers are ticked as normal. + +diff --git a/src/main/java/io/papermc/paper/command/subcommands/EntityCommand.java b/src/main/java/io/papermc/paper/command/subcommands/EntityCommand.java +index 9d9d133e0d973ecda1ef1efc872a51ee10463fd1..f671b74e4179fc29bc600b52e456ba9f78d8bbd6 100644 +--- a/src/main/java/io/papermc/paper/command/subcommands/EntityCommand.java ++++ b/src/main/java/io/papermc/paper/command/subcommands/EntityCommand.java +@@ -109,7 +109,7 @@ public final class EntityCommand implements PaperSubcommand { + ChunkPos chunk = e.chunkPosition(); + info.left++; + info.right.put(chunk, info.right.getOrDefault(chunk, 0) + 1); +- if (!world.isPositionEntityTicking(e.blockPosition())) { ++ if (!world.isPositionEntityTicking(e.blockPosition()) || (e instanceof net.minecraft.world.entity.Marker && !world.paperConfig().entities.markers.tick)) { // Paper - Configurable marker ticking + nonEntityTicking.merge(key, 1, Integer::sum); + } + }); +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index fda4b5f2b848b432138207eff9a77fed6aaf3805..08b819c19eda2ab53b1b37af7a90e38b06b67b85 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -2230,6 +2230,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + } + + public void onTickingStart(Entity entity) { ++ if (entity instanceof net.minecraft.world.entity.Marker && !paperConfig().entities.markers.tick) return; // Paper - Configurable marker ticking + ServerLevel.this.entityTickList.add(entity); + } + diff --git a/patches/server/0690-Expand-FallingBlock-API.patch b/patches/server/0690-Expand-FallingBlock-API.patch new file mode 100644 index 0000000000..789dd54c78 --- /dev/null +++ b/patches/server/0690-Expand-FallingBlock-API.patch @@ -0,0 +1,107 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Owen1212055 <[email protected]> +Date: Sun, 5 Dec 2021 14:58:17 -0500 +Subject: [PATCH] Expand FallingBlock API + +- add auto expire setting +- add setter for block data +- add accessors for block state + +== AT == +public net.minecraft.world.entity.item.FallingBlockEntity blockState + +Co-authored-by: Lukas Planz <[email protected]> + +diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +index eb1745915190e69bb467fca2dbc46e0727530ba0..692d78bff5f7f01c8545e0e1785953ccc0f00e2d 100644 +--- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java ++++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java +@@ -71,6 +71,7 @@ public class FallingBlockEntity extends Entity { + public CompoundTag blockData; + public boolean forceTickAfterTeleportToDuplicate; + protected static final EntityDataAccessor<BlockPos> DATA_START_POS = SynchedEntityData.defineId(FallingBlockEntity.class, EntityDataSerializers.BLOCK_POS); ++ public boolean autoExpire = true; // Paper - Expand FallingBlock API + + public FallingBlockEntity(EntityType<? extends FallingBlockEntity> type, Level world) { + super(type, world); +@@ -191,7 +192,7 @@ public class FallingBlockEntity extends Entity { + } + + if (!this.onGround() && !flag1) { +- if (this.time > 100 && (blockposition.getY() <= this.level().getMinY() || blockposition.getY() > this.level().getMaxY()) || this.time > 600) { ++ if ((this.time > 100 && autoExpire) && (blockposition.getY() <= this.level().getMinY() || blockposition.getY() > this.level().getMaxY()) || (this.time > 600 && autoExpire)) { // Paper - Expand FallingBlock API + if (this.dropItem && worldserver.getGameRules().getBoolean(GameRules.RULE_DOENTITYDROPS)) { + this.spawnAtLocation(worldserver, (ItemLike) block); + } +@@ -338,6 +339,7 @@ public class FallingBlockEntity extends Entity { + } + + nbt.putBoolean("CancelDrop", this.cancelDrop); ++ if (!autoExpire) {nbt.putBoolean("Paper.AutoExpire", false);} // Paper - Expand FallingBlock API + } + + @Override +@@ -365,6 +367,11 @@ public class FallingBlockEntity extends Entity { + this.blockState = Blocks.SAND.defaultBlockState(); + } + ++ // Paper start - Expand FallingBlock API ++ if (nbt.contains("Paper.AutoExpire")) { ++ this.autoExpire = nbt.getBoolean("Paper.AutoExpire"); ++ } ++ // Paper end - Expand FallingBlock API + } + + public void setHurtsEntities(float fallHurtAmount, int fallHurtMax) { +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftFallingBlock.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftFallingBlock.java +index a7a3f74b846112d752fe04162b30805961457b11..1359d25a32b4a5d5e8e68ce737bd19f7b5afaf69 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftFallingBlock.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftFallingBlock.java +@@ -33,6 +33,31 @@ public class CraftFallingBlock extends CraftEntity implements FallingBlock { + public BlockData getBlockData() { + return CraftBlockData.fromData(this.getHandle().getBlockState()); + } ++ // Paper start - Expand FallingBlock API ++ @Override ++ public void setBlockData(final BlockData blockData) { ++ Preconditions.checkArgument(blockData != null, "blockData"); ++ final net.minecraft.world.level.block.state.BlockState oldState = this.getHandle().blockState, newState = ((CraftBlockData) blockData).getState(); ++ this.getHandle().blockState = newState; ++ this.getHandle().blockData = null; ++ ++ if (oldState != newState) this.update(); ++ } ++ ++ @Override ++ public org.bukkit.block.BlockState getBlockState() { ++ return org.bukkit.craftbukkit.block.CraftBlockStates.getBlockState(this.getHandle().blockState, this.getHandle().blockData); ++ } ++ ++ @Override ++ public void setBlockState(final org.bukkit.block.BlockState blockState) { ++ Preconditions.checkArgument(blockState != null, "blockState"); ++ // Calls #update if needed, the block data compound tag is not synced with the client and hence can be mutated after the sync with clients. ++ // The call also clears any potential old block data. ++ this.setBlockData(blockState.getBlockData()); ++ if (blockState instanceof final org.bukkit.craftbukkit.block.CraftBlockEntityState<?> tileEntity) this.getHandle().blockData = tileEntity.getSnapshotNBT(); ++ } ++ // Paper end - Expand FallingBlock API + + @Override + public boolean getDropItem() { +@@ -101,4 +126,15 @@ public class CraftFallingBlock extends CraftEntity implements FallingBlock { + this.setHurtEntities(true); + } + } ++ // Paper start - Expand FallingBlock API ++ @Override ++ public boolean doesAutoExpire() { ++ return this.getHandle().autoExpire; ++ } ++ ++ @Override ++ public void shouldAutoExpire(boolean autoExpires) { ++ this.getHandle().autoExpire = autoExpires; ++ } ++ // Paper end - Expand FallingBlock API + } diff --git a/patches/server/0691-Add-support-for-Proxy-Protocol.patch b/patches/server/0691-Add-support-for-Proxy-Protocol.patch new file mode 100644 index 0000000000..7486b23586 --- /dev/null +++ b/patches/server/0691-Add-support-for-Proxy-Protocol.patch @@ -0,0 +1,65 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: PanSzelescik <[email protected]> +Date: Thu, 7 Apr 2022 16:13:39 +0200 +Subject: [PATCH] Add support for Proxy Protocol + + +diff --git a/build.gradle.kts b/build.gradle.kts +index 3be55cc6e95c02fb43d6e1cd13d05e4e4857ac6b..eaccf005560af84beb98065ea4ac0adaef71768e 100644 +--- a/build.gradle.kts ++++ b/build.gradle.kts +@@ -41,6 +41,7 @@ dependencies { + log4jPlugins.annotationProcessorConfigurationName("org.apache.logging.log4j:log4j-core:2.19.0") // Paper - Needed to generate meta for our Log4j plugins + runtimeOnly(log4jPlugins.output) + alsoShade(log4jPlugins.output) ++ implementation("io.netty:netty-codec-haproxy:4.1.97.Final") // Paper - Add support for proxy protocol + // Paper end + implementation("org.apache.logging.log4j:log4j-iostreams:2.24.1") // Paper - remove exclusion + implementation("org.ow2.asm:asm-commons:9.7.1") +diff --git a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java +index c63c194c44646e6bc1a59426552787011fc2ced5..c62df32af11636ad408b584fcc590590ce4fb0d0 100644 +--- a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java ++++ b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java +@@ -104,6 +104,12 @@ public class ServerConnectionListener { + ServerConnectionListener.LOGGER.info("Using default channel type"); + } + ++ // Paper start - Warn people with console access that HAProxy is in use. ++ if (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.proxyProtocol) { ++ ServerConnectionListener.LOGGER.warn("Using HAProxy, please ensure the server port is adequately firewalled."); ++ } ++ // Paper end - Warn people with console access that HAProxy is in use. ++ + this.channels.add(((ServerBootstrap) ((ServerBootstrap) (new ServerBootstrap()).channel(oclass)).childHandler(new ChannelInitializer<Channel>() { + protected void initChannel(Channel channel) { + try { +@@ -123,6 +129,29 @@ public class ServerConnectionListener { + Connection object = j > 0 ? new RateKickingConnection(j) : new Connection(PacketFlow.SERVERBOUND); // CraftBukkit - decompile error + + //ServerConnectionListener.this.connections.add(object); // Paper ++ // Paper start - Add support for Proxy Protocol ++ if (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.proxyProtocol) { ++ channel.pipeline().addAfter("timeout", "haproxy-decoder", new io.netty.handler.codec.haproxy.HAProxyMessageDecoder()); ++ channel.pipeline().addAfter("haproxy-decoder", "haproxy-handler", new ChannelInboundHandlerAdapter() { ++ @Override ++ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ++ if (msg instanceof io.netty.handler.codec.haproxy.HAProxyMessage message) { ++ if (message.command() == io.netty.handler.codec.haproxy.HAProxyCommand.PROXY) { ++ String realaddress = message.sourceAddress(); ++ int realport = message.sourcePort(); ++ ++ SocketAddress socketaddr = new java.net.InetSocketAddress(realaddress, realport); ++ ++ Connection connection = (Connection) channel.pipeline().get("packet_handler"); ++ connection.address = socketaddr; ++ } ++ } else { ++ super.channelRead(ctx, msg); ++ } ++ } ++ }); ++ } ++ // Paper end - Add support for proxy protocol + pending.add(object); // Paper - prevent blocking on adding a new connection while the server is ticking + ((Connection) object).configurePacketHandler(channelpipeline); + ((Connection) object).setListenerForServerboundHandshake(new ServerHandshakePacketListenerImpl(ServerConnectionListener.this.server, (Connection) object)); diff --git a/patches/server/0692-Fix-OfflinePlayer-getBedSpawnLocation.patch b/patches/server/0692-Fix-OfflinePlayer-getBedSpawnLocation.patch new file mode 100644 index 0000000000..4726c2f65c --- /dev/null +++ b/patches/server/0692-Fix-OfflinePlayer-getBedSpawnLocation.patch @@ -0,0 +1,46 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Mon, 30 May 2022 16:03:36 -0700 +Subject: [PATCH] Fix OfflinePlayer#getBedSpawnLocation + +When calling getBedSpawnLocation on an +instance of CraftOfflinePlayer the world was incorrect +due to the logic for reading the NBT not being up-to-date. + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java +index e0d342a0ddd140b342f7af138c71596c6d718788..30c1c203022c2ed777dcddfd68ef2e3752c62ea1 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java +@@ -37,6 +37,7 @@ import org.bukkit.profile.PlayerProfile; + + @SerializableAs("Player") + public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializable { ++ private static final org.slf4j.Logger LOGGER = com.mojang.logging.LogUtils.getLogger(); // Paper + private final GameProfile profile; + private final CraftServer server; + private final PlayerDataStorage storage; +@@ -361,11 +362,20 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa + if (data == null) return null; + + if (data.contains("SpawnX") && data.contains("SpawnY") && data.contains("SpawnZ")) { +- String spawnWorld = data.getString("SpawnWorld"); +- if (spawnWorld.equals("")) { +- spawnWorld = this.server.getWorlds().get(0).getName(); ++ // Paper start - fix wrong world ++ final float respawnAngle = data.getFloat("SpawnAngle"); ++ org.bukkit.World spawnWorld = this.server.getWorld(data.getString("SpawnWorld")); // legacy ++ if (data.contains("SpawnDimension")) { ++ com.mojang.serialization.DataResult<net.minecraft.resources.ResourceKey<net.minecraft.world.level.Level>> result = net.minecraft.world.level.Level.RESOURCE_KEY_CODEC.parse(net.minecraft.nbt.NbtOps.INSTANCE, data.get("SpawnDimension")); ++ net.minecraft.resources.ResourceKey<net.minecraft.world.level.Level> levelKey = result.resultOrPartial(LOGGER::error).orElse(net.minecraft.world.level.Level.OVERWORLD); ++ net.minecraft.server.level.ServerLevel level = this.server.console.getLevel(levelKey); ++ spawnWorld = level != null ? level.getWorld() : spawnWorld; + } +- return new Location(this.server.getWorld(spawnWorld), data.getInt("SpawnX"), data.getInt("SpawnY"), data.getInt("SpawnZ")); ++ if (spawnWorld == null) { ++ return null; ++ } ++ return new Location(spawnWorld, data.getInt("SpawnX"), data.getInt("SpawnY"), data.getInt("SpawnZ"), respawnAngle, 0); ++ // Paper end + } + return null; + } diff --git a/patches/server/0693-Fix-FurnaceInventory-for-smokers-and-blast-furnaces.patch b/patches/server/0693-Fix-FurnaceInventory-for-smokers-and-blast-furnaces.patch new file mode 100644 index 0000000000..7158b83635 --- /dev/null +++ b/patches/server/0693-Fix-FurnaceInventory-for-smokers-and-blast-furnaces.patch @@ -0,0 +1,49 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic <[email protected]> +Date: Sat, 1 Jan 2022 23:11:26 -0800 +Subject: [PATCH] Fix FurnaceInventory for smokers and blast furnaces + + +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftTileInventoryConverter.java b/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftTileInventoryConverter.java +index a6c758c5c5da2fb3f2d251bc109f72a5d8b0eb14..ad2cb9a1352abd855bf11a390c9788835857380a 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftTileInventoryConverter.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftTileInventoryConverter.java +@@ -65,7 +65,7 @@ public abstract class CraftTileInventoryConverter implements CraftInventoryCreat + return new CraftInventory(tileEntity); + } + +- public static class Furnace extends CraftTileInventoryConverter { ++ public static class Furnace extends AbstractFurnaceInventoryConverter { // Paper - Furnace, BlastFurnace, and Smoker are pretty much identical + + @Override + public Container getTileEntity() { +@@ -73,6 +73,11 @@ public abstract class CraftTileInventoryConverter implements CraftInventoryCreat + return furnace; + } + ++ // Paper start - abstract furnace converter to apply to all 3 furnaces ++ } ++ ++ public static abstract class AbstractFurnaceInventoryConverter extends CraftTileInventoryConverter { ++ // Paper end - abstract furnace converter to apply to all 3 furnaces + // Paper start + @Override + public Inventory createInventory(InventoryHolder owner, InventoryType type, net.kyori.adventure.text.Component title) { +@@ -170,7 +175,7 @@ public abstract class CraftTileInventoryConverter implements CraftInventoryCreat + } + } + +- public static class BlastFurnace extends CraftTileInventoryConverter { ++ public static class BlastFurnace extends AbstractFurnaceInventoryConverter { // Paper - Furnace, BlastFurnace, and Smoker are pretty much identical + + @Override + public Container getTileEntity() { +@@ -186,7 +191,7 @@ public abstract class CraftTileInventoryConverter implements CraftInventoryCreat + } + } + +- public static class Smoker extends CraftTileInventoryConverter { ++ public static class Smoker extends AbstractFurnaceInventoryConverter { // Paper - Furnace, BlastFurnace, and Smoker are pretty much identical + + @Override + public Container getTileEntity() { |