This commit is contained in:
Bjarne Koll 2024-10-25 12:28:34 +02:00
parent 227caac7f0
commit afbb0d88dd
No known key found for this signature in database
GPG key ID: 27F6CCCF55D2EE62
15 changed files with 156 additions and 145 deletions

View file

@ -1,73 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: masmc05 <masmc05@gmail.com>
Date: Sun, 11 Aug 2024 03:01:52 +0300
Subject: [PATCH] Item serialization as json
diff --git a/src/main/java/net/minecraft/world/item/component/CustomData.java b/src/main/java/net/minecraft/world/item/component/CustomData.java
index 6b7245cf05ea4b6ce05462eb3164bce7f5d76a03..ac1914438307e8a7cc3a3b6352e88a0638f8a33b 100644
--- a/src/main/java/net/minecraft/world/item/component/CustomData.java
+++ b/src/main/java/net/minecraft/world/item/component/CustomData.java
@@ -28,7 +28,17 @@ import org.slf4j.Logger;
public final class CustomData {
private static final Logger LOGGER = LogUtils.getLogger();
public static final CustomData EMPTY = new CustomData(new CompoundTag());
- public static final Codec<CustomData> CODEC = Codec.withAlternative(CompoundTag.CODEC, TagParser.AS_CODEC)
+ // Paper start - Item serialization as json
+ public static ThreadLocal<Boolean> SERIALIZE_CUSTOM_AS_SNBT = ThreadLocal.withInitial(() -> false);
+ public static final Codec<CustomData> CODEC = Codec.either(CompoundTag.CODEC, TagParser.AS_CODEC)
+ .xmap(com.mojang.datafixers.util.Either::unwrap, data -> { // Both will be used for deserialization, but we decide which one to use for serialization
+ if (!SERIALIZE_CUSTOM_AS_SNBT.get()) {
+ return com.mojang.datafixers.util.Either.left(data); // First codec
+ } else {
+ return com.mojang.datafixers.util.Either.right(data); // Second codec
+ }
+ })
+ // Paper end - Item serialization as json
.xmap(CustomData::new, component -> component.tag);
public static final Codec<CustomData> CODEC_WITH_ID = CODEC.validate(
component -> component.getUnsafe().contains("id", 8) ? DataResult.success(component) : DataResult.error(() -> "Missing id for entity in: " + component)
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
index 8b2dbdfcdc4e98602f6bfd48d2c53840730f4691..d06aab9bd5cd901c8367f9680f5d27ddb17b3dc4 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
@@ -511,6 +511,39 @@ public final class CraftMagicNumbers implements UnsafeValues {
return CraftItemStack.asCraftMirror(net.minecraft.world.item.ItemStack.parse(MinecraftServer.getServer().registryAccess(), compound).orElseThrow());
}
+ @Override
+ public com.google.gson.JsonObject serializeItemAsJson(ItemStack itemStack) {
+ Preconditions.checkNotNull(itemStack, "Cannot serialize empty ItemStack");
+ Preconditions.checkArgument(!itemStack.isEmpty(), "Cannot serialize empty ItemStack");
+
+ net.minecraft.core.RegistryAccess.Frozen reg = net.minecraft.server.MinecraftServer.getServer().registryAccess();
+ com.mojang.serialization.DynamicOps<com.google.gson.JsonElement> ops = reg.createSerializationContext(com.mojang.serialization.JsonOps.INSTANCE);
+ com.google.gson.JsonObject item;
+ // Serialize as SNBT to preserve exact NBT types; vanilla codecs already can handle such deserialization.
+ net.minecraft.world.item.component.CustomData.SERIALIZE_CUSTOM_AS_SNBT.set(true);
+ try {
+ item = net.minecraft.world.item.ItemStack.CODEC.encodeStart(ops, CraftItemStack.unwrap(itemStack)).getOrThrow().getAsJsonObject();
+ } finally {
+ net.minecraft.world.item.component.CustomData.SERIALIZE_CUSTOM_AS_SNBT.set(false);
+ }
+ item.addProperty("DataVersion", this.getDataVersion());
+ return item;
+ }
+
+ @Override
+ public ItemStack deserializeItemFromJson(com.google.gson.JsonObject data) throws IllegalArgumentException {
+ Preconditions.checkNotNull(data, "null cannot be deserialized");
+
+ final int dataVersion = data.get("DataVersion").getAsInt();
+ final int currentVersion = org.bukkit.craftbukkit.util.CraftMagicNumbers.INSTANCE.getDataVersion();
+ data = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertJson(
+ ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.ITEM_STACK,
+ data, false, dataVersion, currentVersion
+ );
+ com.mojang.serialization.DynamicOps<com.google.gson.JsonElement> ops = MinecraftServer.getServer().registryAccess().createSerializationContext(com.mojang.serialization.JsonOps.INSTANCE);
+ return CraftItemStack.asCraftMirror(net.minecraft.world.item.ItemStack.CODEC.parse(ops, data).getOrThrow(IllegalArgumentException::new));
+ }
+
@Override
public byte[] serializeEntity(org.bukkit.entity.Entity entity) {
Preconditions.checkNotNull(entity, "null cannot be serialized");

View file

@ -1,26 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: strnq <dev@aruus.uk>
Date: Sat, 14 Sep 2024 12:53:13 +0300
Subject: [PATCH] Validate slot in PlayerInventory#setSlot
The CraftPlayerInventory implementation sends a container_set_slot
packet to the client which will error if an invalid slot is passed to
the setSlot method, making a validation necessary over simply silently
ignoring invalid slot values.
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java
index eafa54c870c3e2aef30c3f9f96f516607a7cae24..8dea4321e41080829b474ad7b5a12c6a622181fd 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java
@@ -70,6 +70,11 @@ public class CraftInventoryPlayer extends CraftInventory implements org.bukkit.i
@Override
public void setItem(int index, ItemStack item) {
+ // Paper start - Validate setItem index
+ if (index < 0 || index > 40) {
+ throw new ArrayIndexOutOfBoundsException("Index must be between 0 and 40");
+ }
+ // Paper end - Validate setItem index
super.setItem(index, item);
if (this.getHolder() == null) return;
ServerPlayer player = ((CraftPlayer) this.getHolder()).getHandle();

View file

@ -1,163 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Bjarne Koll <lynxplay101@gmail.com>
Date: Mon, 16 Sep 2024 23:07:29 +0200
Subject: [PATCH] Remove wall-time / unused skip tick protection
Spigot still maintains some partial implementation of "tick skipping", a
practice in which the MinecraftServer.currentTick field is updated not
by an increment of one per actual tick, but instead set to
System.currentTimeMillis() / 50. This behaviour means that the tracked
tick may "skip" a tick value in case a previous tick took more than the
expected 50ms.
To compensate for this in important paths, spigot/craftbukkit
implements "wall-time". Instead of incrementing/decrementing ticks on
block entities/entities by one for each call to their tick() method,
they instead increment/decrement important values, like
an ItemEntity's age or pickupDelay, by the difference of
`currentTick - lastTick`, where `lastTick` is the value of
`currentTick` during the last tick() call.
These "fixes" however do not play nicely with minecraft's simulation
distance as entities/block entities implementing the above behaviour
would "catch up" their values when moving from a non-ticking chunk to a
ticking one as their `lastTick` value remains stuck on the last tick in
a ticking chunk and hence lead to a large "catch up" once ticked again.
Paper completely removes the "tick skipping" behaviour (See patch
"Further-improve-server-tick-loop"), making the above precautions
completely unnecessary, which also rids paper of the previous described
incompatibility with non-ticking chunks.
diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
index 03cfa29bdb426a9fb6b1b6be6e897da48d4f2f3e..4423973d4d9a2c3879d98d1d4c8b8c117c677ac5 100644
--- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
+++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
@@ -60,7 +60,7 @@ public class ItemEntity extends Entity implements TraceableEntity {
@Nullable
public UUID target;
public final float bobOffs;
- private int lastTick = MinecraftServer.currentTick - 1; // CraftBukkit
+ // private int lastTick = MinecraftServer.currentTick - 1; // CraftBukkit // Paper - remove anti tick skipping measures / wall time
public boolean canMobPickup = true; // Paper - Item#canEntityPickup
private int despawnRate = -1; // Paper - Alternative item-despawn-rate
public net.kyori.adventure.util.TriState frictionState = net.kyori.adventure.util.TriState.NOT_SET; // Paper - Friction API
@@ -153,12 +153,11 @@ public class ItemEntity extends Entity implements TraceableEntity {
this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
} else {
super.tick();
- // CraftBukkit start - Use wall time for pickup and despawn timers
- int elapsedTicks = MinecraftServer.currentTick - this.lastTick;
- if (this.pickupDelay != 32767) this.pickupDelay -= elapsedTicks;
- if (this.age != -32768) this.age += elapsedTicks;
- this.lastTick = MinecraftServer.currentTick;
- // CraftBukkit end
+ // Paper start - remove anti tick skipping measures / wall time - revert to vanilla
+ if (this.pickupDelay > 0 && this.pickupDelay != 32767) {
+ --this.pickupDelay;
+ }
+ // Paper end - remove anti tick skipping measures / wall time - revert to vanilla
this.xo = this.getX();
this.yo = this.getY();
@@ -211,7 +210,7 @@ public class ItemEntity extends Entity implements TraceableEntity {
this.mergeWithNeighbours();
}
- /* CraftBukkit start - moved up
+ // Paper - remove anti tick skipping measures / wall time - revert to vanilla /* CraftBukkit start - moved up
if (this.age != -32768) {
++this.age;
}
@@ -242,12 +241,14 @@ public class ItemEntity extends Entity implements TraceableEntity {
// Spigot start - copied from above
@Override
public void inactiveTick() {
- // CraftBukkit start - Use wall time for pickup and despawn timers
- int elapsedTicks = MinecraftServer.currentTick - this.lastTick;
- if (this.pickupDelay != 32767) this.pickupDelay -= elapsedTicks;
- if (this.age != -32768) this.age += elapsedTicks;
- this.lastTick = MinecraftServer.currentTick;
- // CraftBukkit end
+ // Paper start - remove anti tick skipping measures / wall time - copied from above
+ if (this.pickupDelay > 0 && this.pickupDelay != 32767) {
+ --this.pickupDelay;
+ }
+ if (this.age != -32768) {
+ ++this.age;
+ }
+ // Paper end - remove anti tick skipping measures / wall time - copied from above
if (!this.level().isClientSide && this.age >= this.despawnRate) { // Spigot // Paper - Alternative item-despawn-rate
// CraftBukkit start - fire ItemDespawnEvent
diff --git a/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java
index 2280004638fd19ed018cb3e77d53a018b34ec516..2b43337ac63d051718a2074fcc46e128a1d65129 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java
@@ -96,7 +96,7 @@ public class Zombie extends Monster {
private boolean canBreakDoors;
private int inWaterTime;
public int conversionTime;
- private int lastTick = MinecraftServer.currentTick; // CraftBukkit - add field
+ // private int lastTick = MinecraftServer.currentTick; // CraftBukkit - add field // Paper - remove anti tick skipping measures / wall time
private boolean shouldBurnInDay = true; // Paper - Add more Zombie API
public Zombie(EntityType<? extends Zombie> type, Level world) {
@@ -219,10 +219,7 @@ public class Zombie extends Monster {
public void tick() {
if (!this.level().isClientSide && this.isAlive() && !this.isNoAi()) {
if (this.isUnderWaterConverting()) {
- // CraftBukkit start - Use wall time instead of ticks for conversion
- int elapsedTicks = MinecraftServer.currentTick - this.lastTick;
- this.conversionTime -= elapsedTicks;
- // CraftBukkit end
+ --this.conversionTime; // Paper - remove anti tick skipping measures / wall time
if (this.conversionTime < 0) {
this.doUnderWaterConversion();
}
@@ -239,7 +236,7 @@ public class Zombie extends Monster {
}
super.tick();
- this.lastTick = MinecraftServer.currentTick; // CraftBukkit
+ // this.lastTick = MinecraftServer.currentTick; // CraftBukkit // Paper - remove anti tick skipping measures / wall time
}
@Override
@@ -280,7 +277,7 @@ public class Zombie extends Monster {
}
// Paper end - Add more Zombie API
public void startUnderWaterConversion(int ticksUntilWaterConversion) {
- this.lastTick = MinecraftServer.currentTick; // CraftBukkit
+ // this.lastTick = MinecraftServer.currentTick; // CraftBukkit // Paper - remove anti tick skipping measures / wall time
this.conversionTime = ticksUntilWaterConversion;
this.getEntityData().set(Zombie.DATA_DROWNED_CONVERSION_ID, true);
}
diff --git a/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java
index a2fafef89d5354e2cb02f5672810909950a57777..bf2c303a314205590a2839e0f729af3a9ff40a86 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java
@@ -54,7 +54,7 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements
public int fuel;
protected final ContainerData dataAccess;
// CraftBukkit start - add fields and methods
- private int lastTick = MinecraftServer.currentTick;
+ // private int lastTick = MinecraftServer.currentTick; // Paper - remove anti tick skipping measures / wall time
public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>();
private int maxStack = MAX_STACK;
@@ -169,12 +169,10 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements
boolean flag1 = blockEntity.brewTime > 0;
ItemStack itemstack1 = (ItemStack) blockEntity.items.get(3);
- // CraftBukkit start - Use wall time instead of ticks for brewing
- int elapsedTicks = MinecraftServer.currentTick - blockEntity.lastTick;
- blockEntity.lastTick = MinecraftServer.currentTick;
+ // Paper - remove anti tick skipping measures / wall time
if (flag1) {
- blockEntity.brewTime -= elapsedTicks;
+ --blockEntity.brewTime; // Paper - remove anti tick skipping measures / wall time - revert to vanilla
boolean flag2 = blockEntity.brewTime <= 0; // == -> <=
// CraftBukkit end

View file

@ -1,22 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Newwind <support@newwindserver.com>
Date: Fri, 20 Sep 2024 14:17:37 +0200
Subject: [PATCH] Disable pretty printing for advancement saving
Reduces json size by about 25%
Not sure why advancements even had pretty printing enabled.
My best guess was by accident on mojang's part, especially since stats json files don't have pretty printing.
diff --git a/src/main/java/net/minecraft/server/PlayerAdvancements.java b/src/main/java/net/minecraft/server/PlayerAdvancements.java
index 9fabf9322acd663c4452b562494e74aa42eb19da..862a4bf003b7f810fb57dbcd150a1417c902b633 100644
--- a/src/main/java/net/minecraft/server/PlayerAdvancements.java
+++ b/src/main/java/net/minecraft/server/PlayerAdvancements.java
@@ -50,7 +50,7 @@ import org.slf4j.Logger;
public class PlayerAdvancements {
private static final Logger LOGGER = LogUtils.getLogger();
- private static final Gson GSON = (new GsonBuilder()).setPrettyPrinting().create();
+ private static final Gson GSON = (new GsonBuilder()).create(); // Paper - Remove pretty printing from advancements
private final PlayerList playerList;
private final Path playerSavePath;
private AdvancementTree tree;

View file

@ -1,52 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Nassim Jahnke <nassim@njahnke.dev>
Date: Sat, 21 Sep 2024 22:01:52 +0200
Subject: [PATCH] Fix PlayerCommandPreprocessEvent on signed commands
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index fcccf989c25f0a259b160c4ff7873f7009e64d14..befeaac4786760f6847a5945da2296a3e68dbb17 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -2245,24 +2245,32 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
PlayerCommandPreprocessEvent event = new PlayerCommandPreprocessEvent(this.getCraftPlayer(), command, new LazyPlayerSet(this.server));
this.cserver.getPluginManager().callEvent(event);
-
- if (event.isCancelled()) {
- return;
- }
command = event.getMessage().substring(1);
- ParseResults<CommandSourceStack> parseresults = this.parseCommand(command);
- // CraftBukkit end
-
- Map map;
+ // Paper start - Fix cancellation and message changing
+ ParseResults<CommandSourceStack> parseresults = this.parseCommand(packet.command());
+ Map<String, PlayerChatMessage> map;
try {
- map = (packet.command().equals(command)) ? this.collectSignedArguments(packet, SignableCommand.of(parseresults), lastSeenMessages) : Collections.emptyMap(); // CraftBukkit
+ // Always parse the original command to add to the chat chain
+ map = this.collectSignedArguments(packet, SignableCommand.of(parseresults), lastSeenMessages);
} catch (SignedMessageChain.DecodeException signedmessagechain_a) {
this.handleMessageDecodeFailure(signedmessagechain_a);
return;
}
+ if (event.isCancelled()) {
+ // Only now are we actually good to return
+ return;
+ }
+
+ // Remove signed parts if the command was changed
+ if (!command.equals(packet.command())) {
+ parseresults = this.parseCommand(command);
+ map = Collections.emptyMap();
+ }
+ // Paper end - Fix cancellation and message changing
+
CommandSigningContext.SignedArguments commandsigningcontext_a = new CommandSigningContext.SignedArguments(map);
parseresults = Commands.<CommandSourceStack>mapSource(parseresults, (commandlistenerwrapper) -> { // CraftBukkit - decompile error

View file

@ -1,33 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: masmc05 <masmc05@gmail.com>
Date: Wed, 25 Sep 2024 16:26:04 +0300
Subject: [PATCH] Add enchantWithLevels with enchantment registry set
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java
index fef91dbede067f1ab99a9c7d463a2c55cc6cae3a..4abd939223a9d5a0c52a64e22c29fe1de85500e9 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java
@@ -319,6 +319,22 @@ public final class CraftItemFactory implements ItemFactory {
);
}
+ @Override
+ public ItemStack enchantWithLevels(ItemStack itemStack, int levels, io.papermc.paper.registry.set.RegistryKeySet<org.bukkit.enchantments.Enchantment> keySet, java.util.Random random) {
+ return enchantWithLevels(
+ itemStack,
+ levels,
+ Optional.of(
+ io.papermc.paper.registry.set.PaperRegistrySets.convertToNms(
+ Registries.ENCHANTMENT,
+ net.minecraft.server.MinecraftServer.getServer().registryAccess().createSerializationContext(net.minecraft.nbt.NbtOps.INSTANCE).lookupProvider,
+ keySet
+ )
+ ),
+ random
+ );
+ }
+
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
private ItemStack enchantWithLevels(
ItemStack itemStack,

View file

@ -1,154 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Fri, 27 Sep 2024 17:13:16 -0700
Subject: [PATCH] Improve entity effect API
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
index cd789c235acf740ec29c30b180e7fbe1a140caa9..89c8713d2c2206d1b0d8c0a392c9d13b3e736f0c 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
@@ -1299,4 +1299,15 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
return this.getHandle().getScoreboardName();
}
// Paper end - entity scoreboard name
+
+ // Paper start - broadcast hurt animation
+ @Override
+ public void broadcastHurtAnimation(java.util.Collection<Player> players) {
+ //noinspection SuspiciousMethodCalls
+ Preconditions.checkArgument(!players.contains(this), "Cannot broadcast hurt animation to self without a yaw");
+ for (final org.bukkit.entity.Player player : players) {
+ ((CraftPlayer) player).sendHurtAnimation(0, this);
+ }
+ }
+ // Paper end - broadcast hurt animation
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index ad740739437be632fc7fedec488a7d0c49534688..42d7660efe5baa6f796f2a7606686c765b6f2478 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -1277,6 +1277,11 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override
public void sendHurtAnimation(float yaw) {
+ // Paper start - Add target entity to sendHurtAnimation
+ this.sendHurtAnimation(yaw, this);
+ }
+ public void sendHurtAnimation(float yaw, org.bukkit.entity.Entity target) {
+ // Paper end - Add target entity to sendHurtAnimation
if (this.getHandle().connection == null) {
return;
}
@@ -1286,7 +1291,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
* This makes no sense. We'll add 90 to it so that 0 = front, clockwise from there.
*/
float actualYaw = yaw + 90;
- this.getHandle().connection.send(new ClientboundHurtAnimationPacket(this.getEntityId(), actualYaw));
+ this.getHandle().connection.send(new ClientboundHurtAnimationPacket(target.getEntityId(), actualYaw)); // Paper - Add target entity to sendHurtAnimation
}
@Override
@@ -3553,4 +3558,14 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
((ca.spottedleaf.moonrise.patches.chunk_system.player.ChunkSystemServerPlayer)this.getHandle())
.moonrise$getViewDistanceHolder().setSendViewDistance(viewDistance);
}
+
+ // Paper start - entity effect API
+ @Override
+ public void sendEntityEffect(final org.bukkit.EntityEffect effect, final org.bukkit.entity.Entity target) {
+ if (this.getHandle().connection == null || !effect.isApplicableTo(target)) {
+ return;
+ }
+ this.getHandle().connection.send(new net.minecraft.network.protocol.game.ClientboundEntityEventPacket(((CraftEntity) target).getHandle(), effect.getData()));
+ }
+ // Paper end - entity effect API
}
diff --git a/src/test/java/org/bukkit/EntityEffectTest.java b/src/test/java/org/bukkit/EntityEffectTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5e64dfaeba167374dc45c5becfb2e7114657dff6
--- /dev/null
+++ b/src/test/java/org/bukkit/EntityEffectTest.java
@@ -0,0 +1,82 @@
+package org.bukkit;
+
+import com.google.common.base.Joiner;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import net.minecraft.world.entity.EntityEvent;
+import org.bukkit.support.environment.Normal;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.fail;
+
+@Normal
+public class EntityEffectTest {
+
+ private static List<Byte> collectNmsLevelEvents() throws ReflectiveOperationException {
+ final List<Byte> events = new ArrayList<>();
+ for (final Field field : EntityEvent.class.getFields()) {
+ if (Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers()) && field.getType() == byte.class) {
+ events.add((byte) field.get(null));
+ }
+ }
+ for (int i = 22; i <= 28; i++) {
+ events.remove(Byte.valueOf((byte) i)); // all have existing API (debug info and op level)
+ }
+ events.remove(Byte.valueOf(EntityEvent.STOP_ATTACKING)); // not used on client anywhere
+ events.remove(Byte.valueOf(EntityEvent.USE_ITEM_COMPLETE)); // not suitable for API (complete using item on Player)
+ events.remove(Byte.valueOf(EntityEvent.FISHING_ROD_REEL_IN)); // not suitable for API (fishing rod reel in on FishingHook)
+ events.add((byte) 0); // handled on Arrow (for some reason it's not in the EntityEvent nms file as a constant)
+ return events;
+ }
+
+ private static boolean isNotDeprecated(EntityEffect effect) throws ReflectiveOperationException {
+ return !EntityEffect.class.getDeclaredField(effect.name()).isAnnotationPresent(Deprecated.class);
+ }
+
+ @Test
+ public void checkAllApiExists() throws ReflectiveOperationException {
+ Map<Byte, EntityEffect> toId = new HashMap<>();
+ for (final EntityEffect effect : EntityEffect.values()) {
+ if (isNotDeprecated(effect)) {
+ toId.put(effect.getData(), effect);
+ }
+ }
+
+ final Set<Byte> missingEvents = new HashSet<>();
+ for (final Byte event : collectNmsLevelEvents()) {
+ if (toId.get(event) == null) {
+ missingEvents.add(event);
+ }
+ }
+ if (!missingEvents.isEmpty()) {
+ fail("Missing API EntityEffects:\n" + Joiner.on("\n").join(missingEvents));
+ }
+ }
+
+ @Test
+ public void checkNoExtraApi() throws ReflectiveOperationException {
+ Map<Byte, EntityEffect> toId = new HashMap<>();
+ for (final EntityEffect effect : EntityEffect.values()) {
+ if (isNotDeprecated(effect)) {
+ toId.put(effect.getData(), effect);
+ }
+ }
+
+ final List<Byte> nmsEvents = collectNmsLevelEvents();
+ final Set<EntityEffect> extraApiEffects = new HashSet<>();
+ for (final Map.Entry<Byte, EntityEffect> entry : toId.entrySet()) {
+ if (!nmsEvents.contains(entry.getKey())) {
+ extraApiEffects.add(entry.getValue());
+ }
+ }
+ if (!extraApiEffects.isEmpty()) {
+ fail("Extra API EntityEffects:\n" + Joiner.on("\n").join(extraApiEffects));
+ }
+ }
+}

View file

@ -1,181 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tamion <70228790+notTamion@users.noreply.github.com>
Date: Sun, 15 Sep 2024 19:17:12 +0200
Subject: [PATCH] Add recipeBrewTime
== AT ==
public net.minecraft.world.inventory.BrewingStandMenu brewingStandData
diff --git a/src/main/java/io/papermc/paper/inventory/BrewingSimpleContainerData.java b/src/main/java/io/papermc/paper/inventory/BrewingSimpleContainerData.java
new file mode 100644
index 0000000000000000000000000000000000000000..84dead75191634c3aa6031781a2ff3087171793b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/inventory/BrewingSimpleContainerData.java
@@ -0,0 +1,11 @@
+package io.papermc.paper.inventory;
+
+import net.minecraft.world.inventory.SimpleContainerData;
+
+public class BrewingSimpleContainerData extends SimpleContainerData {
+
+ public BrewingSimpleContainerData() {
+ super(3);
+ this.set(2, 400);
+ }
+}
diff --git a/src/main/java/net/minecraft/world/inventory/BrewingStandMenu.java b/src/main/java/net/minecraft/world/inventory/BrewingStandMenu.java
index 68c529cb38d61cd3a0f39bef0f666057fc219c9b..6ec207e91f93b3ab625515dc75367ba399818876 100644
--- a/src/main/java/net/minecraft/world/inventory/BrewingStandMenu.java
+++ b/src/main/java/net/minecraft/world/inventory/BrewingStandMenu.java
@@ -41,14 +41,14 @@ public class BrewingStandMenu extends AbstractContainerMenu {
// CraftBukkit end
public BrewingStandMenu(int syncId, Inventory playerInventory) {
- this(syncId, playerInventory, new SimpleContainer(5), new SimpleContainerData(2));
+ this(syncId, playerInventory, new SimpleContainer(5), new io.papermc.paper.inventory.BrewingSimpleContainerData()); // Paper - Add totalBrewTime
}
public BrewingStandMenu(int syncId, Inventory playerInventory, Container inventory, ContainerData propertyDelegate) {
super(MenuType.BREWING_STAND, syncId);
this.player = playerInventory; // CraftBukkit
checkContainerSize(inventory, 5);
- checkContainerDataCount(propertyDelegate, 2);
+ checkContainerDataCount(propertyDelegate, 3); // Paper - Add recipeBrewTime
this.brewingStand = inventory;
this.brewingStandData = propertyDelegate;
PotionBrewing potionbrewer = playerInventory.player.level().potionBrewing();
@@ -60,7 +60,20 @@ public class BrewingStandMenu extends AbstractContainerMenu {
// Paper end - custom potion mixes
this.ingredientSlot = this.addSlot(new BrewingStandMenu.IngredientsSlot(potionbrewer, inventory, 3, 79, 17));
this.addSlot(new BrewingStandMenu.FuelSlot(inventory, 4, 17, 17));
- this.addDataSlots(propertyDelegate);
+ // Paper start - Add recipeBrewTime
+ this.addDataSlots(new SimpleContainerData(2) {
+ @Override
+ public int get(final int index) {
+ if (index == 0) return 400 * propertyDelegate.get(index) / propertyDelegate.get(2);
+ return propertyDelegate.get(index);
+ }
+
+ @Override
+ public void set(final int index, final int value) {
+ propertyDelegate.set(index, value);
+ }
+ });
+ // Paper end - Add recipeBrewTime
int j;
diff --git a/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java
index bf2c303a314205590a2839e0f729af3a9ff40a86..0a93bacd62249bae1800ff306b8a7c765b0e5a8b 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java
@@ -49,6 +49,7 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements
public static final int NUM_DATA_VALUES = 2;
private NonNullList<ItemStack> items;
public int brewTime;
+ public int recipeBrewTime = 400; // Paper - Add recipeBrewTime
private boolean[] lastPotionCount;
private Item ingredient;
public int fuel;
@@ -99,6 +100,11 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements
case 1:
j = BrewingStandBlockEntity.this.fuel;
break;
+ // Paper start - Add recipeBrewTime
+ case 2:
+ j = BrewingStandBlockEntity.this.recipeBrewTime;
+ break;
+ // Paper end - Add recipeBrewTime
default:
j = 0;
}
@@ -114,13 +120,18 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements
break;
case 1:
BrewingStandBlockEntity.this.fuel = value;
+ // Paper start - Add recipeBrewTime
+ case 2:
+ BrewingStandBlockEntity.this.recipeBrewTime = value;
+ break;
+ // Paper end - Add recipeBrewTime
}
}
@Override
public int getCount() {
- return 2;
+ return 3; // Paper - Add recipeBrewTime
}
};
}
@@ -188,7 +199,8 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements
// CraftBukkit start
BrewingStartEvent event = new BrewingStartEvent(CraftBlock.at(world, pos), CraftItemStack.asCraftMirror(itemstack1), 400);
world.getCraftServer().getPluginManager().callEvent(event);
- blockEntity.brewTime = event.getTotalBrewTime(); // 400 -> event.getTotalBrewTime()
+ blockEntity.recipeBrewTime = event.getRecipeBrewTime(); // Paper - use recipe brew time from event
+ blockEntity.brewTime = event.getBrewingTime(); // 400 -> event.getTotalBrewTime() // Paper - use brewing time from event
// CraftBukkit end
blockEntity.ingredient = itemstack1.getItem();
setChanged(world, pos, state);
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBrewingStand.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBrewingStand.java
index e9f55c898de827afe6c9f951cbe1b46eea5f4149..f330c17b11566102b4db430fef013101b3275bda 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBrewingStand.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBrewingStand.java
@@ -41,6 +41,19 @@ public class CraftBrewingStand extends CraftContainer<BrewingStandBlockEntity> i
this.getSnapshot().brewTime = brewTime;
}
+ // Paper start - Add recipeBrewTime
+ @Override
+ public void setRecipeBrewTime(int recipeBrewTime) {
+ com.google.common.base.Preconditions.checkArgument(recipeBrewTime > 0, "recipeBrewTime must be positive");
+ this.getSnapshot().recipeBrewTime = recipeBrewTime;
+ }
+
+ @Override
+ public int getRecipeBrewTime() {
+ return this.getSnapshot().recipeBrewTime;
+ }
+ // Paper end - Add recipeBrewTime
+
@Override
public int getFuelLevel() {
return this.getSnapshot().fuel;
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java
index 674e3a827f8fb64e5c0beefb3c1874d6e8aee4e5..6d3f9d5dab6c9a2860ae31cae24310aa2d62da7c 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java
@@ -163,7 +163,7 @@ public class CraftContainer extends AbstractContainerMenu {
this.delegate = new EnchantmentMenu(windowId, bottom);
break;
case BREWING:
- this.delegate = new BrewingStandMenu(windowId, bottom, top, new SimpleContainerData(2));
+ this.delegate = new BrewingStandMenu(windowId, bottom, top, new io.papermc.paper.inventory.BrewingSimpleContainerData()); // Paper - Add recipeBrewTime
break;
case HOPPER:
this.delegate = new HopperMenu(windowId, bottom, top);
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftBrewingStandView.java b/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftBrewingStandView.java
index aeb5a9c996ba6b6d812735bc78e3e5aec2c9d269..6e88347d74f6bd20d7808e0d556997ab73861e7c 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftBrewingStandView.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftBrewingStandView.java
@@ -35,4 +35,17 @@ public class CraftBrewingStandView extends CraftInventoryView<BrewingStandMenu,
Preconditions.checkArgument(brewingTicks > 0, "The given brewing ticks must be greater than 0");
this.container.setData(BrewingStandBlockEntity.DATA_BREW_TIME, brewingTicks);
}
+
+ // Paper start - Add recipeBrewTime
+ @Override
+ public void setRecipeBrewTime(int recipeBrewTime) {
+ com.google.common.base.Preconditions.checkArgument(recipeBrewTime > 0, "recipeBrewTime must be positive");
+ this.container.brewingStandData.set(2, recipeBrewTime);
+ }
+
+ @Override
+ public int getRecipeBrewTime() {
+ return this.container.brewingStandData.get(2);
+ }
+ // Paper end - Add recipeBrewTime
}

