This commit is contained in:
Bjarne Koll 2024-06-14 10:56:28 +02:00
parent 14c03366ed
commit a399f23903
No known key found for this signature in database
GPG key ID: 27F6CCCF55D2EE62
36 changed files with 185 additions and 186 deletions

View file

@ -1,171 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com>
Date: Tue, 27 Jun 2023 21:09:11 -0400
Subject: [PATCH] Dont resend blocks on interactions
In general, the client now has an acknowledgment system which will prevent block changes made by the client to be reverted correctly.
It should be noted that this system does not yet support block entities, so those still need to resynced when needed.
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
index dd78a1685bd841577aa9d5010fffda1468901baf..c5f3ba82b3ded415ff4bffc9eabf9526695e1ba7 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
@@ -202,7 +202,7 @@ public class ServerPlayerGameMode {
PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_BLOCK, pos, direction, this.player.getInventory().getSelected(), InteractionHand.MAIN_HAND);
if (event.isCancelled()) {
// Let the client know the block still exists
- this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos));
+ // this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos)); // Paper - Don't resync blocks
// Update any tile entity data for this block
capturedBlockEntity = true; // Paper - Send block entities after destroy prediction
return;
@@ -217,7 +217,7 @@ public class ServerPlayerGameMode {
// Spigot start - handle debug stick left click for non-creative
if (this.player.getMainHandItem().is(net.minecraft.world.item.Items.DEBUG_STICK)
&& ((net.minecraft.world.item.DebugStickItem) net.minecraft.world.item.Items.DEBUG_STICK).handleInteraction(this.player, this.level.getBlockState(pos), this.level, pos, false, this.player.getMainHandItem())) {
- this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos));
+ // this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos)); // Paper - Don't resync block
return;
}
// Spigot end
@@ -235,15 +235,17 @@ public class ServerPlayerGameMode {
// CraftBukkit start - Swings at air do *NOT* exist.
if (event.useInteractedBlock() == Event.Result.DENY) {
// If we denied a door from opening, we need to send a correcting update to the client, as it already opened the door.
- BlockState data = this.level.getBlockState(pos);
- if (data.getBlock() instanceof DoorBlock) {
- // For some reason *BOTH* the bottom/top part have to be marked updated.
- boolean bottom = data.getValue(DoorBlock.HALF) == DoubleBlockHalf.LOWER;
- this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos));
- this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, bottom ? pos.above() : pos.below()));
- } else if (data.getBlock() instanceof TrapDoorBlock) {
- this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos));
- }
+ // Paper start - Don't resync blocks
+ //BlockState data = this.level.getBlockState(pos);
+ //if (data.getBlock() instanceof DoorBlock) {
+ // // For some reason *BOTH* the bottom/top part have to be marked updated.
+ // boolean bottom = data.getValue(DoorBlock.HALF) == DoubleBlockHalf.LOWER;
+ // this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos));
+ // this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, bottom ? pos.above() : pos.below()));
+ //} else if (data.getBlock() instanceof TrapDoorBlock) {
+ // this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos));
+ //}
+ // Paper end - Don't resync blocks
} else if (!iblockdata.isAir()) {
iblockdata.attack(this.level, pos, this.player);
f = iblockdata.getDestroyProgress(this.player, this.player.level(), pos);
@@ -252,7 +254,7 @@ public class ServerPlayerGameMode {
if (event.useItemInHand() == Event.Result.DENY) {
// If we 'insta destroyed' then the client needs to be informed.
if (f > 1.0f) {
- this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos));
+ // this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos)); // Paper - Don't resync blocks
}
return;
}
@@ -260,7 +262,7 @@ public class ServerPlayerGameMode {
if (blockEvent.isCancelled()) {
// Let the client know the block still exists
- this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos));
+ // this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos)); // Paper - Don't resync block
return;
}
@@ -351,7 +353,7 @@ public class ServerPlayerGameMode {
// Tell client the block is gone immediately then process events
// Don't tell the client if its a creative sword break because its not broken!
- if (this.level.getBlockEntity(pos) == null && !isSwordNoBreak) {
+ if (false && this.level.getBlockEntity(pos) == null && !isSwordNoBreak) { // Paper - Don't resync block
ClientboundBlockUpdatePacket packet = new ClientboundBlockUpdatePacket(pos, Blocks.AIR.defaultBlockState());
this.player.connection.send(packet);
}
@@ -377,13 +379,15 @@ public class ServerPlayerGameMode {
if (isSwordNoBreak) {
return false;
}
+ // Paper start - Don't resync blocks
// Let the client know the block still exists
- this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos));
+ //this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos));
// Brute force all possible updates
- for (Direction dir : Direction.values()) {
- this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos.relative(dir)));
- }
+ //for (Direction dir : Direction.values()) {
+ // this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos.relative(dir)));
+ //}
+ // Paper end - Don't resync blocks
// Update any tile entity data for this block
if (!captureSentBlockEntities) { // Paper - Send block entities after destroy prediction
@@ -532,16 +536,18 @@ public class ServerPlayerGameMode {
if (event.useInteractedBlock() == Event.Result.DENY) {
// If we denied a door from opening, we need to send a correcting update to the client, as it already opened the door.
if (iblockdata.getBlock() instanceof DoorBlock) {
- boolean bottom = iblockdata.getValue(DoorBlock.HALF) == DoubleBlockHalf.LOWER;
- player.connection.send(new ClientboundBlockUpdatePacket(world, bottom ? blockposition.above() : blockposition.below()));
+ // Paper start - Don't resync blocks
+ // boolean bottom = iblockdata.getValue(DoorBlock.HALF) == DoubleBlockHalf.LOWER;
+ // player.connection.send(new ClientboundBlockUpdatePacket(world, bottom ? blockposition.above() : blockposition.below()));
+ // Paper end - Don't resync blocks
} else if (iblockdata.getBlock() instanceof CakeBlock) {
player.getBukkitEntity().sendHealthUpdate(); // SPIGOT-1341 - reset health for cake
} else if (this.interactItemStack.getItem() instanceof DoubleHighBlockItem) {
// send a correcting update to the client, as it already placed the upper half of the bisected item
- player.connection.send(new ClientboundBlockUpdatePacket(world, blockposition.relative(hitResult.getDirection()).above()));
+ //player.connection.send(new ClientboundBlockUpdatePacket(world, blockposition.relative(hitResult.getDirection()).above())); // Paper - Don't resync blocks
// send a correcting update to the client for the block above as well, this because of replaceable blocks (such as grass, sea grass etc)
- player.connection.send(new ClientboundBlockUpdatePacket(world, blockposition.above()));
+ //player.connection.send(new ClientboundBlockUpdatePacket(world, blockposition.above())); // Paper - Don't resync blocks
// Paper start - extend Player Interact cancellation // TODO: consider merging this into the extracted method
} else if (iblockdata.is(Blocks.JIGSAW) || iblockdata.is(Blocks.STRUCTURE_BLOCK) || iblockdata.getBlock() instanceof net.minecraft.world.level.block.CommandBlock) {
player.connection.send(new net.minecraft.network.protocol.game.ClientboundContainerClosePacket(this.player.containerMenu.containerId));
diff --git a/src/main/java/net/minecraft/world/item/BucketItem.java b/src/main/java/net/minecraft/world/item/BucketItem.java
index 6d494b80ae002aea00afa44adf83dad1ae5bbbc1..49557d6f22c5725c663a231deab019d4f6fe95fa 100644
--- a/src/main/java/net/minecraft/world/item/BucketItem.java
+++ b/src/main/java/net/minecraft/world/item/BucketItem.java
@@ -78,7 +78,7 @@ public class BucketItem extends Item implements DispensibleContainerItem {
PlayerBucketFillEvent event = CraftEventFactory.callPlayerBucketFillEvent((ServerLevel) world, user, blockposition, blockposition, movingobjectpositionblock.getDirection(), itemstack, dummyFluid.getItem(), hand);
if (event.isCancelled()) {
- ((ServerPlayer) user).connection.send(new ClientboundBlockUpdatePacket(world, blockposition)); // SPIGOT-5163 (see PlayerInteractManager)
+ // ((ServerPlayer) user).connection.send(new ClientboundBlockUpdatePacket(world, blockposition)); // SPIGOT-5163 (see PlayerInteractManager) // Paper - Don't resend blocks
((ServerPlayer) user).getBukkitEntity().updateInventory(); // SPIGOT-4541
return InteractionResultHolder.fail(itemstack);
}
@@ -185,7 +185,7 @@ public class BucketItem extends Item implements DispensibleContainerItem {
if (flag2 && entityhuman != null) {
PlayerBucketEmptyEvent event = CraftEventFactory.callPlayerBucketEmptyEvent((ServerLevel) world, entityhuman, blockposition, clicked, enumdirection, itemstack, enumhand);
if (event.isCancelled()) {
- ((ServerPlayer) entityhuman).connection.send(new ClientboundBlockUpdatePacket(world, blockposition)); // SPIGOT-4238: needed when looking through entity
+ // ((ServerPlayer) entityhuman).connection.send(new ClientboundBlockUpdatePacket(world, blockposition)); // SPIGOT-4238: needed when looking through entity // Paper - Don't resend blocks
((ServerPlayer) entityhuman).getBukkitEntity().updateInventory(); // SPIGOT-4541
return false;
}
diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java
index 4485199c90e3efbce3e6a18a75703e86a9bdde3c..3f6082b656ec8c9b96b40fbd1c926d5211ffe4c6 100644
--- a/src/main/java/net/minecraft/world/item/ItemStack.java
+++ b/src/main/java/net/minecraft/world/item/ItemStack.java
@@ -479,10 +479,12 @@ public final class ItemStack implements DataComponentHolder {
world.preventPoiUpdated = false;
// Brute force all possible updates
- BlockPos placedPos = ((CraftBlock) placeEvent.getBlock()).getPosition();
- for (Direction dir : Direction.values()) {
- ((ServerPlayer) entityhuman).connection.send(new ClientboundBlockUpdatePacket(world, placedPos.relative(dir)));
- }
+ // Paper start - Don't resync blocks
+ // BlockPos placedPos = ((CraftBlock) placeEvent.getBlock()).getPosition();
+ // for (Direction dir : Direction.values()) {
+ // ((ServerPlayer) entityhuman).connection.send(new ClientboundBlockUpdatePacket(world, placedPos.relative(dir)));
+ // }
+ // Paper end - Don't resync blocks
SignItem.openSign = null; // SPIGOT-6758 - Reset on early return
} else {
// Change the stack to its new contents if it hasn't been tampered with.

View file

@ -1,79 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Sat, 16 Dec 2023 14:46:01 -0800
Subject: [PATCH] add more scoreboard API
diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java
index b36e5574c10e6d70a399e2ac0704fd4f43dbb444..2d3abf2a1da487ead74d698cc5ea4eb729c35c8d 100644
--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java
+++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java
@@ -185,6 +185,19 @@ final class CraftObjective extends CraftScoreboardComponent implements Objective
final CraftObjective other = (CraftObjective) obj;
return !(this.objective != other.objective && (this.objective == null || !this.objective.equals(other.objective)));
}
+ // Paper start - add more score API
+ @Override
+ public boolean willAutoUpdateDisplay() {
+ this.checkState();
+ return this.objective.displayAutoUpdate();
+ }
+
+ @Override
+ public void setAutoUpdateDisplay(final boolean autoUpdateDisplay) {
+ this.checkState();
+ this.objective.setDisplayAutoUpdate(autoUpdateDisplay);
+ }
+ // Paper end - add more score API
}
diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java
index ceb1a39c02c3cfa7632a0fdca414c7046888fcb1..74d9c407e971804bed420370f7b684d8658eb5aa 100644
--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java
+++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java
@@ -74,4 +74,44 @@ final class CraftScore implements Score {
board.resetSinglePlayerScore(entry, this.objective.getHandle());
}
// Paper end
+
+ // Paper start - add more score API
+ @Override
+ public boolean isTriggerable() {
+ if (this.objective.getTrackedCriteria() != org.bukkit.scoreboard.Criteria.TRIGGER) {
+ return false;
+ }
+ final Scoreboard board = this.objective.checkState().board;
+ final ReadOnlyScoreInfo scoreInfo = board.getPlayerScoreInfo(this.entry, this.objective.getHandle());
+ return scoreInfo != null && !scoreInfo.isLocked();
+ }
+
+ @Override
+ public void setTriggerable(final boolean triggerable) {
+ com.google.common.base.Preconditions.checkArgument(this.objective.getTrackedCriteria() == org.bukkit.scoreboard.Criteria.TRIGGER, "the criteria isn't 'trigger'");
+ final Scoreboard board = this.objective.checkState().board;
+ if (triggerable) {
+ board.getOrCreatePlayerScore(this.entry, this.objective.getHandle()).unlock();
+ } else {
+ board.getOrCreatePlayerScore(this.entry, this.objective.getHandle()).lock();
+ }
+ }
+
+ @Override
+ public net.kyori.adventure.text.Component customName() {
+ final Scoreboard board = this.objective.checkState().board;
+ final ReadOnlyScoreInfo scoreInfo = board.getPlayerScoreInfo(this.entry, this.objective.getHandle());
+ if (scoreInfo == null) {
+ return null; // If score doesn't exist, don't create one
+ }
+ final net.minecraft.network.chat.Component display = board.getOrCreatePlayerScore(this.entry, this.objective.getHandle()).display();
+ return display == null ? null : io.papermc.paper.adventure.PaperAdventure.asAdventure(display);
+ }
+
+ @Override
+ public void customName(final net.kyori.adventure.text.Component customName) {
+ final Scoreboard board = this.objective.checkState().board;
+ board.getOrCreatePlayerScore(this.entry, this.objective.getHandle()).display(io.papermc.paper.adventure.PaperAdventure.asVanilla(customName));
+ }
+ // Paper end - add more score API
}

View file

@ -1,102 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Wed, 20 Dec 2023 02:03:05 -0800
Subject: [PATCH] Improve Registry
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java b/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
index a644309edb612d97da290f86a1ef6fe597c7d85d..3adf18d5e736abff701a4866ee1f8403aeafca84 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
@@ -143,6 +143,7 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
private final Class<?> bukkitClass; // Paper - relax preload class
private final Map<NamespacedKey, B> cache = new HashMap<>();
+ private final Map<B, NamespacedKey> byValue = new java.util.IdentityHashMap<>(); // Paper - improve Registry
private final net.minecraft.core.Registry<M> minecraftRegistry;
private final BiFunction<NamespacedKey, M, B> minecraftToBukkit;
private final BiFunction<NamespacedKey, ApiVersion, NamespacedKey> serializationUpdater; // Paper - rename to make it *clear* what it is *only* for
@@ -191,6 +192,7 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
}
this.cache.put(namespacedKey, bukkit);
+ this.byValue.put(bukkit, namespacedKey); // Paper - improve Registry
return bukkit;
}
@@ -213,4 +215,11 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
return this.minecraftToBukkit.apply(namespacedKey, minecraft);
}
+
+ // Paper start - improve Registry
+ @Override
+ public NamespacedKey getKey(final B value) {
+ return this.byValue.get(value);
+ }
+ // Paper end - improve Registry
}
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/trim/CraftTrimMaterial.java b/src/main/java/org/bukkit/craftbukkit/inventory/trim/CraftTrimMaterial.java
index cd3e35867075e65f46051fb88d8a2460a8bb4b53..76627683f256a034a147765db693a9fd2ab9613f 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/trim/CraftTrimMaterial.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/trim/CraftTrimMaterial.java
@@ -54,6 +54,7 @@ public class CraftTrimMaterial implements TrimMaterial, Handleable<net.minecraft
@Override
@NotNull
public NamespacedKey getKey() {
+ if (true) return java.util.Objects.requireNonNull(org.bukkit.Registry.TRIM_MATERIAL.getKey(this), () -> this + " doesn't have a key"); // Paper
return this.key;
}
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/trim/CraftTrimPattern.java b/src/main/java/org/bukkit/craftbukkit/inventory/trim/CraftTrimPattern.java
index 364f8d7a7106259401154d91b1b79869d014a469..f336bf98574e4fdeabc3b210629834393ec11a74 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/trim/CraftTrimPattern.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/trim/CraftTrimPattern.java
@@ -54,6 +54,7 @@ public class CraftTrimPattern implements TrimPattern, Handleable<net.minecraft.w
@Override
@NotNull
public NamespacedKey getKey() {
+ if (true) return java.util.Objects.requireNonNull(org.bukkit.Registry.TRIM_PATTERN.getKey(this), () -> this + " doesn't have a key"); // Paper
return this.key;
}
diff --git a/src/test/java/org/bukkit/registry/PerRegistryTest.java b/src/test/java/org/bukkit/registry/PerRegistryTest.java
index 523b4b208e05c6b70014440200e3196cc84f36cc..12b93a6e091de47522d060fa0cd84fe41318c46a 100644
--- a/src/test/java/org/bukkit/registry/PerRegistryTest.java
+++ b/src/test/java/org/bukkit/registry/PerRegistryTest.java
@@ -49,19 +49,22 @@ public class PerRegistryTest extends AbstractTestingBase {
@ParameterizedTest
@MethodSource("data")
- public void testGet(Registry<?> registry) {
+ public <T extends Keyed> void testGet(Registry<T> registry) { // Paper - improve Registry
registry.forEach(element -> {
+ NamespacedKey key = registry.getKey(element); // Paper - improve Registry
+ assertNotNull(key); // Paper - improve Registry
// Values in the registry should be referentially equal to what is returned with #get()
// This ensures that new instances are not created each time #get() is invoked
- assertSame(element, registry.get(element.getKey()));
+ assertSame(element, registry.get(key)); // Paper - improve Registry
});
}
@ParameterizedTest
@MethodSource("data")
- public void testMatch(Registry<?> registry) {
+ public <T extends Keyed> void testMatch(Registry<T> registry) { // Paper - improve Registry
registry.forEach(element -> {
- NamespacedKey key = element.getKey();
+ NamespacedKey key = registry.getKey(element); // Paper - improve Registry
+ assertNotNull(key); // Paper - improve Registry
this.assertSameMatchWithKeyMessage(registry, element, key.toString()); // namespace:key
this.assertSameMatchWithKeyMessage(registry, element, key.getKey()); // key
@@ -72,7 +75,7 @@ public class PerRegistryTest extends AbstractTestingBase {
});
}
- private void assertSameMatchWithKeyMessage(Registry<?> registry, Keyed element, String key) {
+ private <T extends Keyed> void assertSameMatchWithKeyMessage(Registry<T> registry, T element, String key) { // Paper - improve Registry
assertSame(element, registry.match(key), key);
}

View file

@ -1,66 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Sat, 9 Dec 2023 19:15:59 -0800
Subject: [PATCH] Fix NPE on null loc for EntityTeleportEvent
EntityTeleportEvent#setTo is marked as nullable and so is the
getTo method. This fixes the handling of a null "to" location
by treating it the same as the event being cancelled. This is
already existing behavior for the EntityPortalEvent (which
extends EntityTeleportEvent).
diff --git a/src/main/java/net/minecraft/server/commands/TeleportCommand.java b/src/main/java/net/minecraft/server/commands/TeleportCommand.java
index a306b30af19277386a2f3e560b4902a8b5796f2a..54851f6cc0d5fddb32a9a1e84a4f5ae41af18758 100644
--- a/src/main/java/net/minecraft/server/commands/TeleportCommand.java
+++ b/src/main/java/net/minecraft/server/commands/TeleportCommand.java
@@ -169,9 +169,10 @@ public class TeleportCommand {
Location to = new Location(world.getWorld(), x, y, z, f2, f3);
EntityTeleportEvent event = new EntityTeleportEvent(target.getBukkitEntity(), target.getBukkitEntity().getLocation(), to);
world.getCraftServer().getPluginManager().callEvent(event);
- if (event.isCancelled()) {
+ if (event.isCancelled() || event.getTo() == null) { // Paper
return;
}
+ to = event.getTo(); // Paper - actually track new location
x = to.getX();
y = to.getY();
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index cb07999629ba2d56602b3ae06ef06e350a8d1fb1..b7af45eac986097ef53fa90bb4edd67f8829f13c 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -4173,7 +4173,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
if (!(this instanceof ServerPlayer)) {
EntityTeleportEvent teleport = new EntityTeleportEvent(this.getBukkitEntity(), new Location(this.level().getWorld(), d3, d4, d5), new Location(this.level().getWorld(), d0, d6, d2));
this.level().getCraftServer().getPluginManager().callEvent(teleport);
- if (!teleport.isCancelled()) {
+ if (!teleport.isCancelled() && teleport.getTo() != null) { // Paper
Location to = teleport.getTo();
this.teleportTo(to.getX(), to.getY(), to.getZ());
} else {
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/FollowOwnerGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/FollowOwnerGoal.java
index 8fa9282acd87132516329083f774345df3310cf2..edd29c2f4d0151d512618115a8fb4b7423171491 100644
--- a/src/main/java/net/minecraft/world/entity/ai/goal/FollowOwnerGoal.java
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/FollowOwnerGoal.java
@@ -130,7 +130,7 @@ public class FollowOwnerGoal extends Goal {
} else {
// CraftBukkit start
EntityTeleportEvent event = CraftEventFactory.callEntityTeleportEvent(this.tamable, (double) x + 0.5D, (double) y, (double) z + 0.5D);
- if (event.isCancelled()) {
+ if (event.isCancelled() || event.getTo() == null) { // Paper
return false;
}
Location to = event.getTo();
diff --git a/src/main/java/net/minecraft/world/entity/monster/Shulker.java b/src/main/java/net/minecraft/world/entity/monster/Shulker.java
index c2005b86ac9ff6aa03ef7937c2b7a228addc4f01..bd6171d75fbf4a0debef3892d6f67356dc811b4d 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Shulker.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Shulker.java
@@ -415,7 +415,7 @@ public class Shulker extends AbstractGolem implements VariantHolder<Optional<Dye
if (enumdirection != null) {
// CraftBukkit start
EntityTeleportEvent teleportEvent = CraftEventFactory.callEntityTeleportEvent(this, blockposition1.getX(), blockposition1.getY(), blockposition1.getZ());
- if (teleportEvent.isCancelled()) {
+ if (teleportEvent.isCancelled() || teleportEvent.getTo() == null) { // Paper
return false;
} else {
blockposition1 = CraftLocation.toBlockPosition(teleportEvent.getTo());

View file

@ -1,73 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Lukas Planz <lukas.planz@web.de>
Date: Tue, 5 Sep 2023 20:34:20 +0200
Subject: [PATCH] Add experience points 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 77624e34143dc37ca3b523a81b9a041ffb3199bd..32f6c029bf2f9a16d48ac73ff217c7462c64f175 100644
--- a/src/main/java/net/minecraft/world/entity/player/Player.java
+++ b/src/main/java/net/minecraft/world/entity/player/Player.java
@@ -1855,7 +1855,7 @@ public abstract class Player extends LivingEntity {
}
public int getXpNeededForNextLevel() {
- return this.experienceLevel >= 30 ? 112 + (this.experienceLevel - 30) * 9 : (this.experienceLevel >= 15 ? 37 + (this.experienceLevel - 15) * 5 : 7 + this.experienceLevel * 2);
+ return this.experienceLevel >= 30 ? 112 + (this.experienceLevel - 30) * 9 : (this.experienceLevel >= 15 ? 37 + (this.experienceLevel - 15) * 5 : 7 + this.experienceLevel * 2); // Paper - diff on change; calculateTotalExperiencePoints
}
// Paper start - send while respecting visibility
private static void sendSoundEffect(Player fromEntity, double x, double y, double z, SoundEvent soundEffect, SoundSource soundCategory, float volume, float pitch) {
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index e9bd24861bca4554898306117bb533d19171cedf..68a0b6b8650e9e80e8e8c4037d92389cae899d72 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -1902,6 +1902,49 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
Preconditions.checkArgument(exp >= 0, "Total experience points must not be negative (%s)", exp);
this.getHandle().totalExperience = exp;
}
+ // Paper start
+ @Override
+ public int calculateTotalExperiencePoints() {
+ return calculateTotalExperiencePoints(this.getLevel()) + Math.round(this.getExperiencePointsNeededForNextLevel() * getExp());
+ }
+
+ @Override
+ public void setExperienceLevelAndProgress(final int totalExperience) {
+ Preconditions.checkArgument(totalExperience >= 0, "Total experience points must not be negative (%s)", totalExperience);
+ int level = calculateLevelsForExperiencePoints(totalExperience);
+ int remainingPoints = totalExperience - calculateTotalExperiencePoints(level);
+
+ this.getHandle().experienceLevel = level;
+ this.getHandle().experienceProgress = (float) remainingPoints / this.getExperiencePointsNeededForNextLevel();
+ this.getHandle().lastSentExp = -1;
+ }
+
+ @Override
+ public int getExperiencePointsNeededForNextLevel() {
+ return this.getHandle().getXpNeededForNextLevel();
+ }
+
+ // See https://minecraft.wiki/w/Experience#Leveling_up for reference
+ private int calculateTotalExperiencePoints(int level) {
+ if (level <= 16) {
+ return (int) (Math.pow(level, 2) + 6 * level);
+ } else if (level <= 31) {
+ return (int) (2.5 * Math.pow(level, 2) - 40.5 * level + 360.0);
+ } else {
+ return (int) (4.5 * Math.pow(level, 2) - 162.5 * level + 2220.0);
+ }
+ }
+
+ private int calculateLevelsForExperiencePoints(int points) {
+ if (points <= 352) { // Level 0-16
+ return (int) Math.floor(Math.sqrt(points + 9) - 3);
+ } else if (points <= 1507) { // Level 17-31
+ return (int) Math.floor(8.1 + Math.sqrt(0.4 * (points - (7839.0 / 40.0))));
+ } else { // 32+
+ return (int) Math.floor((325.0 / 18.0) + Math.sqrt((2.0 / 9.0) * (points - (54215.0 / 72.0))));
+ }
+ }
+ // Paper end
@Override
public void sendExperienceChange(float progress) {

View file

@ -1,368 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Tue, 18 May 2021 12:32:02 -0700
Subject: [PATCH] Add drops to shear events
diff --git a/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java
index fbed5d57db0c0e79996f85571b9af0071fa953c7..a024c697a65bbab27408da1d6a75e531d9719b47 100644
--- a/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java
+++ b/src/main/java/net/minecraft/core/dispenser/ShearsDispenseItemBehavior.java
@@ -104,11 +104,14 @@ public class ShearsDispenseItemBehavior extends OptionalDispenseItemBehavior {
if (entityliving instanceof Shearable ishearable) {
if (ishearable.readyForShearing()) {
// CraftBukkit start
- if (CraftEventFactory.callBlockShearEntityEvent(entityliving, bukkitBlock, craftItem).isCancelled()) {
+ // Paper start - Add drops to shear events
+ org.bukkit.event.block.BlockShearEntityEvent event = CraftEventFactory.callBlockShearEntityEvent(entityliving, bukkitBlock, craftItem, ishearable.generateDefaultDrops());
+ if (event.isCancelled()) {
+ // Paper end - Add drops to shear events
continue;
}
// CraftBukkit end
- ishearable.shear(SoundSource.BLOCKS);
+ ishearable.shear(SoundSource.BLOCKS, CraftItemStack.asNMSCopy(event.getDrops())); // Paper - Add drops to shear events
worldserver.gameEvent((Entity) null, (Holder) GameEvent.SHEAR, blockposition);
return true;
}
diff --git a/src/main/java/net/minecraft/world/entity/Shearable.java b/src/main/java/net/minecraft/world/entity/Shearable.java
index 5e8cc5cfac8888628c6d513148f41be09ca65a2c..2ee48ac3b665db2b02bcb1a30ec972d43a3725b0 100644
--- a/src/main/java/net/minecraft/world/entity/Shearable.java
+++ b/src/main/java/net/minecraft/world/entity/Shearable.java
@@ -3,7 +3,13 @@ package net.minecraft.world.entity;
import net.minecraft.sounds.SoundSource;
public interface Shearable {
+ default void shear(SoundSource soundCategory, java.util.List<net.minecraft.world.item.ItemStack> drops) { this.shear(soundCategory); } // Paper - Add drops to shear events
void shear(SoundSource shearedSoundCategory);
boolean readyForShearing();
+ // Paper start - custom shear drops; ensure all implementing entities override this
+ default java.util.List<net.minecraft.world.item.ItemStack> generateDefaultDrops() {
+ return java.util.Collections.emptyList();
+ }
+ // Paper end - custom shear drops
}
diff --git a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java
index aa125e3043b120935aaa015803f065f99eb8d050..0c21959f57ae88fcd0a4d6dc911c1ce347c96528 100644
--- a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java
+++ b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java
@@ -123,11 +123,18 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder<Mushroo
return InteractionResult.sidedSuccess(this.level().isClientSide);
} else if (itemstack.is(Items.SHEARS) && this.readyForShearing()) {
// CraftBukkit start
- if (!CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand)) {
- return InteractionResult.PASS;
+ // Paper start - custom shear drops
+ java.util.List<ItemStack> drops = this.generateDefaultDrops();
+ org.bukkit.event.player.PlayerShearEntityEvent event = CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand, drops);
+ if (event != null) {
+ if (event.isCancelled()) {
+ return InteractionResult.PASS;
+ }
+ drops = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getDrops());
}
+ // Paper end - custom shear drops
// CraftBukkit end
- this.shear(SoundSource.PLAYERS);
+ this.shear(SoundSource.PLAYERS, drops); // Paper - custom shear drops
this.gameEvent(GameEvent.SHEAR, player);
if (!this.level().isClientSide) {
itemstack.hurtAndBreak(1, player, getSlotForHand(hand));
@@ -164,6 +171,22 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder<Mushroo
@Override
public void shear(SoundSource shearedSoundCategory) {
+ // Paper start - custom shear drops
+ this.shear(shearedSoundCategory, this.generateDefaultDrops());
+ }
+
+ @Override
+ public java.util.List<ItemStack> generateDefaultDrops() {
+ java.util.List<ItemStack> dropEntities = new java.util.ArrayList<>(5);
+ for (int i = 0; i < 5; ++i) {
+ dropEntities.add(new ItemStack(this.getVariant().getBlockState().getBlock()));
+ }
+ return dropEntities;
+ }
+
+ @Override
+ public void shear(SoundSource shearedSoundCategory, java.util.List<ItemStack> drops) { // If drops is null, need to generate drops
+ // Paper end - custom shear drops
this.level().playSound((Player) null, (Entity) this, SoundEvents.MOOSHROOM_SHEAR, shearedSoundCategory, 1.0F, 1.0F);
if (!this.level().isClientSide()) {
Cow entitycow = (Cow) EntityType.COW.create(this.level());
@@ -193,17 +216,12 @@ public class MushroomCow extends Cow implements Shearable, VariantHolder<Mushroo
this.discard(EntityRemoveEvent.Cause.TRANSFORMATION); // CraftBukkit - from above and add Bukkit remove cause
// CraftBukkit end
- for (int i = 0; i < 5; ++i) {
- // CraftBukkit start
- ItemEntity entityitem = new ItemEntity(this.level(), this.getX(), this.getY(1.0D), this.getZ(), new ItemStack(this.getVariant().blockState.getBlock()));
- EntityDropItemEvent event = new EntityDropItemEvent(this.getBukkitEntity(), (org.bukkit.entity.Item) entityitem.getBukkitEntity());
- Bukkit.getPluginManager().callEvent(event);
- if (event.isCancelled()) {
- continue;
- }
- this.level().addFreshEntity(entityitem);
- // CraftBukkit end
+ // Paper start - custom shear drops; moved drop generation to separate method
+ for (final ItemStack drop : drops) {
+ ItemEntity entityitem = new ItemEntity(this.level(), this.getX(), this.getY(1.0D), this.getZ(), drop);
+ this.spawnAtLocation(entityitem);
}
+ // Paper end - custom shear drops
}
}
diff --git a/src/main/java/net/minecraft/world/entity/animal/Sheep.java b/src/main/java/net/minecraft/world/entity/animal/Sheep.java
index 784a89f39e0b243e2968dca33b60ae40e58d824e..3ce86f952a18cae7fda1903916903b31a63a40b4 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Sheep.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Sheep.java
@@ -258,11 +258,18 @@ public class Sheep extends Animal implements Shearable {
if (itemstack.is(Items.SHEARS)) {
if (!this.level().isClientSide && this.readyForShearing()) {
// CraftBukkit start
- if (!CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand)) {
- return InteractionResult.PASS;
+ // Paper start - custom shear drops
+ java.util.List<ItemStack> drops = this.generateDefaultDrops();
+ org.bukkit.event.player.PlayerShearEntityEvent event = CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand, drops);
+ if (event != null) {
+ if (event.isCancelled()) {
+ return InteractionResult.PASS;
+ }
+ drops = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getDrops());
}
+ // Paper end - custom shear drops
// CraftBukkit end
- this.shear(SoundSource.PLAYERS);
+ this.shear(SoundSource.PLAYERS, drops); // Paper
this.gameEvent(GameEvent.SHEAR, player);
itemstack.hurtAndBreak(1, player, getSlotForHand(hand));
return InteractionResult.SUCCESS;
@@ -276,13 +283,30 @@ public class Sheep extends Animal implements Shearable {
@Override
public void shear(SoundSource shearedSoundCategory) {
+ // Paper start - custom shear drops
+ this.shear(shearedSoundCategory, this.generateDefaultDrops());
+ }
+
+ @Override
+ public java.util.List<ItemStack> generateDefaultDrops() {
+ int count = 1 + this.random.nextInt(3);
+ java.util.List<ItemStack> dropEntities = new java.util.ArrayList<>(count);
+ for (int j = 0; j < count; ++j) {
+ dropEntities.add(new ItemStack(Sheep.ITEM_BY_DYE.get(this.getColor())));
+ }
+ return dropEntities;
+ }
+
+ @Override
+ public void shear(SoundSource shearedSoundCategory, java.util.List<ItemStack> drops) {
+ // Paper end - custom shear drops
this.level().playSound((Player) null, (Entity) this, SoundEvents.SHEEP_SHEAR, shearedSoundCategory, 1.0F, 1.0F);
this.setSheared(true);
int i = 1 + this.random.nextInt(3);
- for (int j = 0; j < i; ++j) {
+ for (final ItemStack drop : drops) { // Paper - custom shear drops (moved drop generation to separate method)
this.forceDrops = true; // CraftBukkit
- ItemEntity entityitem = this.spawnAtLocation((ItemLike) Sheep.ITEM_BY_DYE.get(this.getColor()), 1);
+ ItemEntity entityitem = this.spawnAtLocation(drop, 1); // Paper - custom shear drops
this.forceDrops = false; // CraftBukkit
if (entityitem != null) {
diff --git a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java
index 2de1a2f666da9db1832907e1651dbff948e37252..5c2ed3c39c8eb850f3be1e2ea5b5a7ea266e16d1 100644
--- a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java
+++ b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java
@@ -146,11 +146,18 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM
if (itemstack.is(Items.SHEARS) && this.readyForShearing()) {
// CraftBukkit start
- if (!CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand)) {
- return InteractionResult.PASS;
+ // Paper start - custom shear drops
+ java.util.List<ItemStack> drops = this.generateDefaultDrops();
+ org.bukkit.event.player.PlayerShearEntityEvent event = CraftEventFactory.handlePlayerShearEntityEvent(player, this, itemstack, hand, drops);
+ if (event != null) {
+ if (event.isCancelled()) {
+ return InteractionResult.PASS;
+ }
+ drops = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getDrops());
}
+ // Paper end - custom shear drops
// CraftBukkit end
- this.shear(SoundSource.PLAYERS);
+ this.shear(SoundSource.PLAYERS, drops); // Paper
this.gameEvent(GameEvent.SHEAR, player);
if (!this.level().isClientSide) {
itemstack.hurtAndBreak(1, player, getSlotForHand(hand));
@@ -164,12 +171,28 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM
@Override
public void shear(SoundSource shearedSoundCategory) {
+ // Paper start - custom shear drops
+ this.shear(shearedSoundCategory, this.generateDefaultDrops());
+ }
+
+ @Override
+ public java.util.List<ItemStack> generateDefaultDrops() {
+ return java.util.Collections.singletonList(new ItemStack(Items.CARVED_PUMPKIN));
+ }
+
+ @Override
+ public void shear(SoundSource shearedSoundCategory, java.util.List<ItemStack> drops) {
+ // Paper end - custom shear drops
this.level().playSound((Player) null, (Entity) this, SoundEvents.SNOW_GOLEM_SHEAR, shearedSoundCategory, 1.0F, 1.0F);
if (!this.level().isClientSide()) {
this.setPumpkin(false);
- this.forceDrops = true; // CraftBukkit
- this.spawnAtLocation(new ItemStack(Items.CARVED_PUMPKIN), this.getEyeHeight());
- this.forceDrops = false; // CraftBukkit
+ // Paper start - custom shear drops (moved drop generation to separate method)
+ for (final ItemStack drop : drops) {
+ this.forceDrops = true;
+ this.spawnAtLocation(drop, this.getEyeHeight());
+ this.forceDrops = false;
+ }
+ // Paper end - custom shear drops
}
}
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 9c0bfddd479eef688d2509ad700371c3c860ff1a..e9f9b041ae7195e9d23bd446454b1d8c47a1ace1 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Bogged.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Bogged.java
@@ -135,6 +135,15 @@ public class Bogged extends AbstractSkeleton implements Shearable {
}
private void spawnShearedMushrooms() {
+ // Paper start - shear drops API
+ this.generateShearedMushrooms(stack -> {
+ this.forceDrops = true;
+ this.spawnAtLocation(stack);
+ this.forceDrops = false;
+ });
+ }
+ private void generateShearedMushrooms(java.util.function.Consumer<ItemStack> stackConsumer) {
+ // Paper end - shear drops API
if (this.level() instanceof ServerLevel serverLevel && serverLevel.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) {
LootTable lootTable = serverLevel.getServer().reloadableRegistries().getLootTable(BuiltInLootTables.BOGGED_SHEAR);
LootParams lootParams = new LootParams.Builder(serverLevel)
@@ -143,11 +152,20 @@ public class Bogged extends AbstractSkeleton implements Shearable {
.create(LootContextParamSets.SHEARING);
for (ItemStack itemStack : lootTable.getRandomItems(lootParams)) {
- this.spawnAtLocation(itemStack);
+ stackConsumer.accept(itemStack); // Paper
}
}
}
+ // Paper start - shear drops API
+ @Override
+ public java.util.List<ItemStack> generateDefaultDrops() {
+ final java.util.List<ItemStack> drops = new java.util.ArrayList<>();
+ this.generateShearedMushrooms(drops::add);
+ return drops;
+ }
+ // Paper end - shear drops API
+
@Override
public boolean readyForShearing() {
return !this.isSheared() && this.isAlive();
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index 8b322ade83e8e571af7c2e91a3c0d0784a7b9fad..e20feab675d05ae5b5b19870546585936c757970 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -1666,20 +1666,20 @@ public class CraftEventFactory {
player.level().getCraftServer().getPluginManager().callEvent(event);
}
- public static BlockShearEntityEvent callBlockShearEntityEvent(Entity animal, org.bukkit.block.Block dispenser, CraftItemStack is) {
- BlockShearEntityEvent bse = new BlockShearEntityEvent(dispenser, animal.getBukkitEntity(), is);
+ public static BlockShearEntityEvent callBlockShearEntityEvent(Entity animal, org.bukkit.block.Block dispenser, CraftItemStack is, List<ItemStack> drops) { // Paper - custom shear drops
+ BlockShearEntityEvent bse = new BlockShearEntityEvent(dispenser, animal.getBukkitEntity(), is, Lists.transform(drops, CraftItemStack::asCraftMirror)); // Paper - custom shear drops
Bukkit.getPluginManager().callEvent(bse);
return bse;
}
- public static boolean handlePlayerShearEntityEvent(net.minecraft.world.entity.player.Player player, Entity sheared, ItemStack shears, InteractionHand hand) {
+ public static PlayerShearEntityEvent handlePlayerShearEntityEvent(net.minecraft.world.entity.player.Player player, Entity sheared, ItemStack shears, InteractionHand hand, List<ItemStack> drops) { // Paper - custom shear drops
if (!(player instanceof ServerPlayer)) {
- return true;
+ return null; // Paper - custom shear drops
}
- PlayerShearEntityEvent event = new PlayerShearEntityEvent((Player) player.getBukkitEntity(), sheared.getBukkitEntity(), CraftItemStack.asCraftMirror(shears), (hand == InteractionHand.OFF_HAND ? EquipmentSlot.OFF_HAND : EquipmentSlot.HAND));
+ PlayerShearEntityEvent event = new PlayerShearEntityEvent((Player) player.getBukkitEntity(), sheared.getBukkitEntity(), CraftItemStack.asCraftMirror(shears), (hand == InteractionHand.OFF_HAND ? EquipmentSlot.OFF_HAND : EquipmentSlot.HAND), Lists.transform(drops, CraftItemStack::asCraftMirror)); // Paper - custom shear drops
Bukkit.getPluginManager().callEvent(event);
- return !event.isCancelled();
+ return event; // Paper - custom shear drops
}
public static Cancellable handleStatisticsIncrease(net.minecraft.world.entity.player.Player entityHuman, net.minecraft.stats.Stat<?> statistic, int current, int newValue) {
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
index 776437ae42865baccab4fb5d3f2715c8f0519b8a..520b637e0f281d3d3018681ec7b48b06c47f621e 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
@@ -65,6 +65,16 @@ public final class CraftItemStack extends ItemStack {
return stack;
}
+ // Paper start
+ public static java.util.List<net.minecraft.world.item.ItemStack> asNMSCopy(java.util.List<? extends ItemStack> originals) {
+ final java.util.List<net.minecraft.world.item.ItemStack> items = new java.util.ArrayList<>(originals.size());
+ for (final ItemStack original : originals) {
+ items.add(asNMSCopy(original));
+ }
+ return items;
+ }
+ // Paper end
+
public static net.minecraft.world.item.ItemStack copyNMSStack(net.minecraft.world.item.ItemStack original, int amount) {
net.minecraft.world.item.ItemStack stack = original.copy();
stack.setCount(amount);
diff --git a/src/test/java/io/papermc/paper/entity/ShearableDropsTest.java b/src/test/java/io/papermc/paper/entity/ShearableDropsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e612515f7709dbe5d1fa5751337cdc34fce10a98
--- /dev/null
+++ b/src/test/java/io/papermc/paper/entity/ShearableDropsTest.java
@@ -0,0 +1,34 @@
+package io.papermc.paper.entity;
+
+import io.github.classgraph.ClassGraph;
+import io.github.classgraph.ClassInfo;
+import io.github.classgraph.MethodInfoList;
+import io.github.classgraph.ScanResult;
+import java.util.ArrayList;
+import net.minecraft.world.entity.Shearable;
+import org.bukkit.support.AbstractTestingBase;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class ShearableDropsTest extends AbstractTestingBase {
+
+ static Iterable<ClassInfo> parameters() {
+ try (ScanResult scanResult = new ClassGraph()
+ .enableClassInfo()
+ .enableMethodInfo()
+ .whitelistPackages("net.minecraft")
+ .scan()
+ ) {
+ return new ArrayList<>(scanResult.getClassesImplementing(Shearable.class.getName()));
+ }
+ }
+
+ @ParameterizedTest
+ @MethodSource("parameters")
+ void checkShearableDropOverrides(final ClassInfo classInfo) {
+ final MethodInfoList generateDefaultDrops = classInfo.getDeclaredMethodInfo("generateDefaultDrops");
+ assertEquals(1, generateDefaultDrops.size(), classInfo.getName() + " doesn't implement Shearable#generateDefaultDrops");
+ }
+}

View file

@ -1,53 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Cryptite <cryptite@gmail.com>
Date: Mon, 1 May 2023 16:22:43 -0500
Subject: [PATCH] Add PlayerShieldDisableEvent
Called whenever a players shield is disabled. This is mainly caused by
attacking players or monsters that carry axes.
The event, while similar to the PlayerItemCooldownEvent, offers other
behaviour and can hence not be implemented as a childtype of said event.
Specifically, cancelling the event prevents the game events from being
sent to the player.
Plugins listening to just the PlayerItemCooldownEvent may not want said
sideeffects, meaning the disable event cannot share a handlerlist with
the cooldown event
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 32f6c029bf2f9a16d48ac73ff217c7462c64f175..9952da86af1a1a050e7485675ca2d6fb83d02422 100644
--- a/src/main/java/net/minecraft/world/entity/player/Player.java
+++ b/src/main/java/net/minecraft/world/entity/player/Player.java
@@ -989,7 +989,7 @@ public abstract class Player extends LivingEntity {
protected void blockUsingShield(LivingEntity attacker) {
super.blockUsingShield(attacker);
if (attacker.canDisableShield()) {
- this.disableShield();
+ this.disableShield(attacker); // Paper - Add PlayerShieldDisableEvent
}
}
@@ -1473,8 +1473,21 @@ public abstract class Player extends LivingEntity {
this.attack(target);
}
+ @io.papermc.paper.annotation.DoNotUse @Deprecated // Paper - Add PlayerShieldDisableEvent
public void disableShield() {
- this.getCooldowns().addCooldown(Items.SHIELD, 100);
+ // Paper start - Add PlayerShieldDisableEvent
+ this.disableShield(null);
+ }
+ public void disableShield(@Nullable LivingEntity attacker) {
+ final org.bukkit.entity.Entity finalAttacker = attacker != null ? attacker.getBukkitEntity() : null;
+ if (finalAttacker != null) {
+ final io.papermc.paper.event.player.PlayerShieldDisableEvent shieldDisableEvent = new io.papermc.paper.event.player.PlayerShieldDisableEvent((org.bukkit.entity.Player) getBukkitEntity(), finalAttacker, 100);
+ if (!shieldDisableEvent.callEvent()) return;
+ this.getCooldowns().addCooldown(Items.SHIELD, shieldDisableEvent.getCooldown());
+ } else {
+ this.getCooldowns().addCooldown(Items.SHIELD, 100);
+ }
+ // Paper end - Add PlayerShieldDisableEvent
this.stopUsingItem();
this.level().broadcastEntityEvent(this, (byte) 30);
}

View file

@ -1,156 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Nassim Jahnke <nassim@njahnke.dev>
Date: Thu, 4 Jan 2024 13:49:14 +0100
Subject: [PATCH] Validate ResourceLocation in NBT reading
diff --git a/src/main/java/net/minecraft/nbt/NbtUtils.java b/src/main/java/net/minecraft/nbt/NbtUtils.java
index 1d7c20fe14882fdeddf315a8923669e3385652f5..f88dd37783b3c155c23b547c360b8d3c16e030c0 100644
--- a/src/main/java/net/minecraft/nbt/NbtUtils.java
+++ b/src/main/java/net/minecraft/nbt/NbtUtils.java
@@ -149,8 +149,10 @@ public final class NbtUtils {
if (!nbt.contains("Name", 8)) {
return Blocks.AIR.defaultBlockState();
} else {
- ResourceLocation resourceLocation = new ResourceLocation(nbt.getString("Name"));
- Optional<? extends Holder<Block>> optional = blockLookup.get(ResourceKey.create(Registries.BLOCK, resourceLocation));
+ // Paper start - Validate resource location
+ ResourceLocation resourceLocation = ResourceLocation.tryParse(nbt.getString("Name"));
+ Optional<? extends Holder<Block>> optional = resourceLocation != null ? blockLookup.get(ResourceKey.create(Registries.BLOCK, resourceLocation)) : Optional.empty();
+ // Paper end - Validate resource location
if (optional.isEmpty()) {
return Blocks.AIR.defaultBlockState();
} else {
diff --git a/src/main/java/net/minecraft/resources/ResourceLocation.java b/src/main/java/net/minecraft/resources/ResourceLocation.java
index d93b623ce973b63d4f3a77bfe459f51af7cb3c1c..2c4553312f2f37f8613ac813708b4b95f9675e9f 100644
--- a/src/main/java/net/minecraft/resources/ResourceLocation.java
+++ b/src/main/java/net/minecraft/resources/ResourceLocation.java
@@ -37,6 +37,13 @@ public class ResourceLocation implements Comparable<ResourceLocation> {
private final String path;
protected ResourceLocation(String namespace, String path, @Nullable ResourceLocation.Dummy extraData) {
+ // Paper start - Validate ResourceLocation
+ // Check for the max network string length (capped at Short.MAX_VALUE) as well as the max bytes of a StringTag (length written as an unsigned short)
+ final String resourceLocation = namespace + ":" + path;
+ if (resourceLocation.length() > Short.MAX_VALUE || io.netty.buffer.ByteBufUtil.utf8MaxBytes(resourceLocation) > 2 * Short.MAX_VALUE + 1) {
+ throw new ResourceLocationException("Resource location too long: " + resourceLocation);
+ }
+ // Paper end - Validate ResourceLocation
this.namespace = namespace;
this.path = path;
}
diff --git a/src/main/java/net/minecraft/world/RandomizableContainer.java b/src/main/java/net/minecraft/world/RandomizableContainer.java
index 68fadc351464dde459eb4cc86660cf9add40d04e..a9a80f8bc4a6f250fe3c20482c395058f024fabd 100644
--- a/src/main/java/net/minecraft/world/RandomizableContainer.java
+++ b/src/main/java/net/minecraft/world/RandomizableContainer.java
@@ -50,7 +50,7 @@ public interface RandomizableContainer extends Container {
default boolean tryLoadLootTable(CompoundTag nbt) {
if (nbt.contains("LootTable", 8)) {
- this.setLootTable(ResourceKey.create(Registries.LOOT_TABLE, new ResourceLocation(nbt.getString("LootTable"))));
+ this.setLootTable(net.minecraft.Optionull.map(ResourceLocation.tryParse(nbt.getString("LootTable")), rl -> ResourceKey.create(Registries.LOOT_TABLE, rl))); // Paper - Validate ResourceLocation
if (this.lootableData() != null && this.getLootTable() != null) this.lootableData().loadNbt(nbt); // Paper - LootTable API
if (nbt.contains("LootTableSeed", 4)) {
this.setLootTableSeed(nbt.getLong("LootTableSeed"));
diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java
index 8c7cc9c5af0b8d8bef9b6e2d3d3e723cd76f3212..56b51096ca4147363a843accf6ef2510f05e8f1a 100644
--- a/src/main/java/net/minecraft/world/entity/EntityType.java
+++ b/src/main/java/net/minecraft/world/entity/EntityType.java
@@ -623,7 +623,7 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
}
public static Optional<EntityType<?>> by(CompoundTag nbt) {
- return BuiltInRegistries.ENTITY_TYPE.getOptional(new ResourceLocation(nbt.getString("id")));
+ return BuiltInRegistries.ENTITY_TYPE.getOptional(ResourceLocation.tryParse(nbt.getString("id"))); // Paper - Validate ResourceLocation
}
@Nullable
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index b7af45eac986097ef53fa90bb4edd67f8829f13c..958b25e62b040943c346114ef19d56104ae0a844 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -911,12 +911,13 @@ public abstract class LivingEntity extends Entity implements Attackable {
if (nbt.contains("SleepingX", 99) && nbt.contains("SleepingY", 99) && nbt.contains("SleepingZ", 99)) {
BlockPos blockposition = new BlockPos(nbt.getInt("SleepingX"), nbt.getInt("SleepingY"), nbt.getInt("SleepingZ"));
-
+ if (this.position().distanceToSqr(blockposition.getX(), blockposition.getY(), blockposition.getZ()) < 16 * 16) { // Paper - The sleeping pos will always also set the actual pos, so a desync suggests something is wrong
this.setSleepingPos(blockposition);
this.entityData.set(LivingEntity.DATA_POSE, Pose.SLEEPING);
if (!this.firstTick) {
this.setPosToBed(blockposition);
}
+ } // Paper - The sleeping pos will always also set the actual pos, so a desync suggests something is wrong
}
if (nbt.contains("Brain", 10)) {
diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java
index 5581fd6656fc13ea1d036d6caab2aba39c5320ac..748a6d81f409cf91a7aa2c88ded04f1d428cf0f6 100644
--- a/src/main/java/net/minecraft/world/entity/Mob.java
+++ b/src/main/java/net/minecraft/world/entity/Mob.java
@@ -645,7 +645,7 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Targeti
this.setLeftHanded(nbt.getBoolean("LeftHanded"));
if (nbt.contains("DeathLootTable", 8)) {
- this.lootTable = ResourceKey.create(Registries.LOOT_TABLE, new ResourceLocation(nbt.getString("DeathLootTable")));
+ this.lootTable = net.minecraft.Optionull.map(ResourceLocation.tryParse(nbt.getString("DeathLootTable")), rl -> ResourceKey.create(Registries.LOOT_TABLE, rl)); // Paper - Validate ResourceLocation
this.lootTableSeed = nbt.getLong("DeathLootTableSeed");
}
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 f1bf129aecb6840d79c537338e4057557f07790b..5bc4ba7c055992a32197db96d86a3906c11900d8 100644
--- a/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java
+++ b/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java
@@ -561,7 +561,7 @@ public abstract class AbstractArrow extends Projectile {
this.setCritArrow(nbt.getBoolean("crit"));
this.setPierceLevel(nbt.getByte("PierceLevel"));
if (nbt.contains("SoundEvent", 8)) {
- this.soundEvent = (SoundEvent) BuiltInRegistries.SOUND_EVENT.getOptional(new ResourceLocation(nbt.getString("SoundEvent"))).orElse(this.getDefaultHitGroundSoundEvent());
+ this.soundEvent = (SoundEvent) BuiltInRegistries.SOUND_EVENT.getOptional(ResourceLocation.tryParse(nbt.getString("SoundEvent"))).orElse(this.getDefaultHitGroundSoundEvent()); // Paper - Validate resource location
}
this.setShotFromCrossbow(nbt.getBoolean("ShotFromCrossbow"));
diff --git a/src/main/java/net/minecraft/world/entity/vehicle/ContainerEntity.java b/src/main/java/net/minecraft/world/entity/vehicle/ContainerEntity.java
index d976a6e3a79a01392a5033b05864d82782a30916..845eff7401b811c179dc9dee70eca0d724be5c80 100644
--- a/src/main/java/net/minecraft/world/entity/vehicle/ContainerEntity.java
+++ b/src/main/java/net/minecraft/world/entity/vehicle/ContainerEntity.java
@@ -73,7 +73,7 @@ public interface ContainerEntity extends Container, MenuProvider {
default void readChestVehicleSaveData(CompoundTag nbt, HolderLookup.Provider registriesLookup) {
this.clearItemStacks();
if (nbt.contains("LootTable", 8)) {
- this.setLootTable(ResourceKey.create(Registries.LOOT_TABLE, new ResourceLocation(nbt.getString("LootTable"))));
+ this.setLootTable(net.minecraft.Optionull.map(ResourceLocation.tryParse(nbt.getString("LootTable")), rl -> ResourceKey.create(Registries.LOOT_TABLE, rl))); // Paper - Validate ResourceLocation
// Paper start - LootTable API
if (this.getLootTable() != null) {
this.lootableData().loadNbt(nbt);
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 f40b2582d9087f9dbb5cab950304698f33fdd879..a99fe191c429bb528209dd0f31b509acf9cccbb5 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
@@ -295,7 +295,12 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit
while (iterator.hasNext()) {
String s = (String) iterator.next();
- this.recipesUsed.put(new ResourceLocation(s), nbttagcompound1.getInt(s));
+ // Paper start - Validate ResourceLocation
+ final ResourceLocation resourceLocation = ResourceLocation.tryParse(s);
+ if (resourceLocation != null) {
+ this.recipesUsed.put(resourceLocation, nbttagcompound1.getInt(s));
+ }
+ // Paper end - Validate ResourceLocation
}
// Paper start - cook speed multiplier API
diff --git a/src/main/java/net/minecraft/world/level/block/entity/BrushableBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BrushableBlockEntity.java
index f78a9698afc87408fc46de2d3d00c923500885f4..dc02a3d84b397f634f77f4df9c06e245cc4dcb75 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/BrushableBlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/BrushableBlockEntity.java
@@ -202,7 +202,7 @@ public class BrushableBlockEntity extends BlockEntity {
private boolean tryLoadLootTable(CompoundTag nbt) {
if (nbt.contains("LootTable", 8)) {
- this.lootTable = ResourceKey.create(Registries.LOOT_TABLE, new ResourceLocation(nbt.getString("LootTable")));
+ this.lootTable = net.minecraft.Optionull.map(ResourceLocation.tryParse(nbt.getString("LootTable")), rl -> ResourceKey.create(Registries.LOOT_TABLE, rl)); // Paper - Validate ResourceLocation
this.lootTableSeed = nbt.getLong("LootTableSeed");
return true;
} else {

View file

@ -1,94 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com>
Date: Sat, 30 Dec 2023 15:00:06 -0500
Subject: [PATCH] Properly handle experience dropping on block break
This causes spawnAfterBreak to spawn xp by default, removing the need to manually add xp wherever this method is used.
For classes that use custom xp amounts, they can drop the resources with disabling
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
index 2f018922fb12dc4c3c9c28c7b0597a2b97d1cb02..51c4877ffb77ece41e90d9c6ac06c04dc941e35c 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -618,7 +618,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
if (drop) {
BlockEntity tileentity = iblockdata.hasBlockEntity() ? this.getBlockEntity(pos) : null;
- Block.dropResources(iblockdata, this, pos, tileentity, breakingEntity, ItemStack.EMPTY);
+ Block.dropResources(iblockdata, this, pos, tileentity, breakingEntity, ItemStack.EMPTY, false); // Paper - Properly handle xp dropping
+ iblockdata.getBlock().popExperience((ServerLevel) this, pos, xp, breakingEntity); // Paper - Properly handle xp dropping; custom amount
}
boolean flag1 = this.setBlock(pos, fluid.createLegacyBlock(), 3, maxUpdateDepth);
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 a757a45d423e1729c9374391df4186ae0522e1ac..7f0c0ca49e7575c18935b71e3180d112440289f7 100644
--- a/src/main/java/net/minecraft/world/level/block/Block.java
+++ b/src/main/java/net/minecraft/world/level/block/Block.java
@@ -313,23 +313,31 @@ public class Block extends BlockBehaviour implements ItemLike {
for (ItemStack drop : Block.getDrops(state, serverLevel, pos, blockEntity)) {
items.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(drop));
}
+ Block block = state.getBlock(); // Paper - Properly handle xp dropping
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.setExpToDrop(block.getExpDrop(state, serverLevel, pos, net.minecraft.world.item.ItemStack.EMPTY, true)); // Paper - Properly handle xp dropping
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);
+ state.spawnAfterBreak(serverLevel, pos, ItemStack.EMPTY, false); // Paper - Properly handle xp dropping
+ block.popExperience(serverLevel, pos, event.getExpToDrop()); // Paper - Properly handle xp dropping
}
return true;
}
// Paper end - Add BlockBreakBlockEvent
public static void dropResources(BlockState state, Level world, BlockPos pos, @Nullable BlockEntity blockEntity, @Nullable Entity entity, ItemStack tool) {
+ // Paper start - Properly handle xp dropping
+ dropResources(state, world, pos, blockEntity, entity, tool, true);
+ }
+ public static void dropResources(BlockState state, Level world, BlockPos pos, @Nullable BlockEntity blockEntity, @Nullable Entity entity, ItemStack tool, boolean dropExperience) {
+ // Paper end - Properly handle xp dropping
if (world instanceof ServerLevel) {
Block.getDrops(state, (ServerLevel) world, pos, blockEntity, entity, tool).forEach((itemstack1) -> {
Block.popResource(world, pos, itemstack1);
});
- state.spawnAfterBreak((ServerLevel) world, pos, tool, true);
+ state.spawnAfterBreak((ServerLevel) world, pos, tool, dropExperience); // Paper - Properly handle xp dropping
}
}
@@ -413,7 +421,7 @@ public class Block extends BlockBehaviour implements ItemLike {
player.awardStat(Stats.BLOCK_MINED.get(this));
player.causeFoodExhaustion(0.005F, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason.BLOCK_MINED); // CraftBukkit - EntityExhaustionEvent
if (includeDrops) { // Paper - fix drops not preventing stats/food exhaustion
- Block.dropResources(state, world, pos, blockEntity, player, tool);
+ Block.dropResources(state, world, pos, blockEntity, player, tool, dropExp); // Paper - Properly handle xp dropping
} // Paper - fix drops not preventing stats/food exhaustion
}
diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
index c762a006b3f586b32209c20e85f6b8bf169dbd06..f87d9cb38caf3bf92fd32f2118f76799ede418db 100644
--- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
+++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
@@ -1118,6 +1118,7 @@ public abstract class BlockBehaviour implements FeatureElement {
public void spawnAfterBreak(ServerLevel world, BlockPos pos, ItemStack tool, boolean dropExperience) {
this.getBlock().spawnAfterBreak(this.asState(), world, pos, tool, dropExperience);
+ if (dropExperience) {getBlock().popExperience(world, pos, this.getBlock().getExpDrop(asState(), world, pos, tool, true));} // Paper - Properly handle xp dropping
}
public List<ItemStack> getDrops(LootParams.Builder builder) {
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
index 461a66c323a74db5a70981fafc5fa20f54f0f40d..ac11f18690434922179b61ffcc3036dea025b0cb 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
@@ -509,7 +509,7 @@ public class CraftBlock implements Block {
// Modelled off EntityHuman#hasBlock
if (block != Blocks.AIR && (item == null || !iblockdata.requiresCorrectToolForDrops() || nmsItem.isCorrectToolForDrops(iblockdata))) {
- net.minecraft.world.level.block.Block.dropResources(iblockdata, this.world.getMinecraftWorld(), this.position, this.world.getBlockEntity(this.position), null, nmsItem);
+ net.minecraft.world.level.block.Block.dropResources(iblockdata, this.world.getMinecraftWorld(), this.position, this.world.getBlockEntity(this.position), null, nmsItem, false); // Paper - Properly handle xp dropping
// Paper start - improve Block#breanNaturally
if (triggerEffect) {
if (iblockdata.getBlock() instanceof net.minecraft.world.level.block.BaseFireBlock) {

View file

@ -1,170 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Nassim Jahnke <nassim@njahnke.dev>
Date: Sat, 6 Jan 2024 14:31:00 +0100
Subject: [PATCH] Fixup NamespacedKey handling
diff --git a/src/main/java/net/minecraft/world/inventory/LoomMenu.java b/src/main/java/net/minecraft/world/inventory/LoomMenu.java
index 4f3f6ea43030853bd9df067358a1f4d16c40e6d4..531336c44c46555fef8c001fe8ca00c93624ad42 100644
--- a/src/main/java/net/minecraft/world/inventory/LoomMenu.java
+++ b/src/main/java/net/minecraft/world/inventory/LoomMenu.java
@@ -171,12 +171,28 @@ public class LoomMenu extends AbstractContainerMenu {
return stillValid(this.access, player, Blocks.LOOM);
}
+ private static final org.slf4j.Logger LOGGER = com.mojang.logging.LogUtils.getLogger(); // Paper - handle custom banner pattern, skip the event
+ private static boolean PRINTED_PATTERN_TYPE_NAG = false; // Paper - handle custom banner pattern, skip the event
+
@Override
public boolean clickMenuButton(net.minecraft.world.entity.player.Player player, int id) {
if (id >= 0 && id < this.selectablePatterns.size()) {
+ // Paper start - handle custom banner pattern, skip the event (todo remove once this is supported)
+ java.util.Optional<org.bukkit.block.banner.PatternType> patternType = org.bukkit.craftbukkit.CraftRegistry.unwrapAndConvertHolder(org.bukkit.Registry.BANNER_PATTERN, this.selectablePatterns.get(id));
+ if (patternType.isEmpty()) {
+ if (!PRINTED_PATTERN_TYPE_NAG) {
+ LOGGER.warn("A datapack added a custom banner pattern, those are not supported yet in the API, skipping the PlayerLoomPatternSelectEvent for {}.", player.getScoreboardName());
+ PRINTED_PATTERN_TYPE_NAG = true;
+ }
+ this.selectedBannerPatternIndex.set(id);
+ this.setupResultSlot((Holder) this.selectablePatterns.get(id));
+ return true;
+ }
+ // Paper end - handle custom banner pattern
+
// 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))));
+ io.papermc.paper.event.player.PlayerLoomPatternSelectEvent event = new io.papermc.paper.event.player.PlayerLoomPatternSelectEvent((Player) player.getBukkitEntity(), ((CraftInventoryLoom) getBukkitView().getTopInventory()), patternType.get());
if (!event.callEvent()) {
player.containerMenu.sendAllDataToRemote();
return false;
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftLootTable.java b/src/main/java/org/bukkit/craftbukkit/CraftLootTable.java
index 209c6b64e79c29ea3bb84ddbe89a8bff66f81d0f..1f90f4b3f310b8cf5750c3a581be178f19b979d2 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftLootTable.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftLootTable.java
@@ -38,7 +38,7 @@ public class CraftLootTable implements org.bukkit.loot.LootTable {
}
public static org.bukkit.loot.LootTable minecraftToBukkit(ResourceKey<LootTable> minecraft) {
- return (minecraft == null) ? null : Bukkit.getLootTable(CraftLootTable.minecraftToBukkitKey(minecraft));
+ return (minecraft == null || minecraft.location().getPath().isEmpty()) ? null : Bukkit.getLootTable(CraftLootTable.minecraftToBukkitKey(minecraft)); // Paper - fix some NamespacedKey parsing
}
public static NamespacedKey minecraftToBukkitKey(ResourceKey<LootTable> minecraft) {
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java b/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
index 3adf18d5e736abff701a4866ee1f8403aeafca84..4a5778d1751b774c825bbce0e870e2998278afe3 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
@@ -110,6 +110,16 @@ 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.");
}
+ // Paper start - fixup upstream being dum
+ public static <T extends org.bukkit.Keyed, M> java.util.Optional<T> unwrapAndConvertHolder(final io.papermc.paper.registry.RegistryKey<T> registryKey, final Holder<M> value) {
+ return unwrapAndConvertHolder(io.papermc.paper.registry.RegistryAccess.registryAccess().getRegistry(registryKey), value);
+ }
+
+ public static <T extends org.bukkit.Keyed, M> java.util.Optional<T> unwrapAndConvertHolder(final Registry<T> registry, final Holder<M> value) {
+ return value.unwrapKey().map(key -> registry.get(CraftNamespacedKey.fromMinecraft(key.location())));
+ }
+ // Paper end - fixup upstream being dum
+
// Paper - move to PaperRegistries
// Paper - NOTE: As long as all uses of the method below relate to *serialization* via ConfigurationSerializable, it's fine
diff --git a/src/main/java/org/bukkit/craftbukkit/attribute/CraftAttribute.java b/src/main/java/org/bukkit/craftbukkit/attribute/CraftAttribute.java
index cc97638e038ea64ad180ebfded2528aa07d1809e..10e4318782107644f67818109784fff60d017e0a 100644
--- a/src/main/java/org/bukkit/craftbukkit/attribute/CraftAttribute.java
+++ b/src/main/java/org/bukkit/craftbukkit/attribute/CraftAttribute.java
@@ -37,6 +37,7 @@ public class CraftAttribute {
string = FieldRename.convertAttributeName(ApiVersion.CURRENT, string);
string = string.toLowerCase(Locale.ROOT);
NamespacedKey key = NamespacedKey.fromString(string);
+ if (key == null) return null; // Paper - Fixup NamespacedKey handling
// Now also convert from when keys where saved
return CraftRegistry.get(Registry.ATTRIBUTE, key, ApiVersion.CURRENT);
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBanner.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBanner.java
index 65a9213ce8197d50a58f94edfd60c25c2be848be..28d8fd2e3eb87e989621ffa6b0e5005bd181391c 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBanner.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBanner.java
@@ -36,7 +36,11 @@ public class CraftBanner extends CraftBlockEntityState<BannerBlockEntity> implem
if (banner.getPatterns() != null) {
for (int i = 0; i < banner.getPatterns().layers().size(); i++) {
BannerPatternLayers.Layer p = banner.getPatterns().layers().get(i);
- this.patterns.add(new Pattern(DyeColor.getByWoolData((byte) p.color().getId()), CraftPatternType.minecraftHolderToBukkit(p.pattern())));
+ // Paper start - fix upstream not handling custom banner pattern
+ java.util.Optional<org.bukkit.block.banner.PatternType> type = org.bukkit.craftbukkit.CraftRegistry.unwrapAndConvertHolder(org.bukkit.Registry.BANNER_PATTERN, p.pattern());
+ if (type.isEmpty()) continue;
+ this.patterns.add(new Pattern(DyeColor.getByWoolData((byte) p.color().getId()), type.get()));
+ // Paper end
}
}
}
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmor.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmor.java
index 12b95c4455e741b65b844eab362f02bce54eb525..13b91cddffbe8ae6f07ce5c0ae45beba151e1aca 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmor.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmor.java
@@ -69,8 +69,9 @@ public class CraftMetaArmor extends CraftMetaItem implements ArmorMeta {
super(tag);
getOrEmpty(tag, CraftMetaArmor.TRIM).ifPresent((trimCompound) -> {
- TrimMaterial trimMaterial = CraftTrimMaterial.minecraftHolderToBukkit(trimCompound.material());
- TrimPattern trimPattern = CraftTrimPattern.minecraftHolderToBukkit(trimCompound.pattern());
+ TrimMaterial trimMaterial = org.bukkit.craftbukkit.CraftRegistry.unwrapAndConvertHolder(io.papermc.paper.registry.RegistryKey.TRIM_MATERIAL, trimCompound.material()).orElse(null); // Paper - fix upstream not being correct
+ TrimPattern trimPattern = org.bukkit.craftbukkit.CraftRegistry.unwrapAndConvertHolder(io.papermc.paper.registry.RegistryKey.TRIM_PATTERN, trimCompound.pattern()).orElse(null); // Paper - fix upstream not being correct
+ if (trimMaterial == null || trimPattern == null) return; // Paper - just delete the trim because upstream is not doing this right
this.trim = new ArmorTrim(trimMaterial, trimPattern);
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBanner.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBanner.java
index 524aadad91c855f6c201999831824f7ce06f9ed6..d53df6f114c285b880167385807775e400c80fc9 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBanner.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBanner.java
@@ -80,7 +80,7 @@ public class CraftMetaBanner extends CraftMetaItem implements BannerMeta {
for (int i = 0; i < Math.min(patterns.size(), 20); i++) {
BannerPatternLayers.Layer p = patterns.get(i);
DyeColor color = DyeColor.getByWoolData((byte) p.color().getId());
- PatternType pattern = CraftPatternType.minecraftHolderToBukkit(p.pattern());
+ PatternType pattern = org.bukkit.craftbukkit.CraftRegistry.unwrapAndConvertHolder(org.bukkit.Registry.BANNER_PATTERN, p.pattern()).orElse(null); // Paper - fix upstream not handling custom banner pattern
if (color != null && pattern != null) {
this.patterns.add(new Pattern(color, pattern));
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMusicInstrument.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMusicInstrument.java
index 67905f804ccc102faa942499f5ba218f710ab9cc..4eb2993903f5fa9fb9fd65282a42f26b3aa1e7bd 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMusicInstrument.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMusicInstrument.java
@@ -31,7 +31,7 @@ public class CraftMetaMusicInstrument extends CraftMetaItem implements MusicInst
super(tag);
getOrEmpty(tag, CraftMetaMusicInstrument.GOAT_HORN_INSTRUMENT).ifPresent((instrument) -> {
- this.instrument = CraftMusicInstrument.minecraftHolderToBukkit(instrument);
+ this.instrument = org.bukkit.craftbukkit.CraftRegistry.unwrapAndConvertHolder(org.bukkit.Registry.INSTRUMENT, instrument).orElse(null); // Paper - fix upstream not handling custom instruments
});
}
diff --git a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionType.java b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionType.java
index 82a50b06c08b632f77d73745e1fa9bd22dfd950a..f1d8ed4a2b8959873b02d57f6a40323a841f3d7f 100644
--- a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionType.java
+++ b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionType.java
@@ -69,6 +69,7 @@ public class CraftPotionType implements PotionType.InternalPotionData {
string = FieldRename.convertPotionTypeName(ApiVersion.CURRENT, string);
string = string.toLowerCase(Locale.ROOT);
NamespacedKey key = NamespacedKey.fromString(string);
+ if (key == null) return null; // Paper - Fixup NamespacedKey handling
// Now also convert from when keys where saved
return CraftRegistry.get(Registry.POTION, key, ApiVersion.CURRENT);
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftNamespacedKey.java b/src/main/java/org/bukkit/craftbukkit/util/CraftNamespacedKey.java
index 5f40d240b879e3989897b6e45725a8e5a6a7f194..5014192edb9616ce725fc1592832034789527b6f 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftNamespacedKey.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftNamespacedKey.java
@@ -13,7 +13,7 @@ public final class CraftNamespacedKey {
return null;
}
ResourceLocation minecraft = ResourceLocation.tryParse(string);
- return (minecraft == null) ? null : CraftNamespacedKey.fromMinecraft(minecraft);
+ return (minecraft == null || minecraft.getPath().isEmpty()) ? null : CraftNamespacedKey.fromMinecraft(minecraft); // Paper - Bukkit's parser does not match Vanilla for empty paths
}
public static NamespacedKey fromString(String string) {

View file

@ -1,44 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: FireInstall <kettnerl@hu-berlin.de>
Date: Sat, 20 Jan 2024 16:20:06 +0100
Subject: [PATCH] Expose LootTable of DecoratedPot
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftDecoratedPot.java b/src/main/java/org/bukkit/craftbukkit/block/CraftDecoratedPot.java
index 6e0258d4d6a07e0f471640a9edda0adf7ef6cd9e..47cc3ec5ccd3bb51a08b3f179cb29030948b8c11 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftDecoratedPot.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftDecoratedPot.java
@@ -43,6 +43,33 @@ public class CraftDecoratedPot extends CraftBlockEntityState<DecoratedPotBlockEn
return new CraftInventoryDecoratedPot(this.getTileEntity());
}
+ // Paper start - expose loot table
+ @Override
+ public void setLootTable(final org.bukkit.loot.LootTable table) {
+ this.setLootTable(table, this.getSeed());
+ }
+
+ @Override
+ public void setLootTable(org.bukkit.loot.LootTable table, long seed) {
+ this.getSnapshot().setLootTable(org.bukkit.craftbukkit.CraftLootTable.bukkitToMinecraft(table), seed);
+ }
+
+ @Override
+ public org.bukkit.loot.LootTable getLootTable() {
+ return org.bukkit.craftbukkit.CraftLootTable.minecraftToBukkit(this.getSnapshot().getLootTable());
+ }
+
+ @Override
+ public void setSeed(final long seed) {
+ this.getSnapshot().setLootTableSeed(seed);
+ }
+
+ @Override
+ public long getSeed() {
+ return this.getSnapshot().getLootTableSeed();
+ }
+ // Paper end - expose loot table
+
@Override
public void setSherd(Side face, Material sherd) {
Preconditions.checkArgument(face != null, "face must not be null");

View file

@ -1,59 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Mon, 27 Apr 2020 00:04:16 -0700
Subject: [PATCH] Reduce allocation of Vec3D by entity tracker
diff --git a/src/main/java/net/minecraft/network/protocol/game/VecDeltaCodec.java b/src/main/java/net/minecraft/network/protocol/game/VecDeltaCodec.java
index a3d247c93ac1a2d872ff0e3841efc3d7b84fcbc1..23fe7c4c1844d321efed6b7b886c422761ad6594 100644
--- a/src/main/java/net/minecraft/network/protocol/game/VecDeltaCodec.java
+++ b/src/main/java/net/minecraft/network/protocol/game/VecDeltaCodec.java
@@ -5,7 +5,7 @@ import org.jetbrains.annotations.VisibleForTesting;
public class VecDeltaCodec {
private static final double TRUNCATION_STEPS = 4096.0;
- private Vec3 base = Vec3.ZERO;
+ public Vec3 base = Vec3.ZERO; // Paper
@VisibleForTesting
static long encode(double value) {
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 027f2734b0ff226437f4c94430215929b6ea2219..35f627c58e93c03ee58b44877398432bba57dc2d 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -1748,10 +1748,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
public void updatePlayer(ServerPlayer player) {
org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot
if (player != this.entity) {
- Vec3 vec3d = player.position().subtract(this.entity.position());
+ // Paper start - remove allocation of Vec3D here
+ // Vec3 vec3d = player.position().subtract(this.entity.position());
+ double vec3d_dx = player.getX() - this.entity.getX();
+ double vec3d_dz = player.getZ() - this.entity.getZ();
+ // Paper end - remove allocation of Vec3D here
int i = ChunkMap.this.getPlayerViewDistance(player);
double d0 = (double) Math.min(this.getEffectiveRange(), i * 16);
- double d1 = vec3d.x * vec3d.x + vec3d.z * vec3d.z;
+ double d1 = vec3d_dx * vec3d_dx + vec3d_dz * vec3d_dz; // Paper
double d2 = d0 * d0;
boolean flag = d1 <= d2 && this.entity.broadcastToPlayer(player) && ChunkMap.this.isChunkTracked(player, this.entity.chunkPosition().x, this.entity.chunkPosition().z);
// Paper start - Configurable entity tracking range by Y
diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java
index 0373b2de2b992b648f346d6df3960a3586a54567..22eec853588ded2d255ab69d408f8e987832abe2 100644
--- a/src/main/java/net/minecraft/server/level/ServerEntity.java
+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java
@@ -164,7 +164,13 @@ public class ServerEntity {
i = Mth.floor(this.entity.getYRot() * 256.0F / 360.0F);
j = Mth.floor(this.entity.getXRot() * 256.0F / 360.0F);
Vec3 vec3d = this.entity.trackingPosition();
- boolean flag1 = this.positionCodec.delta(vec3d).lengthSqr() >= 7.62939453125E-6D;
+ // Paper start - reduce allocation of Vec3D here
+ Vec3 base = this.positionCodec.base;
+ double vec3d_dx = vec3d.x - base.x;
+ double vec3d_dy = vec3d.y - base.y;
+ double vec3d_dz = vec3d.z - base.z;
+ boolean flag1 = (vec3d_dx * vec3d_dx + vec3d_dy * vec3d_dy + vec3d_dz * vec3d_dz) >= 7.62939453125E-6D;
+ // Paper end - reduce allocation of Vec3D here
Packet<?> packet1 = null;
boolean flag2 = flag1 || this.tickCount % 60 == 0;
boolean flag3 = Math.abs(i - this.yRotp) >= 1 || Math.abs(j - this.xRotp) >= 1;

View file

@ -1,240 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Thu, 2 Jul 2020 16:12:10 -0700
Subject: [PATCH] Add PlayerTradeEvent and PlayerPurchaseEvent
Co-authored-by: Alexander <protonull@protonmail.com>
diff --git a/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java b/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java
index 6e29b5729b8184d13065c9c4e18f6e450c9d88a6..d323cf157f2a910916baa9ce3f7e5bc81648c47d 100644
--- a/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java
+++ b/src/main/java/net/minecraft/world/entity/npc/AbstractVillager.java
@@ -139,11 +139,24 @@ public abstract class AbstractVillager extends AgeableMob implements InventoryCa
@Override
public void overrideXp(int experience) {}
+ // Paper start - Add PlayerTradeEvent and PlayerPurchaseEvent
+ @Override
+ public void processTrade(MerchantOffer recipe, @Nullable io.papermc.paper.event.player.PlayerPurchaseEvent event) { // The MerchantRecipe passed in here is the one set by the PlayerPurchaseEvent
+ if (event == null || event.willIncreaseTradeUses()) {
+ recipe.increaseUses();
+ }
+ if (event == null || event.isRewardingExp()) {
+ this.rewardTradeXp(recipe);
+ }
+ this.notifyTrade(recipe);
+ }
+ // Paper end - Add PlayerTradeEvent and PlayerPurchaseEvent
+
@Override
public void notifyTrade(MerchantOffer offer) {
- offer.increaseUses();
+ // offer.increaseUses(); // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent
this.ambientSoundTime = -this.getAmbientSoundInterval();
- this.rewardTradeXp(offer);
+ // this.rewardTradeXp(offer); // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent
if (this.tradingPlayer instanceof ServerPlayer) {
CriteriaTriggers.TRADE.trigger((ServerPlayer) this.tradingPlayer, this, offer.getResult());
}
diff --git a/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java b/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java
index 4c7e91977fa590abfe7eb3704d8008ed6d4e3ab3..32910f677b0522ac8ec513fa0d00b714b52cfae4 100644
--- a/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java
+++ b/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java
@@ -768,6 +768,14 @@ public abstract class AbstractContainerMenu {
public abstract boolean stillValid(Player player);
protected boolean moveItemStackTo(ItemStack stack, int startIndex, int endIndex, boolean fromLast) {
+ // Paper start - Add PlayerTradeEvent and PlayerPurchaseEvent
+ return this.moveItemStackTo(stack, startIndex, endIndex, fromLast, false);
+ }
+ protected boolean moveItemStackTo(ItemStack stack, int startIndex, int endIndex, boolean fromLast, boolean isCheck) {
+ if (isCheck) {
+ stack = stack.copy();
+ }
+ // Paper end - Add PlayerTradeEvent and PlayerPurchaseEvent
boolean flag1 = false;
int k = startIndex;
@@ -791,6 +799,11 @@ public abstract class AbstractContainerMenu {
slot = (Slot) this.slots.get(k);
itemstack1 = slot.getItem();
+ // Paper start - Add PlayerTradeEvent and PlayerPurchaseEvent; clone if only a check
+ if (isCheck) {
+ itemstack1 = itemstack1.copy();
+ }
+ // Paper end - Add PlayerTradeEvent and PlayerPurchaseEvent
if (!itemstack1.isEmpty() && ItemStack.isSameItemSameComponents(stack, itemstack1)) {
l = itemstack1.getCount() + stack.getCount();
int i1 = slot.getMaxStackSize(itemstack1);
@@ -798,12 +811,16 @@ public abstract class AbstractContainerMenu {
if (l <= i1) {
stack.setCount(0);
itemstack1.setCount(l);
+ if (!isCheck) { // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent
slot.setChanged();
+ } // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent
flag1 = true;
} else if (itemstack1.getCount() < i1) {
stack.shrink(i1 - itemstack1.getCount());
itemstack1.setCount(i1);
+ if (!isCheck) { // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent
slot.setChanged();
+ } // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent
flag1 = true;
}
}
@@ -834,10 +851,21 @@ public abstract class AbstractContainerMenu {
slot = (Slot) this.slots.get(k);
itemstack1 = slot.getItem();
+ // Paper start - Add PlayerTradeEvent and PlayerPurchaseEvent
+ if (isCheck) {
+ itemstack1 = itemstack1.copy();
+ }
+ // Paper end - Add PlayerTradeEvent and PlayerPurchaseEvent
if (itemstack1.isEmpty() && slot.mayPlace(stack)) {
l = slot.getMaxStackSize(stack);
+ // Paper start - Add PlayerTradeEvent and PlayerPurchaseEvent
+ if (isCheck) {
+ stack.shrink(Math.min(stack.getCount(), l));
+ } else {
+ // Paper end - Add PlayerTradeEvent and PlayerPurchaseEvent
slot.setByPlayer(stack.split(Math.min(stack.getCount(), l)));
slot.setChanged();
+ } // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent
flag1 = true;
break;
}
diff --git a/src/main/java/net/minecraft/world/inventory/MerchantMenu.java b/src/main/java/net/minecraft/world/inventory/MerchantMenu.java
index ecefd4075c097e2118ec23e87baf36465c40f85f..2992e86f5f83431e230162380b33721df785ba91 100644
--- a/src/main/java/net/minecraft/world/inventory/MerchantMenu.java
+++ b/src/main/java/net/minecraft/world/inventory/MerchantMenu.java
@@ -135,12 +135,12 @@ public class MerchantMenu extends AbstractContainerMenu {
itemstack = itemstack1.copy();
if (slot == 2) {
- if (!this.moveItemStackTo(itemstack1, 3, 39, true)) {
+ if (!this.moveItemStackTo(itemstack1, 3, 39, true, true)) { // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent
return ItemStack.EMPTY;
}
- slot1.onQuickCraft(itemstack1, itemstack);
- this.playTradeSound();
+ // slot1.onQuickCraft(itemstack1, itemstack); // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent; moved to after the non-check moveItemStackTo call
+ // this.playTradeSound();
} else if (slot != 0 && slot != 1) {
if (slot >= 3 && slot < 30) {
if (!this.moveItemStackTo(itemstack1, 30, 39, false)) {
@@ -153,6 +153,7 @@ public class MerchantMenu extends AbstractContainerMenu {
return ItemStack.EMPTY;
}
+ if (slot != 2) { // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent; moved down for slot 2
if (itemstack1.isEmpty()) {
slot1.setByPlayer(ItemStack.EMPTY);
} else {
@@ -164,6 +165,21 @@ public class MerchantMenu extends AbstractContainerMenu {
}
slot1.onTake(player, itemstack1);
+ } // Paper start - Add PlayerTradeEvent and PlayerPurchaseEvent; handle slot 2
+ if (slot == 2) { // is merchant result slot
+ slot1.onTake(player, itemstack1);
+ if (itemstack1.isEmpty()) {
+ slot1.set(ItemStack.EMPTY);
+ return ItemStack.EMPTY;
+ }
+
+ this.moveItemStackTo(itemstack1, 3, 39, true, false); // This should always succeed because it's checked above
+
+ slot1.onQuickCraft(itemstack1, itemstack);
+ this.playTradeSound();
+ slot1.set(ItemStack.EMPTY); // itemstack1 should ALWAYS be empty
+ }
+ // Paper end - Add PlayerTradeEvent and PlayerPurchaseEvent
}
return itemstack;
diff --git a/src/main/java/net/minecraft/world/inventory/MerchantResultSlot.java b/src/main/java/net/minecraft/world/inventory/MerchantResultSlot.java
index 26227033613a641a9d5a6f29356b19e54753b3f1..c2376538215604210d08acd33e8e5fdc8a35c7d3 100644
--- a/src/main/java/net/minecraft/world/inventory/MerchantResultSlot.java
+++ b/src/main/java/net/minecraft/world/inventory/MerchantResultSlot.java
@@ -47,13 +47,32 @@ public class MerchantResultSlot extends Slot {
@Override
public void onTake(Player player, ItemStack stack) {
- this.checkTakeAchievements(stack);
+ // this.checkTakeAchievements(stack); // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent; move to after event is called and not cancelled
MerchantOffer merchantOffer = this.slots.getActiveOffer();
+ // Paper start - Add PlayerTradeEvent and PlayerPurchaseEvent
+ io.papermc.paper.event.player.PlayerPurchaseEvent event = null;
+ if (merchantOffer != null && player instanceof net.minecraft.server.level.ServerPlayer serverPlayer) {
+ if (this.merchant instanceof net.minecraft.world.entity.npc.AbstractVillager abstractVillager) {
+ event = new io.papermc.paper.event.player.PlayerTradeEvent(serverPlayer.getBukkitEntity(), (org.bukkit.entity.AbstractVillager) abstractVillager.getBukkitEntity(), merchantOffer.asBukkit(), true, true);
+ } else if (this.merchant instanceof org.bukkit.craftbukkit.inventory.CraftMerchantCustom.MinecraftMerchant) {
+ event = new io.papermc.paper.event.player.PlayerPurchaseEvent(serverPlayer.getBukkitEntity(), merchantOffer.asBukkit(), false, true);
+ }
+ if (event != null) {
+ if (!event.callEvent()) {
+ stack.setCount(0);
+ event.getPlayer().updateInventory();
+ return;
+ }
+ merchantOffer = org.bukkit.craftbukkit.inventory.CraftMerchantRecipe.fromBukkit(event.getTrade()).toMinecraft();
+ }
+ }
+ this.checkTakeAchievements(stack);
+ // Paper end - Add PlayerTradeEvent and PlayerPurchaseEvent
if (merchantOffer != null) {
ItemStack itemStack = this.slots.getItem(0);
ItemStack itemStack2 = this.slots.getItem(1);
if (merchantOffer.take(itemStack, itemStack2) || merchantOffer.take(itemStack2, itemStack)) {
- this.merchant.notifyTrade(merchantOffer);
+ this.merchant.processTrade(merchantOffer, event); // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent
player.awardStat(Stats.TRADED_WITH_VILLAGER);
this.slots.setItem(0, itemStack);
this.slots.setItem(1, itemStack2);
diff --git a/src/main/java/net/minecraft/world/item/trading/Merchant.java b/src/main/java/net/minecraft/world/item/trading/Merchant.java
index 5a350948a4735902f5c612592bc9d100445a0c8a..716b30dcd7e63c66736c448dd136c9f74dc7fe43 100644
--- a/src/main/java/net/minecraft/world/item/trading/Merchant.java
+++ b/src/main/java/net/minecraft/world/item/trading/Merchant.java
@@ -20,6 +20,7 @@ public interface Merchant {
void overrideOffers(MerchantOffers offers);
+ default void processTrade(MerchantOffer merchantRecipe, @Nullable io.papermc.paper.event.player.PlayerPurchaseEvent event) { this.notifyTrade(merchantRecipe); } // Paper
void notifyTrade(MerchantOffer offer);
void notifyTradeUpdated(ItemStack stack);
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchantCustom.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchantCustom.java
index adf22ce4f0bcd3bd57dc2030c6c92d3df96566e3..e33ddcd967a427abfda9e6692338da4996a81c6c 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchantCustom.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMerchantCustom.java
@@ -74,10 +74,25 @@ public class CraftMerchantCustom extends CraftMerchant {
return this.trades;
}
+ // Paper start - Add PlayerTradeEvent and PlayerPurchaseEvent
+ @Override
+ public void processTrade(MerchantOffer merchantRecipe, @javax.annotation.Nullable io.papermc.paper.event.player.PlayerPurchaseEvent event) { // The MerchantRecipe passed in here is the one set by the PlayerPurchaseEvent
+ /** Based on {@link net.minecraft.world.entity.npc.AbstractVillager#processTrade(MerchantOffer, io.papermc.paper.event.player.PlayerPurchaseEvent)} */
+ if (getTradingPlayer() instanceof net.minecraft.server.level.ServerPlayer) {
+ if (event == null || event.willIncreaseTradeUses()) {
+ merchantRecipe.increaseUses();
+ }
+ if (event == null || event.isRewardingExp()) {
+ this.tradingPlayer.level().addFreshEntity(new net.minecraft.world.entity.ExperienceOrb(this.tradingPlayer.level(), this.tradingPlayer.getX(), this.tradingPlayer.getY(), this.tradingPlayer.getZ(), merchantRecipe.getXp(), org.bukkit.entity.ExperienceOrb.SpawnReason.VILLAGER_TRADE, this.tradingPlayer, null));
+ }
+ }
+ this.notifyTrade(merchantRecipe);
+ }
+ // Paper end - Add PlayerTradeEvent and PlayerPurchaseEvent
@Override
public void notifyTrade(MerchantOffer offer) {
// increase recipe's uses
- offer.increaseUses();
+ // offer.increaseUses(); // Paper - Add PlayerTradeEvent and PlayerPurchaseEvent; handled above in processTrade
}
@Override

View file

@ -1,22 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Chase Henderson <henderson.chase@gmail.com>
Date: Fri, 5 Jan 2024 03:50:10 -0500
Subject: [PATCH] Add ShulkerDuplicateEvent
diff --git a/src/main/java/net/minecraft/world/entity/monster/Shulker.java b/src/main/java/net/minecraft/world/entity/monster/Shulker.java
index bd6171d75fbf4a0debef3892d6f67356dc811b4d..e03119f88719c8d6d44793a6b3706ae97b2da307 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Shulker.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Shulker.java
@@ -491,6 +491,11 @@ public class Shulker extends AbstractGolem implements VariantHolder<Optional<Dye
if (entityshulker != null) {
entityshulker.setVariant(this.getVariant());
entityshulker.moveTo(vec3d);
+ // Paper start - Shulker duplicate event
+ if (!new io.papermc.paper.event.entity.ShulkerDuplicateEvent((org.bukkit.entity.Shulker) entityshulker.getBukkitEntity(), (org.bukkit.entity.Shulker) this.getBukkitEntity()).callEvent()) {
+ return;
+ }
+ // Paper end - Shulker duplicate event
this.level().addFreshEntity(entityshulker, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); // CraftBukkit - the mysteries of life
}

View file

@ -1,26 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Luis <luisc99@icloud.com>
Date: Thu, 11 Jan 2024 19:58:23 +0100
Subject: [PATCH] Add api for spawn egg texture colors
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
index b5abf9c7e5e09c670ae2435c23587e0482fbe917..b485a14a11f468e16d1da672f981e678c7a1522e 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
@@ -654,6 +654,15 @@ public final class CraftMagicNumbers implements UnsafeValues {
return CraftRegistry.get(registry, namespacedKey, ApiVersion.CURRENT);
}
+ // Paper start - spawn egg color visibility
+ @Override
+ public org.bukkit.Color getSpawnEggLayerColor(final EntityType entityType, final int layer) {
+ final net.minecraft.world.entity.EntityType<?> nmsType = org.bukkit.craftbukkit.entity.CraftEntityType.bukkitToMinecraft(entityType);
+ final net.minecraft.world.item.SpawnEggItem eggItem = net.minecraft.world.item.SpawnEggItem.byId(nmsType);
+ return eggItem == null ? null : org.bukkit.Color.fromRGB(eggItem.getColor(layer));
+ }
+ // Paper end - spawn egg color visibility
+
/**
* This helper class represents the different NBT Tags.
* <p>

View file

@ -1,781 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Tue, 18 Jul 2023 17:49:38 -0700
Subject: [PATCH] Add Lifecycle Event system
This event system is separate from Bukkit's event system and is
meant for managing resources across reloads and from points in the
PluginBootstrap.
diff --git a/src/main/java/io/papermc/paper/plugin/bootstrap/PluginBootstrapContextImpl.java b/src/main/java/io/papermc/paper/plugin/bootstrap/PluginBootstrapContextImpl.java
index 30b50e6294c6eaade5e17cfaf34600d122e6251c..0bb7694188d5fb75bb756ce75d0060ea980027ee 100644
--- a/src/main/java/io/papermc/paper/plugin/bootstrap/PluginBootstrapContextImpl.java
+++ b/src/main/java/io/papermc/paper/plugin/bootstrap/PluginBootstrapContextImpl.java
@@ -1,6 +1,8 @@
package io.papermc.paper.plugin.bootstrap;
import io.papermc.paper.plugin.configuration.PluginMeta;
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager;
+import io.papermc.paper.plugin.lifecycle.event.PaperLifecycleEventManager;
import io.papermc.paper.plugin.provider.PluginProvider;
import java.nio.file.Path;
import net.kyori.adventure.text.logger.slf4j.ComponentLogger;
@@ -12,6 +14,10 @@ public final class PluginBootstrapContextImpl implements BootstrapContext {
private final Path dataFolder;
private final ComponentLogger logger;
private final Path pluginSource;
+ // Paper start - lifecycle events
+ private boolean allowsLifecycleRegistration = true;
+ private final PaperLifecycleEventManager<BootstrapContext> lifecycleEventManager = new PaperLifecycleEventManager<>(this, () -> this.allowsLifecycleRegistration); // Paper - lifecycle events
+ // Paper end - lifecycle events
public PluginBootstrapContextImpl(PluginMeta config, Path dataFolder, ComponentLogger logger, Path pluginSource) {
this.config = config;
@@ -45,4 +51,20 @@ public final class PluginBootstrapContextImpl implements BootstrapContext {
public @NotNull Path getPluginSource() {
return this.pluginSource;
}
+
+ // Paper start - lifecycle event system
+ @Override
+ public @NotNull PluginMeta getPluginMeta() {
+ return this.config;
+ }
+
+ @Override
+ public LifecycleEventManager<BootstrapContext> getLifecycleManager() {
+ return this.lifecycleEventManager;
+ }
+
+ public void lockLifecycleEventRegistration() {
+ this.allowsLifecycleRegistration = false;
+ }
+ // Paper end
}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventRunner.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventRunner.java
new file mode 100644
index 0000000000000000000000000000000000000000..f84c9c80e701231e5c33ac3c5573f1093e80f38b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventRunner.java
@@ -0,0 +1,110 @@
+package io.papermc.paper.plugin.lifecycle.event;
+
+import com.google.common.base.Suppliers;
+import com.mojang.logging.LogUtils;
+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
+import io.papermc.paper.plugin.lifecycle.event.registrar.PaperRegistrar;
+import io.papermc.paper.plugin.lifecycle.event.registrar.RegistrarEvent;
+import io.papermc.paper.plugin.lifecycle.event.registrar.RegistrarEventImpl;
+import io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent;
+import io.papermc.paper.plugin.lifecycle.event.types.AbstractLifecycleEventType;
+import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType;
+import io.papermc.paper.plugin.lifecycle.event.types.OwnerAwareLifecycleEvent;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import org.bukkit.plugin.Plugin;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.framework.qual.DefaultQualifier;
+import org.slf4j.Logger;
+
+@DefaultQualifier(NonNull.class)
+public class LifecycleEventRunner {
+
+ private static final Logger LOGGER = LogUtils.getClassLogger();
+ private static final Supplier<Set<LifecycleEventType<?, ?, ?>>> BLOCKS_RELOADING = Suppliers.memoize(() -> Set.of( // lazy due to cyclic initialization
+ ));
+ public static final LifecycleEventRunner INSTANCE = new LifecycleEventRunner();
+
+ private final List<LifecycleEventType<?, ?, ?>> lifecycleEventTypes = new ArrayList<>();
+ private boolean blockPluginReloading = false;
+
+ public void checkRegisteredHandler(final LifecycleEventOwner owner, final LifecycleEventType<?, ?, ?> eventType) {
+ /*
+ Lifecycle event handlers for reloadable events that are registered from the BootstrapContext prevent
+ the server from reloading plugins. This is because reloading plugins requires disabling all the plugins,
+ running the reload logic (which would include places where these events should fire) and then re-enabling plugins.
+ */
+ if (owner instanceof BootstrapContext && BLOCKS_RELOADING.get().contains(eventType)) {
+ this.blockPluginReloading = true;
+ }
+ }
+
+ public boolean blocksPluginReloading() {
+ return this.blockPluginReloading;
+ }
+
+ public <O extends LifecycleEventOwner, E extends LifecycleEvent, ET extends LifecycleEventType<O, E, ?>> ET addEventType(final ET eventType) {
+ this.lifecycleEventTypes.add(eventType);
+ return eventType;
+ }
+
+ public <O extends LifecycleEventOwner, E extends PaperLifecycleEvent> void callEvent(final LifecycleEventType<O, ? super E, ?> eventType, final E event) {
+ this.callEvent(eventType, event, $ -> true);
+ }
+
+ public <O extends LifecycleEventOwner, E extends PaperLifecycleEvent> void callEvent(final LifecycleEventType<O, ? super E, ?> eventType, final E event, final Predicate<? super O> ownerPredicate) {
+ final AbstractLifecycleEventType<O, ? super E, ?, ?> lifecycleEventType = (AbstractLifecycleEventType<O, ? super E, ?, ?>) eventType;
+ lifecycleEventType.forEachHandler(registeredHandler -> {
+ try {
+ if (event instanceof final OwnerAwareLifecycleEvent<?> ownerAwareEvent) {
+ ownerAwareGenericHelper(ownerAwareEvent, registeredHandler.owner());
+ }
+ registeredHandler.lifecycleEventHandler().run(event);
+ } catch (final Throwable ex) {
+ LOGGER.error("Could not run '{}' lifecycle event handler from {}", lifecycleEventType.name(), registeredHandler.owner().getPluginMeta().getDisplayName(), ex);
+ } finally {
+ if (event instanceof final OwnerAwareLifecycleEvent<?> ownerAwareEvent) {
+ ownerAwareEvent.setOwner(null);
+ }
+ }
+ }, handler -> ownerPredicate.test(handler.owner()));
+ event.invalidate();
+ }
+
+ private static <O extends LifecycleEventOwner> void ownerAwareGenericHelper(final OwnerAwareLifecycleEvent<O> event, final LifecycleEventOwner possibleOwner) {
+ final @Nullable O owner = event.castOwner(possibleOwner);
+ if (owner != null) {
+ event.setOwner(owner);
+ } else {
+ throw new IllegalStateException("Found invalid owner " + possibleOwner + " for event " + event);
+ }
+ }
+
+ public void unregisterAllEventHandlersFor(final Plugin plugin) {
+ for (final LifecycleEventType<?, ?, ?> lifecycleEventType : this.lifecycleEventTypes) {
+ this.removeEventHandlersOwnedBy(lifecycleEventType, plugin);
+ }
+ }
+
+ private <O extends LifecycleEventOwner> void removeEventHandlersOwnedBy(final LifecycleEventType<O, ?, ?> eventType, final Plugin possibleOwner) {
+ final AbstractLifecycleEventType<O, ?, ?, ?> lifecycleEventType = (AbstractLifecycleEventType<O, ?, ?, ?>) eventType;
+ lifecycleEventType.removeMatching(registeredHandler -> registeredHandler.owner().getPluginMeta().getName().equals(possibleOwner.getPluginMeta().getName()));
+ }
+
+ @SuppressWarnings("unchecked")
+ public <O extends LifecycleEventOwner, R extends PaperRegistrar<? super O>> void callStaticRegistrarEvent(final LifecycleEventType<O, ? extends RegistrarEvent<? super R>, ?> lifecycleEventType, final R registrar, final Class<? extends O> ownerClass) {
+ this.callEvent((LifecycleEventType<O, RegistrarEvent<? super R>, ?>) lifecycleEventType, new RegistrarEventImpl<>(registrar, ownerClass), ownerClass::isInstance);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <O extends LifecycleEventOwner, R extends PaperRegistrar<? super O>> void callReloadableRegistrarEvent(final LifecycleEventType<O, ? extends ReloadableRegistrarEvent<? super R>, ?> lifecycleEventType, final R registrar, final Class<? extends O> ownerClass, final ReloadableRegistrarEvent.Cause cause) {
+ this.callEvent((LifecycleEventType<O, ReloadableRegistrarEvent<? super R>, ?>) lifecycleEventType, new RegistrarEventImpl.ReloadableImpl<>(registrar, ownerClass, cause), ownerClass::isInstance);
+ }
+
+ private LifecycleEventRunner() {
+ }
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/PaperLifecycleEvent.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/PaperLifecycleEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..e941405269a773e8a77e26ffd1afd84f53fadff5
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/PaperLifecycleEvent.java
@@ -0,0 +1,10 @@
+package io.papermc.paper.plugin.lifecycle.event;
+
+public interface PaperLifecycleEvent extends LifecycleEvent {
+
+ // called after all handlers have been run. Can be
+ // used to invalid various contexts to plugins can't
+ // try to re-use them by storing them from the event
+ default void invalidate() {
+ }
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/PaperLifecycleEventManager.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/PaperLifecycleEventManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..f1be5b9a29435bae0afd2bd951bfe88d1669e7eb
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/PaperLifecycleEventManager.java
@@ -0,0 +1,26 @@
+package io.papermc.paper.plugin.lifecycle.event;
+
+import com.google.common.base.Preconditions;
+import io.papermc.paper.plugin.lifecycle.event.handler.configuration.AbstractLifecycleEventHandlerConfiguration;
+import io.papermc.paper.plugin.lifecycle.event.handler.configuration.LifecycleEventHandlerConfiguration;
+import java.util.function.BooleanSupplier;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+@DefaultQualifier(NonNull.class)
+public final class PaperLifecycleEventManager<O extends LifecycleEventOwner> implements LifecycleEventManager<O> {
+
+ private final O owner;
+ public final BooleanSupplier registrationCheck;
+
+ public PaperLifecycleEventManager(final O owner, final BooleanSupplier registrationCheck) {
+ this.owner = owner;
+ this.registrationCheck = registrationCheck;
+ }
+
+ @Override
+ public void registerEventHandler(final LifecycleEventHandlerConfiguration<? super O> handlerConfiguration) {
+ Preconditions.checkState(this.registrationCheck.getAsBoolean(), "Cannot register lifecycle event handlers");
+ ((AbstractLifecycleEventHandlerConfiguration<? super O, ?, ?>) handlerConfiguration).registerFrom(this.owner);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/AbstractLifecycleEventHandlerConfiguration.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/AbstractLifecycleEventHandlerConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..6a85a4f581612efff04c1a955493aa2e32476277
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/AbstractLifecycleEventHandlerConfiguration.java
@@ -0,0 +1,26 @@
+package io.papermc.paper.plugin.lifecycle.event.handler.configuration;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
+import io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler;
+import io.papermc.paper.plugin.lifecycle.event.types.AbstractLifecycleEventType;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+@DefaultQualifier(NonNull.class)
+public abstract class AbstractLifecycleEventHandlerConfiguration<O extends LifecycleEventOwner, E extends LifecycleEvent, CI extends AbstractLifecycleEventHandlerConfiguration<O, E, CI>> implements LifecycleEventHandlerConfiguration<O> {
+
+ private final LifecycleEventHandler<? super E> handler;
+ private final AbstractLifecycleEventType<O, E, ?, CI> type;
+
+ protected AbstractLifecycleEventHandlerConfiguration(final LifecycleEventHandler<? super E> handler, final AbstractLifecycleEventType<O, E, ?, CI> type) {
+ this.handler = handler;
+ this.type = type;
+ }
+
+ public abstract CI config();
+
+ public final void registerFrom(final O owner) {
+ this.type.tryRegister(owner, this.handler, this.config());
+ }
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/MonitorLifecycleEventHandlerConfigurationImpl.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/MonitorLifecycleEventHandlerConfigurationImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..e0699fcd0a098abc5e1206e7c0fa80b96eca7884
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/MonitorLifecycleEventHandlerConfigurationImpl.java
@@ -0,0 +1,33 @@
+package io.papermc.paper.plugin.lifecycle.event.handler.configuration;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
+import io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler;
+import io.papermc.paper.plugin.lifecycle.event.types.AbstractLifecycleEventType;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+@DefaultQualifier(NonNull.class)
+public class MonitorLifecycleEventHandlerConfigurationImpl<O extends LifecycleEventOwner, E extends LifecycleEvent> extends AbstractLifecycleEventHandlerConfiguration<O, E, MonitorLifecycleEventHandlerConfigurationImpl<O, E>> implements MonitorLifecycleEventHandlerConfiguration<O> {
+
+ private boolean monitor = false;
+
+ public MonitorLifecycleEventHandlerConfigurationImpl(final LifecycleEventHandler<? super E> handler, final AbstractLifecycleEventType<O, E, ?, MonitorLifecycleEventHandlerConfigurationImpl<O, E>> eventType) {
+ super(handler, eventType);
+ }
+
+ @Override
+ public MonitorLifecycleEventHandlerConfigurationImpl<O, E> config() {
+ return this;
+ }
+
+ public boolean isMonitor() {
+ return this.monitor;
+ }
+
+ @Override
+ public MonitorLifecycleEventHandlerConfiguration<O> monitor() {
+ this.monitor = true;
+ return this;
+ }
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/PrioritizedLifecycleEventHandlerConfigurationImpl.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/PrioritizedLifecycleEventHandlerConfigurationImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..c1d0070fc1594f7a7c29d7dc679da7b347a7140b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/PrioritizedLifecycleEventHandlerConfigurationImpl.java
@@ -0,0 +1,43 @@
+package io.papermc.paper.plugin.lifecycle.event.handler.configuration;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
+import io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler;
+import io.papermc.paper.plugin.lifecycle.event.types.AbstractLifecycleEventType;
+import java.util.OptionalInt;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+@DefaultQualifier(NonNull.class)
+public class PrioritizedLifecycleEventHandlerConfigurationImpl<O extends LifecycleEventOwner, E extends LifecycleEvent> extends AbstractLifecycleEventHandlerConfiguration<O, E, PrioritizedLifecycleEventHandlerConfigurationImpl<O, E>> implements PrioritizedLifecycleEventHandlerConfiguration<O> {
+
+ private static final OptionalInt DEFAULT_PRIORITY = OptionalInt.of(0);
+ private static final OptionalInt MONITOR_PRIORITY = OptionalInt.empty();
+
+ private OptionalInt priority = DEFAULT_PRIORITY;
+
+ public PrioritizedLifecycleEventHandlerConfigurationImpl(final LifecycleEventHandler<? super E> handler, final AbstractLifecycleEventType<O, E, ?, PrioritizedLifecycleEventHandlerConfigurationImpl<O, E>> eventType) {
+ super(handler, eventType);
+ }
+
+ @Override
+ public PrioritizedLifecycleEventHandlerConfigurationImpl<O, E> config() {
+ return this;
+ }
+
+ public OptionalInt priority() {
+ return this.priority;
+ }
+
+ @Override
+ public PrioritizedLifecycleEventHandlerConfiguration<O> priority(final int priority) {
+ this.priority = OptionalInt.of(priority);
+ return this;
+ }
+
+ @Override
+ public PrioritizedLifecycleEventHandlerConfiguration<O> monitor() {
+ this.priority = MONITOR_PRIORITY;
+ return this;
+ }
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/PaperRegistrar.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/PaperRegistrar.java
new file mode 100644
index 0000000000000000000000000000000000000000..b2586c881988fbabe07eef1b43eb1b55f2d3fa52
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/PaperRegistrar.java
@@ -0,0 +1,15 @@
+package io.papermc.paper.plugin.lifecycle.event.registrar;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+@DefaultQualifier(NonNull.class)
+public interface PaperRegistrar<O extends LifecycleEventOwner> extends Registrar {
+
+ void setCurrentContext(@Nullable O owner);
+
+ default void invalidate() {
+ }
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/RegistrarEventImpl.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/RegistrarEventImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..6d530c52aaf0dc2cdfe3bd56af557274a7f44256
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/RegistrarEventImpl.java
@@ -0,0 +1,70 @@
+package io.papermc.paper.plugin.lifecycle.event.registrar;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
+import io.papermc.paper.plugin.lifecycle.event.PaperLifecycleEvent;
+import io.papermc.paper.plugin.lifecycle.event.types.OwnerAwareLifecycleEvent;
+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 RegistrarEventImpl<R extends PaperRegistrar<? super O>, O extends LifecycleEventOwner> implements PaperLifecycleEvent, OwnerAwareLifecycleEvent<O>, RegistrarEvent<R> {
+
+ private final R registrar;
+ private final Class<? extends O> ownerClass;
+
+ public RegistrarEventImpl(final R registrar, final Class<? extends O> ownerClass) {
+ this.registrar = registrar;
+ this.ownerClass = ownerClass;
+ }
+
+ @Override
+ public R registrar() {
+ return this.registrar;
+ }
+
+ @Override
+ public final void setOwner(final @Nullable O owner) {
+ this.registrar.setCurrentContext(owner);
+ }
+
+ @Override
+ public final @Nullable O castOwner(final LifecycleEventOwner owner) {
+ return this.ownerClass.isInstance(owner) ? this.ownerClass.cast(owner) : null;
+ }
+
+ @Override
+ public void invalidate() {
+ this.registrar.invalidate();
+ }
+
+ @Override
+ public String toString() {
+ return "RegistrarEventImpl{" +
+ "registrar=" + this.registrar +
+ ", ownerClass=" + this.ownerClass +
+ '}';
+ }
+
+ public static class ReloadableImpl<R extends PaperRegistrar<? super O>, O extends LifecycleEventOwner> extends RegistrarEventImpl<R, O> implements ReloadableRegistrarEvent<R> {
+
+ private final ReloadableRegistrarEvent.Cause cause;
+
+ public ReloadableImpl(final R registrar, final Class<? extends O> ownerClass, final Cause cause) {
+ super(registrar, ownerClass);
+ this.cause = cause;
+ }
+
+ @Override
+ public Cause cause() {
+ return this.cause;
+ }
+
+ @Override
+ public String toString() {
+ return "ReloadableImpl{" +
+ "cause=" + this.cause +
+ "} " + super.toString();
+ }
+ }
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/AbstractLifecycleEventType.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/AbstractLifecycleEventType.java
new file mode 100644
index 0000000000000000000000000000000000000000..a65fb37f4a729e2fe9fb81af822db626ec7e6d7b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/AbstractLifecycleEventType.java
@@ -0,0 +1,50 @@
+package io.papermc.paper.plugin.lifecycle.event.types;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner;
+import io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler;
+import io.papermc.paper.plugin.lifecycle.event.handler.configuration.AbstractLifecycleEventHandlerConfiguration;
+import io.papermc.paper.plugin.lifecycle.event.handler.configuration.LifecycleEventHandlerConfiguration;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+@DefaultQualifier(NonNull.class)
+public abstract class AbstractLifecycleEventType<O extends LifecycleEventOwner, E extends LifecycleEvent, C extends LifecycleEventHandlerConfiguration<O>, CI extends AbstractLifecycleEventHandlerConfiguration<O, E, CI>> implements LifecycleEventType<O, E, C> {
+
+ private final String name;
+ private final Class<? extends O> ownerType;
+
+ protected AbstractLifecycleEventType(final String name, final Class<? extends O> ownerType) {
+ this.name = name;
+ this.ownerType = ownerType;
+ }
+
+ @Override
+ public String name() {
+ return this.name;
+ }
+
+ private void verifyOwner(final O owner) {
+ if (!this.ownerType.isInstance(owner)) {
+ throw new IllegalArgumentException("You cannot register the lifecycle event '" + this.name + "' on " + owner);
+ }
+ }
+
+ public abstract void forEachHandler(Consumer<? super RegisteredHandler<O, E>> consumer, Predicate<? super RegisteredHandler<O, E>> predicate);
+
+ public abstract void removeMatching(Predicate<? super RegisteredHandler<O, E>> predicate);
+
+ protected abstract void register(O owner, LifecycleEventHandler<? super E> handler, CI config);
+
+ public final void tryRegister(final O owner, final LifecycleEventHandler<? super E> handler, final CI config) {
+ this.verifyOwner(owner);
+ LifecycleEventRunner.INSTANCE.checkRegisteredHandler(owner, this);
+ this.register(owner, handler, config);
+ }
+
+ public record RegisteredHandler<O, E extends LifecycleEvent>(O owner, LifecycleEventHandler<? super E> lifecycleEventHandler) {
+ }
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventTypeProviderImpl.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventTypeProviderImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..0886edad92b40276f268bd745b31bac359fd28af
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventTypeProviderImpl.java
@@ -0,0 +1,25 @@
+package io.papermc.paper.plugin.lifecycle.event.types;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+@DefaultQualifier(NonNull.class)
+public final class LifecycleEventTypeProviderImpl implements LifecycleEventTypeProvider {
+
+ public static LifecycleEventTypeProviderImpl instance() {
+ return (LifecycleEventTypeProviderImpl) LifecycleEventTypeProvider.PROVIDER;
+ }
+
+ @Override
+ public <O extends LifecycleEventOwner, E extends LifecycleEvent> LifecycleEventType.Monitorable<O, E> monitor(final String name, final Class<? extends O> ownerType) {
+ return LifecycleEventRunner.INSTANCE.addEventType(new MonitorableLifecycleEventType<>(name, ownerType));
+ }
+
+ @Override
+ public <O extends LifecycleEventOwner, E extends LifecycleEvent> LifecycleEventType.Prioritizable<O, E> prioritized(final String name, final Class<? extends O> ownerType) {
+ return LifecycleEventRunner.INSTANCE.addEventType(new PrioritizableLifecycleEventType<>(name, ownerType));
+ }
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/MonitorableLifecycleEventType.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/MonitorableLifecycleEventType.java
new file mode 100644
index 0000000000000000000000000000000000000000..6d92c1d3adf220154dfe7cba3a3f8158356c3e3c
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/MonitorableLifecycleEventType.java
@@ -0,0 +1,54 @@
+package io.papermc.paper.plugin.lifecycle.event.types;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
+import io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler;
+import io.papermc.paper.plugin.lifecycle.event.handler.configuration.MonitorLifecycleEventHandlerConfiguration;
+import io.papermc.paper.plugin.lifecycle.event.handler.configuration.MonitorLifecycleEventHandlerConfigurationImpl;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+@DefaultQualifier(NonNull.class)
+public class MonitorableLifecycleEventType<O extends LifecycleEventOwner, E extends LifecycleEvent> extends AbstractLifecycleEventType<O, E, MonitorLifecycleEventHandlerConfiguration<O>, MonitorLifecycleEventHandlerConfigurationImpl<O, E>> implements LifecycleEventType.Monitorable<O, E> {
+
+ final List<RegisteredHandler<O, E>> handlers = new ArrayList<>();
+ int nonMonitorIdx = 0;
+
+ public MonitorableLifecycleEventType(final String name, final Class<? extends O> ownerType) {
+ super(name, ownerType);
+ }
+
+ @Override
+ public MonitorLifecycleEventHandlerConfigurationImpl<O, E> newHandler(final LifecycleEventHandler<? super E> handler) {
+ return new MonitorLifecycleEventHandlerConfigurationImpl<>(handler, this);
+ }
+
+ @Override
+ protected void register(final O owner, final LifecycleEventHandler<? super E> handler, final MonitorLifecycleEventHandlerConfigurationImpl<O, E> config) {
+ final RegisteredHandler<O, E> registeredHandler = new RegisteredHandler<>(owner, handler);
+ if (!config.isMonitor()) {
+ this.handlers.add(this.nonMonitorIdx, registeredHandler);
+ this.nonMonitorIdx++;
+ } else {
+ this.handlers.add(registeredHandler);
+ }
+ }
+
+ @Override
+ public void forEachHandler(final Consumer<? super RegisteredHandler<O, E>> consumer, final Predicate<? super RegisteredHandler<O, E>> predicate) {
+ for (final RegisteredHandler<O, E> handler : this.handlers) {
+ if (predicate.test(handler)) {
+ consumer.accept(handler);
+ }
+ }
+ }
+
+ @Override
+ public void removeMatching(final Predicate<? super RegisteredHandler<O, E>> predicate) {
+ this.handlers.removeIf(predicate);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/OwnerAwareLifecycleEvent.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/OwnerAwareLifecycleEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..3e7e7474f301c0725fa2bcd6e19e476fc35f2d5a
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/OwnerAwareLifecycleEvent.java
@@ -0,0 +1,15 @@
+package io.papermc.paper.plugin.lifecycle.event.types;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+@DefaultQualifier(NonNull.class)
+public interface OwnerAwareLifecycleEvent<O extends LifecycleEventOwner> extends LifecycleEvent {
+
+ void setOwner(@Nullable O owner);
+
+ @Nullable O castOwner(LifecycleEventOwner owner);
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/PrioritizableLifecycleEventType.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/PrioritizableLifecycleEventType.java
new file mode 100644
index 0000000000000000000000000000000000000000..6629f7fabf66ce761024268043cc30076ba8a3f1
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/PrioritizableLifecycleEventType.java
@@ -0,0 +1,64 @@
+package io.papermc.paper.plugin.lifecycle.event.types;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
+import io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler;
+import io.papermc.paper.plugin.lifecycle.event.handler.configuration.PrioritizedLifecycleEventHandlerConfiguration;
+import io.papermc.paper.plugin.lifecycle.event.handler.configuration.PrioritizedLifecycleEventHandlerConfigurationImpl;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.OptionalInt;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+@DefaultQualifier(NonNull.class)
+public class PrioritizableLifecycleEventType<O extends LifecycleEventOwner, E extends LifecycleEvent> extends AbstractLifecycleEventType<O, E, PrioritizedLifecycleEventHandlerConfiguration<O>, PrioritizedLifecycleEventHandlerConfigurationImpl<O, E>> implements LifecycleEventType.Prioritizable<O, E> {
+
+ private static final Comparator<PrioritizedHandler<?, ?>> COMPARATOR = Comparator.comparing(PrioritizedHandler::priority, (o1, o2) -> {
+ if (o1.equals(o2)) {
+ return 0;
+ } else if (o1.isEmpty()) {
+ return 1;
+ } else if (o2.isEmpty()) {
+ return -1;
+ } else {
+ return Integer.compare(o1.getAsInt(), o2.getAsInt());
+ }
+ });
+
+ private final List<PrioritizedHandler<O, E>> handlers = new ArrayList<>();
+
+ public PrioritizableLifecycleEventType(final String name, final Class<? extends O> ownerType) {
+ super(name, ownerType);
+ }
+
+ @Override
+ public PrioritizedLifecycleEventHandlerConfiguration<O> newHandler(final LifecycleEventHandler<? super E> handler) {
+ return new PrioritizedLifecycleEventHandlerConfigurationImpl<>(handler, this);
+ }
+
+ @Override
+ protected void register(final O owner, final LifecycleEventHandler<? super E> handler, final PrioritizedLifecycleEventHandlerConfigurationImpl<O, E> config) {
+ this.handlers.add(new PrioritizedHandler<>(new RegisteredHandler<>(owner, handler), config.priority()));
+ this.handlers.sort(COMPARATOR);
+ }
+
+ @Override
+ public void forEachHandler(final Consumer<? super RegisteredHandler<O, E>> consumer, final Predicate<? super RegisteredHandler<O, E>> predicate) {
+ for (final PrioritizedHandler<O, E> handler : this.handlers) {
+ if (predicate.test(handler.handler())) {
+ consumer.accept(handler.handler());
+ }
+ }
+ }
+
+ @Override
+ public void removeMatching(final Predicate<? super RegisteredHandler<O, E>> predicate) {
+ this.handlers.removeIf(prioritizedHandler -> predicate.test(prioritizedHandler.handler()));
+ }
+
+ private record PrioritizedHandler<O extends LifecycleEventOwner, E extends LifecycleEvent>(RegisteredHandler<O, E> handler, OptionalInt priority) {}
+}
diff --git a/src/main/java/io/papermc/paper/plugin/manager/PaperPluginInstanceManager.java b/src/main/java/io/papermc/paper/plugin/manager/PaperPluginInstanceManager.java
index 834b85f24df023642f8abf7213fe578ac8c17a3e..3e82ea07ca4194844c5528446e2c4a46ff4acee5 100644
--- a/src/main/java/io/papermc/paper/plugin/manager/PaperPluginInstanceManager.java
+++ b/src/main/java/io/papermc/paper/plugin/manager/PaperPluginInstanceManager.java
@@ -293,6 +293,15 @@ class PaperPluginInstanceManager {
+ pluginName + " (Is it up to date?)", ex, plugin); // Paper
}
+ // Paper start - lifecycle event system
+ try {
+ io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.unregisterAllEventHandlersFor(plugin);
+ } catch (Throwable ex) {
+ this.handlePluginException("Error occurred (in the plugin loader) while unregistering lifecycle event handlers for "
+ + pluginName + " (Is it up to date?)", ex, plugin);
+ }
+ // Paper end
+
try {
this.server.getMessenger().unregisterIncomingPluginChannel(plugin);
this.server.getMessenger().unregisterOutgoingPluginChannel(plugin);
diff --git a/src/main/java/io/papermc/paper/plugin/storage/BootstrapProviderStorage.java b/src/main/java/io/papermc/paper/plugin/storage/BootstrapProviderStorage.java
index 2e96308696e131f3f013469a395e5ddda2c5d529..65a66e484c1c39c5f41d97db52f31c67b4479d20 100644
--- a/src/main/java/io/papermc/paper/plugin/storage/BootstrapProviderStorage.java
+++ b/src/main/java/io/papermc/paper/plugin/storage/BootstrapProviderStorage.java
@@ -32,8 +32,9 @@ public class BootstrapProviderStorage extends SimpleProviderStorage<PluginBootst
@Override
public boolean load(PluginProvider<PluginBootstrap> provider, PluginBootstrap provided) {
try {
- BootstrapContext context = PluginBootstrapContextImpl.create(provider, PluginInitializerManager.instance().pluginDirectoryPath());
+ PluginBootstrapContextImpl context = PluginBootstrapContextImpl.create(provider, PluginInitializerManager.instance().pluginDirectoryPath()); // Paper - lifecycle events
provided.bootstrap(context);
+ context.lockLifecycleEventRegistration(); // Paper - lifecycle events
return true;
} catch (Throwable e) {
LOGGER.error("Failed to run bootstrapper for %s. This plugin will not be loaded.".formatted(provider.getSource()), e);
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index cce628673b6173f17ac81bce5469a7dbd8a2c648..fd4417e2863fb028a1b22b4f5c00d585dd127ad1 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -1035,6 +1035,11 @@ public final class CraftServer implements Server {
@Override
public void reload() {
+ // Paper start - lifecycle events
+ if (io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.blocksPluginReloading()) {
+ throw new IllegalStateException("A lifecycle event handler has been registered which makes reloading plugins not possible");
+ }
+ // Paper end - lifecycle events
org.spigotmc.WatchdogThread.hasStarted = false; // Paper - Disable watchdog early timeout on reload
this.reloadCount++;
this.configuration = YamlConfiguration.loadConfiguration(this.getConfigFile());
diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java b/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java
index d96399e9bf1a58db5a4a22e58abb99e7660e0694..66bdac50130f523f9dc4379b103b7a469f9ca36b 100644
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java
+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/MinecraftInternalPlugin.java
@@ -143,4 +143,11 @@ public class MinecraftInternalPlugin extends PluginBase {
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
throw new UnsupportedOperationException("Not supported.");
}
+
+ // Paper start - lifecycle events
+ @Override
+ public @NotNull io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager<org.bukkit.plugin.Plugin> getLifecycleManager() {
+ throw new UnsupportedOperationException("Not supported.");
+ }
+ // Paper end - lifecycle events
}
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
index b485a14a11f468e16d1da672f981e678c7a1522e..5143b8f81ce31e5d88f5de6b2856c3b7e3676048 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
@@ -663,6 +663,13 @@ public final class CraftMagicNumbers implements UnsafeValues {
}
// Paper end - spawn egg color visibility
+ // Paper start - lifecycle event API
+ @Override
+ public io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager<org.bukkit.plugin.Plugin> createPluginLifecycleEventManager(final org.bukkit.plugin.java.JavaPlugin plugin, final java.util.function.BooleanSupplier registrationCheck) {
+ return new io.papermc.paper.plugin.lifecycle.event.PaperLifecycleEventManager<>(plugin, registrationCheck);
+ }
+ // Paper end - lifecycle event API
+
/**
* This helper class represents the different NBT Tags.
* <p>
diff --git a/src/main/resources/META-INF/services/io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventTypeProvider b/src/main/resources/META-INF/services/io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventTypeProvider
new file mode 100644
index 0000000000000000000000000000000000000000..808b1192b60348ad05f0bfbdeda6f94df4876743
--- /dev/null
+++ b/src/main/resources/META-INF/services/io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventTypeProvider
@@ -0,0 +1 @@
+io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventTypeProviderImpl
diff --git a/src/test/java/io/papermc/paper/plugin/PaperTestPlugin.java b/src/test/java/io/papermc/paper/plugin/PaperTestPlugin.java
index 1d14f530ef888102e47eeeaf0d1a6076e51871c4..90cf0c702ca2ff9de64d9718ecba5f2d128953a6 100644
--- a/src/test/java/io/papermc/paper/plugin/PaperTestPlugin.java
+++ b/src/test/java/io/papermc/paper/plugin/PaperTestPlugin.java
@@ -143,4 +143,11 @@ public class PaperTestPlugin extends PluginBase {
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
throw new UnsupportedOperationException("Not supported.");
}
+
+ // Paper start - lifecycle events
+ @Override
+ public io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager<org.bukkit.plugin.Plugin> getLifecycleManager() {
+ throw new UnsupportedOperationException("Not supported.");
+ }
+ // Paper end - lifecycle events
}

View file

@ -1,32 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Yannick Lamprecht <yannicklamprecht@live.de>
Date: Mon, 22 Jan 2024 13:27:30 +0100
Subject: [PATCH] ItemStack Tooltip API
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
index 5143b8f81ce31e5d88f5de6b2856c3b7e3676048..f6c33a9e2900890e4f6cb19784e01ff438cce83d 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
@@ -639,6 +639,21 @@ public final class CraftMagicNumbers implements UnsafeValues {
}
// Paper end - fix custom stats criteria creation
+ // Paper start - expose itemstack tooltip lines
+ @Override
+ public java.util.List<net.kyori.adventure.text.Component> computeTooltipLines(final ItemStack itemStack, final io.papermc.paper.inventory.tooltip.TooltipContext tooltipContext, final org.bukkit.entity.Player player) {
+ Preconditions.checkArgument(tooltipContext != null, "tooltipContext cannot be null");
+ net.minecraft.world.item.TooltipFlag.Default flag = tooltipContext.isAdvanced() ? net.minecraft.world.item.TooltipFlag.ADVANCED : net.minecraft.world.item.TooltipFlag.NORMAL;
+ if (tooltipContext.isCreative()) {
+ flag = flag.asCreative();
+ }
+ final java.util.List<net.minecraft.network.chat.Component> lines = CraftItemStack.asNMSCopy(itemStack).getTooltipLines(
+ net.minecraft.world.item.Item.TooltipContext.of(player == null ? net.minecraft.server.MinecraftServer.getServer().registryAccess() : ((org.bukkit.craftbukkit.entity.CraftPlayer) player).getHandle().level().registryAccess()),
+ player == null ? null : ((org.bukkit.craftbukkit.entity.CraftPlayer) player).getHandle(), flag);
+ return lines.stream().map(io.papermc.paper.adventure.PaperAdventure::asAdventure).toList();
+ }
+ // Paper end - expose itemstack tooltip lines
+
@Override
public String get(Class<?> aClass, String s) {
if (aClass == Enchantment.class) {

View file

@ -1,70 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Warrior <50800980+Warriorrrr@users.noreply.github.com>
Date: Sat, 10 Feb 2024 10:03:48 +0100
Subject: [PATCH] Add getChunkSnapshot includeLightData parameter
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
index f8c2d91958d6e4a1452fcf32c16fa8b97ea271a2..36a611d06131be00197c915871b8323544bb4972 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
@@ -328,12 +328,21 @@ public class CraftChunk implements Chunk {
@Override
public ChunkSnapshot getChunkSnapshot(boolean includeMaxBlockY, boolean includeBiome, boolean includeBiomeTempRain) {
+ // Paper start - Add getChunkSnapshot includeLightData parameter
+ return getChunkSnapshot(includeMaxBlockY, includeBiome, includeBiomeTempRain, true);
+ }
+
+ @Override
+ public ChunkSnapshot getChunkSnapshot(boolean includeMaxBlockY, boolean includeBiome, boolean includeBiomeTempRain, boolean includeLightData) {
+ // Paper end - Add getChunkSnapshot includeLightData parameter
ChunkAccess chunk = this.getHandle(ChunkStatus.FULL);
LevelChunkSection[] cs = chunk.getSections();
PalettedContainer[] sectionBlockIDs = new PalettedContainer[cs.length];
- byte[][] sectionSkyLights = new byte[cs.length][];
- byte[][] sectionEmitLights = new byte[cs.length][];
+ // Paper start - Add getChunkSnapshot includeLightData parameter
+ byte[][] sectionSkyLights = includeLightData ? new byte[cs.length][] : null;
+ byte[][] sectionEmitLights = includeLightData ? new byte[cs.length][] : null;
+ // Paper end - Add getChunkSnapshot includeLightData parameter
boolean[] sectionEmpty = new boolean[cs.length];
PalettedContainerRO<Holder<net.minecraft.world.level.biome.Biome>>[] biome = (includeBiome || includeBiomeTempRain) ? new PalettedContainer[cs.length] : null;
@@ -350,6 +359,7 @@ public class CraftChunk implements Chunk {
}
// Paper end - Fix ChunkSnapshot#isSectionEmpty(int)
+ if (includeLightData) { // Paper - Add getChunkSnapshot includeLightData parameter
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
if (skyLightArray == null) {
@@ -365,6 +375,7 @@ public class CraftChunk implements Chunk {
sectionEmitLights[i] = new byte[2048];
System.arraycopy(emitLightArray.getData(), 0, sectionEmitLights[i], 0, 2048);
}
+ } // Paper - Add getChunkSnapshot includeLightData parameter
if (biome != null) {
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/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java b/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java
index 85029f1acfdbb411d9ebdf95838d6db3898f4e58..0756b5adb3039997feadeb94afb10b596abd9424 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftChunkSnapshot.java
@@ -118,6 +118,7 @@ public class CraftChunkSnapshot implements ChunkSnapshot {
@Override
public final int getBlockSkyLight(int x, int y, int z) {
+ Preconditions.checkState(this.skylight != null, "ChunkSnapshot created without light data. Please call getSnapshot with includeLightData=true"); // Paper - Add getChunkSnapshot includeLightData parameter
this.validateChunkCoordinates(x, y, z);
int off = ((y & 0xF) << 7) | (z << 3) | (x >> 1);
@@ -126,6 +127,7 @@ public class CraftChunkSnapshot implements ChunkSnapshot {
@Override
public final int getBlockEmittedLight(int x, int y, int z) {
+ Preconditions.checkState(this.emitlight != null, "ChunkSnapshot created without light data. Please call getSnapshot with includeLightData=true"); // Paper - Add getChunkSnapshot includeLightData parameter
this.validateChunkCoordinates(x, y, z);
int off = ((y & 0xF) << 7) | (z << 3) | (x >> 1);

View file

@ -1,208 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: vicisacat <victor.branchu@gmail.com>
Date: Fri, 17 Nov 2023 20:22:43 +0100
Subject: [PATCH] Add FluidState API
diff --git a/src/main/java/io/papermc/paper/block/fluid/PaperFluidData.java b/src/main/java/io/papermc/paper/block/fluid/PaperFluidData.java
new file mode 100644
index 0000000000000000000000000000000000000000..479bc32241ebadf8bbc1080b601f61391ad37fa4
--- /dev/null
+++ b/src/main/java/io/papermc/paper/block/fluid/PaperFluidData.java
@@ -0,0 +1,110 @@
+package io.papermc.paper.block.fluid;
+
+import com.google.common.base.Preconditions;
+import io.papermc.paper.block.fluid.type.PaperFallingFluidData;
+import io.papermc.paper.block.fluid.type.PaperFlowingFluidData;
+import io.papermc.paper.util.MCUtil;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+import net.minecraft.world.level.material.FluidState;
+import net.minecraft.world.level.material.LavaFluid;
+import net.minecraft.world.level.material.WaterFluid;
+import org.bukkit.Fluid;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.CraftFluid;
+import org.bukkit.craftbukkit.CraftWorld;
+import org.bukkit.craftbukkit.util.CraftVector;
+import org.bukkit.util.Vector;
+import org.jetbrains.annotations.NotNull;
+
+public class PaperFluidData implements FluidData {
+
+ private final FluidState state;
+
+ protected PaperFluidData(final FluidState state) {
+ this.state = state;
+ }
+
+ /**
+ * Provides the internal server representation of this fluid data.
+ * @return the fluid state.
+ */
+ public FluidState getState() {
+ return this.state;
+ }
+
+ @Override
+ public final @NotNull Fluid getFluidType() {
+ return CraftFluid.minecraftToBukkit(this.state.getType());
+ }
+
+ @Override
+ public @NotNull PaperFluidData clone() {
+ try {
+ return (PaperFluidData) super.clone();
+ } catch (final CloneNotSupportedException ex) {
+ throw new AssertionError("Clone not supported", ex);
+ }
+ }
+
+ @Override
+ public @NotNull Vector computeFlowDirection(final Location location) {
+ Preconditions.checkArgument(location.getWorld() != null, "Cannot compute flow direction on world-less location");
+ return CraftVector.toBukkit(this.state.getFlow(
+ ((CraftWorld) location.getWorld()).getHandle(),
+ MCUtil.toBlockPosition(location)
+ ));
+ }
+
+ @Override
+ public int getLevel() {
+ return this.state.getAmount();
+ }
+
+ @Override
+ public float computeHeight(@NotNull final Location location) {
+ Preconditions.checkArgument(location.getWorld() != null, "Cannot compute height on world-less location");
+ return this.state.getHeight(((CraftWorld) location.getWorld()).getHandle(), MCUtil.toBlockPos(location));
+ }
+
+ @Override
+ public boolean isSource() {
+ return this.state.isSource();
+ }
+
+ @Override
+ public int hashCode() {
+ return this.state.hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ return obj instanceof final PaperFluidData paperFluidData && this.state.equals(paperFluidData.state);
+ }
+
+ @Override
+ public String toString() {
+ return "PaperFluidData{" + this.state + "}";
+ }
+
+ /* Registry */
+ private static final Map<Class<? extends net.minecraft.world.level.material.Fluid>, Function<FluidState, PaperFluidData>> MAP = new HashMap<>();
+ static {
+ //<editor-fold desc="PaperFluidData Registration" defaultstate="collapsed">
+ register(LavaFluid.Source.class, PaperFallingFluidData::new);
+ register(WaterFluid.Source.class, PaperFallingFluidData::new);
+ register(LavaFluid.Flowing.class, PaperFlowingFluidData::new);
+ register(WaterFluid.Flowing.class, PaperFlowingFluidData::new);
+ //</editor-fold>
+ }
+
+ static void register(final Class<? extends net.minecraft.world.level.material.Fluid> fluid, final Function<FluidState, PaperFluidData> creator) {
+ Preconditions.checkState(MAP.put(fluid, creator) == null, "Duplicate mapping %s->%s", fluid, creator);
+ MAP.put(fluid, creator);
+ }
+
+ public static PaperFluidData createData(final FluidState state) {
+ return MAP.getOrDefault(state.getType().getClass(), PaperFluidData::new).apply(state);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/block/fluid/package-info.java b/src/main/java/io/papermc/paper/block/fluid/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..cfabb814ebd281aab299c6c655266ff357e08806
--- /dev/null
+++ b/src/main/java/io/papermc/paper/block/fluid/package-info.java
@@ -0,0 +1,5 @@
+@DefaultQualifier(NonNull.class)
+package io.papermc.paper.block.fluid;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
diff --git a/src/main/java/io/papermc/paper/block/fluid/type/PaperFallingFluidData.java b/src/main/java/io/papermc/paper/block/fluid/type/PaperFallingFluidData.java
new file mode 100644
index 0000000000000000000000000000000000000000..655dbd83ff4e632f1168b75e9b402b05aa9d8edf
--- /dev/null
+++ b/src/main/java/io/papermc/paper/block/fluid/type/PaperFallingFluidData.java
@@ -0,0 +1,18 @@
+
+package io.papermc.paper.block.fluid.type;
+
+import io.papermc.paper.block.fluid.PaperFluidData;
+import net.minecraft.world.level.material.FlowingFluid;
+import net.minecraft.world.level.material.FluidState;
+
+public class PaperFallingFluidData extends PaperFluidData implements FallingFluidData {
+
+ public PaperFallingFluidData(final FluidState state) {
+ super(state);
+ }
+
+ @Override
+ public boolean isFalling() {
+ return this.getState().getValue(FlowingFluid.FALLING);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/block/fluid/type/PaperFlowingFluidData.java b/src/main/java/io/papermc/paper/block/fluid/type/PaperFlowingFluidData.java
new file mode 100644
index 0000000000000000000000000000000000000000..c0c2805cb045cdd835b402776a6923fe2ecc2a99
--- /dev/null
+++ b/src/main/java/io/papermc/paper/block/fluid/type/PaperFlowingFluidData.java
@@ -0,0 +1,11 @@
+package io.papermc.paper.block.fluid.type;
+
+import net.minecraft.world.level.material.FluidState;
+
+public class PaperFlowingFluidData extends PaperFallingFluidData implements FlowingFluidData {
+
+ public PaperFlowingFluidData(final FluidState state) {
+ super(state);
+ }
+
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java b/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java
index 5cdc44c73b536f2ed2dcd49dbeccf0f69f614dba..ef1bfab74e93f48c621e7b4a3a54f59b84bdd064 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java
@@ -107,6 +107,13 @@ public abstract class CraftRegionAccessor implements RegionAccessor {
return CraftBlock.at(this.getHandle(), new BlockPos(x, y, z)).getState();
}
+ // Paper start - FluidState API
+ @Override
+ public io.papermc.paper.block.fluid.FluidData getFluidData(final int x, final int y, final int z) {
+ return io.papermc.paper.block.fluid.PaperFluidData.createData(getHandle().getFluidState(new BlockPos(x, y, z)));
+ }
+ // Paper end
+
@Override
public BlockData getBlockData(Location location) {
return this.getBlockData(location.getBlockX(), location.getBlockY(), location.getBlockZ());
diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java b/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java
index eaa9ba70b0b80d86eb376a0641420093a7c9dfdb..25598df0bb0d4347b2c17b6ec0afbfe4ecf808b9 100644
--- a/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java
+++ b/src/main/java/org/bukkit/craftbukkit/generator/CraftLimitedRegion.java
@@ -303,4 +303,11 @@ public class CraftLimitedRegion extends CraftRegionAccessor implements LimitedRe
return centerChunkZ;
}
// Paper end - Add more LimitedRegion API
+ // Paper start - Fluid API
+ @Override
+ public io.papermc.paper.block.fluid.FluidData getFluidData(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.getFluidData(x, y, z);
+ }
+ // Paper end
}

View file

@ -1,138 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: David Mayr <davidliebtkekse@gmail.com>
Date: Sat, 16 Dec 2023 10:40:29 +0100
Subject: [PATCH] add number format api
== AT ==
public net.minecraft.network.chat.numbers.FixedFormat value
public net.minecraft.network.chat.numbers.StyledFormat style
diff --git a/src/main/java/io/papermc/paper/util/PaperScoreboardFormat.java b/src/main/java/io/papermc/paper/util/PaperScoreboardFormat.java
new file mode 100644
index 0000000000000000000000000000000000000000..6064086cc76ef0df999c7057121d0ac22bd4df65
--- /dev/null
+++ b/src/main/java/io/papermc/paper/util/PaperScoreboardFormat.java
@@ -0,0 +1,38 @@
+package io.papermc.paper.util;
+
+import io.papermc.paper.adventure.PaperAdventure;
+import io.papermc.paper.scoreboard.numbers.NumberFormat;
+
+public final class PaperScoreboardFormat {
+
+ private PaperScoreboardFormat() {
+ }
+
+ public static net.minecraft.network.chat.numbers.NumberFormat asVanilla(final NumberFormat format) {
+ final net.minecraft.network.chat.numbers.NumberFormat vanilla;
+ if (format instanceof final io.papermc.paper.scoreboard.numbers.StyledFormat styled) {
+ vanilla = new net.minecraft.network.chat.numbers.StyledFormat(PaperAdventure.asVanilla(styled.style()));
+ } else if (format instanceof final io.papermc.paper.scoreboard.numbers.FixedFormat fixed) {
+ vanilla = new net.minecraft.network.chat.numbers.FixedFormat(io.papermc.paper.adventure.PaperAdventure
+ .asVanilla(fixed.component()));
+ } else if (format.equals(NumberFormat.blank())) {
+ vanilla = net.minecraft.network.chat.numbers.BlankFormat.INSTANCE;
+ } else {
+ throw new IllegalArgumentException("Unknown format type " + format.getClass());
+ }
+
+ return vanilla;
+ }
+
+ public static NumberFormat asPaper(final net.minecraft.network.chat.numbers.NumberFormat vanilla) {
+ if (vanilla instanceof final net.minecraft.network.chat.numbers.StyledFormat styled) {
+ return NumberFormat.styled(PaperAdventure.asAdventure(styled.style));
+ } else if (vanilla instanceof final net.minecraft.network.chat.numbers.FixedFormat fixed) {
+ return NumberFormat.fixed(io.papermc.paper.adventure.PaperAdventure.asAdventure(fixed.value));
+ } else if (vanilla instanceof net.minecraft.network.chat.numbers.BlankFormat) {
+ return NumberFormat.blank();
+ }
+
+ throw new IllegalArgumentException("Unknown format type " + vanilla.getClass());
+ }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java
index 2d3abf2a1da487ead74d698cc5ea4eb729c35c8d..1fec80c4f02aab3770c05bac8bfa2b622625e630 100644
--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java
+++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java
@@ -153,6 +153,34 @@ final class CraftObjective extends CraftScoreboardComponent implements Objective
}
// Paper end
+ // Paper start - add number format
+ @Override
+ public io.papermc.paper.scoreboard.numbers.NumberFormat numberFormat() {
+ this.checkState();
+
+ net.minecraft.network.chat.numbers.NumberFormat vanilla = this.objective.numberFormat();
+
+ if (vanilla == null) {
+ return null;
+ }
+
+ return io.papermc.paper.util.PaperScoreboardFormat.asPaper(vanilla);
+ }
+
+
+ @Override
+ public void numberFormat(io.papermc.paper.scoreboard.numbers.NumberFormat format) {
+ this.checkState();
+
+ if (format == null) {
+ this.objective.setNumberFormat(null);
+ return;
+ }
+
+ this.objective.setNumberFormat(io.papermc.paper.util.PaperScoreboardFormat.asVanilla(format));
+ }
+ // Paper end - add number format
+
@Override
public void unregister() {
CraftScoreboard scoreboard = this.checkState();
diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java
index 74d9c407e971804bed420370f7b684d8658eb5aa..e307e897d6e1ba4cb21883dfeaf334bfbf56cfc4 100644
--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java
+++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java
@@ -55,6 +55,41 @@ final class CraftScore implements Score {
this.objective.checkState().board.getOrCreatePlayerScore(this.entry, this.objective.getHandle()).set(score);
}
+
+ // Paper start - add number format
+ @Override
+ public io.papermc.paper.scoreboard.numbers.NumberFormat numberFormat() {
+ ReadOnlyScoreInfo scoreInfo = this.objective.checkState().board
+ .getPlayerScoreInfo(this.entry, this.objective.getHandle());
+
+ if (scoreInfo == null) {
+ return null;
+ }
+
+ net.minecraft.network.chat.numbers.NumberFormat vanilla = scoreInfo.numberFormat();
+
+ if (vanilla == null) {
+ return null;
+ }
+
+ return io.papermc.paper.util.PaperScoreboardFormat.asPaper(vanilla);
+ }
+
+
+ @Override
+ public void numberFormat(io.papermc.paper.scoreboard.numbers.NumberFormat format) {
+ final net.minecraft.world.scores.ScoreAccess access = this.objective.checkState()
+ .board.getOrCreatePlayerScore(this.entry, this.objective.getHandle());
+
+ if (format == null) {
+ access.numberFormatOverride(null);
+ return;
+ }
+
+ access.numberFormatOverride(io.papermc.paper.util.PaperScoreboardFormat.asVanilla(format));
+ }
+ // Paper end - add number format
+
@Override
public boolean isScoreSet() {
Scoreboard board = this.objective.checkState().board;

View file

@ -1,32 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Yannick Lamprecht <yannicklamprecht@live.de>
Date: Sat, 10 Feb 2024 20:50:01 +0100
Subject: [PATCH] improve BanList types
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index fd4417e2863fb028a1b22b4f5c00d585dd127ad1..16a736e8327450712630b1659b156da879a57352 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -2242,6 +2242,21 @@ public final class CraftServer implements Server {
};
}
+ // Paper start - add BanListType (which has a generic)
+ @SuppressWarnings("unchecked")
+ @Override
+ public <B extends BanList<E>, E> B getBanList(final io.papermc.paper.ban.BanListType<B> type) {
+ Preconditions.checkArgument(type != null, "BanList.BanType cannot be null");
+ if (type == io.papermc.paper.ban.BanListType.IP) {
+ return (B) new CraftIpBanList(this.playerList.getIpBans());
+ } else if (type == io.papermc.paper.ban.BanListType.PROFILE) {
+ return (B) new CraftProfileBanList(this.playerList.getBans());
+ } else {
+ throw new IllegalArgumentException("Unknown BanListType: " + type);
+ }
+ }
+ // Paper end - add BanListType (which has a generic)
+
@Override
public void setWhitelist(boolean value) {
this.playerList.setUsingWhiteList(value);

View file

@ -1,31 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: vicisacat <victor.branchu@gmail.com>
Date: Fri, 15 Mar 2024 17:35:40 +0100
Subject: [PATCH] Expanded Hopper API
== AT ==
public net.minecraft.world.level.block.entity.HopperBlockEntity setCooldown(I)V
public net.minecraft.world.level.block.entity.HopperBlockEntity cooldownTime
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftHopper.java b/src/main/java/org/bukkit/craftbukkit/block/CraftHopper.java
index 2995b407415c77288a54c3f4eb8cb93e1a289283..f3c4d3835a18475e2cd2f519ac3dd9d9b59c454d 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftHopper.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftHopper.java
@@ -40,4 +40,17 @@ public class CraftHopper extends CraftLootable<HopperBlockEntity> implements Hop
public CraftHopper copy(Location location) {
return new CraftHopper(this, location);
}
+
+ // Paper start - Expanded Hopper API
+ @Override
+ public void setTransferCooldown(final int cooldown) {
+ com.google.common.base.Preconditions.checkArgument(cooldown >= 0, "Hooper transfer cooldown cannot be negative (" + cooldown + ")");
+ getSnapshot().setCooldown(cooldown);
+ }
+
+ @Override
+ public int getTransferCooldown() {
+ return getSnapshot().cooldownTime;
+ }
+ // Paper end - Expanded Hopper API
}

View file

@ -1,28 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Badbird5907 <50347938+Badbird5907@users.noreply.github.com>
Date: Mon, 4 Mar 2024 22:18:28 -0500
Subject: [PATCH] Add BlockBreakProgressUpdateEvent
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 39cf7ae086108d8077121829368b81605d046fab..4616672c4be74421e2acbc9df96f7c8ff2782e55 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -1322,6 +1322,17 @@ public class ServerLevel extends Level implements WorldGenLevel {
if (entity instanceof Player) entityhuman = (Player) entity;
// CraftBukkit end
+ // Paper start - Add BlockBreakProgressUpdateEvent
+ // If a plugin is using this method to send destroy packets for a client-side only entity id, no block progress occurred on the server.
+ // Hence, do not call the event.
+ if (entity != null) {
+ float progressFloat = Mth.clamp(progress, 0, 10) / 10.0f;
+ org.bukkit.craftbukkit.block.CraftBlock bukkitBlock = org.bukkit.craftbukkit.block.CraftBlock.at(this, pos);
+ new io.papermc.paper.event.block.BlockBreakProgressUpdateEvent(bukkitBlock, progressFloat, entity.getBukkitEntity())
+ .callEvent();
+ }
+ // Paper end - Add BlockBreakProgressUpdateEvent
+
while (iterator.hasNext()) {
ServerPlayer entityplayer = (ServerPlayer) iterator.next();

View file

@ -1,35 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com>
Date: Tue, 26 Mar 2024 21:42:23 -0400
Subject: [PATCH] Deprecate ItemStack#setType
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
index 520b637e0f281d3d3018681ec7b48b06c47f621e..7249ff939dfa786395595f687338315b779e0931 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
@@ -711,4 +711,24 @@ public final class CraftItemStack extends ItemStack {
static boolean hasItemMeta(net.minecraft.world.item.ItemStack item) {
return !(item == null || item.getComponentsPatch().isEmpty());
}
+ // Paper start - with type
+ @Override
+ public ItemStack withType(final Material type) {
+ if (type == Material.AIR) {
+ return CraftItemStack.asCraftMirror(null);
+ }
+
+ final net.minecraft.world.item.ItemStack copy = new net.minecraft.world.item.ItemStack(
+ CraftItemType.bukkitToMinecraft(type), this.getAmount()
+ );
+
+ if (this.handle != null) {
+ copy.applyComponents(this.handle.getComponentsPatch());
+ }
+
+ final CraftItemStack mirrored = CraftItemStack.asCraftMirror(copy);
+ mirrored.setItemMeta(mirrored.getItemMeta());
+ return mirrored;
+ }
+ // Paper end
}

View file

@ -1,43 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Janet Blackquill <uhhadd@gmail.com>
Date: Sun, 7 Apr 2024 16:52:42 -0400
Subject: [PATCH] Add CartographyItemEvent
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index e13097ceba283fdf165d7e6327fe78590252fe94..bead9625a04a9c1139c54d6a56f055b80eb2d701 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -3092,6 +3092,19 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
}
}
+ // Paper start - cartography item event
+ if (packet.getSlotNum() == net.minecraft.world.inventory.CartographyTableMenu.RESULT_SLOT && top instanceof org.bukkit.inventory.CartographyInventory cartographyInventory) {
+ org.bukkit.inventory.ItemStack result = cartographyInventory.getResult();
+ if (result != null && !result.isEmpty()) {
+ if (click == ClickType.NUMBER_KEY) {
+ event = new io.papermc.paper.event.player.CartographyItemEvent(inventory, type, packet.getSlotNum(), click, action, packet.getButtonNum());
+ } else {
+ event = new io.papermc.paper.event.player.CartographyItemEvent(inventory, type, packet.getSlotNum(), click, action);
+ }
+ }
+ }
+ // Paper end - cartography item event
+
event.setCancelled(cancelled);
AbstractContainerMenu oldContainer = this.player.containerMenu; // SPIGOT-1224
this.cserver.getPluginManager().callEvent(event);
diff --git a/src/main/java/net/minecraft/world/inventory/CartographyTableMenu.java b/src/main/java/net/minecraft/world/inventory/CartographyTableMenu.java
index ab98637bf967ac19f0bc06e8cb7f18a8b13ec809..a90e2c56c54797b2fec40eb3865835c5f640a544 100644
--- a/src/main/java/net/minecraft/world/inventory/CartographyTableMenu.java
+++ b/src/main/java/net/minecraft/world/inventory/CartographyTableMenu.java
@@ -71,7 +71,7 @@ public class CartographyTableMenu extends AbstractContainerMenu {
this.resultContainer = new ResultContainer(this.createBlockHolder(context)) { // Paper - Add missing InventoryHolders
@Override
public void setChanged() {
- CartographyTableMenu.this.slotsChanged(this);
+ // CartographyTableMenu.this.slotsChanged(this); // Paper - Add CatographyItemEvent - do not recompute results if the result slot changes - allows to set the result slot via api
super.setChanged();
}

View file

@ -1,106 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Fri, 4 Mar 2022 09:46:33 -0800
Subject: [PATCH] More Raid API
== AT ==
public net.minecraft.world.entity.raid.Raid raidEvent
diff --git a/src/main/java/net/minecraft/world/entity/raid/Raid.java b/src/main/java/net/minecraft/world/entity/raid/Raid.java
index 364d233ca318bbf8c1bc3acfbaf2d2420fdf922f..fdff9788eaf663be79214b2ca491f0f0444f6136 100644
--- a/src/main/java/net/minecraft/world/entity/raid/Raid.java
+++ b/src/main/java/net/minecraft/world/entity/raid/Raid.java
@@ -110,6 +110,11 @@ public class Raid {
private Raid.RaidStatus status;
private int celebrationTicks;
private Optional<BlockPos> waveSpawnPos;
+ // Paper start
+ private static final String PDC_NBT_KEY = "BukkitValues";
+ private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry PDC_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry();
+ public final org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer persistentDataContainer = new org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer(PDC_TYPE_REGISTRY);
+ // Paper end
public Raid(int id, ServerLevel world, BlockPos pos) {
this.raidEvent = new ServerBossEvent(Raid.RAID_NAME_COMPONENT, BossEvent.BossBarColor.RED, BossEvent.BossBarOverlay.NOTCHED_10);
@@ -153,6 +158,11 @@ public class Raid {
this.heroesOfTheVillage.add(NbtUtils.loadUUID(nbtbase));
}
}
+ // Paper start
+ if (nbt.contains(PDC_NBT_KEY, net.minecraft.nbt.Tag.TAG_COMPOUND)) {
+ this.persistentDataContainer.putAll(nbt.getCompound(PDC_NBT_KEY));
+ }
+ // Paper end
}
@@ -870,6 +880,11 @@ public class Raid {
}
nbt.put("HeroesOfTheVillage", nbttaglist);
+ // Paper start
+ if (!this.persistentDataContainer.isEmpty()) {
+ nbt.put(PDC_NBT_KEY, this.persistentDataContainer.toTagCompound());
+ }
+ // Paper end
return nbt;
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftRaid.java b/src/main/java/org/bukkit/craftbukkit/CraftRaid.java
index b8ce1c1c2447f9cff1717bfcfd6eb911ade0d4b3..51f21af9d75769abdcba713b9aa33392e994d9b0 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftRaid.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftRaid.java
@@ -103,4 +103,34 @@ public final class CraftRaid implements Raid {
public net.minecraft.world.entity.raid.Raid getHandle() {
return this.handle;
}
+
+ // Paper start - more Raid API
+ @Override
+ public int getId() {
+ return this.handle.getId();
+ }
+
+ @Override
+ public org.bukkit.boss.BossBar getBossBar() {
+ return new org.bukkit.craftbukkit.boss.CraftBossBar(this.handle.raidEvent);
+ }
+
+ @Override
+ public org.bukkit.persistence.PersistentDataContainer getPersistentDataContainer() {
+ return this.handle.persistentDataContainer;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || this.getClass() != o.getClass()) return false;
+ final org.bukkit.craftbukkit.CraftRaid craftRaid = (org.bukkit.craftbukkit.CraftRaid) o;
+ return this.handle.equals(craftRaid.handle);
+ }
+
+ @Override
+ public int hashCode() {
+ return this.handle.hashCode();
+ }
+ // Paper end - more Raid API
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index 2536a37ab9e5a7aa7373b1880f55cdb8c32a6f53..44ed6bd76fb9e81f6c0d99fe46173685dbbfe2a7 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -2398,6 +2398,14 @@ public class CraftWorld extends CraftRegionAccessor implements World {
return (raid == null) ? null : new CraftRaid(raid);
}
+ // Paper start - more Raid API
+ @Override
+ public @Nullable Raid getRaid(final int id) {
+ final net.minecraft.world.entity.raid.@Nullable Raid nmsRaid = this.world.getRaids().raidMap.get(id);
+ return nmsRaid != null ? new CraftRaid(nmsRaid) : null;
+ }
+ // Paper end - more Raid API
+
@Override
public List<Raid> getRaids() {
Raids persistentRaid = this.world.getRaids();

View file

@ -1,103 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: leguan <longboard.noah@gmail.com>
Date: Sun, 10 Mar 2024 20:10:41 +0100
Subject: [PATCH] Add onboarding message for initial server start
diff --git a/src/main/java/io/papermc/paper/configuration/Configurations.java b/src/main/java/io/papermc/paper/configuration/Configurations.java
index 7684e71f802f3d19e5340713b45cc84860ce9495..96142deb42700f888ea08689ab62c27ef2b881fd 100644
--- a/src/main/java/io/papermc/paper/configuration/Configurations.java
+++ b/src/main/java/io/papermc/paper/configuration/Configurations.java
@@ -129,6 +129,7 @@ public abstract class Configurations<G, W> {
if (Files.notExists(configFile)) {
node = CommentedConfigurationNode.root(loader.defaultOptions());
node.node(Configuration.VERSION_FIELD).raw(this.globalConfigVersion());
+ GlobalConfiguration.isFirstStart = true;
} else {
node = loader.load();
this.verifyGlobalConfigVersion(node);
diff --git a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
index ab5089781b8866cd6ad1b9570634ba84d936cfe7..4de88f74182bb596c6b5ad0351cc0dacefd0ce96 100644
--- a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
+++ b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
@@ -25,6 +25,7 @@ public class GlobalConfiguration extends ConfigurationPart {
private static final Logger LOGGER = LogUtils.getLogger();
static final int CURRENT_VERSION = 29; // (when you change the version, change the comment, so it conflicts on rebases): <insert changes here>
private static GlobalConfiguration instance;
+ public static boolean isFirstStart = false;
public static GlobalConfiguration get() {
return instance;
}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 2a92268dfc8ee264e4ee6ffc56b40a87e334acc8..1a9e323659dcff12ce53919eb3d6d6f66f835292 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1129,6 +1129,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
long tickSection = Util.getNanos();
long currentTime;
// Paper end - further improve server tick loop
+ // Paper start - Add onboarding message for initial server start
+ if (io.papermc.paper.configuration.GlobalConfiguration.isFirstStart) {
+ LOGGER.info("*************************************************************************************");
+ LOGGER.info("This is the first time you're starting this server.");
+ LOGGER.info("It's recommended you read our 'Getting Started' documentation for guidance.");
+ LOGGER.info("View this and more helpful information here: https://docs.papermc.io/paper/next-steps");
+ LOGGER.info("*************************************************************************************");
+ }
+ // Paper end - Add onboarding message for initial server start
+
while (this.running) {
long i;
diff --git a/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java b/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java
index 4d3fe4f56e0b264fa030409919caf52d5f421d46..759062d219ff490a3cb19e710c4d18e3e08288e0 100644
--- a/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java
+++ b/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java
@@ -90,6 +90,7 @@ public class MinecraftServerGui extends JComponent {
this.setLayout(new BorderLayout());
try {
+ this.add(this.buildOnboardingPanel(), "North"); // Paper - Add onboarding message for initial server start
this.add(this.buildChatPanel(), "Center");
this.add(this.buildInfoPanel(), "West");
} catch (Exception exception) {
@@ -115,6 +116,39 @@ public class MinecraftServerGui extends JComponent {
return jpanel;
}
+ // Paper start - Add onboarding message for initial server start
+ private JComponent buildOnboardingPanel() {
+ String onboardingLink = "https://docs.papermc.io/paper/next-steps";
+ JPanel jPanel = new JPanel();
+
+ javax.swing.JLabel jLabel = new javax.swing.JLabel("If you need help setting up your server you can visit:");
+ jLabel.setFont(MinecraftServerGui.MONOSPACED);
+
+ javax.swing.JLabel link = new javax.swing.JLabel("<html><u> " + onboardingLink + "</u></html>");
+ link.setFont(MinecraftServerGui.MONOSPACED);
+ link.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR));
+ link.addMouseListener(new java.awt.event.MouseAdapter() {
+ @Override
+ public void mouseClicked(final java.awt.event.MouseEvent e) {
+ try {
+ java.awt.Desktop.getDesktop().browse(java.net.URI.create(onboardingLink));
+ } catch (java.io.IOException exception) {
+ LOGGER.error("Unable to find a default browser. Please manually visit the website: " + onboardingLink, exception);
+ } catch (UnsupportedOperationException exception) {
+ LOGGER.error("This platform does not support the BROWSE action. Please manually visit the website: " + onboardingLink, exception);
+ } catch (SecurityException exception) {
+ LOGGER.error("This action has been denied by the security manager. Please manually visit the website: " + onboardingLink, exception);
+ }
+ }
+ });
+
+ jPanel.add(jLabel);
+ jPanel.add(link);
+
+ return jPanel;
+ }
+ // Paper end - Add onboarding message for initial server start
+
private JComponent buildPlayerPanel() {
JList<?> jlist = new PlayerListComponent(this.server);
JScrollPane jscrollpane = new JScrollPane(jlist, 22, 30);

View file

@ -1,22 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Gero <gecam59@gmail.com>
Date: Mon, 19 Feb 2024 17:39:59 +0100
Subject: [PATCH] Configurable max block/fluid ticks
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 4616672c4be74421e2acbc9df96f7c8ff2782e55..3a1ecdaebfd64f4703eafc92b5895e1dbddd8ee6 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -500,9 +500,9 @@ public class ServerLevel extends Level implements WorldGenLevel {
if (!this.isDebug() && flag) {
j = this.getGameTime();
gameprofilerfiller.push("blockTicks");
- this.blockTicks.tick(j, 65536, this::tickBlock);
+ this.blockTicks.tick(j, paperConfig().environment.maxBlockTicks, this::tickBlock); // Paper - configurable max block ticks
gameprofilerfiller.popPush("fluidTicks");
- this.fluidTicks.tick(j, 65536, this::tickFluid);
+ this.fluidTicks.tick(j, paperConfig().environment.maxFluidTicks, this::tickFluid); // Paper - configurable max fluid ticks
gameprofilerfiller.pop();
}
this.timings.scheduledBlocks.stopTiming(); // Paper

View file

@ -1,41 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Sat, 21 Aug 2021 21:54:16 -0700
Subject: [PATCH] Fix bees aging inside hives
Fixes bees incorrectly being aged up due to upstream's
resetting the ticks inside hive on a failed release
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 6fb7ec36f6f7a3021fac4b9e31fd333dfd5ea5e5..7b263fab4f0014400b3b8e7e33db32f9a125f6ba 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
@@ -314,7 +314,7 @@ public class BeehiveBlockEntity extends BlockEntity {
iterator.remove();
// CraftBukkit start
} else {
- tileentitybeehive_hivebee.ticksInHive = tileentitybeehive_hivebee.occupant.minTicksInHive / 2; // Not strictly Vanilla behaviour in cases where bees cannot spawn but still reasonable
+ tileentitybeehive_hivebee.exitTickCounter = tileentitybeehive_hivebee.occupant.minTicksInHive / 2; // Not strictly Vanilla behaviour in cases where bees cannot spawn but still reasonable // Paper - Fix bees aging inside hives; use exitTickCounter to keep actual bee life
// CraftBukkit end
}
}
@@ -474,15 +474,18 @@ public class BeehiveBlockEntity extends BlockEntity {
private static class BeeData {
private final BeehiveBlockEntity.Occupant occupant;
+ private int exitTickCounter; // Paper - Fix bees aging inside hives; separate counter for checking if bee should exit to reduce exit attempts
private int ticksInHive;
BeeData(BeehiveBlockEntity.Occupant data) {
this.occupant = data;
this.ticksInHive = data.ticksInHive();
+ this.exitTickCounter = this.ticksInHive; // Paper - Fix bees aging inside hives
}
public boolean tick() {
- return this.ticksInHive++ > this.occupant.minTicksInHive;
+ this.ticksInHive++; // Paper - Fix bees aging inside hives
+ return this.exitTickCounter++ > this.occupant.minTicksInHive; // Paper - Fix bees aging inside hives
}
public BeehiveBlockEntity.Occupant toOccupant() {

View file

@ -1,19 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Warrior <50800980+Warriorrrr@users.noreply.github.com>
Date: Thu, 18 Jan 2024 23:25:09 +0100
Subject: [PATCH] Disable memory reserve allocating
diff --git a/src/main/java/net/minecraft/CrashReport.java b/src/main/java/net/minecraft/CrashReport.java
index 06c514b2d0674cd7df6692981f020437ea0f2f91..bcb6a3b3cd17ce5db9aaf6bd3ec7a0ec1b44b979 100644
--- a/src/main/java/net/minecraft/CrashReport.java
+++ b/src/main/java/net/minecraft/CrashReport.java
@@ -250,7 +250,7 @@ public class CrashReport {
}
public static void preload() {
- MemoryReserve.allocate();
+ // MemoryReserve.allocate(); // Paper - Disable memory reserve allocating
(new CrashReport("Don't panic!", new Throwable())).getFriendlyReport();
}
}

View file

@ -1,19 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: RodneyMKay <36546810+RodneyMKay@users.noreply.github.com>
Date: Sun, 11 Feb 2024 20:05:11 +0100
Subject: [PATCH] Fire EntityDamageByEntityEvent for unowned wither skulls
diff --git a/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java b/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java
index 729aa0125479ab839792a3f35c981046899421b5..02931861de955352e71d6f5ffc720a17304815fe 100644
--- a/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java
+++ b/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java
@@ -72,7 +72,7 @@ public class WitherSkull extends AbstractHurtingProjectile {
}
}
} else {
- flag = entity.hurt(this.damageSources().magic(), 5.0F);
+ flag = entity.hurt(this.damageSources().magic().customCausingEntity(this), 5.0F); // Paper - Fire EntityDamageByEntityEvent for unowned wither skulls
}
if (flag && entity instanceof LivingEntity) {

View file

@ -1,211 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Sat, 9 Mar 2024 14:13:04 -0800
Subject: [PATCH] Fix DamageSource API
Uses the correct entity in the EntityDamageByEntity event
Returns the correct entity for API's DamageSource#getCausingEntity
diff --git a/src/main/java/net/minecraft/world/damagesource/DamageSource.java b/src/main/java/net/minecraft/world/damagesource/DamageSource.java
index 533ea6cbb813c8d1dc2bb3f65fd94d4fa5dd591e..dd9638bdb228a53e72820e0e7cf6fe6fcc08fe4b 100644
--- a/src/main/java/net/minecraft/world/damagesource/DamageSource.java
+++ b/src/main/java/net/minecraft/world/damagesource/DamageSource.java
@@ -29,7 +29,8 @@ public class DamageSource {
private boolean sweep = false;
private boolean melting = false;
private boolean poison = false;
- private Entity customEntityDamager = null; // This field is a helper for when causing entity damage is not set by vanilla
+ @Nullable
+ private Entity customEventDamager = null; // This field is a helper for when causing entity damage is not set by vanilla // Paper - fix DamageSource API
public DamageSource sweep() {
this.sweep = true;
@@ -58,18 +59,19 @@ public class DamageSource {
return this.poison;
}
- public Entity getDamager() {
- return (this.customEntityDamager != null) ? this.customEntityDamager : this.directEntity;
+ // Paper start - fix DamageSource API
+ @Nullable
+ public Entity getCustomEventDamager() {
+ return (this.customEventDamager != null) ? this.customEventDamager : this.directEntity;
}
- public DamageSource customEntityDamager(Entity entity) {
- // This method is not intended for change the causing entity if is already set
- // also is only necessary if the entity passed is not the direct entity or different from the current causingEntity
- if (this.customEntityDamager != null || this.directEntity == entity || this.causingEntity == entity) {
- return this;
+ public DamageSource customEventDamager(Entity entity) {
+ if (this.directEntity != null) {
+ throw new IllegalStateException("Cannot set custom event damager when direct entity is already set (report a bug to Paper)");
}
DamageSource damageSource = this.cloneInstance();
- damageSource.customEntityDamager = entity;
+ damageSource.customEventDamager = entity;
+ // Paper end - fix DamageSource API
return damageSource;
}
diff --git a/src/main/java/net/minecraft/world/damagesource/DamageSources.java b/src/main/java/net/minecraft/world/damagesource/DamageSources.java
index abda6094e02ebd2589ba0e4760e574fcf44dc8e4..5ec8cbd07a1830876f58e1fd33de6df4466d7e95 100644
--- a/src/main/java/net/minecraft/world/damagesource/DamageSources.java
+++ b/src/main/java/net/minecraft/world/damagesource/DamageSources.java
@@ -258,13 +258,7 @@ public class DamageSources {
}
public DamageSource explosion(@Nullable Entity source, @Nullable Entity attacker) {
- // CraftBukkit start
- return this.explosion(source, attacker, attacker != null && source != null ? DamageTypes.PLAYER_EXPLOSION : DamageTypes.EXPLOSION);
- }
-
- public DamageSource explosion(@Nullable Entity entity, @Nullable Entity entity1, ResourceKey<DamageType> resourceKey) {
- return this.source(resourceKey, entity, entity1);
- // CraftBukkit end
+ return this.source(attacker != null && source != null ? DamageTypes.PLAYER_EXPLOSION : DamageTypes.EXPLOSION, source, attacker); // Paper - revert to vanilla
}
public DamageSource sonicBoom(Entity attacker) {
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index d6ec2fe1a4a004a2d2f7d6e5bfd8fd45b27ebb7a..62363c09111aaa31220fb260940744c097af7b3c 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -3200,7 +3200,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
return;
}
- if (!this.hurt(this.damageSources().lightningBolt().customEntityDamager(lightning), 5.0F)) {
+ if (!this.hurt(this.damageSources().lightningBolt().customEventDamager(lightning), 5.0F)) { // Paper - fix DamageSource API
return;
}
// CraftBukkit end
diff --git a/src/main/java/net/minecraft/world/entity/animal/Turtle.java b/src/main/java/net/minecraft/world/entity/animal/Turtle.java
index b4d68a08ec1e4ec7bcc123bcb6c2dc90272430cf..ffffa53fcaa6ec8681b283fa20bb5a3320ad9c11 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Turtle.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Turtle.java
@@ -334,7 +334,7 @@ public class Turtle extends Animal {
@Override
public void thunderHit(ServerLevel world, LightningBolt lightning) {
- this.hurt(this.damageSources().lightningBolt().customEntityDamager(lightning), Float.MAX_VALUE); // CraftBukkit
+ this.hurt(this.damageSources().lightningBolt().customEventDamager(lightning), Float.MAX_VALUE); // CraftBukkit // Paper - fix DamageSource API
}
@Override
diff --git a/src/main/java/net/minecraft/world/entity/decoration/HangingEntity.java b/src/main/java/net/minecraft/world/entity/decoration/HangingEntity.java
index 0ffff5329fa2c7833f9ec71528cb7f951cf78109..bf2d91bbb4bf401696f5f5d14a67e3920a179084 100644
--- a/src/main/java/net/minecraft/world/entity/decoration/HangingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/decoration/HangingEntity.java
@@ -203,7 +203,7 @@ public abstract class HangingEntity extends Entity {
} else {
if (!this.isRemoved() && !this.level().isClientSide) {
// CraftBukkit start - fire break events
- Entity damager = (source.isIndirect()) ? source.getEntity() : source.getDirectEntity();
+ Entity damager = (source.isIndirect() && source.getEntity() != null) ? source.getEntity() : source.getDirectEntity(); // Paper - fix DamageSource API
HangingBreakEvent event;
if (damager != null) {
event = new HangingBreakByEntityEvent((Hanging) this.getBukkitEntity(), damager.getBukkitEntity(), source.is(DamageTypeTags.IS_EXPLOSION) ? HangingBreakEvent.RemoveCause.EXPLOSION : HangingBreakEvent.RemoveCause.ENTITY);
diff --git a/src/main/java/net/minecraft/world/entity/monster/Creeper.java b/src/main/java/net/minecraft/world/entity/monster/Creeper.java
index 7db5de71b0353d9807978c0351870fd99b76ee67..0ae4ba060b2ce2c79e1235c939f3c1926eb6e33e 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Creeper.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Creeper.java
@@ -271,7 +271,7 @@ public class Creeper extends Monster implements PowerableMob {
if (!event.isCancelled()) {
// CraftBukkit end
this.dead = true;
- this.level().explode(this, this.level().damageSources().explosion(this, this.entityIgniter, net.minecraft.world.damagesource.DamageTypes.EXPLOSION), null, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), Level.ExplosionInteraction.MOB); // CraftBukkit
+ this.level().explode(this, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), Level.ExplosionInteraction.MOB); // CraftBukkit // Paper - fix DamageSource API (revert to vanilla, no, just no, don't change this)
this.discard(EntityRemoveEvent.Cause.EXPLODE); // CraftBukkit - add Bukkit remove cause
this.spawnLingeringCloud();
// CraftBukkit start
diff --git a/src/main/java/net/minecraft/world/entity/projectile/EvokerFangs.java b/src/main/java/net/minecraft/world/entity/projectile/EvokerFangs.java
index edc5cfd3f3eab176df9080cf932dac3905f1b26d..f8aa723f18e28b02d4b7055404922ff8d7a73f8d 100644
--- a/src/main/java/net/minecraft/world/entity/projectile/EvokerFangs.java
+++ b/src/main/java/net/minecraft/world/entity/projectile/EvokerFangs.java
@@ -133,7 +133,7 @@ public class EvokerFangs extends Entity implements TraceableEntity {
if (target.isAlive() && !target.isInvulnerable() && target != entityliving1) {
if (entityliving1 == null) {
- target.hurt(this.damageSources().magic().customEntityDamager(this), 6.0F); // CraftBukkit
+ target.hurt(this.damageSources().magic().customEventDamager(this), 6.0F); // CraftBukkit // Paper - fix DamageSource API
} else {
if (entityliving1.isAlliedTo((Entity) target)) {
return;
diff --git a/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java b/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java
index ced6bf2fc4556c9b2b32ac382058506e931c1475..519755b7f8bc7e8bb9fab135fc5bf7de3a9419f9 100644
--- a/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java
+++ b/src/main/java/net/minecraft/world/entity/projectile/ThrownEnderpearl.java
@@ -85,7 +85,7 @@ public class ThrownEnderpearl extends ThrowableItemProjectile {
entityplayer.connection.teleport(teleEvent.getTo());
entity.resetFallDistance();
- entity.hurt(this.damageSources().fall().customEntityDamager(this), 5.0F); // CraftBukkit
+ entity.hurt(this.damageSources().fall().customEventDamager(this), 5.0F); // CraftBukkit // Paper - fix DamageSource API
}
// CraftBukkit end
this.level().playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_TELEPORT, SoundSource.PLAYERS);
diff --git a/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java b/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java
index 02931861de955352e71d6f5ffc720a17304815fe..55b4b5ad5f084c9a271a716d076672478d6486ba 100644
--- a/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java
+++ b/src/main/java/net/minecraft/world/entity/projectile/WitherSkull.java
@@ -72,7 +72,7 @@ public class WitherSkull extends AbstractHurtingProjectile {
}
}
} else {
- flag = entity.hurt(this.damageSources().magic().customCausingEntity(this), 5.0F); // Paper - Fire EntityDamageByEntityEvent for unowned wither skulls
+ flag = entity.hurt(this.damageSources().magic().customEventDamager(this), 5.0F); // Paper - Fire EntityDamageByEntityEvent for unowned wither skulls // Paper - fix DamageSource API
}
if (flag && entity instanceof LivingEntity) {
diff --git a/src/main/java/org/bukkit/craftbukkit/damage/CraftDamageSource.java b/src/main/java/org/bukkit/craftbukkit/damage/CraftDamageSource.java
index ab67c5caaff6e8c7de293b528636f53254b805bd..6d92856b5089e946a85d519cadce080bc6c3574f 100644
--- a/src/main/java/org/bukkit/craftbukkit/damage/CraftDamageSource.java
+++ b/src/main/java/org/bukkit/craftbukkit/damage/CraftDamageSource.java
@@ -47,7 +47,7 @@ public class CraftDamageSource implements DamageSource {
@Override
public org.bukkit.entity.Entity getDirectEntity() {
- net.minecraft.world.entity.Entity entity = this.getHandle().getDamager();
+ net.minecraft.world.entity.Entity entity = this.getHandle().getDirectEntity(); // Paper - fix DamageSource API
return (entity != null) ? entity.getBukkitEntity() : null;
}
@@ -65,7 +65,7 @@ public class CraftDamageSource implements DamageSource {
@Override
public boolean isIndirect() {
- return this.getHandle().getEntity() != this.getHandle().getDamager();
+ return this.getHandle().isIndirect(); // Paper - fix DamageSource API
}
@Override
diff --git a/src/main/java/org/bukkit/craftbukkit/damage/CraftDamageSourceBuilder.java b/src/main/java/org/bukkit/craftbukkit/damage/CraftDamageSourceBuilder.java
index 4c6e15535fa40aad8cf1920f392589404f9ba79c..35eb95ef6fb6a0f7ea63351e90741c489fdd15f9 100644
--- a/src/main/java/org/bukkit/craftbukkit/damage/CraftDamageSourceBuilder.java
+++ b/src/main/java/org/bukkit/craftbukkit/damage/CraftDamageSourceBuilder.java
@@ -41,6 +41,11 @@ public class CraftDamageSourceBuilder implements DamageSource.Builder {
@Override
public DamageSource build() {
+ // Paper start - fix DamageCause API
+ if (this.causingEntity != null && this.directEntity == null) {
+ throw new IllegalArgumentException("Direct entity must be set if causing entity is set");
+ }
+ // Paper end - fix DamageCause API
return CraftDamageSource.buildFromBukkit(this.damageType, this.causingEntity, this.directEntity, this.damageLocation);
}
}
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index e20feab675d05ae5b5b19870546585936c757970..96070da795755f71e99979288e32b7d7e2d869fb 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -1084,7 +1084,7 @@ public class CraftEventFactory {
private static EntityDamageEvent handleEntityDamageEvent(Entity entity, DamageSource source, Map<DamageModifier, Double> modifiers, Map<DamageModifier, Function<? super Double, Double>> modifierFunctions, boolean cancelled) {
CraftDamageSource bukkitDamageSource = new CraftDamageSource(source);
- Entity damager = (source.getDamager() != null) ? source.getDamager() : source.getEntity();
+ final Entity damager = source.getCustomEventDamager(); // Paper - fix DamageSource API
if (source.is(DamageTypeTags.IS_EXPLOSION)) {
if (damager == null) {
return CraftEventFactory.callEntityDamageEvent(source.getDirectBlock(), source.getDirectBlockState(), entity, DamageCause.BLOCK_EXPLOSION, bukkitDamageSource, modifiers, modifierFunctions, cancelled);

View file

@ -1,59 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Pierpaolo Coletta <p.coletta@glyart.com>
Date: Sat, 30 Mar 2024 21:06:10 +0100
Subject: [PATCH] Fix creation of invalid block entity during world generation
diff --git a/src/main/java/net/minecraft/server/level/WorldGenRegion.java b/src/main/java/net/minecraft/server/level/WorldGenRegion.java
index a59eece9c7a8c33cb8ce963906e993c3462684fb..386fbf79afe91af445f54aeab7d1296d1407a4d8 100644
--- a/src/main/java/net/minecraft/server/level/WorldGenRegion.java
+++ b/src/main/java/net/minecraft/server/level/WorldGenRegion.java
@@ -323,7 +323,7 @@ public class WorldGenRegion implements WorldGenLevel {
return false;
} else {
ChunkAccess ichunkaccess = this.getChunk(pos);
- BlockState iblockdata1 = ichunkaccess.setBlockState(pos, state, false);
+ BlockState iblockdata1 = ichunkaccess.setBlockState(pos, state, false); final BlockState previousBlockState = iblockdata1; // Paper - Clear block entity before setting up a DUMMY block entity - obfhelper
if (iblockdata1 != null) {
this.level.onBlockStateChange(pos, iblockdata1, state);
@@ -339,6 +339,17 @@ public class WorldGenRegion implements WorldGenLevel {
ichunkaccess.removeBlockEntity(pos);
}
} else {
+ // Paper start - Clear block entity before setting up a DUMMY block entity
+ // The concept of removing a block entity when the block itself changes is generally lifted
+ // from LevelChunk#setBlockState.
+ // It is however to note that this may only run if the block actually changes.
+ // Otherwise a chest block entity generated by a structure template that is later "updated" to
+ // be waterlogged would remove its existing block entity (see PaperMC/Paper#10750)
+ // This logic is *also* found in LevelChunk#setBlockState.
+ if (previousBlockState != null && !java.util.Objects.equals(previousBlockState.getBlock(), state.getBlock())) {
+ ichunkaccess.removeBlockEntity(pos);
+ }
+ // Paper end - Clear block entity before setting up a DUMMY block entity
CompoundTag nbttagcompound = new CompoundTag();
nbttagcompound.putInt("x", pos.getX());
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
index 744db9eec4f7bdeb32f83300960a7fce63b393d8..8de6ad8b131061b2dae440dff71e2e6e7af2de39 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
@@ -1071,9 +1071,14 @@ public class LevelChunk extends ChunkAccess {
if (this.blockEntity.getType().isValid(iblockdata)) {
this.ticker.tick(LevelChunk.this.level, this.blockEntity.getBlockPos(), iblockdata, this.blockEntity);
this.loggedInvalidBlockState = false;
- } else if (!this.loggedInvalidBlockState) {
- this.loggedInvalidBlockState = true;
- LevelChunk.LOGGER.warn("Block entity {} @ {} state {} invalid for ticking:", new Object[]{LogUtils.defer(this::getType), LogUtils.defer(this::getPos), iblockdata});
+ // Paper start - Remove the Block Entity if it's invalid
+ } else {
+ LevelChunk.this.removeBlockEntity(this.getPos());
+ if (!this.loggedInvalidBlockState) {
+ this.loggedInvalidBlockState = true;
+ LevelChunk.LOGGER.warn("Block entity {} @ {} state {} invalid for ticking:", new Object[]{LogUtils.defer(this::getType), LogUtils.defer(this::getPos), iblockdata});
+ }
+ // Paper end - Remove the Block Entity if it's invalid
}
gameprofilerfiller.pop();

View file

@ -1,108 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Sat, 29 Oct 2022 17:02:42 -0700
Subject: [PATCH] Fix possible StackOverflowError for some dispenses
For saddles, carpets, horse armor, and chests for horse-likes
a BlockDispenseEvent handler that always mutated the item without
changing the type would result in a SO error because when it went
to find the replacement dispense behavior (since the item "changed")
it didn't properly handle if the replacement was the same instance
of dispense behavior.
Additionally equippable mob heads, wither skulls, and carved pumpkins
are subject to the same possible error.
diff --git a/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java b/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java
index 4fa838bf97ede6e1c893ba64f53aa7af5db0405a..32084df594649d8da04052bbfa111896d8ea1f91 100644
--- a/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java
+++ b/src/main/java/net/minecraft/core/dispenser/DispenseItemBehavior.java
@@ -238,7 +238,7 @@ public interface DispenseItemBehavior {
// Chain to handler for new item
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
- if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != ArmorItem.DISPENSE_ITEM_BEHAVIOR) {
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) { // Paper - fix possible StackOverflowError
idispensebehavior.dispense(pointer, eventStack);
return stack;
}
@@ -294,7 +294,7 @@ public interface DispenseItemBehavior {
// Chain to handler for new item
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
- if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != ArmorItem.DISPENSE_ITEM_BEHAVIOR) {
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) { // Paper - fix possible StackOverflowError
idispensebehavior.dispense(pointer, eventStack);
return stack;
}
@@ -368,7 +368,7 @@ public interface DispenseItemBehavior {
// Chain to handler for new item
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
- if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != ArmorItem.DISPENSE_ITEM_BEHAVIOR) {
+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) { // Paper - fix possible StackOverflowError
idispensebehavior.dispense(pointer, eventStack);
return stack;
}
@@ -710,7 +710,7 @@ public interface DispenseItemBehavior {
OptionalDispenseItemBehavior dispensebehaviormaybe1 = new OptionalDispenseItemBehavior() {
@Override
protected ItemStack execute(BlockSource pointer, ItemStack stack) {
- this.setSuccess(ArmorItem.dispenseArmor(pointer, stack));
+ this.setSuccess(ArmorItem.dispenseArmor(pointer, stack, this)); // Paper - fix possible StackOverflowError
return stack;
}
};
@@ -764,7 +764,7 @@ public interface DispenseItemBehavior {
stack.shrink(1);
this.setSuccess(true);
} else {
- this.setSuccess(ArmorItem.dispenseArmor(pointer, stack));
+ this.setSuccess(ArmorItem.dispenseArmor(pointer, stack, this)); // Paper - fix possible StackOverflowError
}
return stack;
@@ -810,7 +810,7 @@ public interface DispenseItemBehavior {
stack.shrink(1);
this.setSuccess(true);
} else {
- this.setSuccess(ArmorItem.dispenseArmor(pointer, stack));
+ this.setSuccess(ArmorItem.dispenseArmor(pointer, stack, this)); // Paper - fix possible StackOverflowError
}
return stack;
diff --git a/src/main/java/net/minecraft/world/item/ArmorItem.java b/src/main/java/net/minecraft/world/item/ArmorItem.java
index e766397aae3f73548b290b0809b9d1ca0967ea39..786e4a8700cb84b16dd9b8892a0d1d5803924d81 100644
--- a/src/main/java/net/minecraft/world/item/ArmorItem.java
+++ b/src/main/java/net/minecraft/world/item/ArmorItem.java
@@ -48,14 +48,20 @@ public class ArmorItem extends Item implements Equipable {
public static final DispenseItemBehavior DISPENSE_ITEM_BEHAVIOR = new DefaultDispenseItemBehavior() {
@Override
protected ItemStack execute(BlockSource pointer, ItemStack stack) {
- return ArmorItem.dispenseArmor(pointer, stack) ? stack : super.execute(pointer, stack);
+ return ArmorItem.dispenseArmor(pointer, stack, this) ? stack : super.execute(pointer, stack); // Paper - fix possible StackOverflowError
}
};
protected final ArmorItem.Type type;
protected final Holder<ArmorMaterial> material;
private final Supplier<ItemAttributeModifiers> defaultModifiers;
+ @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper
public static boolean dispenseArmor(BlockSource pointer, ItemStack armor) {
+ // Paper start
+ return dispenseArmor(pointer, armor, null);
+ }
+ public static boolean dispenseArmor(BlockSource pointer, ItemStack armor, @javax.annotation.Nullable DispenseItemBehavior currentBehavior) {
+ // Paper end
BlockPos blockposition = pointer.pos().relative((Direction) pointer.state().getValue(DispenserBlock.FACING));
List<LivingEntity> list = pointer.level().getEntitiesOfClass(LivingEntity.class, new AABB(blockposition), EntitySelector.NO_SPECTATORS.and(new EntitySelector.MobCanWearArmorEntitySelector(armor)));
@@ -86,7 +92,7 @@ public class ArmorItem extends Item implements Equipable {
// Chain to handler for new item
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
DispenseItemBehavior idispensebehavior = (DispenseItemBehavior) DispenserBlock.DISPENSER_REGISTRY.get(eventStack.getItem());
- if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != ArmorItem.DISPENSE_ITEM_BEHAVIOR) {
+ if (idispensebehavior != DispenseItemBehavior.NOOP && (currentBehavior == null || idispensebehavior != currentBehavior)) { // Paper - fix possible StackOverflowError
idispensebehavior.dispense(pointer, eventStack);
return true;
}

View file

@ -1,202 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Nassim Jahnke <nassim@njahnke.dev>
Date: Mon, 5 Feb 2024 11:54:04 +0100
Subject: [PATCH] Improve tag parser handling
diff --git a/src/main/java/com/mojang/brigadier/CommandDispatcher.java b/src/main/java/com/mojang/brigadier/CommandDispatcher.java
index 92848b64a78fce7a92e1657c2da6fc5ee53eea44..4b4f812eb13d5f03bcf3f8724d8aa8dbbc724e8b 100644
--- a/src/main/java/com/mojang/brigadier/CommandDispatcher.java
+++ b/src/main/java/com/mojang/brigadier/CommandDispatcher.java
@@ -304,9 +304,15 @@ public class CommandDispatcher<S> {
}
final CommandContextBuilder<S> context = contextSoFar.copy();
final StringReader reader = new StringReader(originalReader);
+ boolean stop = false; // Paper - Handle non-recoverable exceptions
try {
try {
child.parse(reader, context);
+ // Paper start - Handle non-recoverable exceptions
+ } catch (final io.papermc.paper.brigadier.TagParseCommandSyntaxException e) {
+ stop = true;
+ throw e;
+ // Paper end - Handle non-recoverable exceptions
} catch (final RuntimeException ex) {
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherParseException().createWithContext(reader, ex.getMessage());
}
@@ -321,6 +327,7 @@ public class CommandDispatcher<S> {
}
errors.put(child, ex);
reader.setCursor(cursor);
+ if (stop) return new ParseResults<>(contextSoFar, originalReader, errors); // Paper - Handle non-recoverable exceptions
continue;
}
diff --git a/src/main/java/io/papermc/paper/brigadier/TagParseCommandSyntaxException.java b/src/main/java/io/papermc/paper/brigadier/TagParseCommandSyntaxException.java
new file mode 100644
index 0000000000000000000000000000000000000000..a375ad4ba9db990b24a2b9ff366fcba66b753815
--- /dev/null
+++ b/src/main/java/io/papermc/paper/brigadier/TagParseCommandSyntaxException.java
@@ -0,0 +1,15 @@
+package io.papermc.paper.brigadier;
+
+import com.mojang.brigadier.LiteralMessage;
+import com.mojang.brigadier.exceptions.CommandSyntaxException;
+import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
+import net.minecraft.network.chat.Component;
+
+public final class TagParseCommandSyntaxException extends CommandSyntaxException {
+
+ private static final SimpleCommandExceptionType EXCEPTION_TYPE = new SimpleCommandExceptionType(new LiteralMessage("Error parsing NBT"));
+
+ public TagParseCommandSyntaxException(final String message) {
+ super(EXCEPTION_TYPE, Component.literal(message));
+ }
+}
diff --git a/src/main/java/net/minecraft/nbt/TagParser.java b/src/main/java/net/minecraft/nbt/TagParser.java
index da101bca71f4710812621b98f0a0d8cab180346a..3cd112584accb8e8f050ac99738eed11c902e60e 100644
--- a/src/main/java/net/minecraft/nbt/TagParser.java
+++ b/src/main/java/net/minecraft/nbt/TagParser.java
@@ -49,6 +49,7 @@ public class TagParser {
}, CompoundTag::toString);
public static final Codec<CompoundTag> LENIENT_CODEC = Codec.withAlternative(AS_CODEC, CompoundTag.CODEC);
private final StringReader reader;
+ private int depth; // Paper
public static CompoundTag parseTag(String string) throws CommandSyntaxException {
return new TagParser(new StringReader(string)).readSingleStruct();
@@ -159,6 +160,7 @@ public class TagParser {
public CompoundTag readStruct() throws CommandSyntaxException {
this.expect('{');
+ this.increaseDepth(); // Paper
CompoundTag compoundTag = new CompoundTag();
this.reader.skipWhitespace();
@@ -182,6 +184,7 @@ public class TagParser {
}
this.expect('}');
+ this.depth--; // Paper
return compoundTag;
}
@@ -191,6 +194,7 @@ public class TagParser {
if (!this.reader.canRead()) {
throw ERROR_EXPECTED_VALUE.createWithContext(this.reader);
} else {
+ this.increaseDepth(); // Paper
ListTag listTag = new ListTag();
TagType<?> tagType = null;
@@ -216,6 +220,7 @@ public class TagParser {
}
this.expect(']');
+ this.depth--; // Paper
return listTag;
}
}
@@ -288,4 +293,11 @@ public class TagParser {
this.reader.skipWhitespace();
this.reader.expect(c);
}
+
+ private void increaseDepth() throws CommandSyntaxException {
+ this.depth++;
+ if (this.depth > 512) {
+ throw new io.papermc.paper.brigadier.TagParseCommandSyntaxException("NBT tag is too complex, depth > 512");
+ }
+ }
}
diff --git a/src/main/java/net/minecraft/network/chat/contents/TranslatableContents.java b/src/main/java/net/minecraft/network/chat/contents/TranslatableContents.java
index 56e641bc5f6edc657647993ea2efbb7bb9c2f732..4aa6232bf0f72fcde32d257100bd15b1c5192aaa 100644
--- a/src/main/java/net/minecraft/network/chat/contents/TranslatableContents.java
+++ b/src/main/java/net/minecraft/network/chat/contents/TranslatableContents.java
@@ -181,6 +181,15 @@ public class TranslatableContents implements ComponentContents {
@Override
public <T> Optional<T> visit(FormattedText.ContentConsumer<T> visitor) {
+ // Paper start - Count visited parts
+ try {
+ return this.visit(new TranslatableContentConsumer<>(visitor));
+ } catch (IllegalArgumentException ignored) {
+ return visitor.accept("...");
+ }
+ }
+ private <T> Optional<T> visit(TranslatableContentConsumer<T> visitor) {
+ // Paper end - Count visited parts
this.decompose();
for (FormattedText formattedText : this.decomposedParts) {
@@ -192,6 +201,25 @@ public class TranslatableContents implements ComponentContents {
return Optional.empty();
}
+ // Paper start - Count visited parts
+ private static final class TranslatableContentConsumer<T> implements FormattedText.ContentConsumer<T> {
+ private static final IllegalArgumentException EX = new IllegalArgumentException("Too long");
+ private final FormattedText.ContentConsumer<T> visitor;
+ private int visited;
+
+ private TranslatableContentConsumer(FormattedText.ContentConsumer<T> visitor) {
+ this.visitor = visitor;
+ }
+
+ @Override
+ public Optional<T> accept(final String asString) {
+ if (visited++ > 32) {
+ throw EX;
+ }
+ return this.visitor.accept(asString);
+ }
+ }
+ // Paper end - Count visited parts
@Override
public MutableComponent resolve(@Nullable CommandSourceStack source, @Nullable Entity sender, int depth) throws CommandSyntaxException {
diff --git a/src/main/java/net/minecraft/network/protocol/game/ServerboundCommandSuggestionPacket.java b/src/main/java/net/minecraft/network/protocol/game/ServerboundCommandSuggestionPacket.java
index 898b19887ed34c87003fc63cb5905df2ba6234a5..b47eeb23055b135d5567552ba983bfbc3e1fab67 100644
--- a/src/main/java/net/minecraft/network/protocol/game/ServerboundCommandSuggestionPacket.java
+++ b/src/main/java/net/minecraft/network/protocol/game/ServerboundCommandSuggestionPacket.java
@@ -19,7 +19,7 @@ public class ServerboundCommandSuggestionPacket implements Packet<ServerGamePack
private ServerboundCommandSuggestionPacket(FriendlyByteBuf buf) {
this.id = buf.readVarInt();
- this.command = buf.readUtf(32500);
+ this.command = buf.readUtf(2048); // Paper
}
private void write(FriendlyByteBuf buf) {
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index bead9625a04a9c1139c54d6a56f055b80eb2d701..34d4243fb5198e36ea3291ffe8ed2480bc960cdb 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -763,6 +763,13 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
return;
}
// Paper end - Don't suggest if tab-complete is disabled
+ // Paper start
+ final int index;
+ if (packet.getCommand().length() > 64 && ((index = packet.getCommand().indexOf(' ')) == -1 || index >= 64)) {
+ this.disconnect(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM);
+ return;
+ }
+ // Paper end
// Paper start - AsyncTabCompleteEvent
TAB_COMPLETE_EXECUTOR.execute(() -> this.handleCustomCommandSuggestions0(packet));
}
@@ -815,6 +822,13 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
private void sendServerSuggestions(final ServerboundCommandSuggestionPacket packet, final StringReader stringreader) {
// Paper end - AsyncTabCompleteEvent
ParseResults<CommandSourceStack> parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack());
+ // Paper start - Handle non-recoverable exceptions
+ if (!parseresults.getExceptions().isEmpty()
+ && parseresults.getExceptions().values().stream().anyMatch(e -> e instanceof io.papermc.paper.brigadier.TagParseCommandSyntaxException)) {
+ this.disconnect(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM);
+ return;
+ }
+ // Paper end - Handle non-recoverable exceptions
this.server.getCommands().getDispatcher().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> {
// Paper start - Don't tab-complete namespaced commands if send-namespaced is false

View file

@ -1,38 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com>
Date: Wed, 20 Mar 2024 20:41:35 -0400
Subject: [PATCH] Item Mutation Fixes
diff --git a/src/main/java/net/minecraft/world/inventory/EnchantmentMenu.java b/src/main/java/net/minecraft/world/inventory/EnchantmentMenu.java
index 2f62957e061f513e01ec617f22791a7f27d65f02..480d093105073edfd3acdd7b079b4ca5aa5fdc6d 100644
--- a/src/main/java/net/minecraft/world/inventory/EnchantmentMenu.java
+++ b/src/main/java/net/minecraft/world/inventory/EnchantmentMenu.java
@@ -228,7 +228,7 @@ public class EnchantmentMenu extends AbstractContainerMenu {
return false;
} else if (this.costs[id] > 0 && !itemstack.isEmpty() && (player.experienceLevel >= j && player.experienceLevel >= this.costs[id] || player.getAbilities().instabuild)) {
this.access.execute((world, blockposition) -> {
- ItemStack itemstack2 = itemstack;
+ ItemStack itemstack2 = itemstack; // Paper - diff on change
List<EnchantmentInstance> list = this.getEnchantmentList(world.enabledFeatures(), itemstack, id, this.costs[id]);
// CraftBukkit start
@@ -250,10 +250,16 @@ public class EnchantmentMenu extends AbstractContainerMenu {
return;
}
// CraftBukkit end
- if (itemstack.is(Items.BOOK)) {
- itemstack2 = itemstack.transmuteCopy(Items.ENCHANTED_BOOK, 1);
+ // Paper start
+ itemstack2 = org.bukkit.craftbukkit.inventory.CraftItemStack.getOrCloneOnMutation(item, event.getItem());
+ if (itemstack2 != itemstack) {
this.enchantSlots.setItem(0, itemstack2);
}
+ if (itemstack2.is(Items.BOOK)) {
+ itemstack2 = itemstack2.transmuteCopy(Items.ENCHANTED_BOOK, 1);
+ this.enchantSlots.setItem(0, itemstack2);
+ }
+ // Paper end
// CraftBukkit start
for (Map.Entry<org.bukkit.enchantments.Enchantment, Integer> entry : event.getEnchantsToAdd().entrySet()) {