1037
This commit is contained in:
parent
227caac7f0
commit
afbb0d88dd
15 changed files with 156 additions and 145 deletions
73
patches/server/1023-Item-serialization-as-json.patch
Normal file
73
patches/server/1023-Item-serialization-as-json.patch
Normal file
|
@ -0,0 +1,73 @@
|
|||
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 c80fd4960dfbb0fde37363e7df25b0a5411bdb11..ff7f6916f65466c25a7bde35d64682c15b211697 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 293757b8e96ae7b0e807d807affa3fdab5181d39..f880bf91155b017c954e3e321c5a203c05c2162f 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");
|
|
@ -0,0 +1,26 @@
|
|||
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 656c9a6d8cd42891141ee29ec91ab5d166051ed6..df847c9897f209700a79aa1a8254b708ef7bf260 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();
|
|
@ -0,0 +1,163 @@
|
|||
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 246b5649883e4f305afa5a887b9df0f3735f7593..5d8885bca55503bf7e1a2a4e1bb9b3bd86d55391 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();
|
||||
@@ -212,7 +211,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;
|
||||
}
|
||||
@@ -243,12 +242,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 17974f85d3c1db549ea11e8809954cd9d2af063e..5a6e119d29ecdc45dee40d5984e502fb8e4d1543 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java
|
||||
@@ -98,7 +98,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) {
|
||||
@@ -217,10 +217,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();
|
||||
}
|
||||
@@ -237,7 +234,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
|
||||
@@ -278,7 +275,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 2bafacd7bc56186d9105d2031180f8c4a6940018..4ea29e8f2b39d7b44e0461d6a2cdd3fc257abd44 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
|
||||
@@ -55,7 +55,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;
|
||||
|
||||
@@ -170,12 +170,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
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
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 1dcb8a287be7df2a59b5b4c1345be80637a7f679..8e2eb7b61421ceb063654826941f1a81f6f50bdf 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;
|
|
@ -0,0 +1,52 @@
|
|||
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 1722f11ad070715077f5dcaff008b98f7ee104ab..cae9682df8795c5f73e86c27d717b6f72e7e8592 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -2195,24 +2195,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
|
|
@ -0,0 +1,33 @@
|
|||
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 944dcc1126c947b4c8c3b4fdd174eb57320abbba..260fb93e71812698beb475bab7a05b9b860c6cbd 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java
|
||||
@@ -322,6 +322,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,
|
154
patches/server/1029-Improve-entity-effect-API.patch
Normal file
154
patches/server/1029-Improve-entity-effect-API.patch
Normal file
|
@ -0,0 +1,154 @@
|
|||
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 ca95a36b0149d4b8a67c3b42316c5d9d0415f5dd..64c6f54cc4d0c22bc972b808cb92925cc7526db2 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
@@ -1300,4 +1300,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 8d16575c74b81ada4e4efe70e8f077f07cd0a3f0..852cadccfbd22d535f26ac781aea2fe99686947e 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
||||
@@ -1294,6 +1294,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;
|
||||
}
|
||||
@@ -1303,7 +1308,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
|
||||
@@ -3545,4 +3550,14 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
public void setSendViewDistance(final int viewDistance) {
|
||||
throw new UnsupportedOperationException("Not implemented yet");
|
||||
}
|
||||
+
|
||||
+ // 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));
|
||||
+ }
|
||||
+ }
|
||||
+}
|
181
patches/server/1030-Add-recipeBrewTime.patch
Normal file
181
patches/server/1030-Add-recipeBrewTime.patch
Normal file
|
@ -0,0 +1,181 @@
|
|||
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 182c87a0b7081f6a777c4c7969961c30438b0d86..3be46ecfa382e15d09a88912c498abb6034c3a90 100644
|
||||
--- a/src/main/java/net/minecraft/world/inventory/BrewingStandMenu.java
|
||||
+++ b/src/main/java/net/minecraft/world/inventory/BrewingStandMenu.java
|
||||
@@ -42,14 +42,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();
|
||||
@@ -61,7 +61,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
|
||||
this.addStandardInventorySlots(playerInventory, 8, 84);
|
||||
}
|
||||
|
||||
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 4ea29e8f2b39d7b44e0461d6a2cdd3fc257abd44..02fc9ce21c7d367055da350d21be4870d4242f3a 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
|
||||
@@ -50,6 +50,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;
|
||||
@@ -100,6 +101,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;
|
||||
}
|
||||
@@ -115,13 +121,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
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -189,7 +200,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
|
||||
}
|
203
patches/server/1031-Call-bucket-events-for-cauldrons.patch
Normal file
203
patches/server/1031-Call-bucket-events-for-cauldrons.patch
Normal file
|
@ -0,0 +1,203 @@
|
|||
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 df76185d42075834a39c79515917e03beb938a06..ee2c4c5265d96afe592c5007b0b6ad7649ce5190 100644
|
||||
--- a/src/main/java/net/minecraft/core/cauldron/CauldronInteraction.java
|
||||
+++ b/src/main/java/net/minecraft/core/cauldron/CauldronInteraction.java
|
||||
@@ -53,7 +53,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 InteractionResult.TRY_WITH_EMPTY_HAND;
|
||||
});
|
||||
CauldronInteraction.InteractionMap cauldroninteraction_a = new CauldronInteraction.InteractionMap(name, object2objectopenhashmap);
|
||||
@@ -62,13 +62,13 @@ public interface CauldronInteraction {
|
||||
return cauldroninteraction_a;
|
||||
}
|
||||
|
||||
- InteractionResult interact(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack);
|
||||
+ InteractionResult 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)) {
|
||||
@@ -96,12 +96,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)) {
|
||||
@@ -120,7 +120,7 @@ public interface CauldronInteraction {
|
||||
|
||||
return InteractionResult.SUCCESS;
|
||||
});
|
||||
- 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 InteractionResult.TRY_WITH_EMPTY_HAND;
|
||||
} else {
|
||||
@@ -187,18 +187,18 @@ public interface CauldronInteraction {
|
||||
map1.put(Items.YELLOW_SHULKER_BOX, CauldronInteraction::shulkerBoxInteraction);
|
||||
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);
|
||||
+ }, SoundEvents.BUCKET_FILL_POWDER_SNOW, hitDirection); // Paper - add hitDirection
|
||||
});
|
||||
CauldronInteraction.addDefaultInteractions(map3);
|
||||
}
|
||||
@@ -210,10 +210,24 @@ public interface CauldronInteraction {
|
||||
}
|
||||
|
||||
static InteractionResult 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 InteractionResult 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 InteractionResult.TRY_WITH_EMPTY_HAND;
|
||||
} 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 InteractionResult.PASS;
|
||||
+ }
|
||||
+ 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 InteractionResult.SUCCESS;
|
||||
@@ -234,7 +248,22 @@ public interface CauldronInteraction {
|
||||
}
|
||||
|
||||
static InteractionResult 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 InteractionResult 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 InteractionResult.PASS;
|
||||
+ }
|
||||
+ 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 InteractionResult.SUCCESS;
|
||||
@@ -242,7 +271,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
|
||||
@@ -253,19 +282,19 @@ public interface CauldronInteraction {
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
|
||||
- private static InteractionResult fillWaterInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) {
|
||||
- return CauldronInteraction.emptyBucket(world, pos, player, hand, stack, (BlockState) Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), SoundEvents.BUCKET_EMPTY);
|
||||
+ private static InteractionResult fillWaterInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection
|
||||
+ return CauldronInteraction.emptyBucket(world, pos, player, hand, stack, (BlockState) Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), SoundEvents.BUCKET_EMPTY, hitDirection); // Paper - add hitDirection
|
||||
}
|
||||
|
||||
- private static InteractionResult fillLavaInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) {
|
||||
- return (InteractionResult) (CauldronInteraction.isUnderWater(world, pos) ? InteractionResult.CONSUME : CauldronInteraction.emptyBucket(world, pos, player, hand, stack, Blocks.LAVA_CAULDRON.defaultBlockState(), SoundEvents.BUCKET_EMPTY_LAVA));
|
||||
+ private static InteractionResult fillLavaInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection
|
||||
+ return (InteractionResult) (CauldronInteraction.isUnderWater(world, pos) ? InteractionResult.CONSUME : CauldronInteraction.emptyBucket(world, pos, player, hand, stack, Blocks.LAVA_CAULDRON.defaultBlockState(), SoundEvents.BUCKET_EMPTY_LAVA, hitDirection)); // Paper - add hitDirection
|
||||
}
|
||||
|
||||
- private static InteractionResult fillPowderSnowInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) {
|
||||
- return (InteractionResult) (CauldronInteraction.isUnderWater(world, pos) ? InteractionResult.CONSUME : CauldronInteraction.emptyBucket(world, pos, player, hand, stack, (BlockState) Blocks.POWDER_SNOW_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), SoundEvents.BUCKET_EMPTY_POWDER_SNOW));
|
||||
+ private static InteractionResult fillPowderSnowInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection
|
||||
+ return (InteractionResult) (CauldronInteraction.isUnderWater(world, pos) ? InteractionResult.CONSUME : CauldronInteraction.emptyBucket(world, pos, player, hand, stack, (BlockState) Blocks.POWDER_SNOW_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), SoundEvents.BUCKET_EMPTY_POWDER_SNOW, hitDirection)); // Paper - add hitDirection
|
||||
}
|
||||
|
||||
- private static InteractionResult shulkerBoxInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) {
|
||||
+ private static InteractionResult shulkerBoxInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection
|
||||
Block block = Block.byItem(stack.getItem());
|
||||
|
||||
if (!(block instanceof ShulkerBoxBlock)) {
|
||||
@@ -283,12 +312,11 @@ public interface CauldronInteraction {
|
||||
player.awardStat(Stats.CLEAN_SHULKER_BOX);
|
||||
// LayeredCauldronBlock.lowerFillLevel(iblockdata, world, blockposition); // CraftBukkit
|
||||
}
|
||||
-
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
- private static InteractionResult bannerInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) {
|
||||
+ private static InteractionResult bannerInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection
|
||||
BannerPatternLayers bannerpatternlayers = (BannerPatternLayers) stack.getOrDefault(DataComponents.BANNER_PATTERNS, BannerPatternLayers.EMPTY);
|
||||
|
||||
if (bannerpatternlayers.layers().isEmpty()) {
|
||||
@@ -312,7 +340,7 @@ public interface CauldronInteraction {
|
||||
}
|
||||
}
|
||||
|
||||
- private static InteractionResult dyedItemIteration(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) {
|
||||
+ private static InteractionResult dyedItemIteration(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection
|
||||
if (!stack.is(ItemTags.DYEABLE)) {
|
||||
return InteractionResult.TRY_WITH_EMPTY_HAND;
|
||||
} else if (!stack.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 173fc110217307e225b4951c92ab22a1bef48dd4..e00ab1ed8088a1970249313ed63e09070fc6192d 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/AbstractCauldronBlock.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/AbstractCauldronBlock.java
|
||||
@@ -56,7 +56,7 @@ public abstract class AbstractCauldronBlock extends Block {
|
||||
@Override
|
||||
protected InteractionResult useItemOn(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
|
36
patches/server/1032-Add-PlayerInsertLecternBookEvent.patch
Normal file
36
patches/server/1032-Add-PlayerInsertLecternBookEvent.patch
Normal file
|
@ -0,0 +1,36 @@
|
|||
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 ec6ff0b192ae2f1586095519ad2472e76b2b5589..53f388dd0ba6b77fe49a584883e8c9d49d5a5fba 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/LecternBlock.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/LecternBlock.java
|
||||
@@ -158,7 +158,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);
|
||||
}
|
93
patches/server/1033-Void-damage-configuration-API.patch
Normal file
93
patches/server/1033-Void-damage-configuration-API.patch
Normal file
|
@ -0,0 +1,93 @@
|
|||
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 4f0ff0d333d2de1b4f6beac1ce25e214b971e387..2cf9490a116d70c00f00d41005b78d5b510f2162 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
@@ -853,8 +853,9 @@ 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.getMinY() - 64) || (this.level.getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER
|
||||
+ if (this.getY() < (double) (this.level.getMinY() + 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 f57c830a7286eb8cab1061c8ddebe6abab1fcced..ed624f54bbd7f9fd5a1ddc12a856f41f03571ac9 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
@@ -2702,7 +2702,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 36c3024c188197a777c8077704e1b64552c02d0a..38565ee35799bc8cdf3f224e0f92592a4a11300f 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
@@ -167,6 +167,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
|
||||
@@ -275,6 +310,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
|
45
patches/server/1034-Add-Offline-PDC-API.patch
Normal file
45
patches/server/1034-Add-Offline-PDC-API.patch
Normal file
|
@ -0,0 +1,45 @@
|
|||
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)) {
|
|
@ -0,0 +1,53 @@
|
|||
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 cc5aae32f34305965847ade8b530272b1126b5c9..dc2bc53f6fa84fa09bd86450060ad9878307001c 100644
|
||||
--- a/src/main/java/net/minecraft/world/inventory/AnvilMenu.java
|
||||
+++ b/src/main/java/net/minecraft/world/inventory/AnvilMenu.java
|
||||
@@ -54,6 +54,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);
|
||||
@@ -233,7 +234,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());
|
168
patches/server/1036-Add-proper-async-player-disconnections.patch
Normal file
168
patches/server/1036-Add-proper-async-player-disconnections.patch
Normal file
|
@ -0,0 +1,168 @@
|
|||
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 90a2c61c42cba7e38f167eccdd7a951a947963c4..fff8d15d44613a075b9793c2a41520212166eb3b 100644
|
||||
--- a/src/main/java/net/minecraft/network/Connection.java
|
||||
+++ b/src/main/java/net/minecraft/network/Connection.java
|
||||
@@ -844,6 +844,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 fc242acade3ff06c9213428cde103cf078216382..b0bc66dc7248aae691dcab68b925b52a1695e63f 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
|
||||
@@ -143,11 +143,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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -411,6 +407,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 cae9682df8795c5f73e86c27d717b6f72e7e8592..bffbf87a546cf8b5ffc0a58d853bacd5d7759abf 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -769,7 +769,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
||||
// PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); // Paper - AsyncTabCompleteEvent; run this async
|
||||
// CraftBukkit start
|
||||
if (!this.tabSpamThrottler.isIncrementAndUnderThreshold() && !this.server.getPlayerList().isOp(this.player.getGameProfile()) && !this.server.isSingleplayerOwner(this.player.getGameProfile())) { // Paper - configurable tab spam limits
|
||||
- this.disconnect(Component.translatable("disconnect.spam"), 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
|
||||
@@ -781,7 +781,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
|
||||
@@ -1171,14 +1171,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;
|
||||
@@ -2304,7 +2304,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 {
|
||||
@@ -2327,7 +2327,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;
|
||||
@@ -2498,7 +2498,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
||||
// this.chatSpamThrottler.increment();
|
||||
if (counted && !this.chatSpamThrottler.isIncrementAndUnderThreshold() && !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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2510,7 +2510,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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2658,7 +2658,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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3267,7 +3267,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
|
||||
// Paper start - auto recipe limit
|
||||
if (!org.bukkit.Bukkit.isPrimaryThread()) {
|
||||
if (!this.recipeSpamPackets.isIncrementAndUnderThreshold()) {
|
||||
- this.disconnect(net.minecraft.network.chat.Component.translatable("disconnect.spam"), 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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
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 98bc87fe5d153cc4927f7e1b4a02f61d9dd019a0..9528935a120f7d5a1fdb1a796854478e8a83f833 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 registries) {
|
||||
super.saveAdditional(nbt, registries);
|
||||
- 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(registries.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 registries) {
|
||||
+ final Boolean wasSerialisingForNetwork = serialisingForNetwork.get();
|
||||
+ try {
|
||||
+ serialisingForNetwork.set(Boolean.TRUE);
|
||||
return this.saveWithoutMetadata(registries);
|
||||
+ } finally {
|
||||
+ serialisingForNetwork.set(wasSerialisingForNetwork);
|
||||
+ }
|
||||
+ // Paper end - always send patterns to client
|
||||
}
|
||||
|
||||
public BannerPatternLayers getPatterns() {
|
Loading…
Add table
Add a link
Reference in a new issue