View file

@ -1,190 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Sun, 16 Jan 2022 10:13:33 -0800
Subject: [PATCH] Call bucket events for cauldrons
diff --git a/src/main/java/net/minecraft/core/cauldron/CauldronInteraction.java b/src/main/java/net/minecraft/core/cauldron/CauldronInteraction.java
index f301c20e808b77cb3fcffd9a7c8102928306456e..b584cfe44fbb93d470ca56c091423833c4007d16 100644
--- a/src/main/java/net/minecraft/core/cauldron/CauldronInteraction.java
+++ b/src/main/java/net/minecraft/core/cauldron/CauldronInteraction.java
@@ -60,7 +60,7 @@ public interface CauldronInteraction {
static CauldronInteraction.InteractionMap newInteractionMap(String name) {
Object2ObjectOpenHashMap<Item, CauldronInteraction> object2objectopenhashmap = new Object2ObjectOpenHashMap();
- object2objectopenhashmap.defaultReturnValue((iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
+ object2objectopenhashmap.defaultReturnValue((iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
});
CauldronInteraction.InteractionMap cauldroninteraction_a = new CauldronInteraction.InteractionMap(name, object2objectopenhashmap);
@@ -69,13 +69,13 @@ public interface CauldronInteraction {
return cauldroninteraction_a;
}
- ItemInteractionResult interact(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack);
+ ItemInteractionResult interact(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection); // Paper - add hitDirection
static void bootStrap() {
Map<Item, CauldronInteraction> map = CauldronInteraction.EMPTY.map();
CauldronInteraction.addDefaultInteractions(map);
- map.put(Items.POTION, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
+ map.put(Items.POTION, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
PotionContents potioncontents = (PotionContents) itemstack.get(DataComponents.POTION_CONTENTS);
if (potioncontents != null && potioncontents.is(Potions.WATER)) {
@@ -103,12 +103,12 @@ public interface CauldronInteraction {
Map<Item, CauldronInteraction> map1 = CauldronInteraction.WATER.map();
CauldronInteraction.addDefaultInteractions(map1);
- map1.put(Items.BUCKET, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
+ map1.put(Items.BUCKET, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
return CauldronInteraction.fillBucket(iblockdata, world, blockposition, entityhuman, enumhand, itemstack, new ItemStack(Items.WATER_BUCKET), (iblockdata1) -> {
return (Integer) iblockdata1.getValue(LayeredCauldronBlock.LEVEL) == 3;
- }, SoundEvents.BUCKET_FILL);
+ }, SoundEvents.BUCKET_FILL, hitDirection); // Paper - add hitDirection
});
- map1.put(Items.GLASS_BOTTLE, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
+ map1.put(Items.GLASS_BOTTLE, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
if (!world.isClientSide) {
// CraftBukkit start
if (!LayeredCauldronBlock.lowerFillLevel(iblockdata, world, blockposition, entityhuman, CauldronLevelChangeEvent.ChangeReason.BOTTLE_FILL)) {
@@ -127,7 +127,7 @@ public interface CauldronInteraction {
return ItemInteractionResult.sidedSuccess(world.isClientSide);
});
- map1.put(Items.POTION, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
+ map1.put(Items.POTION, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
if ((Integer) iblockdata.getValue(LayeredCauldronBlock.LEVEL) == 3) {
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
} else {
@@ -194,15 +194,15 @@ public interface CauldronInteraction {
map1.put(Items.YELLOW_SHULKER_BOX, CauldronInteraction.SHULKER_BOX);
Map<Item, CauldronInteraction> map2 = CauldronInteraction.LAVA.map();
- map2.put(Items.BUCKET, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
+ map2.put(Items.BUCKET, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
return CauldronInteraction.fillBucket(iblockdata, world, blockposition, entityhuman, enumhand, itemstack, new ItemStack(Items.LAVA_BUCKET), (iblockdata1) -> {
return true;
- }, SoundEvents.BUCKET_FILL_LAVA);
+ }, SoundEvents.BUCKET_FILL_LAVA, hitDirection); // Paper - add hitDirection
});
CauldronInteraction.addDefaultInteractions(map2);
Map<Item, CauldronInteraction> map3 = CauldronInteraction.POWDER_SNOW.map();
- map3.put(Items.BUCKET, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
+ map3.put(Items.BUCKET, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
return CauldronInteraction.fillBucket(iblockdata, world, blockposition, entityhuman, enumhand, itemstack, new ItemStack(Items.POWDER_SNOW_BUCKET), (iblockdata1) -> {
return (Integer) iblockdata1.getValue(LayeredCauldronBlock.LEVEL) == 3;
}, SoundEvents.BUCKET_FILL_POWDER_SNOW);
@@ -217,10 +217,24 @@ public interface CauldronInteraction {
}
static ItemInteractionResult fillBucket(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, ItemStack output, Predicate<BlockState> fullPredicate, SoundEvent soundEvent) {
+ // Paper start - add hitDirection
+ return fillBucket(state, world, pos, player, hand, stack, output, fullPredicate, soundEvent, null); // Paper - add hitDirection
+ }
+ static ItemInteractionResult fillBucket(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, ItemStack output, Predicate<BlockState> fullPredicate, SoundEvent soundEvent, @javax.annotation.Nullable net.minecraft.core.Direction hitDirection) {
+ // Paper end - add hitDirection
if (!fullPredicate.test(state)) {
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
} else {
if (!world.isClientSide) {
+ // Paper start - fire PlayerBucketFillEvent
+ if (hitDirection != null) {
+ org.bukkit.event.player.PlayerBucketEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketFillEvent((net.minecraft.server.level.ServerLevel) world, player, pos, pos, hitDirection, stack, output.getItem(), hand);
+ if (event.isCancelled()) {
+ return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
+ }
+ output = event.getItemStack() != null ? org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItemStack()) : ItemStack.EMPTY;
+ }
+ // Paper end - fire PlayerBucketFillEvent
// CraftBukkit start
if (!LayeredCauldronBlock.changeLevel(state, world, pos, Blocks.CAULDRON.defaultBlockState(), player, CauldronLevelChangeEvent.ChangeReason.BUCKET_FILL, false)) { // Paper - Call CauldronLevelChangeEvent
return ItemInteractionResult.SUCCESS;
@@ -241,7 +255,22 @@ public interface CauldronInteraction {
}
static ItemInteractionResult emptyBucket(Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, BlockState state, SoundEvent soundEvent) {
+ // Paper start - add hitDirection
+ return emptyBucket(world, pos, player, hand, stack, state, soundEvent, null);
+ }
+ static ItemInteractionResult emptyBucket(Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, BlockState state, SoundEvent soundEvent, @javax.annotation.Nullable net.minecraft.core.Direction hitDirection) {
+ // Paper end - add hitDirection
if (!world.isClientSide) {
+ // Paper start - fire PlayerBucketEmptyEvent
+ ItemStack output = new ItemStack(Items.BUCKET);
+ if (hitDirection != null) {
+ org.bukkit.event.player.PlayerBucketEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketEmptyEvent((net.minecraft.server.level.ServerLevel) world, player, pos, pos, hitDirection, stack, hand);
+ if (event.isCancelled()) {
+ return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
+ }
+ output = event.getItemStack() != null ? org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItemStack()) : ItemStack.EMPTY;
+ }
+ // Paper end - fire PlayerBucketEmptyEvent
// CraftBukkit start
if (!LayeredCauldronBlock.changeLevel(state, world, pos, state, player, CauldronLevelChangeEvent.ChangeReason.BUCKET_EMPTY, false)) { // Paper - Call CauldronLevelChangeEvent
return ItemInteractionResult.SUCCESS;
@@ -249,7 +278,7 @@ public interface CauldronInteraction {
// CraftBukkit end
Item item = stack.getItem();
- player.setItemInHand(hand, ItemUtils.createFilledResult(stack, player, new ItemStack(Items.BUCKET)));
+ player.setItemInHand(hand, ItemUtils.createFilledResult(stack, player, output)); // Paper
player.awardStat(Stats.FILL_CAULDRON);
player.awardStat(Stats.ITEM_USED.get(item));
// world.setBlockAndUpdate(blockposition, iblockdata); // CraftBukkit
@@ -267,16 +296,16 @@ public interface CauldronInteraction {
CauldronInteraction.InteractionMap WATER = CauldronInteraction.newInteractionMap("water");
CauldronInteraction.InteractionMap LAVA = CauldronInteraction.newInteractionMap("lava");
CauldronInteraction.InteractionMap POWDER_SNOW = CauldronInteraction.newInteractionMap("powder_snow");
- CauldronInteraction FILL_WATER = (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
- return CauldronInteraction.emptyBucket(world, blockposition, entityhuman, enumhand, itemstack, (BlockState) Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), SoundEvents.BUCKET_EMPTY);
+ CauldronInteraction FILL_WATER = (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
+ return CauldronInteraction.emptyBucket(world, blockposition, entityhuman, enumhand, itemstack, (BlockState) Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), SoundEvents.BUCKET_EMPTY, hitDirection); // Paper - add hitDirection
};
- CauldronInteraction FILL_LAVA = (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
- return CauldronInteraction.emptyBucket(world, blockposition, entityhuman, enumhand, itemstack, Blocks.LAVA_CAULDRON.defaultBlockState(), SoundEvents.BUCKET_EMPTY_LAVA);
+ CauldronInteraction FILL_LAVA = (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
+ return CauldronInteraction.emptyBucket(world, blockposition, entityhuman, enumhand, itemstack, Blocks.LAVA_CAULDRON.defaultBlockState(), SoundEvents.BUCKET_EMPTY_LAVA, hitDirection); // Paper - add hitDirection
};
- CauldronInteraction FILL_POWDER_SNOW = (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
+ CauldronInteraction FILL_POWDER_SNOW = (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
return CauldronInteraction.emptyBucket(world, blockposition, entityhuman, enumhand, itemstack, (BlockState) Blocks.POWDER_SNOW_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), SoundEvents.BUCKET_EMPTY_POWDER_SNOW);
};
- CauldronInteraction SHULKER_BOX = (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
+ CauldronInteraction SHULKER_BOX = (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
// CraftBukkit end
Block block = Block.byItem(itemstack.getItem());
@@ -299,7 +328,7 @@ public interface CauldronInteraction {
return ItemInteractionResult.sidedSuccess(world.isClientSide);
}
};
- CauldronInteraction BANNER = (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> { // CraftBukkit - decompile error
+ CauldronInteraction BANNER = (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // CraftBukkit - decompile error // Paper - add hitDirection
BannerPatternLayers bannerpatternlayers = (BannerPatternLayers) itemstack.getOrDefault(DataComponents.BANNER_PATTERNS, BannerPatternLayers.EMPTY);
if (bannerpatternlayers.layers().isEmpty()) {
@@ -322,7 +351,7 @@ public interface CauldronInteraction {
return ItemInteractionResult.sidedSuccess(world.isClientSide);
}
};
- CauldronInteraction DYED_ITEM = (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> { // CraftBukkit - decompile error
+ CauldronInteraction DYED_ITEM = (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // CraftBukkit - decompile error // Paper - add hitDirection
if (!itemstack.is(ItemTags.DYEABLE)) {
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
} else if (!itemstack.has(DataComponents.DYED_COLOR)) {
diff --git a/src/main/java/net/minecraft/world/level/block/AbstractCauldronBlock.java b/src/main/java/net/minecraft/world/level/block/AbstractCauldronBlock.java
index 20f2b575c8131621edea0e75fbf38a9fe20a36c4..4fdef6d7bcea0cf6b7d90324398af597660c80e3 100644
--- a/src/main/java/net/minecraft/world/level/block/AbstractCauldronBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/AbstractCauldronBlock.java
@@ -58,7 +58,7 @@ public abstract class AbstractCauldronBlock extends Block {
ItemStack stack, BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit
) {
CauldronInteraction cauldronInteraction = this.interactions.map().get(stack.getItem());
- return cauldronInteraction.interact(state, world, pos, player, hand, stack);
+ return cauldronInteraction.interact(state, world, pos, player, hand, stack, hit.getDirection()); // Paper - pass hit direction
}
@Override

View file

@ -1,36 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Mariell Hoversholm <proximyst@proximyst.com>
Date: Sun, 29 Sep 2024 16:21:26 +0200
Subject: [PATCH] Add PlayerInsertLecternBookEvent
diff --git a/src/main/java/net/minecraft/world/level/block/LecternBlock.java b/src/main/java/net/minecraft/world/level/block/LecternBlock.java
index ebb79907391fe3128d3d16fbe9d8cb0b22bcc9f7..3a1e2f62b297f384cc0dcfb828e523a37c703d6f 100644
--- a/src/main/java/net/minecraft/world/level/block/LecternBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/LecternBlock.java
@@ -157,7 +157,24 @@ public class LecternBlock extends BaseEntityBlock {
BlockEntity tileentity = world.getBlockEntity(pos);
if (tileentity instanceof LecternBlockEntity tileentitylectern) {
- tileentitylectern.setBook(stack.consumeAndReturn(1, user));
+ // Paper start - Add PlayerInsertLecternBookEvent
+ ItemStack eventSourcedBookStack = null;
+ if (user instanceof final net.minecraft.server.level.ServerPlayer serverPlayer) {
+ final io.papermc.paper.event.player.PlayerInsertLecternBookEvent event = new io.papermc.paper.event.player.PlayerInsertLecternBookEvent(
+ serverPlayer.getBukkitEntity(),
+ org.bukkit.craftbukkit.block.CraftBlock.at(world, pos),
+ org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack.copyWithCount(1))
+ );
+ if (!event.callEvent()) return;
+ eventSourcedBookStack = org.bukkit.craftbukkit.inventory.CraftItemStack.unwrap(event.getBook());
+ }
+ if (eventSourcedBookStack == null) {
+ eventSourcedBookStack = stack.consumeAndReturn(1, user);
+ } else {
+ stack.consume(1, user);
+ }
+ tileentitylectern.setBook(eventSourcedBookStack);
+ // Paper end - Add PlayerInsertLecternBookEvent
LecternBlock.resetBookState(user, world, pos, state, true);
world.playSound((Player) null, pos, SoundEvents.BOOK_PUT, SoundSource.BLOCKS, 1.0F, 1.0F);
}

View file

@ -1,94 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Axionize <154778082+Axionize@users.noreply.github.com>
Date: Sun, 29 Sep 2024 14:20:42 -0700
Subject: [PATCH] Void damage configuration API
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index bc45c74987974b4828201e06fc8b1f3fbc0af8b4..4b54d0ea31062972e68ee8fafe3cfaf68f65a5cd 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -931,8 +931,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
}
public void checkBelowWorld() {
+ if (!this.level.getWorld().isVoidDamageEnabled()) return; // Paper - check if void damage is enabled on the world
+
// Paper start - Configurable nether ceiling damage
- if (this.getY() < (double) (this.level.getMinBuildHeight() - 64) || (this.level.getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER
+ if (this.getY() < (double) (this.level.getMinBuildHeight() + this.level.getWorld().getVoidDamageMinBuildHeightOffset()) || (this.level.getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER // Paper - use configured min build height offset
&& this.level.paperConfig().environment.netherCeilingVoidDamageHeight.test(v -> this.getY() >= v)
&& (!(this instanceof Player player) || !player.getAbilities().invulnerable))) {
// Paper end - Configurable nether ceiling damage
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index 30f4f1254fc295442d72d50479e8af635f2fe983..2aa6374cd4a96efd85899be8cd3172a8257bfe6b 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -2625,7 +2625,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
@Override
protected void onBelowWorld() {
- this.hurt(this.damageSources().fellOutOfWorld(), 4.0F);
+ this.hurt(this.damageSources().fellOutOfWorld(), this.level().getWorld().getVoidDamageAmount()); // Paper - use configured void damage amount
}
protected void updateSwingTime() {
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index 38b046da5acac8633db8618a2957187d291f5e73..33e4818ba5a90d78d69baad9f6b1be1b1382e9f3 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -163,6 +163,41 @@ public class CraftWorld extends CraftRegionAccessor implements World {
private final Object2IntOpenHashMap<SpawnCategory> spawnCategoryLimit = new Object2IntOpenHashMap<>();
private final CraftPersistentDataContainer persistentDataContainer = new CraftPersistentDataContainer(CraftWorld.DATA_TYPE_REGISTRY);
private net.kyori.adventure.pointer.Pointers adventure$pointers; // Paper - implement pointers
+ // Paper start - void damage configuration
+ private boolean voidDamageEnabled;
+ private float voidDamageAmount;
+ private double voidDamageMinBuildHeightOffset;
+
+ @Override
+ public boolean isVoidDamageEnabled() {
+ return this.voidDamageEnabled;
+ }
+
+ @Override
+ public void setVoidDamageEnabled(final boolean enabled) {
+ this.voidDamageEnabled = enabled;
+ }
+
+ @Override
+ public float getVoidDamageAmount() {
+ return this.voidDamageAmount;
+ }
+
+ @Override
+ public void setVoidDamageAmount(float voidDamageAmount) {
+ this.voidDamageAmount = voidDamageAmount;
+ }
+
+ @Override
+ public double getVoidDamageMinBuildHeightOffset() {
+ return this.voidDamageMinBuildHeightOffset;
+ }
+
+ @Override
+ public void setVoidDamageMinBuildHeightOffset(double minBuildHeightOffset) {
+ this.voidDamageMinBuildHeightOffset = minBuildHeightOffset;
+ }
+ // Paper end - void damage configuration
// Paper start - Provide fast information methods
@Override
@@ -271,6 +306,12 @@ public class CraftWorld extends CraftRegionAccessor implements World {
}
}
// Paper end - per world spawn limits
+
+ // Paper start - per world void damage height
+ this.voidDamageEnabled = this.world.paperConfig().environment.voidDamageAmount.enabled();
+ this.voidDamageMinBuildHeightOffset = this.world.paperConfig().environment.voidDamageMinBuildHeightOffset;
+ this.voidDamageAmount = (float) this.world.paperConfig().environment.voidDamageAmount.or(0);
+ // Paper end - per world void damage height
}
@Override

View file

@ -1,45 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Lulu13022002 <41980282+Lulu13022002@users.noreply.github.com>
Date: Sat, 9 Jul 2022 17:28:42 +0200
Subject: [PATCH] Add Offline PDC API
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
index 4f4e3ee18d586f61706504218cddc06a38ca5580..94ca0407303c4493ab4928b12ec6ecc75aaca549 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
@@ -325,6 +325,34 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa
}
// Paper end
+ // Paper start - Add Offline PDC API
+ private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry();
+ private io.papermc.paper.persistence.@org.checkerframework.checker.nullness.qual.MonotonicNonNull PersistentDataContainerView persistentDataContainerView;
+
+ @Override
+ public io.papermc.paper.persistence.PersistentDataContainerView getPersistentDataContainer() {
+ if (this.persistentDataContainerView == null) {
+ this.persistentDataContainerView = new io.papermc.paper.persistence.PaperPersistentDataContainerView(DATA_TYPE_REGISTRY) {
+
+ private CompoundTag getPersistentTag() {
+ return net.minecraft.Optionull.map(CraftOfflinePlayer.this.getData(), data -> data.getCompound("BukkitValues"));
+ }
+
+ @Override
+ public CompoundTag toTagCompound() {
+ return java.util.Objects.requireNonNullElseGet(this.getPersistentTag(), CompoundTag::new);
+ }
+
+ @Override
+ public net.minecraft.nbt.Tag getTag(String key) {
+ return net.minecraft.Optionull.map(this.getPersistentTag(), tag -> tag.get(key));
+ }
+ };
+ }
+ return this.persistentDataContainerView;
+ }
+ // Paper end - Add Offline PDC API
+
@Override
public Location getLastDeathLocation() {
if (this.getData().contains("LastDeathLocation", 10)) {

View file

@ -1,53 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Doc <nachito94@msn.com>
Date: Sun, 6 Oct 2024 16:46:36 -0300
Subject: [PATCH] Add AnvilView#bypassEnchantmentLevelRestriction
Anvils, by default, limit applied enchantment levels to their respective
maximum level. The added API enables plugins to disable this behaviour,
allowing enchantments that are overleveled to be applied via the anvil.
diff --git a/src/main/java/net/minecraft/world/inventory/AnvilMenu.java b/src/main/java/net/minecraft/world/inventory/AnvilMenu.java
index d685511104ac552dfc9ae2111e1bfb60fa812102..362278407679f245ebcea778f2199b357339e1fe 100644
--- a/src/main/java/net/minecraft/world/inventory/AnvilMenu.java
+++ b/src/main/java/net/minecraft/world/inventory/AnvilMenu.java
@@ -53,6 +53,7 @@ public class AnvilMenu extends ItemCombinerMenu {
public int maximumRepairCost = 40;
private CraftAnvilView bukkitEntity;
// CraftBukkit end
+ public boolean bypassEnchantmentLevelRestriction = false; // Paper - bypass anvil level restrictions
public AnvilMenu(int syncId, Inventory inventory) {
this(syncId, inventory, ContainerLevelAccess.NULL);
@@ -231,7 +232,7 @@ public class AnvilMenu extends ItemCombinerMenu {
flag2 = true;
} else {
flag1 = true;
- if (i2 > enchantment.getMaxLevel()) {
+ if (i2 > enchantment.getMaxLevel() && !this.bypassEnchantmentLevelRestriction) { // Paper - bypass anvil level restrictions
i2 = enchantment.getMaxLevel();
}
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java b/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java
index fdc2bd4c3ee5f762a72df39c87215e3a15f47db2..f86c95a13dff012de5db3e41ac261e9e8d44d9f3 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java
@@ -50,6 +50,18 @@ public class CraftAnvilView extends CraftInventoryView<AnvilMenu, AnvilInventory
this.container.maximumRepairCost = cost;
}
+ // Paper start
+ @Override
+ public boolean bypassesEnchantmentLevelRestriction() {
+ return this.container.bypassEnchantmentLevelRestriction;
+ }
+
+ @Override
+ public void bypassEnchantmentLevelRestriction(final boolean bypassEnchantmentLevelRestriction) {
+ this.container.bypassEnchantmentLevelRestriction = bypassEnchantmentLevelRestriction;
+ }
+ // Paper end
+
public void updateFromLegacy(CraftInventoryAnvil legacy) {
if (legacy.isRepairCostSet()) {
this.setRepairCost(legacy.getRepairCost());

View file

@ -1,168 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Wed, 16 Oct 2024 06:41:32 -0700
Subject: [PATCH] Add proper async player disconnections
Blocking can cause performance problems
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
index 3e550f8e7cd4f4e16f499a8a2a4b95420270f07a..4a8356a714ed50d4a32bcf046a2e16491bef014b 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
@@ -850,6 +850,14 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
}
+ // Paper start - add proper async disconnect
+ public void enableAutoRead() {
+ if (this.channel != null) {
+ this.channel.config().setAutoRead(true);
+ }
+ }
+ // Paper end - add proper async disconnect
+
public void setupCompression(int compressionThreshold, boolean rejectsBadPackets) {
if (compressionThreshold >= 0) {
com.velocitypowered.natives.compression.VelocityCompressor compressor = com.velocitypowered.natives.util.Natives.compress.get().create(io.papermc.paper.configuration.GlobalConfiguration.get().misc.compressionLevel.or(-1)); // Paper - Use Velocity cipher
diff --git a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
index 6998f32f8d79dbdb6b31ffaa126602fc4a428616..7174f8c89a7cdcf40ff28f6636ecfb23b13ccdaa 100644
--- a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
@@ -136,11 +136,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
this.latency = (this.latency * 3 + i) / 4;
this.keepAlivePending = false;
} else if (!this.isSingleplayerOwner()) {
- // Paper start - This needs to be handled on the main thread for plugins
- server.submit(() -> {
- this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE, PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause
- });
- // Paper end - This needs to be handled on the main thread for plugins
+ this.disconnectAsync(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE, PlayerKickEvent.Cause.TIMEOUT); // Paper - add proper async disconnect
}
}
@@ -404,6 +400,31 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
minecraftserver.scheduleOnMain(networkmanager::handleDisconnection); // Paper
}
+ // Paper start - add proper async disconnect
+ public void disconnectAsync(net.kyori.adventure.text.Component reason, PlayerKickEvent.Cause cause) {
+ this.disconnectAsync(io.papermc.paper.adventure.PaperAdventure.asVanilla(reason), cause);
+ }
+
+ public void disconnectAsync(Component reason, PlayerKickEvent.Cause cause) {
+ this.disconnectAsync(new DisconnectionDetails(reason), cause);
+ }
+
+ public void disconnectAsync(DisconnectionDetails disconnectionInfo, PlayerKickEvent.Cause cause) {
+ if (this.cserver.isPrimaryThread()) {
+ this.disconnect(disconnectionInfo, cause);
+ return;
+ }
+ this.connection.setReadOnly();
+ this.server.scheduleOnMain(() -> {
+ ServerCommonPacketListenerImpl.this.disconnect(disconnectionInfo, cause);
+ if (ServerCommonPacketListenerImpl.this.player.quitReason == null) {
+ // cancelled
+ ServerCommonPacketListenerImpl.this.connection.enableAutoRead();
+ }
+ });
+ }
+ // Paper end - add proper async disconnect
+
protected boolean isSingleplayerOwner() {
return this.server.isSingleplayerOwner(this.playerProfile());
}
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index befeaac4786760f6847a5945da2296a3e68dbb17..064d52d4479727c6a32bf357be8da32d1760e7fc 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -791,7 +791,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
// PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); // Paper - AsyncTabCompleteEvent; run this async
// CraftBukkit start
if (this.chatSpamTickCount.addAndGet(io.papermc.paper.configuration.GlobalConfiguration.get().spamLimiter.tabSpamIncrement) > io.papermc.paper.configuration.GlobalConfiguration.get().spamLimiter.tabSpamLimit && !this.server.getPlayerList().isOp(this.player.getGameProfile())) { // Paper - configurable tab spam limits
- this.disconnect(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - Kick event cause
+ this.disconnectAsync(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - Kick event cause // Paper - add proper async disconnect
return;
}
// CraftBukkit end
@@ -803,7 +803,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
// 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);
+ this.disconnectAsync(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - add proper async disconnect
return;
}
// Paper end
@@ -1190,14 +1190,14 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
if (byteTotal > byteAllowed) {
ServerGamePacketListenerImpl.LOGGER.warn("{} tried to send a book too large. Book size: {} - Allowed: {} - Pages: {}", this.player.getScoreboardName(), byteTotal, byteAllowed, pageList.size());
- this.disconnect(Component.literal("Book too large!"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - kick event cause
+ this.disconnectAsync(Component.literal("Book too large!"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - kick event cause // Paper - add proper async disconnect
return;
}
}
// Paper end - Book size limits
// CraftBukkit start
if (this.lastBookTick + 20 > MinecraftServer.currentTick) {
- this.disconnect(Component.literal("Book edited too quickly!"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - kick event cause
+ this.disconnectAsync(Component.literal("Book edited too quickly!"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - kick event cause // Paper - add proper async disconnect
return;
}
this.lastBookTick = MinecraftServer.currentTick;
@@ -2354,7 +2354,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
private void tryHandleChat(String s, Runnable runnable, boolean sync) { // CraftBukkit
if (ServerGamePacketListenerImpl.isChatMessageIllegal(s)) {
- this.disconnect((Component) Component.translatable("multiplayer.disconnect.illegal_characters"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_CHARACTERS); // Paper
+ this.disconnectAsync((Component) Component.translatable("multiplayer.disconnect.illegal_characters"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_CHARACTERS); // Paper // Paper - add proper async disconnect
} else if (this.player.isRemoved() || this.player.getChatVisibility() == ChatVisiblity.HIDDEN) { // CraftBukkit - dead men tell no tales
this.send(new ClientboundSystemChatPacket(Component.translatable("chat.disabled.options").withStyle(ChatFormatting.RED), false));
} else {
@@ -2377,7 +2377,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
if (optional.isEmpty()) {
ServerGamePacketListenerImpl.LOGGER.warn("Failed to validate message acknowledgements from {}", this.player.getName().getString());
- this.disconnect(ServerGamePacketListenerImpl.CHAT_VALIDATION_FAILED, org.bukkit.event.player.PlayerKickEvent.Cause.CHAT_VALIDATION_FAILED); // Paper - kick event causes
+ this.disconnectAsync(ServerGamePacketListenerImpl.CHAT_VALIDATION_FAILED, org.bukkit.event.player.PlayerKickEvent.Cause.CHAT_VALIDATION_FAILED); // Paper - kick event causes // Paper - add proper async disconnect
}
return optional;
@@ -2550,7 +2550,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
// this.chatSpamTickCount += 20;
if (counted && this.chatSpamTickCount.addAndGet(20) > 200 && !this.server.getPlayerList().isOp(this.player.getGameProfile()) && !this.server.isSingleplayerOwner(this.player.getGameProfile())) { // Paper - exclude from SpigotConfig.spamExclusions
// CraftBukkit end
- this.disconnect((Component) Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - kick event cause
+ this.disconnectAsync((Component) Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - kick event cause // Paper - add proper async disconnect
}
}
@@ -2562,7 +2562,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
synchronized (this.lastSeenMessages) {
if (!this.lastSeenMessages.applyOffset(packet.offset())) {
ServerGamePacketListenerImpl.LOGGER.warn("Failed to validate message acknowledgements from {}", this.player.getName().getString());
- this.disconnect(ServerGamePacketListenerImpl.CHAT_VALIDATION_FAILED, org.bukkit.event.player.PlayerKickEvent.Cause.CHAT_VALIDATION_FAILED); // Paper - kick event causes
+ this.disconnectAsync(ServerGamePacketListenerImpl.CHAT_VALIDATION_FAILED, org.bukkit.event.player.PlayerKickEvent.Cause.CHAT_VALIDATION_FAILED); // Paper - kick event causes // Paper - add proper async disconnect
}
}
@@ -2710,7 +2710,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
}
if (i > 4096) {
- this.disconnect((Component) Component.translatable("multiplayer.disconnect.too_many_pending_chats"), org.bukkit.event.player.PlayerKickEvent.Cause.TOO_MANY_PENDING_CHATS); // Paper - kick event cause
+ this.disconnectAsync((Component) Component.translatable("multiplayer.disconnect.too_many_pending_chats"), org.bukkit.event.player.PlayerKickEvent.Cause.TOO_MANY_PENDING_CHATS); // Paper - kick event cause // Paper - add proper async disconnect
}
}
@@ -3314,7 +3314,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
// Paper start - auto recipe limit
if (!org.bukkit.Bukkit.isPrimaryThread()) {
if (this.recipeSpamPackets.addAndGet(io.papermc.paper.configuration.GlobalConfiguration.get().spamLimiter.recipeSpamIncrement) > io.papermc.paper.configuration.GlobalConfiguration.get().spamLimiter.recipeSpamLimit) {
- this.disconnect(net.minecraft.network.chat.Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - kick event cause
+ this.disconnectAsync(net.minecraft.network.chat.Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - kick event cause // Paper - add proper async disconnect
return;
}
}

View file

@ -1,43 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shane Freeder <theboyetronic@gmail.com>
Date: Sun, 20 Oct 2024 18:23:59 +0100
Subject: [PATCH] Always send Banner patterns to the client
The mojang client will not remove patterns from a Banner when none
are sent inside of an update packet, given that this is not an expected
flow for them, this is not all too surprising. So, we shall resort to always
sending the patterns over the network for update packets.
diff --git a/src/main/java/net/minecraft/world/level/block/entity/BannerBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BannerBlockEntity.java
index 60c26076e7acf869fa0e20fdc14eeec341387d99..60a9f3c7f007d268f24a4fe9e87029fdbc8360f9 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/BannerBlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/BannerBlockEntity.java
@@ -63,7 +63,7 @@ public class BannerBlockEntity extends BlockEntity implements Nameable {
@Override
protected void saveAdditional(CompoundTag nbt, HolderLookup.Provider registryLookup) {
super.saveAdditional(nbt, registryLookup);
- if (!this.patterns.equals(BannerPatternLayers.EMPTY)) {
+ if (!this.patterns.equals(BannerPatternLayers.EMPTY) || serialisingForNetwork.get()) { // Paper - always send patterns to client
nbt.put("patterns", (Tag) BannerPatternLayers.CODEC.encodeStart(registryLookup.createSerializationContext(NbtOps.INSTANCE), this.patterns).getOrThrow());
}
@@ -95,9 +95,18 @@ public class BannerBlockEntity extends BlockEntity implements Nameable {
return ClientboundBlockEntityDataPacket.create(this);
}
+ // Paper start - always send patterns to client
+ ThreadLocal<Boolean> serialisingForNetwork = ThreadLocal.withInitial(() -> Boolean.FALSE);
@Override
public CompoundTag getUpdateTag(HolderLookup.Provider registryLookup) {
- return this.saveWithoutMetadata(registryLookup);
+ final Boolean wasSerialisingForNetwork = serialisingForNetwork.get();
+ try {
+ serialisingForNetwork.set(Boolean.TRUE);
+ return this.saveWithoutMetadata(registryLookup);
+ } finally {
+ serialisingForNetwork.set(wasSerialisingForNetwork);
+ }
+ // Paper end - always send patterns to client
}
public BannerPatternLayers getPatterns() {