From f25c1a33a07f78b259a1d16f9c94aa33998d2718 Mon Sep 17 00:00:00 2001 From: Bjarne Koll Date: Sat, 14 Dec 2024 05:05:32 +0100 Subject: [PATCH] Finish block entity --- .../AbstractFurnaceBlockEntity.java.patch | 295 +++++++++++++++ .../block/entity/BannerBlockEntity.java.patch | 71 ++++ .../block/entity/BarrelBlockEntity.java.patch | 2 +- .../block/entity/BeaconBlockEntity.java.patch | 242 ++++++++++++ .../entity/BeehiveBlockEntity.java.patch | 244 ++++++++++++ .../level/block/entity/BlockEntity.java.patch | 133 +++++++ .../entity/BrewingStandBlockEntity.java.patch | 186 +++++++++ .../entity/BrushableBlockEntity.java.patch | 23 ++ ...alibratedSculkSensorBlockEntity.java.patch | 6 +- .../entity/CampfireBlockEntity.java.patch | 104 +++++ .../block/entity/ChestBlockEntity.java.patch | 2 +- .../ChiseledBookShelfBlockEntity.java.patch | 2 +- .../entity/ConduitBlockEntity.java.patch | 62 +++ .../entity/ContainerOpenersCounter.java.patch | 64 ++-- .../entity/CrafterBlockEntity.java.patch | 50 +++ .../entity/DecoratedPotBlockEntity.java.patch | 31 +- .../entity/DispenserBlockEntity.java.patch | 39 ++ .../block/entity/HopperBlockEntity.java.patch | 2 +- .../block/entity/JigsawBlockEntity.java.patch | 14 +- .../entity/JukeboxBlockEntity.java.patch | 57 +-- .../entity/LecternBlockEntity.java.patch | 139 +++++++ ...andomizableContainerBlockEntity.java.patch | 6 +- .../entity/SculkSensorBlockEntity.java.patch | 33 +- .../SculkShriekerBlockEntity.java.patch | 10 +- .../entity/ShulkerBoxBlockEntity.java.patch | 41 +- .../block/entity/SignBlockEntity.java.patch | 183 +++++++++ .../block/entity/SkullBlockEntity.java.patch | 36 +- .../AbstractFurnaceBlockEntity.java.patch | 356 ------------------ .../block/entity/BannerBlockEntity.java.patch | 81 ---- .../block/entity/BeaconBlockEntity.java.patch | 259 ------------- .../entity/BeehiveBlockEntity.java.patch | 306 --------------- .../level/block/entity/BlockEntity.java.patch | 153 -------- .../block/entity/BlockEntityType.java.patch | 20 - .../entity/BrewingStandBlockEntity.java.patch | 232 ------------ .../entity/BrushableBlockEntity.java.patch | 36 -- .../entity/CampfireBlockEntity.java.patch | 121 ------ .../entity/ConduitBlockEntity.java.patch | 108 ------ .../entity/CrafterBlockEntity.java.patch | 68 ---- .../entity/DispenserBlockEntity.java.patch | 50 --- .../entity/LecternBlockEntity.java.patch | 164 -------- .../block/entity/SignBlockEntity.java.patch | 269 ------------- 41 files changed, 1904 insertions(+), 2396 deletions(-) create mode 100644 paper-server/patches/sources/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java.patch create mode 100644 paper-server/patches/sources/net/minecraft/world/level/block/entity/BannerBlockEntity.java.patch create mode 100644 paper-server/patches/sources/net/minecraft/world/level/block/entity/BeaconBlockEntity.java.patch create mode 100644 paper-server/patches/sources/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java.patch create mode 100644 paper-server/patches/sources/net/minecraft/world/level/block/entity/BlockEntity.java.patch create mode 100644 paper-server/patches/sources/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java.patch create mode 100644 paper-server/patches/sources/net/minecraft/world/level/block/entity/BrushableBlockEntity.java.patch rename paper-server/patches/{unapplied => sources}/net/minecraft/world/level/block/entity/CalibratedSculkSensorBlockEntity.java.patch (92%) create mode 100644 paper-server/patches/sources/net/minecraft/world/level/block/entity/CampfireBlockEntity.java.patch create mode 100644 paper-server/patches/sources/net/minecraft/world/level/block/entity/ConduitBlockEntity.java.patch rename paper-server/patches/{unapplied => sources}/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java.patch (50%) create mode 100644 paper-server/patches/sources/net/minecraft/world/level/block/entity/CrafterBlockEntity.java.patch rename paper-server/patches/{unapplied => sources}/net/minecraft/world/level/block/entity/DecoratedPotBlockEntity.java.patch (58%) create mode 100644 paper-server/patches/sources/net/minecraft/world/level/block/entity/DispenserBlockEntity.java.patch rename paper-server/patches/{unapplied => sources}/net/minecraft/world/level/block/entity/JigsawBlockEntity.java.patch (52%) rename paper-server/patches/{unapplied => sources}/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java.patch (54%) create mode 100644 paper-server/patches/sources/net/minecraft/world/level/block/entity/LecternBlockEntity.java.patch rename paper-server/patches/{unapplied => sources}/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java.patch (85%) rename paper-server/patches/{unapplied => sources}/net/minecraft/world/level/block/entity/SculkSensorBlockEntity.java.patch (72%) rename paper-server/patches/{unapplied => sources}/net/minecraft/world/level/block/entity/SculkShriekerBlockEntity.java.patch (83%) rename paper-server/patches/{unapplied => sources}/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java.patch (56%) create mode 100644 paper-server/patches/sources/net/minecraft/world/level/block/entity/SignBlockEntity.java.patch rename paper-server/patches/{unapplied => sources}/net/minecraft/world/level/block/entity/SkullBlockEntity.java.patch (76%) delete mode 100644 paper-server/patches/unapplied/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java.patch delete mode 100644 paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BannerBlockEntity.java.patch delete mode 100644 paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BeaconBlockEntity.java.patch delete mode 100644 paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java.patch delete mode 100644 paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BlockEntity.java.patch delete mode 100644 paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BlockEntityType.java.patch delete mode 100644 paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java.patch delete mode 100644 paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BrushableBlockEntity.java.patch delete mode 100644 paper-server/patches/unapplied/net/minecraft/world/level/block/entity/CampfireBlockEntity.java.patch delete mode 100644 paper-server/patches/unapplied/net/minecraft/world/level/block/entity/ConduitBlockEntity.java.patch delete mode 100644 paper-server/patches/unapplied/net/minecraft/world/level/block/entity/CrafterBlockEntity.java.patch delete mode 100644 paper-server/patches/unapplied/net/minecraft/world/level/block/entity/DispenserBlockEntity.java.patch delete mode 100644 paper-server/patches/unapplied/net/minecraft/world/level/block/entity/LecternBlockEntity.java.patch delete mode 100644 paper-server/patches/unapplied/net/minecraft/world/level/block/entity/SignBlockEntity.java.patch diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java.patch new file mode 100644 index 0000000000..622ee5f7ef --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java.patch @@ -0,0 +1,295 @@ +--- a/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java +@@ -20,7 +_,6 @@ + import net.minecraft.world.ContainerHelper; + import net.minecraft.world.WorldlyContainer; + import net.minecraft.world.entity.ExperienceOrb; +-import net.minecraft.world.entity.player.Player; + import net.minecraft.world.entity.player.StackedItemContents; + import net.minecraft.world.inventory.ContainerData; + import net.minecraft.world.inventory.RecipeCraftingHolder; +@@ -99,11 +_,44 @@ + }; + public final Reference2IntOpenHashMap>> recipesUsed = new Reference2IntOpenHashMap<>(); + private final RecipeManager.CachedCheck quickCheck; ++ public final RecipeType recipeType; // Paper - cook speed multiplier API ++ public double cookSpeedMultiplier = 1.0; // Paper - cook speed multiplier API + + protected AbstractFurnaceBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState, RecipeType recipeType) { + super(type, pos, blockState); + this.quickCheck = RecipeManager.createCheck(recipeType); +- } ++ this.recipeType = recipeType; // Paper - cook speed multiplier API ++ } ++ ++ // CraftBukkit start - add fields and methods ++ private int maxStack = MAX_STACK; ++ public List transaction = new java.util.ArrayList<>(); ++ ++ public List getContents() { ++ return this.items; ++ } ++ ++ public void onOpen(org.bukkit.craftbukkit.entity.CraftHumanEntity who) { ++ this.transaction.add(who); ++ } ++ ++ public void onClose(org.bukkit.craftbukkit.entity.CraftHumanEntity who) { ++ this.transaction.remove(who); ++ } ++ ++ public List getViewers() { ++ return this.transaction; ++ } ++ ++ @Override ++ public int getMaxStackSize() { ++ return this.maxStack; ++ } ++ ++ public void setMaxStackSize(int size) { ++ this.maxStack = size; ++ } ++ // CraftBukkit end + + private boolean isLit() { + return this.litTimeRemaining > 0; +@@ -121,8 +_,19 @@ + CompoundTag compound = tag.getCompound("RecipesUsed"); + + for (String string : compound.getAllKeys()) { +- this.recipesUsed.put(ResourceKey.create(Registries.RECIPE, ResourceLocation.parse(string)), compound.getInt(string)); +- } ++ // Paper start - Validate ResourceLocation ++ final ResourceLocation resourceLocation = ResourceLocation.tryParse(string); ++ if (resourceLocation != null) { ++ this.recipesUsed.put(ResourceKey.create(Registries.RECIPE, resourceLocation), tag.getInt(string)); ++ } ++ // Paper end - Validate ResourceLocation ++ } ++ ++ // Paper start - cook speed multiplier API ++ if (tag.contains("Paper.CookSpeedMultiplier")) { ++ this.cookSpeedMultiplier = tag.getDouble("Paper.CookSpeedMultiplier"); ++ } ++ // Paper end - cook speed multiplier API + } + + @Override +@@ -132,6 +_,7 @@ + tag.putShort("cooking_total_time", (short)this.cookingTotalTime); + tag.putShort("lit_time_remaining", (short)this.litTimeRemaining); + tag.putShort("lit_total_time", (short)this.litTotalTime); ++ tag.putDouble("Paper.CookSpeedMultiplier", this.cookSpeedMultiplier); // Paper - cook speed multiplier API + ContainerHelper.saveAllItems(tag, this.items, registries); + CompoundTag compoundTag = new CompoundTag(); + this.recipesUsed.forEach((recipe, count) -> compoundTag.putInt(recipe.location().toString(), count)); +@@ -160,11 +_,22 @@ + + int maxStackSize = furnace.getMaxStackSize(); + if (!furnace.isLit() && canBurn(level.registryAccess(), recipeHolder, singleRecipeInput, furnace.items, maxStackSize)) { +- furnace.litTimeRemaining = furnace.getBurnDuration(level.fuelValues(), itemStack); ++ // CraftBukkit start ++ org.bukkit.craftbukkit.inventory.CraftItemStack fuel = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack); ++ ++ org.bukkit.event.inventory.FurnaceBurnEvent furnaceBurnEvent = new org.bukkit.event.inventory.FurnaceBurnEvent( ++ org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), ++ fuel, ++ furnace.getBurnDuration(level.fuelValues(), itemStack) ++ ); ++ if (!furnaceBurnEvent.callEvent()) return; ++ // CraftBukkit end ++ ++ furnace.litTimeRemaining = furnaceBurnEvent.getBurnTime(); // CraftBukkit - respect event output + furnace.litTotalTime = furnace.litTimeRemaining; +- if (furnace.isLit()) { ++ if (furnace.isLit() && furnaceBurnEvent.isBurning()) { // CraftBukkit - respect event output + flag = true; +- if (flag2) { ++ if (flag2 && furnaceBurnEvent.willConsumeFuel()) { // Paper - add consumeFuel to FurnaceBurnEvent + Item item = itemStack.getItem(); + itemStack.shrink(1); + if (itemStack.isEmpty()) { +@@ -175,11 +_,28 @@ + } + + if (furnace.isLit() && canBurn(level.registryAccess(), recipeHolder, singleRecipeInput, furnace.items, maxStackSize)) { ++ // CraftBukkit start ++ if (recipeHolder != null && furnace.cookingTimer == 0) { ++ org.bukkit.craftbukkit.inventory.CraftItemStack source = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(furnace.items.get(0)); ++ org.bukkit.inventory.CookingRecipe recipe = (org.bukkit.inventory.CookingRecipe) recipeHolder.toBukkitRecipe(); ++ ++ org.bukkit.event.inventory.FurnaceStartSmeltEvent event = new org.bukkit.event.inventory.FurnaceStartSmeltEvent( ++ org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), ++ source, ++ recipe, ++ getTotalCookTime(level, furnace, furnace.recipeType, furnace.cookSpeedMultiplier) // Paper - cook speed multiplier API ++ ); ++ event.callEvent(); ++ ++ furnace.cookingTotalTime = event.getTotalCookTime(); ++ } ++ // CraftBukkit end ++ + furnace.cookingTimer++; +- if (furnace.cookingTimer == furnace.cookingTotalTime) { ++ if (furnace.cookingTimer >= furnace.cookingTotalTime) { // Paper - cook speed multiplier API + furnace.cookingTimer = 0; +- furnace.cookingTotalTime = getTotalCookTime(level, furnace); +- if (burn(level.registryAccess(), recipeHolder, singleRecipeInput, furnace.items, maxStackSize)) { ++ furnace.cookingTotalTime = getTotalCookTime(level, furnace, furnace.recipeType, furnace.cookSpeedMultiplier); // Paper - cook speed multiplier API ++ if (burn(level.registryAccess(), recipeHolder, singleRecipeInput, furnace.items, maxStackSize, level, furnace.worldPosition)) { // CraftBukkit + furnace.setRecipeUsed(recipeHolder); + } + +@@ -233,17 +_,47 @@ + @Nullable RecipeHolder recipe, + SingleRecipeInput recipeInput, + NonNullList items, +- int maxStackSize ++ int maxStackSize, ++ net.minecraft.world.level.Level level, // CraftBukkit ++ BlockPos blockPos // CraftBukkit + ) { + if (recipe != null && canBurn(registryAccess, recipe, recipeInput, items, maxStackSize)) { +- ItemStack itemStack = items.get(0); +- ItemStack itemStack1 = recipe.value().assemble(recipeInput, registryAccess); +- ItemStack itemStack2 = items.get(2); ++ ItemStack itemStack = items.get(0); final ItemStack ingredient = itemStack; // Paper - OBFHELPER ++ ItemStack itemStack1 = recipe.value().assemble(recipeInput, registryAccess); ItemStack result = itemStack1; // Paper - OBFHELPER ++ ItemStack itemStack2 = items.get(2); final ItemStack existingResults = itemStack2; // Paper - OBFHELPER ++ // CraftBukkit start - fire FurnaceSmeltEvent ++ org.bukkit.craftbukkit.inventory.CraftItemStack apiIngredient = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(ingredient); ++ org.bukkit.inventory.ItemStack apiResult = org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(result); ++ ++ org.bukkit.event.inventory.FurnaceSmeltEvent furnaceSmeltEvent = new org.bukkit.event.inventory.FurnaceSmeltEvent( ++ org.bukkit.craftbukkit.block.CraftBlock.at(level, blockPos), ++ apiIngredient, ++ apiResult, ++ (org.bukkit.inventory.CookingRecipe) recipe.toBukkitRecipe() // Paper - Add recipe to cook events ++ ); ++ if (!furnaceSmeltEvent.callEvent()) return false; ++ ++ apiResult = furnaceSmeltEvent.getResult(); ++ itemStack1 = result = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(apiResult); ++ ++ if (!result.isEmpty()) { ++ if (existingResults.isEmpty()) { ++ items.set(2, result.copy()); ++ } else if (org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(existingResults).isSimilar(apiResult)) { ++ existingResults.grow(result.getCount()); ++ } else { ++ return false; ++ } ++ } ++ ++ /* + if (itemStack2.isEmpty()) { + items.set(2, itemStack1.copy()); + } else if (ItemStack.isSameItemSameComponents(itemStack2, itemStack1)) { + itemStack2.grow(1); + } ++ */ ++ // CraftBukkit end + + if (itemStack.is(Blocks.WET_SPONGE.asItem()) && !items.get(1).isEmpty() && items.get(1).is(Items.BUCKET)) { + items.set(1, new ItemStack(Items.WATER_BUCKET)); +@@ -260,9 +_,16 @@ + return fuelValues.burnDuration(stack); + } + +- public static int getTotalCookTime(ServerLevel level, AbstractFurnaceBlockEntity furnace) { ++ private static int getTotalCookTime(@Nullable ServerLevel level, AbstractFurnaceBlockEntity furnace, RecipeType recipeType, double cookSpeedMultiplier) { // Paper - cook speed multiplier API + SingleRecipeInput singleRecipeInput = new SingleRecipeInput(furnace.getItem(0)); +- return furnace.quickCheck.getRecipeFor(singleRecipeInput, level).map(recipe -> recipe.value().cookingTime()).orElse(200); ++ // Paper start - cook speed multiplier API ++ /* Scale the recipe's cooking time to the current cookSpeedMultiplier */ ++ int cookTime = level != null ++ ? furnace.quickCheck.getRecipeFor(singleRecipeInput, level).map(holder -> holder.value().cookingTime()).orElse(200) ++ /* passing a null level here is safe. world is only used for map extending recipes which won't happen here */ ++ : (net.minecraft.server.MinecraftServer.getServer().getRecipeManager().getRecipeFor(recipeType, singleRecipeInput, level).map(holder -> holder.value().cookingTime()).orElse(200)); ++ return (int) Math.ceil (cookTime / cookSpeedMultiplier); ++ // Paper end - cook speed multiplier AP + } + + @Override +@@ -306,7 +_,7 @@ + this.items.set(index, stack); + stack.limitSize(this.getMaxStackSize(stack)); + if (index == 0 && !flag && this.level instanceof ServerLevel serverLevel) { +- this.cookingTotalTime = getTotalCookTime(serverLevel, this); ++ this.cookingTotalTime = getTotalCookTime(serverLevel, this, this.recipeType, this.cookSpeedMultiplier); // Paper - cook speed multiplier API + this.cookingTimer = 0; + this.setChanged(); + } +@@ -339,11 +_,11 @@ + } + + @Override +- public void awardUsedRecipes(Player player, List items) { ++ public void awardUsedRecipes(net.minecraft.world.entity.player.Player player, List items) { + } + +- public void awardUsedRecipesAndPopExperience(ServerPlayer player) { +- List> recipesToAwardAndPopExperience = this.getRecipesToAwardAndPopExperience(player.serverLevel(), player.position()); ++ public void awardUsedRecipesAndPopExperience(ServerPlayer player, ItemStack itemstack, int amount) { // CraftBukkit ++ List> recipesToAwardAndPopExperience = this.getRecipesToAwardAndPopExperience(player.serverLevel(), player.position(), this.worldPosition, player, itemstack, amount); // CraftBukkit - overload for exp spawn events + player.awardRecipes(recipesToAwardAndPopExperience); + + for (RecipeHolder recipeHolder : recipesToAwardAndPopExperience) { +@@ -356,26 +_,52 @@ + } + + public List> getRecipesToAwardAndPopExperience(ServerLevel level, Vec3 popVec) { ++ // CraftBukkit start ++ return this.getRecipesToAwardAndPopExperience(level, popVec, this.worldPosition, null, null, 0); ++ } ++ public List> getRecipesToAwardAndPopExperience(ServerLevel level, Vec3 popVec, BlockPos blockPos, ServerPlayer serverPlayer, ItemStack itemStack, int amount) { ++ // CraftBukkit end + List> list = Lists.newArrayList(); + + for (Entry>> entry : this.recipesUsed.reference2IntEntrySet()) { + level.recipeAccess().byKey(entry.getKey()).ifPresent(recipe -> { ++ if (!(recipe.value() instanceof AbstractCookingRecipe)) return; // Paper - don't process non-cooking recipes + list.add((RecipeHolder)recipe); +- createExperience(level, popVec, entry.getIntValue(), ((AbstractCookingRecipe)recipe.value()).experience()); ++ createExperience(level, popVec, entry.getIntValue(), ((AbstractCookingRecipe)recipe.value()).experience(), blockPos, serverPlayer, itemStack, amount); + }); + } + + return list; + } + +- private static void createExperience(ServerLevel level, Vec3 popVec, int recipeIndex, float experience) { ++ private static void createExperience(ServerLevel level, Vec3 popVec, int recipeIndex, float experience, BlockPos blockPos, ServerPlayer serverPlayer, ItemStack itemStack, int amount) { // CraftBukkit + int floor = Mth.floor(recipeIndex * experience); + float fraction = Mth.frac(recipeIndex * experience); + if (fraction != 0.0F && Math.random() < fraction) { + floor++; + } + +- ExperienceOrb.award(level, popVec, floor); ++ // CraftBukkit start - fire FurnaceExtractEvent / BlockExpEvent ++ org.bukkit.event.block.BlockExpEvent event; ++ if (amount != 0) { ++ event = new org.bukkit.event.inventory.FurnaceExtractEvent( ++ serverPlayer.getBukkitEntity(), ++ org.bukkit.craftbukkit.block.CraftBlock.at(level, blockPos), ++ org.bukkit.craftbukkit.inventory.CraftItemType.minecraftToBukkit(itemStack.getItem()), ++ amount, ++ floor ++ ); ++ } else { ++ event = new org.bukkit.event.block.BlockExpEvent( ++ org.bukkit.craftbukkit.block.CraftBlock.at(level, blockPos), ++ floor ++ ); ++ } ++ event.callEvent(); ++ floor = event.getExpToDrop(); ++ // CraftBukkit end ++ ++ ExperienceOrb.award(level, popVec, floor, org.bukkit.entity.ExperienceOrb.SpawnReason.FURNACE, serverPlayer); // Paper + } + + @Override diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BannerBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BannerBlockEntity.java.patch new file mode 100644 index 0000000000..152410ce51 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BannerBlockEntity.java.patch @@ -0,0 +1,71 @@ +--- a/net/minecraft/world/level/block/entity/BannerBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/BannerBlockEntity.java +@@ -23,7 +_,7 @@ + public static final int MAX_PATTERNS = 6; + private static final String TAG_PATTERNS = "patterns"; + @Nullable +- private Component name; ++ public Component name; // Paper - AT public + public DyeColor baseColor; + private BannerPatternLayers patterns = BannerPatternLayers.EMPTY; + +@@ -50,7 +_,7 @@ + @Override + protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.saveAdditional(tag, registries); +- if (!this.patterns.equals(BannerPatternLayers.EMPTY)) { ++ if (!this.patterns.equals(BannerPatternLayers.EMPTY) || serialisingForNetwork.get()) { // Paper - always send patterns to client + tag.put("patterns", BannerPatternLayers.CODEC.encodeStart(registries.createSerializationContext(NbtOps.INSTANCE), this.patterns).getOrThrow()); + } + +@@ -70,7 +_,7 @@ + BannerPatternLayers.CODEC + .parse(registries.createSerializationContext(NbtOps.INSTANCE), tag.get("patterns")) + .resultOrPartial(string -> LOGGER.error("Failed to parse banner patterns: '{}'", string)) +- .ifPresent(bannerPatternLayers -> this.patterns = bannerPatternLayers); ++ .ifPresent(bannerPatternLayers -> this.setPatterns(bannerPatternLayers)); // CraftBukkit - apply limits + } + } + +@@ -79,9 +_,18 @@ + return ClientboundBlockEntityDataPacket.create(this); + } + ++ // Paper start - always send patterns to client ++ ThreadLocal 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() { +@@ -101,7 +_,7 @@ + @Override + protected void applyImplicitComponents(BlockEntity.DataComponentInput componentInput) { + super.applyImplicitComponents(componentInput); +- this.patterns = componentInput.getOrDefault(DataComponents.BANNER_PATTERNS, BannerPatternLayers.EMPTY); ++ this.setPatterns(componentInput.getOrDefault(DataComponents.BANNER_PATTERNS, BannerPatternLayers.EMPTY)); // CraftBukkit - apply limits + this.name = componentInput.get(DataComponents.CUSTOM_NAME); + } + +@@ -117,4 +_,13 @@ + tag.remove("patterns"); + tag.remove("CustomName"); + } ++ ++ // CraftBukkit start ++ public void setPatterns(BannerPatternLayers bannerpatternlayers) { ++ if (bannerpatternlayers.layers().size() > 20) { ++ bannerpatternlayers = new BannerPatternLayers(java.util.List.copyOf(bannerpatternlayers.layers().subList(0, 20))); ++ } ++ this.patterns = bannerpatternlayers; ++ } ++ // CraftBukkit end + } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BarrelBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BarrelBlockEntity.java.patch index 24372abac2..05d3f943e3 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BarrelBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BarrelBlockEntity.java.patch @@ -24,7 +24,7 @@ + } + + @Override -+ public java.util.List getViewers() { ++ public List getViewers() { + return this.transaction; + } + diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BeaconBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BeaconBlockEntity.java.patch new file mode 100644 index 0000000000..acc3c3c614 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BeaconBlockEntity.java.patch @@ -0,0 +1,242 @@ +--- a/net/minecraft/world/level/block/entity/BeaconBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/BeaconBlockEntity.java +@@ -106,6 +_,51 @@ + return 3; + } + }; ++ // CraftBukkit start - add fields and methods ++ public org.bukkit.potion.PotionEffect getPrimaryEffect() { ++ return (this.primaryPower != null) ++ ? org.bukkit.craftbukkit.potion.CraftPotionUtil.toBukkit(new MobEffectInstance( ++ this.primaryPower, ++ BeaconBlockEntity.getLevel(this.levels), ++ BeaconBlockEntity.getAmplification(this.levels, this.primaryPower, this.secondaryPower), ++ true, ++ true ++ )) ++ : null; ++ } ++ ++ public org.bukkit.potion.PotionEffect getSecondaryEffect() { ++ return (BeaconBlockEntity.hasSecondaryEffect(this.levels, this.primaryPower, this.secondaryPower)) ++ ? org.bukkit.craftbukkit.potion.CraftPotionUtil.toBukkit(new MobEffectInstance( ++ this.secondaryPower, ++ BeaconBlockEntity.getLevel(this.levels), ++ BeaconBlockEntity.getAmplification(this.levels, this.primaryPower, this.secondaryPower), ++ true, ++ true ++ )) ++ : null; ++ } ++ // CraftBukkit end ++ // Paper start - Custom beacon ranges ++ private final String PAPER_RANGE_TAG = "Paper.Range"; ++ private double effectRange = -1; ++ ++ public double getEffectRange() { ++ if (this.effectRange < 0) { ++ return this.levels * 10 + 10; ++ } else { ++ return effectRange; ++ } ++ } ++ ++ public void setEffectRange(double range) { ++ this.effectRange = range; ++ } ++ ++ public void resetEffectRange() { ++ this.effectRange = -1; ++ } ++ // Paper end - Custom beacon ranges + + @Nullable + static Holder filterEffect(@Nullable Holder effect) { +@@ -163,17 +_,26 @@ + blockEntity.lastCheckY++; + } + +- int i = blockEntity.levels; ++ int i = blockEntity.levels; final int originalLevels = i; // Paper - OBFHELPER + if (level.getGameTime() % 80L == 0L) { + if (!blockEntity.beamSections.isEmpty()) { + blockEntity.levels = updateBase(level, x, y, z); + } + + if (blockEntity.levels > 0 && !blockEntity.beamSections.isEmpty()) { +- applyEffects(level, pos, blockEntity.levels, blockEntity.primaryPower, blockEntity.secondaryPower); ++ applyEffects(level, pos, blockEntity.levels, blockEntity.primaryPower, blockEntity.secondaryPower, blockEntity); // Paper - Custom beacon ranges + playSound(level, pos, SoundEvents.BEACON_AMBIENT); + } + } ++ // Paper start - beacon activation/deactivation events ++ if (originalLevels <= 0 && blockEntity.levels > 0) { ++ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); ++ new io.papermc.paper.event.block.BeaconActivatedEvent(block).callEvent(); ++ } else if (originalLevels > 0 && blockEntity.levels <= 0) { ++ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos); ++ new io.papermc.paper.event.block.BeaconDeactivatedEvent(block).callEvent(); ++ } ++ // Paper end - beacon activation/deactivation events + + if (blockEntity.lastCheckY >= height) { + blockEntity.lastCheckY = level.getMinY() - 1; +@@ -224,36 +_,99 @@ + + @Override + public void setRemoved() { ++ // Paper start - beacon activation/deactivation events ++ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, worldPosition); ++ new io.papermc.paper.event.block.BeaconDeactivatedEvent(block).callEvent(); ++ // Paper end - beacon activation/deactivation events ++ // Paper start - fix MC-153086 ++ if (this.levels > 0 && !this.beamSections.isEmpty()) { + playSound(this.level, this.worldPosition, SoundEvents.BEACON_DEACTIVATE); ++ } ++ // Paper end + super.setRemoved(); + } + ++ @io.papermc.paper.annotation.DoNotUse // Paper - pass beacon block entity + private static void applyEffects( + Level level, BlockPos pos, int beaconLevel, @Nullable Holder primaryEffect, @Nullable Holder secondaryEffect + ) { ++ // Paper start - pass beacon block entity ++ applyEffects(level, pos, beaconLevel, primaryEffect, secondaryEffect, null); ++ } ++ private static void applyEffects( ++ Level level, BlockPos pos, int beaconLevel, @Nullable Holder primaryEffect, @Nullable Holder secondaryEffect, @Nullable BeaconBlockEntity blockEntity ++ ) { ++ // Paper emd - pass beacon block entity + if (!level.isClientSide && primaryEffect != null) { +- double d = beaconLevel * 10 + 10; +- int i = 0; +- if (beaconLevel >= 4 && Objects.equals(primaryEffect, secondaryEffect)) { +- i = 1; +- } +- +- int i1 = (9 + beaconLevel * 2) * 20; +- AABB aabb = new AABB(pos).inflate(d).expandTowards(0.0, level.getHeight(), 0.0); +- List entitiesOfClass = level.getEntitiesOfClass(Player.class, aabb); +- +- for (Player player : entitiesOfClass) { +- player.addEffect(new MobEffectInstance(primaryEffect, i1, i, true, true)); +- } +- +- if (beaconLevel >= 4 && !Objects.equals(primaryEffect, secondaryEffect) && secondaryEffect != null) { +- for (Player player : entitiesOfClass) { +- player.addEffect(new MobEffectInstance(secondaryEffect, i1, 0, true, true)); ++ double d = computeBeaconRange(beaconLevel); // Paper - diff out applyEffects logic components - see below ++ int i = computeEffectAmplifier(beaconLevel, primaryEffect, secondaryEffect); // Paper - diff out applyEffects logic components - see below ++ ++ int i1 = computeEffectDuration(beaconLevel); // Paper - diff out applyEffects logic components - see below ++ List entitiesOfClass = getHumansInRange(level, pos, beaconLevel, blockEntity); // Paper - diff out applyEffects logic components - see below ++ ++ applyEffectsAndCallEvent(level, pos, entitiesOfClass, new MobEffectInstance(primaryEffect, i1, i, true, true), true); // Paper - BeaconEffectEvent ++ ++ if (hasSecondaryEffect(beaconLevel, primaryEffect, secondaryEffect)) { // Paper - diff out applyEffects logic components - see below ++ applyEffectsAndCallEvent(level, pos, entitiesOfClass, new MobEffectInstance(secondaryEffect, i1, 0, true, true), false); // Paper - BeaconEffectEvent ++ } ++ } ++ } ++ ++ // Paper start - diff out applyEffects logic components ++ // Generally smarter than spigot trying to split the logic up, as that diff is giant. ++ private static int computeEffectDuration(final int beaconLevel) { ++ return (9 + beaconLevel * 2) * 20; // Diff from applyEffects ++ } ++ ++ private static int computeEffectAmplifier(final int beaconLevel, @Nullable Holder primaryEffect, @Nullable Holder secondaryEffect) { ++ int i = 0; ++ if (beaconLevel >= 4 && Objects.equals(primaryEffect, secondaryEffect)) { ++ i = 1; ++ } ++ return i; ++ } ++ ++ private static double computeBeaconRange(final int beaconLevel) { ++ return beaconLevel * 10 + 10; // Diff from applyEffects ++ } ++ ++ public static List getHumansInRange(final Level level, final BlockPos pos, final int beaconLevel, final @Nullable BeaconBlockEntity blockEntity) { ++ final double d = blockEntity != null ? blockEntity.getEffectRange() : computeBeaconRange(beaconLevel); ++ AABB aabb = new AABB(pos).inflate(d).expandTowards(0.0, level.getHeight(), 0.0); // Diff from applyEffects ++ // Improve performance of human lookup by switching to a global player iteration when searching over 128 blocks ++ List list; ++ if (d <= 128.0) { ++ list = level.getEntitiesOfClass(Player.class, aabb); // Diff from applyEffect ++ } else { ++ list = new java.util.ArrayList<>(); ++ for (final Player player : level.players()) { ++ if (!net.minecraft.world.entity.EntitySelector.NO_SPECTATORS.test(player)) continue; ++ if (player.getBoundingBox().intersects(aabb)) { ++ list.add(player); + } + } + } +- } +- ++ return list; ++ } ++ ++ private static boolean hasSecondaryEffect(final int beaconLevel, final Holder primaryEffect, final @Nullable Holder secondaryEffect) { ++ return beaconLevel >= 4 && !Objects.equals(primaryEffect, secondaryEffect) && secondaryEffect != null; ++ } ++ // Paper end - diff out applyEffects logic components ++ ++ // Paper start - BeaconEffectEvent ++ private static void applyEffectsAndCallEvent(final Level level, final BlockPos position, final List players, final MobEffectInstance mobEffectInstance, final boolean isPrimary) { ++ final org.bukkit.potion.PotionEffect apiEffect = org.bukkit.craftbukkit.potion.CraftPotionUtil.toBukkit(mobEffectInstance); ++ final org.bukkit.craftbukkit.block.CraftBlock apiBlock = org.bukkit.craftbukkit.block.CraftBlock.at(level, position); ++ for (final Player player : players) { ++ final com.destroystokyo.paper.event.block.BeaconEffectEvent event = new com.destroystokyo.paper.event.block.BeaconEffectEvent( ++ apiBlock, apiEffect, (org.bukkit.entity.Player) player.getBukkitEntity(), isPrimary ++ ); ++ if (!event.callEvent()) continue; ++ player.addEffect(org.bukkit.craftbukkit.potion.CraftPotionUtil.fromBukkit(event.getEffect())); ++ } ++ } ++ // Paper end - BeaconEffectEvent + public static void playSound(Level level, BlockPos pos, SoundEvent sound) { + level.playSound(null, pos, sound, SoundSource.BLOCKS, 1.0F, 1.0F); + } +@@ -282,7 +_,7 @@ + private static Holder loadEffect(CompoundTag tag, String key) { + if (tag.contains(key, 8)) { + ResourceLocation resourceLocation = ResourceLocation.tryParse(tag.getString(key)); +- return resourceLocation == null ? null : BuiltInRegistries.MOB_EFFECT.get(resourceLocation).map(BeaconBlockEntity::filterEffect).orElse(null); ++ return resourceLocation == null ? null : BuiltInRegistries.MOB_EFFECT.get(resourceLocation).orElse(null); // CraftBukkit - persist manually set non-default beacon effects (SPIGOT-3598) + } else { + return null; + } +@@ -293,11 +_,13 @@ + super.loadAdditional(tag, registries); + this.primaryPower = loadEffect(tag, "primary_effect"); + this.secondaryPower = loadEffect(tag, "secondary_effect"); ++ this.levels = tag.getInt("Levels"); // CraftBukkit - SPIGOT-5053, use where available + if (tag.contains("CustomName", 8)) { + this.name = parseCustomNameSafe(tag.getString("CustomName"), registries); + } + + this.lockKey = LockCode.fromTag(tag, registries); ++ this.effectRange = tag.contains(PAPER_RANGE_TAG, 6) ? tag.getDouble(PAPER_RANGE_TAG) : -1; // Paper - Custom beacon ranges + } + + @Override +@@ -311,6 +_,7 @@ + } + + this.lockKey.addToTag(tag, registries); ++ tag.putDouble(PAPER_RANGE_TAG, this.effectRange); // Paper - Custom beacon ranges + } + + public void setCustomName(@Nullable Component name) { +@@ -326,7 +_,7 @@ + @Nullable + @Override + public AbstractContainerMenu createMenu(int containerId, Inventory playerInventory, Player player) { +- return BaseContainerBlockEntity.canUnlock(player, this.lockKey, this.getDisplayName()) ++ return BaseContainerBlockEntity.canUnlock(player, this.lockKey, this.getDisplayName(), this) // Paper - Add BlockLockCheckEvent + ? new BeaconMenu(containerId, playerInventory, this.dataAccess, ContainerLevelAccess.create(this.level, this.getBlockPos())) + : null; + } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java.patch new file mode 100644 index 0000000000..2ab50074f8 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java.patch @@ -0,0 +1,244 @@ +--- a/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java +@@ -83,6 +_,7 @@ + private List stored = Lists.newArrayList(); + @Nullable + public BlockPos savedFlowerPos; ++ public int maxBees = 3; // CraftBukkit - allow setting max amount of bees a hive can hold + + public BeehiveBlockEntity(BlockPos pos, BlockState blockState) { + super(BlockEntityType.BEEHIVE, pos, blockState); +@@ -116,7 +_,7 @@ + } + + public boolean isFull() { +- return this.stored.size() == 3; ++ return this.stored.size() == this.maxBees; // CraftBukkit + } + + public void emptyAllLivingFromHive(@Nullable Player player, BlockState state, BeehiveBlockEntity.BeeReleaseStatus releaseStatus) { +@@ -127,7 +_,7 @@ + Bee bee = (Bee)entity; + if (player.position().distanceToSqr(entity.position()) <= 16.0) { + if (!this.isSedated()) { +- bee.setTarget(player); ++ bee.setTarget(player, org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER, true); // CraftBukkit + } else { + bee.setStayOutOfHiveCountdown(400); + } +@@ -138,8 +_,14 @@ + } + + private List releaseAllOccupants(BlockState state, BeehiveBlockEntity.BeeReleaseStatus releaseStatus) { ++ // CraftBukkit start - This allows us to bypass the night/rain/emergency check ++ return this.releaseBees(state, releaseStatus, false); ++ } ++ ++ public List releaseBees(BlockState state, BeehiveBlockEntity.BeeReleaseStatus releaseStatus, boolean force) { ++ // CraftBukkit end - This allows us to bypass t he night/rain/emergecny check + List list = Lists.newArrayList(); +- this.stored.removeIf(data -> releaseOccupant(this.level, this.worldPosition, state, data.toOccupant(), list, releaseStatus, this.savedFlowerPos)); ++ this.stored.removeIf(data -> releaseOccupant(this.level, this.worldPosition, state, data.toOccupant(), list, releaseStatus, this.savedFlowerPos, force)); // CraftBukkit - This allows us to bypass t he night/rain/emergecny check + if (!list.isEmpty()) { + super.setChanged(); + } +@@ -152,6 +_,11 @@ + return this.stored.size(); + } + ++ // Paper start - Add EntityBlockStorage clearEntities ++ public void clearBees() { ++ this.stored.clear(); ++ } ++ // Paper end - Add EntityBlockStorage clearEntities + public static int getHoneyLevel(BlockState state) { + return state.getValue(BeehiveBlock.HONEY_LEVEL); + } +@@ -162,7 +_,16 @@ + } + + public void addOccupant(Bee bee) { +- if (this.stored.size() < 3) { ++ if (this.stored.size() < this.maxBees) { // CraftBukkit ++ // CraftBukkit start ++ if (this.level != null) { ++ org.bukkit.event.entity.EntityEnterBlockEvent event = new org.bukkit.event.entity.EntityEnterBlockEvent(bee.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(this.level, this.getBlockPos())); ++ if (!event.callEvent()) { ++ bee.setStayOutOfHiveCountdown(400); ++ return; ++ } ++ } ++ // CraftBukkit end + bee.stopRiding(); + bee.ejectPassengers(); + bee.dropLeash(); +@@ -187,7 +_,7 @@ + this.level.gameEvent(GameEvent.BLOCK_CHANGE, blockPos, GameEvent.Context.of(bee, this.getBlockState())); + } + +- bee.discard(); ++ bee.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.ENTER_BLOCK); // CraftBukkit - add Bukkit remove cause + super.setChanged(); + } + } +@@ -205,7 +_,21 @@ + BeehiveBlockEntity.BeeReleaseStatus releaseStatus, + @Nullable BlockPos storedFlowerPos + ) { +- if (Bee.isNightOrRaining(level) && releaseStatus != BeehiveBlockEntity.BeeReleaseStatus.EMERGENCY) { ++ // CraftBukkit start ++ return releaseOccupant(level, pos, state, occupant, storedInHives, releaseStatus, storedFlowerPos, false); ++ } ++ private static boolean releaseOccupant( ++ Level level, ++ BlockPos pos, ++ BlockState state, ++ BeehiveBlockEntity.Occupant occupant, ++ @Nullable List storedInHives, ++ BeehiveBlockEntity.BeeReleaseStatus releaseStatus, ++ @Nullable BlockPos storedFlowerPos, ++ boolean force ++ ) { ++ if (!force && Bee.isNightOrRaining(level) && releaseStatus != BeehiveBlockEntity.BeeReleaseStatus.EMERGENCY) { ++ // CraftBukkit end + return false; + } else { + Direction direction = state.getValue(BeehiveBlock.FACING); +@@ -216,6 +_,17 @@ + } else { + Entity entity = occupant.createEntity(level, pos); + if (entity != null) { ++ // CraftBukkit start ++ if (entity instanceof Bee) { ++ float bbWidth = entity.getBbWidth(); ++ double d = flag ? 0.0 : 0.55 + bbWidth / 2.0F; ++ double d1 = pos.getX() + 0.5 + d * direction.getStepX(); ++ double d2 = pos.getY() + 0.5 - entity.getBbHeight() / 2.0F; ++ double d3 = pos.getZ() + 0.5 + d * direction.getStepZ(); ++ entity.moveTo(d1, d2, d3, entity.getYRot(), entity.getXRot()); ++ } ++ if (!level.addFreshEntity(entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BEEHIVE)) return false; // CraftBukkit - SpawnReason, moved from below ++ // CraftBukkit end + if (entity instanceof Bee bee) { + if (storedFlowerPos != null && !bee.hasSavedFlowerPos() && level.random.nextFloat() < 0.9F) { + bee.setSavedFlowerPos(storedFlowerPos); +@@ -231,7 +_,13 @@ + i--; + } + +- level.setBlockAndUpdate(pos, state.setValue(BeehiveBlock.HONEY_LEVEL, Integer.valueOf(honeyLevel + i))); ++ // Paper start - Fire EntityChangeBlockEvent in more places ++ BlockState newBlockState = state.setValue(BeehiveBlock.HONEY_LEVEL, Integer.valueOf(honeyLevel + i)); ++ ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entity, pos, newBlockState)) { ++ level.setBlockAndUpdate(pos, newBlockState); ++ } ++ // Paper end - Fire EntityChangeBlockEvent in more places + } + } + } +@@ -240,17 +_,19 @@ + storedInHives.add(bee); + } + ++ /* CraftBukkit start - move up + float bbWidth = entity.getBbWidth(); + double d = flag ? 0.0 : 0.55 + bbWidth / 2.0F; + double d1 = pos.getX() + 0.5 + d * direction.getStepX(); + double d2 = pos.getY() + 0.5 - entity.getBbHeight() / 2.0F; + double d3 = pos.getZ() + 0.5 + d * direction.getStepZ(); + entity.moveTo(d1, d2, d3, entity.getYRot(), entity.getXRot()); ++ */ // CraftBukkit end + } + + level.playSound(null, pos, SoundEvents.BEEHIVE_EXIT, SoundSource.BLOCKS, 1.0F, 1.0F); + level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(entity, level.getBlockState(pos))); +- return level.addFreshEntity(entity); ++ return true; // CraftBukkit - moved up + } else { + return false; + } +@@ -276,6 +_,11 @@ + flag = true; + iterator.remove(); + } ++ // Paper start - Fix bees aging inside; use exitTickCounter to keep actual bee life ++ else { ++ beeData.exitTickCounter = beeData.occupant.minTicksInHive / 2; ++ } ++ // Paper end - Fix bees aging inside; use exitTickCounter to keep actual bee life + } + } + +@@ -299,7 +_,7 @@ + @Override + protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.loadAdditional(tag, registries); +- this.stored.clear(); ++ this.stored = Lists.newArrayList(); // CraftBukkit - SPIGOT-7790: create new copy (may be modified in physics event triggered by honey change) + if (tag.contains("bees")) { + BeehiveBlockEntity.Occupant.LIST_CODEC + .parse(NbtOps.INSTANCE, tag.get("bees")) +@@ -308,6 +_,11 @@ + } + + this.savedFlowerPos = NbtUtils.readBlockPos(tag, "flower_pos").orElse(null); ++ // CraftBukkit start ++ if (tag.contains("Bukkit.MaxEntities")) { ++ this.maxBees = tag.getInt("Bukkit.MaxEntities"); ++ } ++ // CraftBukkit end + } + + @Override +@@ -317,12 +_,13 @@ + if (this.hasSavedFlowerPos()) { + tag.put("flower_pos", NbtUtils.writeBlockPos(this.savedFlowerPos)); + } ++ tag.putInt("Bukkit.MaxEntities", this.maxBees); // CraftBukkit + } + + @Override + protected void applyImplicitComponents(BlockEntity.DataComponentInput componentInput) { + super.applyImplicitComponents(componentInput); +- this.stored.clear(); ++ this.stored = Lists.newArrayList(); // CraftBukkit - SPIGOT-7790: create new copy (may be modified in physics event triggered by honey change) + List list = componentInput.getOrDefault(DataComponents.BEES, List.of()); + list.forEach(this::storeBee); + } +@@ -345,15 +_,18 @@ + + static class BeeData { + private final BeehiveBlockEntity.Occupant occupant; ++ private int exitTickCounter; // Paper - Fix bees aging inside hives; separate counter for checking if bee should exit to reduce exit attempts + private int ticksInHive; + + BeeData(BeehiveBlockEntity.Occupant occupant) { + this.occupant = occupant; + this.ticksInHive = occupant.ticksInHive(); ++ this.exitTickCounter = this.ticksInHive; // Paper - Fix bees aging inside hives + } + + public boolean tick() { +- return this.ticksInHive++ > this.occupant.minTicksInHive; ++ this.ticksInHive++; // Paper - Fix bees aging inside hives ++ return this.exitTickCounter++ > this.occupant.minTicksInHive; // Paper - Fix bees aging inside hives + } + + public BeehiveBlockEntity.Occupant toOccupant() { +@@ -424,6 +_,7 @@ + } + + private static void setBeeReleaseData(int ticksInHive, Bee bee) { ++ if (!bee.ageLocked) { // Paper - Honor ageLock + int age = bee.getAge(); + if (age < 0) { + bee.setAge(Math.min(0, age + ticksInHive)); +@@ -432,6 +_,7 @@ + } + + bee.setInLoveTime(Math.max(0, bee.getInLoveTime() - ticksInHive)); ++ } // Paper - Honor ageLock + } + } + } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BlockEntity.java.patch new file mode 100644 index 0000000000..8ed4390459 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BlockEntity.java.patch @@ -0,0 +1,133 @@ +--- a/net/minecraft/world/level/block/entity/BlockEntity.java ++++ b/net/minecraft/world/level/block/entity/BlockEntity.java +@@ -26,6 +_,10 @@ + import org.slf4j.Logger; + + public abstract class BlockEntity { ++ // CraftBukkit start - data containers ++ private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry(); ++ public org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer persistentDataContainer; ++ // CraftBukkit end + private static final Logger LOGGER = LogUtils.getLogger(); + private final BlockEntityType type; + @Nullable +@@ -40,6 +_,7 @@ + this.worldPosition = pos.immutable(); + this.validateBlockState(blockState); + this.blockState = blockState; ++ this.persistentDataContainer = new org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer(DATA_TYPE_REGISTRY); // Paper - always init + } + + private void validateBlockState(BlockState state) { +@@ -70,6 +_,14 @@ + } + + protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) { ++ // Paper start - read persistent data container ++ this.persistentDataContainer.clear(); // Paper - clear instead of init ++ ++ net.minecraft.nbt.Tag persistentDataTag = tag.get("PublicBukkitValues"); ++ if (persistentDataTag instanceof CompoundTag) { ++ this.persistentDataContainer.putAll((CompoundTag) persistentDataTag); ++ } ++ // Paper end - read persistent data container + } + + public final void loadWithComponents(CompoundTag tag, HolderLookup.Provider registries) { +@@ -106,12 +_,22 @@ + .encodeStart(registries.createSerializationContext(NbtOps.INSTANCE), this.components) + .resultOrPartial(string -> LOGGER.warn("Failed to save components: {}", string)) + .ifPresent(tag -> compoundTag.merge((CompoundTag)tag)); ++ // CraftBukkit start - store container ++ if (this.persistentDataContainer != null && !this.persistentDataContainer.isEmpty()) { ++ compoundTag.put("PublicBukkitValues", this.persistentDataContainer.toTagCompound()); ++ } ++ // CraftBukkit end + return compoundTag; + } + + public final CompoundTag saveCustomOnly(HolderLookup.Provider registries) { + CompoundTag compoundTag = new CompoundTag(); + this.saveAdditional(compoundTag, registries); ++ // Paper start - store PDC here as well ++ if (this.persistentDataContainer != null && !this.persistentDataContainer.isEmpty()) { ++ compoundTag.put("PublicBukkitValues", this.persistentDataContainer.toTagCompound()); ++ } ++ // Paper end + return compoundTag; + } + +@@ -220,7 +_,12 @@ + public void fillCrashReportCategory(CrashReportCategory reportCategory) { + reportCategory.setDetail("Name", this::getNameForReporting); + if (this.level != null) { +- CrashReportCategory.populateBlockDetails(reportCategory, this.level, this.worldPosition, this.getBlockState()); ++ // Paper start - Prevent block entity and entity crashes ++ BlockState block = this.getBlockState(); ++ if (block != null) { ++ CrashReportCategory.populateBlockDetails(reportCategory, this.level, this.worldPosition, block); ++ } ++ // Paper end - Prevent block entity and entity crashes + CrashReportCategory.populateBlockDetails(reportCategory, this.level, this.worldPosition, this.level.getBlockState(this.worldPosition)); + } + } +@@ -247,10 +_,16 @@ + } + + public final void applyComponents(DataComponentMap components, DataComponentPatch patch) { ++ // CraftBukkit start ++ this.applyComponentsSet(components, patch); ++ } ++ ++ public final Set> applyComponentsSet(DataComponentMap components, DataComponentPatch patch) { ++ // CraftBukkit end + final Set> set = new HashSet<>(); + set.add(DataComponents.BLOCK_ENTITY_DATA); + set.add(DataComponents.BLOCK_STATE); +- final DataComponentMap dataComponentMap = PatchedDataComponentMap.fromPatch(components, patch); ++ final PatchedDataComponentMap dataComponentMap = PatchedDataComponentMap.fromPatch(components, patch); + this.applyImplicitComponents(new BlockEntity.DataComponentInput() { + @Nullable + @Override +@@ -267,6 +_,10 @@ + }); + DataComponentPatch dataComponentPatch = patch.forget(set::contains); + this.components = dataComponentPatch.split().added(); ++ // CraftBukkit start ++ set.remove(DataComponents.BLOCK_ENTITY_DATA); // Remove as never actually added by applyImplicitComponents ++ return set; ++ // CraftBukkit end + } + + protected void collectImplicitComponents(DataComponentMap.Builder components) { +@@ -300,6 +_,30 @@ + return null; + } + } ++ ++ // CraftBukkit start - add method ++ public org.bukkit.inventory.InventoryHolder getOwner() { ++ // Paper start ++ return getOwner(true); ++ } ++ public org.bukkit.inventory.InventoryHolder getOwner(boolean useSnapshot) { ++ // Paper end ++ if (this.level == null) return null; ++ org.bukkit.block.Block block = this.level.getWorld().getBlockAt(this.worldPosition.getX(), this.worldPosition.getY(), this.worldPosition.getZ()); ++ // if (block.getType() == org.bukkit.Material.AIR) return null; // Paper - actually get the tile entity if it still exists ++ org.bukkit.block.BlockState state = block.getState(useSnapshot); // Paper ++ return state instanceof final org.bukkit.inventory.InventoryHolder inventoryHolder ? inventoryHolder : null; ++ } ++ // CraftBukkit end ++ ++ // Paper start - Sanitize sent data ++ public CompoundTag sanitizeSentNbt(CompoundTag tag) { ++ tag.remove("PublicBukkitValues"); ++ ++ return tag; ++ } ++ // Paper end - Sanitize sent data ++ + + static class ComponentHelper { + public static final Codec COMPONENTS_CODEC = DataComponentMap.CODEC.optionalFieldOf("components", DataComponentMap.EMPTY).codec(); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java.patch new file mode 100644 index 0000000000..769760f7d6 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java.patch @@ -0,0 +1,186 @@ +--- a/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java +@@ -8,7 +_,6 @@ + import net.minecraft.core.NonNullList; + import net.minecraft.nbt.CompoundTag; + import net.minecraft.network.chat.Component; +-import net.minecraft.tags.ItemTags; + import net.minecraft.world.ContainerHelper; + import net.minecraft.world.Containers; + import net.minecraft.world.WorldlyContainer; +@@ -36,6 +_,7 @@ + public static final int NUM_DATA_VALUES = 2; + private NonNullList items = NonNullList.withSize(5, ItemStack.EMPTY); + public int brewTime; ++ public int recipeBrewTime = 400; // Paper - Add recipeBrewTime + private boolean[] lastPotionCount; + private Item ingredient; + public int fuel; +@@ -45,6 +_,7 @@ + return switch (index) { + case 0 -> BrewingStandBlockEntity.this.brewTime; + case 1 -> BrewingStandBlockEntity.this.fuel; ++ case 2 -> BrewingStandBlockEntity.this.recipeBrewTime; // Paper - Add recipeBrewTime + default -> 0; + }; + } +@@ -57,14 +_,50 @@ + break; + case 1: + BrewingStandBlockEntity.this.fuel = value; ++ // Paper start - Add recipeBrewTime ++ break; ++ case 2: ++ BrewingStandBlockEntity.this.recipeBrewTime = value; ++ break; ++ // Paper end - Add recipeBrewTime + } + } + + @Override + public int getCount() { +- return 2; ++ return 3; // Paper - Add recipeBrewTime + } + }; ++ // CraftBukkit start - add fields and methods ++ // private int lastTick = MinecraftServer.currentTick; // Paper - remove anti tick skipping measures / wall time ++ public java.util.List transaction = new java.util.ArrayList<>(); ++ private int maxStack = MAX_STACK; ++ ++ public void onOpen(org.bukkit.craftbukkit.entity.CraftHumanEntity who) { ++ this.transaction.add(who); ++ } ++ ++ public void onClose(org.bukkit.craftbukkit.entity.CraftHumanEntity who) { ++ this.transaction.remove(who); ++ } ++ ++ public List getViewers() { ++ return this.transaction; ++ } ++ ++ public java.util.List getContents() { ++ return this.items; ++ } ++ ++ @Override ++ public int getMaxStackSize() { ++ return this.maxStack; ++ } ++ ++ public void setMaxStackSize(int size) { ++ this.maxStack = size; ++ } ++ // CraftBukkit end + + public BrewingStandBlockEntity(BlockPos pos, BlockState state) { + super(BlockEntityType.BREWING_STAND, pos, state); +@@ -92,9 +_,22 @@ + + public static void serverTick(Level level, BlockPos pos, BlockState state, BrewingStandBlockEntity blockEntity) { + ItemStack itemStack = blockEntity.items.get(4); +- if (blockEntity.fuel <= 0 && itemStack.is(ItemTags.BREWING_FUEL)) { +- blockEntity.fuel = 20; +- itemStack.shrink(1); ++ if (blockEntity.fuel <= 0 && itemStack.is(net.minecraft.tags.ItemTags.BREWING_FUEL)) { ++ // CraftBukkit start ++ org.bukkit.event.inventory.BrewingStandFuelEvent event = new org.bukkit.event.inventory.BrewingStandFuelEvent( ++ org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), ++ org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack), ++ 20 ++ ); ++ if (!event.callEvent()) { ++ return; ++ } ++ ++ blockEntity.fuel = event.getFuelPower(); ++ if (blockEntity.fuel > 0 && event.isConsuming()) { ++ itemStack.shrink(1); ++ } ++ // CraftBukkit end + setChanged(level, pos, state); + } + +@@ -105,7 +_,7 @@ + blockEntity.brewTime--; + boolean flag1 = blockEntity.brewTime == 0; + if (flag1 && isBrewable) { +- doBrew(level, pos, blockEntity.items); ++ doBrew(level, pos, blockEntity.items, blockEntity); // CraftBukkit + } else if (!isBrewable || !itemStack1.is(blockEntity.ingredient)) { + blockEntity.brewTime = 0; + } +@@ -114,6 +_,14 @@ + } else if (isBrewable && blockEntity.fuel > 0) { + blockEntity.fuel--; + blockEntity.brewTime = 400; ++ // CraftBukkit start ++ org.bukkit.event.block.BrewingStartEvent event = new org.bukkit.event.block.BrewingStartEvent( ++ org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), ++ org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack1), 400); ++ event.callEvent(); ++ 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(level, pos, state); + } +@@ -164,13 +_,37 @@ + } + } + +- private static void doBrew(Level level, BlockPos pos, NonNullList items) { ++ private static void doBrew(Level level, BlockPos pos, NonNullList items, BrewingStandBlockEntity brewingStandBlockEntity) { // CraftBukkit + ItemStack itemStack = items.get(3); + PotionBrewing potionBrewing = level.potionBrewing(); + ++ // CraftBukkit start ++ org.bukkit.inventory.InventoryHolder owner = brewingStandBlockEntity.getOwner(); ++ java.util.List brewResults = new java.util.ArrayList<>(3); + for (int i = 0; i < 3; i++) { +- items.set(i, potionBrewing.mix(itemStack, items.get(i))); +- } ++ brewResults.add(i, org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(potionBrewing.mix(itemStack, items.get(i)))); ++ } ++ ++ if (owner != null) { ++ org.bukkit.event.inventory.BrewEvent event = new org.bukkit.event.inventory.BrewEvent( ++ org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), ++ (org.bukkit.inventory.BrewerInventory) owner.getInventory(), ++ brewResults, ++ brewingStandBlockEntity.fuel ++ ); ++ if (!event.callEvent()) { ++ return; ++ } ++ ++ for (int i = 0; i < 3; i++) { ++ if (i < brewResults.size()) { ++ items.set(i, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(brewResults.get(i))); ++ } else { ++ items.set(i, ItemStack.EMPTY); ++ } ++ } ++ } ++ // CraftBukkit end + + itemStack.shrink(1); + ItemStack craftingRemainder = itemStack.getItem().getCraftingRemainder(); +@@ -209,13 +_,13 @@ + + @Override + public boolean canPlaceItem(int index, ItemStack stack) { ++ PotionBrewing potionBrewing = this.level != null ? this.level.potionBrewing() : PotionBrewing.EMPTY; // Paper - move up + if (index == 3) { +- PotionBrewing potionBrewing = this.level != null ? this.level.potionBrewing() : PotionBrewing.EMPTY; + return potionBrewing.isIngredient(stack); + } else { + return index == 4 +- ? stack.is(ItemTags.BREWING_FUEL) +- : (stack.is(Items.POTION) || stack.is(Items.SPLASH_POTION) || stack.is(Items.LINGERING_POTION) || stack.is(Items.GLASS_BOTTLE)) ++ ? stack.is(net.minecraft.tags.ItemTags.BREWING_FUEL) ++ : (stack.is(Items.POTION) || stack.is(Items.SPLASH_POTION) || stack.is(Items.LINGERING_POTION) || stack.is(Items.GLASS_BOTTLE) || potionBrewing.isCustomInput(stack)) // Paper - Custom Potion Mixes + && this.getItem(index).isEmpty(); + } + } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BrushableBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BrushableBlockEntity.java.patch new file mode 100644 index 0000000000..b8231aa9ed --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BrushableBlockEntity.java.patch @@ -0,0 +1,23 @@ +--- a/net/minecraft/world/level/block/entity/BrushableBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/BrushableBlockEntity.java +@@ -138,7 +_,10 @@ + double d5 = blockPos.getZ() + 0.5 * d1 + d2; + ItemEntity itemEntity = new ItemEntity(level, d3, d4, d5, this.item.split(level.random.nextInt(21) + 10)); + itemEntity.setDeltaMovement(Vec3.ZERO); +- level.addFreshEntity(itemEntity); ++ // CraftBukkit start ++ org.bukkit.block.Block bblock = org.bukkit.craftbukkit.block.CraftBlock.at(this.level, this.worldPosition); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDropItemEvent(bblock, bblock.getState(), (ServerPlayer) player, java.util.List.of(itemEntity)); ++ // CraftBukkit end + this.item = ItemStack.EMPTY; + } + } +@@ -167,7 +_,7 @@ + + private boolean tryLoadLootTable(CompoundTag tag) { + if (tag.contains("LootTable", 8)) { +- this.lootTable = ResourceKey.create(Registries.LOOT_TABLE, ResourceLocation.parse(tag.getString("LootTable"))); ++ this.lootTable = net.minecraft.Optionull.map(ResourceLocation.tryParse(tag.getString("LootTable")), rl -> ResourceKey.create(Registries.LOOT_TABLE, rl)); // Paper - Validate ResourceLocation + this.lootTableSeed = tag.getLong("LootTableSeed"); + return true; + } else { diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/CalibratedSculkSensorBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/CalibratedSculkSensorBlockEntity.java.patch similarity index 92% rename from paper-server/patches/unapplied/net/minecraft/world/level/block/entity/CalibratedSculkSensorBlockEntity.java.patch rename to paper-server/patches/sources/net/minecraft/world/level/block/entity/CalibratedSculkSensorBlockEntity.java.patch index 944379decd..196b1b49d7 100644 --- a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/CalibratedSculkSensorBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/CalibratedSculkSensorBlockEntity.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/entity/CalibratedSculkSensorBlockEntity.java +++ b/net/minecraft/world/level/block/entity/CalibratedSculkSensorBlockEntity.java -@@ -20,6 +20,12 @@ +@@ -20,6 +_,12 @@ public VibrationSystem.User createVibrationUser() { return new CalibratedSculkSensorBlockEntity.VibrationUser(this.getBlockPos()); } @@ -12,8 +12,8 @@ + // Paper end - Configurable sculk sensor listener range protected class VibrationUser extends SculkSensorBlockEntity.VibrationUser { - public VibrationUser(final BlockPos pos) { -@@ -28,6 +34,7 @@ + public VibrationUser(final BlockPos pos1) { +@@ -28,6 +_,7 @@ @Override public int getListenerRadius() { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/CampfireBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/CampfireBlockEntity.java.patch new file mode 100644 index 0000000000..3d53a28694 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/CampfireBlockEntity.java.patch @@ -0,0 +1,104 @@ +--- a/net/minecraft/world/level/block/entity/CampfireBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/CampfireBlockEntity.java +@@ -36,6 +_,7 @@ + private final NonNullList items = NonNullList.withSize(4, ItemStack.EMPTY); + public final int[] cookingProgress = new int[4]; + public final int[] cookingTime = new int[4]; ++ public final boolean[] stopCooking = new boolean[4]; // Paper - Add more Campfire API + + public CampfireBlockEntity(BlockPos pos, BlockState blockState) { + super(BlockEntityType.CAMPFIRE, pos, blockState); +@@ -54,14 +_,42 @@ + ItemStack itemStack = campfire.items.get(i); + if (!itemStack.isEmpty()) { + flag = true; ++ if (!campfire.stopCooking[i]) { // Paper - Add more Campfire API + campfire.cookingProgress[i]++; ++ } // Paper - Add more Campfire API + if (campfire.cookingProgress[i] >= campfire.cookingTime[i]) { + SingleRecipeInput singleRecipeInput = new SingleRecipeInput(itemStack); +- ItemStack itemStack1 = check.getRecipeFor(singleRecipeInput, level) ++ final var optionalCookingRecipe = check.getRecipeFor(singleRecipeInput, level); ++ ItemStack itemStack1 = optionalCookingRecipe + .map(recipe -> recipe.value().assemble(singleRecipeInput, level.registryAccess())) + .orElse(itemStack); + if (itemStack1.isItemEnabled(level.enabledFeatures())) { +- Containers.dropItemStack(level, pos.getX(), pos.getY(), pos.getZ(), itemStack1); ++ // CraftBukkit start - fire BlockCookEvent ++ org.bukkit.craftbukkit.inventory.CraftItemStack source = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack); ++ org.bukkit.inventory.ItemStack result = org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(itemStack1); ++ ++ org.bukkit.event.block.BlockCookEvent blockCookEvent = new org.bukkit.event.block.BlockCookEvent( ++ org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), ++ source, ++ result, ++ (org.bukkit.inventory.CookingRecipe) optionalCookingRecipe.map(RecipeHolder::toBukkitRecipe).orElse(null) // Paper -Add recipe to cook events ++ ); ++ ++ if (!blockCookEvent.callEvent()) { ++ return; ++ } ++ ++ result = blockCookEvent.getResult(); ++ itemStack1 = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(result); ++ // CraftBukkit end ++ // Paper start - Fix item locations dropped from campfires ++ double deviation = 0.05F * RandomSource.GAUSSIAN_SPREAD_FACTOR; ++ while (!itemStack1.isEmpty()) { ++ net.minecraft.world.entity.item.ItemEntity droppedItem = new net.minecraft.world.entity.item.ItemEntity(level, pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, itemStack1.split(level.random.nextInt(21) + 10)); ++ droppedItem.setDeltaMovement(level.random.triangle(0.0D, deviation), level.random.triangle(0.2D, deviation), level.random.triangle(0.0D, deviation)); ++ level.addFreshEntity(droppedItem); ++ } ++ // Paper end - Fix item locations dropped from campfires + campfire.items.set(i, ItemStack.EMPTY); + level.sendBlockUpdated(pos, state, state, 3); + level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(state)); +@@ -133,6 +_,17 @@ + int[] intArray = tag.getIntArray("CookingTotalTimes"); + System.arraycopy(intArray, 0, this.cookingTime, 0, Math.min(this.cookingTime.length, intArray.length)); + } ++ ++ // Paper start - Add more Campfire API ++ if (tag.contains("Paper.StopCooking", org.bukkit.craftbukkit.util.CraftMagicNumbers.NBT.TAG_BYTE_ARRAY)) { ++ byte[] abyte = tag.getByteArray("Paper.StopCooking"); ++ boolean[] cookingState = new boolean[4]; ++ for (int index = 0; index < abyte.length; index++) { ++ cookingState[index] = abyte[index] == 1; ++ } ++ System.arraycopy(cookingState, 0, this.stopCooking, 0, Math.min(this.stopCooking.length, abyte.length)); ++ } ++ // Paper end - Add more Campfire API + } + + @Override +@@ -141,6 +_,13 @@ + ContainerHelper.saveAllItems(tag, this.items, true, registries); + tag.putIntArray("CookingTimes", this.cookingProgress); + tag.putIntArray("CookingTotalTimes", this.cookingTime); ++ // Paper start - Add more Campfire API ++ byte[] cookingState = new byte[4]; ++ for (int index = 0; index < cookingState.length; index++) { ++ cookingState[index] = (byte) (this.stopCooking[index] ? 1 : 0); ++ } ++ tag.putByteArray("Paper.StopCooking", cookingState); ++ // Paper end - Add more Campfire API + } + + @Override +@@ -165,7 +_,15 @@ + return false; + } + +- this.cookingTime[i] = recipeFor.get().value().cookingTime(); ++ // CraftBukkit start ++ org.bukkit.event.block.CampfireStartEvent event = new org.bukkit.event.block.CampfireStartEvent( ++ org.bukkit.craftbukkit.block.CraftBlock.at(this.level,this.worldPosition), ++ org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack), ++ (org.bukkit.inventory.CampfireRecipe) recipeFor.get().toBukkitRecipe() ++ ); ++ this.level.getCraftServer().getPluginManager().callEvent(event); ++ this.cookingTime[i] = event.getTotalCookTime(); // i -> event.getTotalCookTime() ++ // CraftBukkit end + this.cookingProgress[i] = 0; + this.items.set(i, stack.consumeAndReturn(1, entity)); + level.gameEvent(GameEvent.BLOCK_CHANGE, this.getBlockPos(), GameEvent.Context.of(entity, this.getBlockState())); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/ChestBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ChestBlockEntity.java.patch index d3845d7cfa..9b690b9eea 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/ChestBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ChestBlockEntity.java.patch @@ -20,7 +20,7 @@ + this.transaction.remove(who); + } + -+ public java.util.List getViewers() { ++ public List getViewers() { + return this.transaction; + } + diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/ChiseledBookShelfBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ChiseledBookShelfBlockEntity.java.patch index 6248b82627..85aac7b304 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/ChiseledBookShelfBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ChiseledBookShelfBlockEntity.java.patch @@ -24,7 +24,7 @@ + } + + @Override -+ public java.util.List getViewers() { ++ public List getViewers() { + return this.transaction; + } + diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/ConduitBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ConduitBlockEntity.java.patch new file mode 100644 index 0000000000..2ca6be14cc --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ConduitBlockEntity.java.patch @@ -0,0 +1,62 @@ +--- a/net/minecraft/world/level/block/entity/ConduitBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/ConduitBlockEntity.java +@@ -9,6 +_,7 @@ + import net.minecraft.core.particles.ParticleTypes; + import net.minecraft.nbt.CompoundTag; + import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; ++import net.minecraft.server.level.ServerLevel; + import net.minecraft.sounds.SoundEvent; + import net.minecraft.sounds.SoundEvents; + import net.minecraft.sounds.SoundSource; +@@ -168,8 +_,20 @@ + } + + private static void applyEffects(Level level, BlockPos pos, List positions) { ++ // CraftBukkit start ++ ConduitBlockEntity.applyEffects(level, pos, ConduitBlockEntity.getRange(positions)); ++ } ++ ++ public static int getRange(List positions) { ++ // CraftBukkit end + int size = positions.size(); + int i = size / 7 * 16; ++ // CraftBukkit start ++ return i; ++ } ++ ++ private static void applyEffects(Level level, BlockPos pos, int i) { // i = effect range in blocks ++ // CraftBukkit end + int x = pos.getX(); + int y = pos.getY(); + int z = pos.getZ(); +@@ -185,6 +_,12 @@ + } + + private static void updateDestroyTarget(Level level, BlockPos pos, BlockState state, List positions, ConduitBlockEntity blockEntity) { ++ // CraftBukkit start - add "damageTarget" boolean ++ ConduitBlockEntity.updateDestroyTarget(level, pos, state, positions, blockEntity, true); ++ } ++ ++ public static void updateDestroyTarget(Level level, BlockPos pos, BlockState state, List positions, ConduitBlockEntity blockEntity, boolean damageTarget) { ++ // CraftBukkit end + LivingEntity livingEntity = blockEntity.destroyTarget; + int size = positions.size(); + if (size < 42) { +@@ -203,7 +_,8 @@ + blockEntity.destroyTarget = null; + } + +- if (blockEntity.destroyTarget != null) { ++ if (damageTarget && blockEntity.destroyTarget != null) { // CraftBukkit ++ if (blockEntity.destroyTarget.hurtServer((net.minecraft.server.level.ServerLevel) level, level.damageSources().magic(), 4.0F)) // CraftBukkit + level.playSound( + null, + blockEntity.destroyTarget.getX(), +@@ -214,7 +_,6 @@ + 1.0F, + 1.0F + ); +- blockEntity.destroyTarget.hurt(level.damageSources().magic(), 4.0F); + } + + if (livingEntity != blockEntity.destroyTarget) { diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java.patch similarity index 50% rename from paper-server/patches/unapplied/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java.patch rename to paper-server/patches/sources/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java.patch index b594786d46..e999d53387 100644 --- a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java.patch @@ -1,76 +1,76 @@ --- a/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java +++ b/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java -@@ -17,6 +17,7 @@ +@@ -13,6 +_,7 @@ private static final int CHECK_TICK_DELAY = 5; private int openCount; private double maxInteractionRange; -+ public boolean opened; // CraftBukkit ++ public boolean opened; // CraftBukki - public ContainerOpenersCounter() {} + protected abstract void onOpen(Level level, BlockPos pos, BlockState state); -@@ -26,11 +27,36 @@ +@@ -20,10 +_,36 @@ - protected abstract void openerCountChanged(Level world, BlockPos pos, BlockState state, int oldViewerCount, int newViewerCount); + protected abstract void openerCountChanged(Level level, BlockPos pos, BlockState state, int count, int openCount); + // CraftBukkit start -+ public void onAPIOpen(Level world, BlockPos blockposition, BlockState iblockdata) { -+ this.onOpen(world, blockposition, iblockdata); ++ public void onAPIOpen(Level level, BlockPos blockPos, BlockState blockState) { ++ this.onOpen(level, blockPos, blockState); + } + -+ public void onAPIClose(Level world, BlockPos blockposition, BlockState iblockdata) { -+ this.onClose(world, blockposition, iblockdata); ++ public void onAPIClose(Level level, BlockPos blockPos, BlockState blockState) { ++ this.onClose(level, blockPos, blockState); + } + -+ public void openerAPICountChanged(Level world, BlockPos blockposition, BlockState iblockdata, int i, int j) { -+ this.openerCountChanged(world, blockposition, iblockdata, i, j); ++ public void openerAPICountChanged(Level world, BlockPos blockPos, BlockState blockState, int count, int openCount) { ++ this.openerCountChanged(world, blockPos, blockState, count, openCount); + } -+ // CraftBukkit end ++ // CraftBukkit en + protected abstract boolean isOwnContainer(Player player); - public void incrementOpeners(Player player, Level world, BlockPos pos, BlockState state) { + public void incrementOpeners(Player player, Level level, BlockPos pos, BlockState state) { + int oldPower = Math.max(0, Math.min(15, this.openCount)); // CraftBukkit - Get power before new viewer is added int i = this.openCount++; - ++ + // CraftBukkit start - Call redstone event -+ if (world.getBlockState(pos).is(net.minecraft.world.level.block.Blocks.TRAPPED_CHEST)) { ++ if (level.getBlockState(pos).is(net.minecraft.world.level.block.Blocks.TRAPPED_CHEST)) { + int newPower = Math.max(0, Math.min(15, this.openCount)); + + if (oldPower != newPower) { -+ org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(world, pos, oldPower, newPower); ++ org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(level, pos, oldPower, newPower); + } + } -+ // CraftBukkit end ++ // CraftBukkit en + if (i == 0) { - this.onOpen(world, pos, state); - world.gameEvent((Entity) player, (Holder) GameEvent.CONTAINER_OPEN, pos); -@@ -42,8 +68,20 @@ + this.onOpen(level, pos, state); + level.gameEvent(player, GameEvent.CONTAINER_OPEN, pos); +@@ -35,7 +_,20 @@ } - public void decrementOpeners(Player player, Level world, BlockPos pos, BlockState state) { + public void decrementOpeners(Player player, Level level, BlockPos pos, BlockState state) { + int oldPower = Math.max(0, Math.min(15, this.openCount)); // CraftBukkit - Get power before new viewer is added + if (this.openCount == 0) return; // Paper - Prevent ContainerOpenersCounter openCount from going negative int i = this.openCount--; - ++ + // CraftBukkit start - Call redstone event -+ if (world.getBlockState(pos).is(net.minecraft.world.level.block.Blocks.TRAPPED_CHEST)) { ++ if (level.getBlockState(pos).is(net.minecraft.world.level.block.Blocks.TRAPPED_CHEST)) { + int newPower = Math.max(0, Math.min(15, this.openCount)); + + if (oldPower != newPower) { -+ org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(world, pos, oldPower, newPower); ++ org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(level, pos, oldPower, newPower); + } + } + // CraftBukkit end + if (this.openCount == 0) { - this.onClose(world, pos, state); - world.gameEvent((Entity) player, (Holder) GameEvent.CONTAINER_CLOSE, pos); -@@ -72,6 +110,7 @@ + this.onClose(level, pos, state); + level.gameEvent(player, GameEvent.CONTAINER_CLOSE, pos); +@@ -60,6 +_,7 @@ } - int i = list.size(); -+ if (this.opened) i++; // CraftBukkit - add dummy count from API - int j = this.openCount; - - if (j != i) { + int size = playersWithContainerOpen.size(); ++ if (this.opened) size++; // CraftBukkit - add dummy count from API + int i = this.openCount; + if (i != size) { + boolean flag = size != 0; diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/CrafterBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/CrafterBlockEntity.java.patch new file mode 100644 index 0000000000..212d7b7f55 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/CrafterBlockEntity.java.patch @@ -0,0 +1,50 @@ +--- a/net/minecraft/world/level/block/entity/CrafterBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/CrafterBlockEntity.java +@@ -56,6 +_,47 @@ + } + }; + ++ // CraftBukkit start - add fields and methods ++ public java.util.List transaction = new java.util.ArrayList<>(); ++ private int maxStack = MAX_STACK; ++ ++ @Override ++ public java.util.List getContents() { ++ return this.items; ++ } ++ ++ @Override ++ public void onOpen(org.bukkit.craftbukkit.entity.CraftHumanEntity who) { ++ this.transaction.add(who); ++ } ++ ++ @Override ++ public void onClose(org.bukkit.craftbukkit.entity.CraftHumanEntity who) { ++ this.transaction.remove(who); ++ } ++ ++ @Override ++ public java.util.List getViewers() { ++ return this.transaction; ++ } ++ ++ @Override ++ public int getMaxStackSize() { ++ return this.maxStack; ++ } ++ ++ @Override ++ public void setMaxStackSize(int size) { ++ this.maxStack = size; ++ } ++ ++ @Override ++ public org.bukkit.Location getLocation() { ++ if (this.level == null) return null; ++ return io.papermc.paper.util.MCUtil.toLocation(this.level, this.worldPosition); ++ } ++ // CraftBukkit en ++ + public CrafterBlockEntity(BlockPos pos, BlockState state) { + super(BlockEntityType.CRAFTER, pos, state); + } diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/DecoratedPotBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/DecoratedPotBlockEntity.java.patch similarity index 58% rename from paper-server/patches/unapplied/net/minecraft/world/level/block/entity/DecoratedPotBlockEntity.java.patch rename to paper-server/patches/sources/net/minecraft/world/level/block/entity/DecoratedPotBlockEntity.java.patch index 6c93436b46..afb231c15d 100644 --- a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/DecoratedPotBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/DecoratedPotBlockEntity.java.patch @@ -1,48 +1,37 @@ --- a/net/minecraft/world/level/block/entity/DecoratedPotBlockEntity.java +++ b/net/minecraft/world/level/block/entity/DecoratedPotBlockEntity.java -@@ -20,8 +20,59 @@ - import net.minecraft.world.level.storage.loot.LootTable; +@@ -20,6 +_,48 @@ import net.minecraft.world.ticks.ContainerSingleItem; -+// CraftBukkit start -+import java.util.ArrayList; -+import java.util.Arrays; -+import java.util.List; -+import org.bukkit.Location; -+import org.bukkit.craftbukkit.entity.CraftHumanEntity; -+import org.bukkit.craftbukkit.util.CraftLocation; -+import org.bukkit.entity.HumanEntity; -+// CraftBukkit end -+ public class DecoratedPotBlockEntity extends BlockEntity implements RandomizableContainer, ContainerSingleItem.BlockContainerSingleItem { - ++ + // CraftBukkit start - add fields and methods -+ public List transaction = new ArrayList<>(); ++ public List transaction = new java.util.ArrayList<>(); + private int maxStack = MAX_STACK; + + @Override + public List getContents() { -+ return Arrays.asList(this.item); ++ return java.util.List.of(this.item); + } + + @Override -+ public void onOpen(CraftHumanEntity who) { ++ public void onOpen(org.bukkit.craftbukkit.entity.CraftHumanEntity who) { + this.transaction.add(who); + } + + @Override -+ public void onClose(CraftHumanEntity who) { ++ public void onClose(org.bukkit.craftbukkit.entity.CraftHumanEntity who) { + this.transaction.remove(who); + } + + @Override -+ public List getViewers() { ++ public java.util.List getViewers() { + return this.transaction; + } + + @Override + public int getMaxStackSize() { -+ return this.maxStack; ++ return this.maxStack; + } + + @Override @@ -51,9 +40,9 @@ + } + + @Override -+ public Location getLocation() { ++ public org.bukkit.Location getLocation() { + if (this.level == null) return null; -+ return CraftLocation.toBukkit(this.worldPosition, this.level.getWorld()); ++ return org.bukkit.craftbukkit.util.CraftLocation.toBukkit(this.worldPosition, this.level.getWorld()); + } + // CraftBukkit end + diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/DispenserBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/DispenserBlockEntity.java.patch new file mode 100644 index 0000000000..b7ffd808ca --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/DispenserBlockEntity.java.patch @@ -0,0 +1,39 @@ +--- a/net/minecraft/world/level/block/entity/DispenserBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/DispenserBlockEntity.java +@@ -17,6 +_,36 @@ + public static final int CONTAINER_SIZE = 9; + private NonNullList items = NonNullList.withSize(9, ItemStack.EMPTY); + ++ // CraftBukkit start - add fields and methods ++ public java.util.List transaction = new java.util.ArrayList<>(); ++ private int maxStack = MAX_STACK; ++ ++ public java.util.List getContents() { ++ return this.items; ++ } ++ ++ public void onOpen(org.bukkit.craftbukkit.entity.CraftHumanEntity who) { ++ this.transaction.add(who); ++ } ++ ++ public void onClose(org.bukkit.craftbukkit.entity.CraftHumanEntity who) { ++ this.transaction.remove(who); ++ } ++ ++ public List getViewers() { ++ return this.transaction; ++ } ++ ++ @Override ++ public int getMaxStackSize() { ++ return this.maxStack; ++ } ++ ++ public void setMaxStackSize(int size) { ++ this.maxStack = size; ++ } ++ // CraftBukkit end ++ + protected DispenserBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState) { + super(type, pos, blockState); + } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/HopperBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/HopperBlockEntity.java.patch index 20e268ca9c..23642e8281 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/HopperBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/HopperBlockEntity.java.patch @@ -20,7 +20,7 @@ + this.transaction.remove(who); + } + -+ public List getViewers() { ++ public java.util.List getViewers() { + return this.transaction; + } + diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/JigsawBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/JigsawBlockEntity.java.patch similarity index 52% rename from paper-server/patches/unapplied/net/minecraft/world/level/block/entity/JigsawBlockEntity.java.patch rename to paper-server/patches/sources/net/minecraft/world/level/block/entity/JigsawBlockEntity.java.patch index 11519f80c2..a023b4d2ec 100644 --- a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/JigsawBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/JigsawBlockEntity.java.patch @@ -1,16 +1,16 @@ --- a/net/minecraft/world/level/block/entity/JigsawBlockEntity.java +++ b/net/minecraft/world/level/block/entity/JigsawBlockEntity.java -@@ -131,7 +131,12 @@ - public void generate(ServerLevel world, int maxDepth, boolean keepJigsaws) { +@@ -131,7 +_,12 @@ + public void generate(ServerLevel level, int maxDepth, boolean keepJigsaws) { BlockPos blockPos = this.getBlockPos().relative(this.getBlockState().getValue(JigsawBlock.ORIENTATION).front()); - Registry registry = world.registryAccess().lookupOrThrow(Registries.TEMPLATE_POOL); -- Holder holder = registry.getOrThrow(this.pool); + Registry registry = level.registryAccess().lookupOrThrow(Registries.TEMPLATE_POOL); +- Holder orThrow = registry.getOrThrow(this.pool); + // Paper start - Replace getHolderOrThrow with a null check -+ Holder holder = registry.get(this.pool).orElse(null); -+ if (holder == null) { ++ Holder orThrow = registry.get(this.pool).orElse(null); ++ if (orThrow == null) { + return; + } + // Paper end - Replace getHolderOrThrow with a null check - JigsawPlacement.generateJigsaw(world, holder, this.target, maxDepth, blockPos, keepJigsaws); + JigsawPlacement.generateJigsaw(level, orThrow, this.target, maxDepth, blockPos, keepJigsaws); } diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java.patch similarity index 54% rename from paper-server/patches/unapplied/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java.patch rename to paper-server/patches/sources/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java.patch index fa8fa48ae9..35ee987e0f 100644 --- a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java.patch @@ -1,40 +1,27 @@ --- a/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java +++ b/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java -@@ -19,13 +19,57 @@ - import net.minecraft.world.phys.Vec3; +@@ -20,6 +_,44 @@ import net.minecraft.world.ticks.ContainerSingleItem; -+// CraftBukkit start -+import java.util.Collections; -+import java.util.List; -+import org.bukkit.Location; -+import org.bukkit.craftbukkit.entity.CraftHumanEntity; -+import org.bukkit.entity.HumanEntity; -+// CraftBukkit end -+ public class JukeboxBlockEntity extends BlockEntity implements ContainerSingleItem.BlockContainerSingleItem { - - public static final String SONG_ITEM_TAG_ID = "RecordItem"; - public static final String TICKS_SINCE_SONG_STARTED_TAG_ID = "ticks_since_song_started"; - private ItemStack item; - private final JukeboxSongPlayer jukeboxSongPlayer; ++ + // CraftBukkit start - add fields and methods -+ public List transaction = new java.util.ArrayList(); ++ public java.util.List transaction = new java.util.ArrayList<>(); + private int maxStack = MAX_STACK; + public boolean opened; - ++ + @Override -+ public List getContents() { ++ public java.util.List getContents() { + return Collections.singletonList(this.item); + } + + @Override -+ public void onOpen(CraftHumanEntity who) { ++ public void onOpen(org.bukkit.craftbukkit.entity.CraftHumanEntity who) { + this.transaction.add(who); + } + + @Override -+ public void onClose(CraftHumanEntity who) { ++ public void onClose(org.bukkit.craftbukkit.entity.CraftHumanEntity who) { + this.transaction.remove(who); + } + @@ -49,16 +36,16 @@ + } + + @Override -+ public Location getLocation() { ++ public org.bukkit.Location getLocation() { + if (this.level == null) return null; -+ return new org.bukkit.Location(this.level.getWorld(), this.worldPosition.getX(), this.worldPosition.getY(), this.worldPosition.getZ()); ++ return io.papermc.paper.util.MCUtil.toLocation(this.level, this.worldPosition); + } + // CraftBukkit end + - public JukeboxBlockEntity(BlockPos pos, BlockState state) { - super(BlockEntityType.JUKEBOX, pos, state); - this.item = ItemStack.EMPTY; -@@ -137,7 +181,7 @@ + public static final String SONG_ITEM_TAG_ID = "RecordItem"; + public static final String TICKS_SINCE_SONG_STARTED_TAG_ID = "ticks_since_song_started"; + private ItemStack item = ItemStack.EMPTY; +@@ -126,7 +_,7 @@ @Override public int getMaxStackSize() { @@ -67,20 +54,14 @@ } @Override -@@ -156,12 +200,17 @@ - } - +@@ -147,9 +_,14 @@ @VisibleForTesting -- public void setSongItemWithoutPlaying(ItemStack stack) { -- this.item = stack; -- JukeboxSong.fromStack(this.level.registryAccess(), stack).ifPresent((holder) -> { -- this.jukeboxSongPlayer.setSongWithoutPlaying(holder, 0L); -+ public void setSongItemWithoutPlaying(ItemStack itemstack, long ticksSinceSongStarted) { // CraftBukkit - add argument -+ this.item = itemstack; + public void setSongItemWithoutPlaying(ItemStack stack) { + this.item = stack; +- JukeboxSong.fromStack(this.level.registryAccess(), stack) + this.jukeboxSongPlayer.song = null; // CraftBukkit - reset -+ JukeboxSong.fromStack(this.level != null ? this.level.registryAccess() : org.bukkit.craftbukkit.CraftRegistry.getMinecraftRegistry(), itemstack).ifPresent((holder) -> { // Paper - fallback to other RegistyrAccess if no level -+ this.jukeboxSongPlayer.setSongWithoutPlaying(holder, ticksSinceSongStarted); // CraftBukkit - add argument - }); ++ JukeboxSong.fromStack(this.level != null ? this.level.registryAccess() : org.bukkit.craftbukkit.CraftRegistry.getMinecraftRegistry(), stack) // Paper - fallback to other RegistyrAccess if no level + .ifPresent(holder -> this.jukeboxSongPlayer.setSongWithoutPlaying((Holder)holder, 0L)); - this.level.updateNeighborsAt(this.getBlockPos(), this.getBlockState().getBlock()); + // CraftBukkit start - add null check for level + if (this.level != null) { diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/LecternBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/LecternBlockEntity.java.patch new file mode 100644 index 0000000000..5f04d187fe --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/LecternBlockEntity.java.patch @@ -0,0 +1,139 @@ +--- a/net/minecraft/world/level/block/entity/LecternBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/LecternBlockEntity.java +@@ -33,6 +_,51 @@ + public static final int SLOT_BOOK = 0; + public static final int NUM_SLOTS = 1; + public final Container bookAccess = new Container() { ++ // CraftBukkit start - add fields and methods ++ public java.util.List transaction = new java.util.ArrayList<>(); ++ private int maxStack = 1; ++ ++ @Override ++ public java.util.List getContents() { ++ return java.util.List.of(LecternBlockEntity.this.book); ++ } ++ ++ @Override ++ public void onOpen(org.bukkit.craftbukkit.entity.CraftHumanEntity who) { ++ this.transaction.add(who); ++ } ++ ++ @Override ++ public void onClose(org.bukkit.craftbukkit.entity.CraftHumanEntity who) { ++ this.transaction.remove(who); ++ } ++ ++ @Override ++ public java.util.List getViewers() { ++ return this.transaction; ++ } ++ ++ @Override ++ public void setMaxStackSize(int i) { ++ this.maxStack = i; ++ } ++ ++ @Override ++ public org.bukkit.Location getLocation() { ++ if (LecternBlockEntity.this.level == null) return null; ++ return io.papermc.paper.util.MCUtil.toLocation(LecternBlockEntity.this.level, LecternBlockEntity.this.worldPosition); ++ } ++ ++ @Override ++ public org.bukkit.inventory.InventoryHolder getOwner() { ++ return LecternBlockEntity.this.getOwner(); ++ } ++ ++ public LecternBlockEntity getLectern() { ++ return LecternBlockEntity.this; ++ } ++ // CraftBukkit end ++ + @Override + public int getContainerSize() { + return 1; +@@ -76,11 +_,19 @@ + + @Override + public void setItem(int slot, ItemStack stack) { ++ // CraftBukkit start ++ if (slot == 0) { ++ LecternBlockEntity.this.setBook(stack); ++ if (LecternBlockEntity.this.getLevel() != null) { ++ LecternBlock.resetBookState(null, LecternBlockEntity.this.getLevel(), LecternBlockEntity.this.getBlockPos(), LecternBlockEntity.this.getBlockState(), LecternBlockEntity.this.hasBook()); ++ } ++ } ++ // CraftBukkit end + } + + @Override + public int getMaxStackSize() { +- return 1; ++ return maxStack; + } + + @Override +@@ -158,7 +_,7 @@ + if (i != this.page) { + this.page = i; + this.setChanged(); +- LecternBlock.signalPageChange(this.getLevel(), this.getBlockPos(), this.getBlockState()); ++ if (this.level != null) LecternBlock.signalPageChange(this.getLevel(), this.getBlockPos(), this.getBlockState()); // CraftBukkit + } + } + +@@ -179,6 +_,36 @@ + return stack; + } + ++ // CraftBukkit start ++ private final CommandSource commandSource = new CommandSource() { ++ ++ @Override ++ public void sendSystemMessage(Component message) { ++ } ++ ++ @Override ++ public org.bukkit.command.CommandSender getBukkitSender(CommandSourceStack commandSourceStack) { ++ return commandSourceStack.getEntity() != null ++ ? commandSourceStack.getEntity().getBukkitEntity() ++ : new org.bukkit.craftbukkit.command.CraftBlockCommandSender(commandSourceStack, LecternBlockEntity.this); ++ } ++ ++ @Override ++ public boolean acceptsSuccess() { ++ return false; ++ } ++ ++ @Override ++ public boolean acceptsFailure() { ++ return false; ++ } ++ ++ @Override ++ public boolean shouldInformAdmins() { ++ return false; ++ } ++ }; ++ // CraftBukkit end + private CommandSourceStack createCommandSourceStack(@Nullable Player player, ServerLevel level) { + String string; + Component component; +@@ -191,7 +_,7 @@ + } + + Vec3 vec3 = Vec3.atCenterOf(this.worldPosition); +- return new CommandSourceStack(CommandSource.NULL, vec3, Vec2.ZERO, level, 2, string, component, level.getServer(), player); ++ return new CommandSourceStack(this.commandSource, vec3, Vec2.ZERO, level, 2, string, component, level.getServer(), player); // CraftBukkit - commandSource + } + + @Override +@@ -223,7 +_,7 @@ + + @Override + public AbstractContainerMenu createMenu(int containerId, Inventory playerInventory, Player player) { +- return new LecternMenu(containerId, this.bookAccess, this.dataAccess); ++ return new LecternMenu(containerId, this.bookAccess, this.dataAccess, playerInventory); // CraftBukkit + } + + @Override diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java.patch similarity index 85% rename from paper-server/patches/unapplied/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java.patch rename to paper-server/patches/sources/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java.patch index 16ff81b3cd..cb89a7e6d1 100644 --- a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java.patch @@ -1,8 +1,8 @@ --- a/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java +++ b/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java -@@ -115,4 +115,13 @@ - nbt.remove("LootTable"); - nbt.remove("LootTableSeed"); +@@ -115,4 +_,13 @@ + tag.remove("LootTable"); + tag.remove("LootTableSeed"); } + + // Paper start - LootTable API diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/SculkSensorBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/SculkSensorBlockEntity.java.patch similarity index 72% rename from paper-server/patches/unapplied/net/minecraft/world/level/block/entity/SculkSensorBlockEntity.java.patch rename to paper-server/patches/sources/net/minecraft/world/level/block/entity/SculkSensorBlockEntity.java.patch index 112cb33190..1f46bbe4dc 100644 --- a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/SculkSensorBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/SculkSensorBlockEntity.java.patch @@ -1,20 +1,20 @@ --- a/net/minecraft/world/level/block/entity/SculkSensorBlockEntity.java +++ b/net/minecraft/world/level/block/entity/SculkSensorBlockEntity.java -@@ -26,6 +26,7 @@ +@@ -26,6 +_,7 @@ private final VibrationSystem.Listener vibrationListener; private final VibrationSystem.User vibrationUser = this.createVibrationUser(); public int lastVibrationFrequency; + @Nullable public Integer rangeOverride = null; // Paper - Configurable sculk sensor listener range - protected SculkSensorBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) { - super(type, pos, state); -@@ -52,8 +53,16 @@ + protected SculkSensorBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState) { + super(type, pos, blockState); +@@ -52,8 +_,16 @@ .resultOrPartial(string -> LOGGER.error("Failed to parse vibration listener for Sculk Sensor: '{}'", string)) - .ifPresent(listener -> this.vibrationData = listener); + .ifPresent(data -> this.vibrationData = data); } + // Paper start - Configurable sculk sensor listener range -+ if (nbt.contains(PAPER_LISTENER_RANGE_NBT_KEY)) { -+ this.rangeOverride = nbt.getInt(PAPER_LISTENER_RANGE_NBT_KEY); ++ if (tag.contains(PAPER_LISTENER_RANGE_NBT_KEY)) { ++ this.rangeOverride = tag.getInt(PAPER_LISTENER_RANGE_NBT_KEY); + } else { + this.rangeOverride = null; + } @@ -23,23 +23,24 @@ + protected static final String PAPER_LISTENER_RANGE_NBT_KEY = "Paper.ListenerRange"; // Paper - Configurable sculk sensor listener range @Override - protected void saveAdditional(CompoundTag nbt, HolderLookup.Provider registries) { - super.saveAdditional(nbt, registries); -@@ -63,7 +72,13 @@ + protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.saveAdditional(tag, registries); +@@ -63,7 +_,13 @@ .encodeStart(registryOps, this.vibrationData) .resultOrPartial(string -> LOGGER.error("Failed to encode vibration listener for Sculk Sensor: '{}'", string)) - .ifPresent(listenerNbt -> nbt.put("listener", listenerNbt)); -+ this.saveRangeOverride(nbt); // Paper - Configurable sculk sensor listener range - } + .ifPresent(tag1 -> tag.put("listener", tag1)); +- } ++ this.saveRangeOverride(tag); // Paper - Configurable sculk sensor listener range ++ } + // Paper start - Configurable sculk sensor listener range -+ protected void saveRangeOverride(CompoundTag nbt) { -+ if (this.rangeOverride != null && this.rangeOverride != VibrationUser.LISTENER_RANGE) nbt.putInt(PAPER_LISTENER_RANGE_NBT_KEY, this.rangeOverride); // only save if it's different from the default ++ protected void saveRangeOverride(CompoundTag tag) { ++ if (this.rangeOverride != null && this.rangeOverride != VibrationUser.LISTENER_RANGE) tag.putInt(PAPER_LISTENER_RANGE_NBT_KEY, this.rangeOverride); // only save if it's different from the default + } + // Paper end - Configurable sculk sensor listener range @Override public VibrationSystem.Data getVibrationData() { -@@ -100,6 +115,7 @@ +@@ -100,6 +_,7 @@ @Override public int getListenerRadius() { diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/SculkShriekerBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/SculkShriekerBlockEntity.java.patch similarity index 83% rename from paper-server/patches/unapplied/net/minecraft/world/level/block/entity/SculkShriekerBlockEntity.java.patch rename to paper-server/patches/sources/net/minecraft/world/level/block/entity/SculkShriekerBlockEntity.java.patch index fb8ab267d1..f6de079656 100644 --- a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/SculkShriekerBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/SculkShriekerBlockEntity.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/entity/SculkShriekerBlockEntity.java +++ b/net/minecraft/world/level/block/entity/SculkShriekerBlockEntity.java -@@ -105,6 +105,13 @@ +@@ -105,6 +_,13 @@ @Nullable public static ServerPlayer tryGetPlayer(@Nullable Entity entity) { @@ -14,12 +14,12 @@ if (entity instanceof ServerPlayer) { return (ServerPlayer)entity; } else { -@@ -190,7 +197,7 @@ - private boolean trySummonWarden(ServerLevel world) { +@@ -190,7 +_,7 @@ + private boolean trySummonWarden(ServerLevel level) { return this.warningLevel >= 4 && SpawnUtil.trySpawnMob( -- EntityType.WARDEN, EntitySpawnReason.TRIGGERED, world, this.getBlockPos(), 20, 5, 6, SpawnUtil.Strategy.ON_TOP_OF_COLLIDER, false -+ EntityType.WARDEN, EntitySpawnReason.TRIGGERED, world, this.getBlockPos(), 20, 5, 6, SpawnUtil.Strategy.ON_TOP_OF_COLLIDER, false, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL, null // Paper - Entity#getEntitySpawnReason +- EntityType.WARDEN, EntitySpawnReason.TRIGGERED, level, this.getBlockPos(), 20, 5, 6, SpawnUtil.Strategy.ON_TOP_OF_COLLIDER, false ++ EntityType.WARDEN, EntitySpawnReason.TRIGGERED, level, this.getBlockPos(), 20, 5, 6, SpawnUtil.Strategy.ON_TOP_OF_COLLIDER, false, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL, null // Paper - Entity#getEntitySpawnReason ) .isPresent(); } diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java.patch similarity index 56% rename from paper-server/patches/unapplied/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java.patch rename to paper-server/patches/sources/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java.patch index 384ab32533..297c0cd136 100644 --- a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java.patch @@ -1,22 +1,11 @@ --- a/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java +++ b/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java -@@ -33,6 +33,10 @@ - import net.minecraft.world.level.material.PushReaction; - import net.minecraft.world.phys.AABB; - import net.minecraft.world.phys.Vec3; -+// CraftBukkit start -+import org.bukkit.craftbukkit.entity.CraftHumanEntity; -+import org.bukkit.entity.HumanEntity; -+// CraftBukkit end - - public class ShulkerBoxBlockEntity extends RandomizableContainerBlockEntity implements WorldlyContainer { - -@@ -52,6 +56,37 @@ +@@ -49,6 +_,37 @@ @Nullable private final DyeColor color; + // CraftBukkit start - add fields and methods -+ public List transaction = new java.util.ArrayList(); ++ public List transaction = new java.util.ArrayList<>(); + private int maxStack = MAX_STACK; + public boolean opened; + @@ -24,15 +13,15 @@ + return this.itemStacks; + } + -+ public void onOpen(CraftHumanEntity who) { ++ public void onOpen(org.bukkit.craftbukkit.entity.CraftHumanEntity who) { + this.transaction.add(who); + } + -+ public void onClose(CraftHumanEntity who) { ++ public void onClose(org.bukkit.craftbukkit.entity.CraftHumanEntity who) { + this.transaction.remove(who); + } + -+ public List getViewers() { ++ public List getViewers() { + return this.transaction; + } + @@ -46,22 +35,22 @@ + } + // CraftBukkit end + - public ShulkerBoxBlockEntity(@Nullable DyeColor color, BlockPos pos, BlockState state) { - super(BlockEntityType.SHULKER_BOX, pos, state); - this.itemStacks = NonNullList.withSize(27, ItemStack.EMPTY); -@@ -184,6 +219,7 @@ + public ShulkerBoxBlockEntity(@Nullable DyeColor color, BlockPos pos, BlockState blockState) { + super(BlockEntityType.SHULKER_BOX, pos, blockState); + this.color = color; +@@ -167,6 +_,7 @@ } - ++this.openCount; -+ if (this.opened) return; // CraftBukkit - only animate if the ShulkerBox hasn't been forced open already by an API call. + this.openCount++; ++ if (this.opened) return; // CraftBukkit - only animate if the ShulkerBox hasn't been forced open already by an API call this.level.blockEvent(this.worldPosition, this.getBlockState().getBlock(), 1, this.openCount); if (this.openCount == 1) { - this.level.gameEvent((Entity) player, (Holder) GameEvent.CONTAINER_OPEN, this.worldPosition); -@@ -197,6 +233,7 @@ + this.level.gameEvent(player, GameEvent.CONTAINER_OPEN, this.worldPosition); +@@ -180,6 +_,7 @@ public void stopOpen(Player player) { if (!this.remove && !player.isSpectator()) { - --this.openCount; + this.openCount--; + if (this.opened) return; // CraftBukkit - only animate if the ShulkerBox hasn't been forced open already by an API call. this.level.blockEvent(this.worldPosition, this.getBlockState().getBlock(), 1, this.openCount); if (this.openCount <= 0) { - this.level.gameEvent((Entity) player, (Holder) GameEvent.CONTAINER_CLOSE, this.worldPosition); + this.level.gameEvent(player, GameEvent.CONTAINER_CLOSE, this.worldPosition); diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/SignBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/SignBlockEntity.java.patch new file mode 100644 index 0000000000..a656788ab2 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/SignBlockEntity.java.patch @@ -0,0 +1,183 @@ +--- a/net/minecraft/world/level/block/entity/SignBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/SignBlockEntity.java +@@ -54,11 +_,16 @@ + return new SignText(); + } + +- public boolean isFacingFrontText(Player player) { ++ public boolean isFacingFrontText(net.minecraft.world.entity.player.Player player) { ++ // Paper start - More Sign Block API ++ return this.isFacingFrontText(player.getX(), player.getZ()); ++ } ++ public boolean isFacingFrontText(double x, double z) { ++ // Paper end - More Sign Block API + if (this.getBlockState().getBlock() instanceof SignBlock signBlock) { + Vec3 signHitboxCenterPosition = signBlock.getSignHitboxCenterPosition(this.getBlockState()); +- double d = player.getX() - (this.getBlockPos().getX() + signHitboxCenterPosition.x); +- double d1 = player.getZ() - (this.getBlockPos().getZ() + signHitboxCenterPosition.z); ++ double d = x - ((double) this.getBlockPos().getX() + signHitboxCenterPosition.x); // Paper - More Sign Block API ++ double d1 = z - ((double) this.getBlockPos().getZ() + signHitboxCenterPosition.z); // Paper - More Sign Block AP + float yRotationDegrees = signBlock.getYRotationDegrees(this.getBlockState()); + float f = (float)(Mth.atan2(d1, d) * 180.0F / (float)Math.PI) - 90.0F; + return Mth.degreesDifferenceAbs(yRotationDegrees, f) <= 90.0F; +@@ -143,11 +_,13 @@ + + public void updateSignText(Player player, boolean isFrontText, List filteredText) { + if (!this.isWaxed() && player.getUUID().equals(this.getPlayerWhoMayEdit()) && this.level != null) { +- this.updateText(signText -> this.setMessages(player, filteredText, signText), isFrontText); ++ this.updateText(signText -> this.setMessages(player, filteredText, signText, isFrontText), isFrontText); // CraftBukkit + this.setAllowedPlayerEditor(null); + this.level.sendBlockUpdated(this.getBlockPos(), this.getBlockState(), this.getBlockState(), 3); + } else { + LOGGER.warn("Player {} just tried to change non-editable sign", player.getName().getString()); ++ if (player.distanceToSqr(this.getBlockPos().getX(), this.getBlockPos().getY(), this.getBlockPos().getZ()) < 32 * 32) // Paper - Dont send far away sign update ++ ((net.minecraft.server.level.ServerPlayer) player).connection.send(this.getUpdatePacket()); // CraftBukkit + } + } + +@@ -156,18 +_,40 @@ + return this.setText(updater.apply(text), isFrontText); + } + +- private SignText setMessages(Player player, List filteredText, SignText text) { ++ private SignText setMessages(Player player, List filteredText, SignText text, boolean front) { // CraftBukkit ++ SignText originalText = text; // CraftBukkit + for (int i = 0; i < filteredText.size(); i++) { + FilteredText filteredText1 = filteredText.get(i); + Style style = text.getMessage(i, player.isTextFilteringEnabled()).getStyle(); + if (player.isTextFilteringEnabled()) { +- text = text.setMessage(i, Component.literal(filteredText1.filteredOrEmpty()).setStyle(style)); ++ text = text.setMessage(i, Component.literal(net.minecraft.util.StringUtil.filterText(filteredText1.filteredOrEmpty())).setStyle(style)); // Paper - filter sign text to chat only + } else { + text = text.setMessage( +- i, Component.literal(filteredText1.raw()).setStyle(style), Component.literal(filteredText1.filteredOrEmpty()).setStyle(style) ++ i, Component.literal(filteredText1.raw()).setStyle(style), Component.literal(net.minecraft.util.StringUtil.filterText(filteredText1.filteredOrEmpty())).setStyle(style) // Paper - filter sign text to chat only + ); + } + } ++ ++ // CraftBukkit start ++ org.bukkit.entity.Player apiPlayer = ((net.minecraft.server.level.ServerPlayer) player).getBukkitEntity(); ++ List lines = new java.util.ArrayList<>(); // Paper - adventure ++ ++ for (int i = 0; i < filteredText.size(); ++i) { ++ lines.add(io.papermc.paper.adventure.PaperAdventure.asAdventure(text.getMessage(i, player.isTextFilteringEnabled()))); // Paper - Adventure ++ } ++ ++ org.bukkit.event.block.SignChangeEvent event = new org.bukkit.event.block.SignChangeEvent(org.bukkit.craftbukkit.block.CraftBlock.at(this.level, this.worldPosition), apiPlayer, new java.util.ArrayList<>(lines), (front) ? org.bukkit.block.sign.Side.FRONT : org.bukkit.block.sign.Side.BACK); // Paper - Adventure ++ if (!event.callEvent()) { ++ return originalText; ++ } ++ ++ Component[] components = org.bukkit.craftbukkit.block.CraftSign.sanitizeLines(event.lines()); // Paper - Adventure ++ for (int i = 0; i < components.length; i++) { ++ if (!java.util.Objects.equals(lines.get(i), event.line(i))) { // Paper - Adventure ++ text = text.setMessage(i, components[i]); ++ } ++ } ++ // CraftBukkit end + + return text; + } +@@ -207,7 +_,23 @@ + Style style = component.getStyle(); + ClickEvent clickEvent = style.getClickEvent(); + if (clickEvent != null && clickEvent.getAction() == ClickEvent.Action.RUN_COMMAND) { +- player.getServer().getCommands().performPrefixedCommand(createCommandSourceStack(player, level, pos), clickEvent.getValue()); ++ // Paper start - Fix commands from signs not firing command events ++ String command = clickEvent.getValue().startsWith("/") ? clickEvent.getValue() : "/" + clickEvent.getValue(); ++ if (org.spigotmc.SpigotConfig.logCommands) { ++ LOGGER.info("{} issued server command: {}", player.getScoreboardName(), command); ++ } ++ io.papermc.paper.event.player.PlayerSignCommandPreprocessEvent event = new io.papermc.paper.event.player.PlayerSignCommandPreprocessEvent( ++ (org.bukkit.entity.Player) player.getBukkitEntity(), ++ command, ++ new org.bukkit.craftbukkit.util.LazyPlayerSet(player.getServer()), ++ (org.bukkit.block.Sign) org.bukkit.craftbukkit.block.CraftBlock.at(this.level, this.worldPosition).getState(), ++ frontText ? org.bukkit.block.sign.Side.FRONT : org.bukkit.block.sign.Side.BACK ++ ); ++ if (!event.callEvent()) { ++ return false; ++ } ++ player.getServer().getCommands().performPrefixedCommand(this.createCommandSourceStack(((org.bukkit.craftbukkit.entity.CraftPlayer) event.getPlayer()).getHandle(), level, pos), event.getMessage()); ++ // Paper end - Fix commands from signs not firing command events + flag = true; + } + } +@@ -215,10 +_,55 @@ + return flag; + } + +- private static CommandSourceStack createCommandSourceStack(@Nullable Player player, Level level, BlockPos pos) { ++ // CraftBukkit start ++ private final CommandSource commandSource = new CommandSource() { ++ ++ @Override ++ public void sendSystemMessage(Component message) {} ++ ++ @Override ++ public org.bukkit.command.CommandSender getBukkitSender(CommandSourceStack commandSourceStack) { ++ return commandSourceStack.getEntity() != null ? commandSourceStack.getEntity().getBukkitEntity() : new org.bukkit.craftbukkit.command.CraftBlockCommandSender(commandSourceStack, SignBlockEntity.this); ++ } ++ ++ @Override ++ public boolean acceptsSuccess() { ++ return false; ++ } ++ ++ @Override ++ public boolean acceptsFailure() { ++ return false; ++ } ++ ++ @Override ++ public boolean shouldInformAdmins() { ++ return false; ++ } ++ }; ++ ++ private CommandSourceStack createCommandSourceStack(@Nullable Player player, Level world, BlockPos pos) { ++ // CraftBukkit end + String string = player == null ? "Sign" : player.getName().getString(); + Component component = (Component)(player == null ? Component.literal("Sign") : player.getDisplayName()); +- return new CommandSourceStack(CommandSource.NULL, Vec3.atCenterOf(pos), Vec2.ZERO, (ServerLevel)level, 2, string, component, level.getServer(), player); ++ ++ // Paper start - Fix commands from signs not firing command events ++ CommandSource commandSource = world.paperConfig().misc.showSignClickCommandFailureMsgsToPlayer ? new io.papermc.paper.commands.DelegatingCommandSource(this.commandSource) { ++ @Override ++ public void sendSystemMessage(Component message) { ++ if (player instanceof final net.minecraft.server.level.ServerPlayer serverPlayer) { ++ serverPlayer.sendSystemMessage(message); ++ } ++ } ++ ++ @Override ++ public boolean acceptsFailure() { ++ return true; ++ } ++ } : this.commandSource; ++ // Paper end - Fix commands from signs not firing command events ++ // CraftBukkit - this ++ return new CommandSourceStack(commandSource, Vec3.atCenterOf(pos), Vec2.ZERO, (ServerLevel) world, 2, string, (Component) component, world.getServer(), player); // Paper - Fix commands from signs not firing command events + } + + @Override +@@ -237,12 +_,17 @@ + + @Nullable + public UUID getPlayerWhoMayEdit() { ++ // CraftBukkit start - unnecessary sign ticking removed, so do this lazily ++ if (this.level != null && this.playerWhoMayEdit != null) { ++ this.clearInvalidPlayerWhoMayEdit(this, this.level, this.playerWhoMayEdit); ++ } ++ // CraftBukkit end + return this.playerWhoMayEdit; + } + + private void markUpdated() { + this.setChanged(); +- this.level.sendBlockUpdated(this.getBlockPos(), this.getBlockState(), this.getBlockState(), 3); ++ if (this.level != null) this.level.sendBlockUpdated(this.getBlockPos(), this.getBlockState(), this.getBlockState(), 3); // CraftBukkit - skip notify if world is null (SPIGOT-5122) + } + + public boolean isWaxed() { diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/SkullBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/SkullBlockEntity.java.patch similarity index 76% rename from paper-server/patches/unapplied/net/minecraft/world/level/block/entity/SkullBlockEntity.java.patch rename to paper-server/patches/sources/net/minecraft/world/level/block/entity/SkullBlockEntity.java.patch index a683a1cb1b..9261f76fc9 100644 --- a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/SkullBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/SkullBlockEntity.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/entity/SkullBlockEntity.java +++ b/net/minecraft/world/level/block/entity/SkullBlockEntity.java -@@ -41,7 +41,7 @@ +@@ -41,7 +_,7 @@ @Nullable private static LoadingCache>> profileCacheByName; @Nullable @@ -9,45 +9,45 @@ public static final Executor CHECKED_MAIN_THREAD_EXECUTOR = runnable -> { Executor executor = mainThreadExecutor; if (executor != null) { -@@ -76,9 +76,9 @@ +@@ -76,9 +_,9 @@ profileCacheById = CacheBuilder.newBuilder() .expireAfterAccess(Duration.ofMinutes(10L)) .maximumSize(256L) - .build(new CacheLoader>>() { + .build(new CacheLoader<>() { // Paper - player profile events @Override -- public CompletableFuture> load(UUID uUID) { -+ public CompletableFuture> load(com.mojang.datafixers.util.Pair uUID) { // Paper - player profile events - return SkullBlockEntity.fetchProfileById(uUID, apiServices, booleanSupplier); +- public CompletableFuture> load(UUID id) { ++ public CompletableFuture> load(com.mojang.datafixers.util.Pair id) { // Paper - player profile events + return SkullBlockEntity.fetchProfileById(id, services, booleanSupplier); } }); -@@ -89,23 +89,29 @@ +@@ -89,23 +_,29 @@ .getAsync(name) .thenCompose( optional -> { - LoadingCache>> loadingCache = profileCacheById; + LoadingCache, CompletableFuture>> loadingCache = profileCacheById; // Paper - player profile events return loadingCache != null && !optional.isEmpty() -- ? loadingCache.getUnchecked(optional.get().getId()).thenApply(optional2 -> optional2.or(() -> optional)) -+ ? loadingCache.getUnchecked(new com.mojang.datafixers.util.Pair<>(optional.get().getId(), optional.get())).thenApply(optional2 -> optional2.or(() -> optional)) // Paper - player profile events +- ? loadingCache.getUnchecked(optional.get().getId()).thenApply(optional1 -> optional1.or(() -> optional)) ++ ? loadingCache.getUnchecked(new com.mojang.datafixers.util.Pair<>(optional.get().getId(), optional.get())).thenApply(optional1 -> optional1.or(() -> optional)) // Paper - player profile events : CompletableFuture.completedFuture(Optional.empty()); } ); } -- static CompletableFuture> fetchProfileById(UUID uuid, Services apiServices, BooleanSupplier booleanSupplier) { -+ static CompletableFuture> fetchProfileById(com.mojang.datafixers.util.Pair pair, Services apiServices, BooleanSupplier booleanSupplier) { // Paper +- static CompletableFuture> fetchProfileById(UUID id, Services services, BooleanSupplier cacheUninitialized) { ++ static CompletableFuture> fetchProfileById(com.mojang.datafixers.util.Pair id, Services services, BooleanSupplier cacheUninitialized) { // Pape return CompletableFuture.supplyAsync(() -> { - if (booleanSupplier.getAsBoolean()) { + if (cacheUninitialized.getAsBoolean()) { return Optional.empty(); } else { -- ProfileResult profileResult = apiServices.sessionService().fetchProfile(uuid, true); +- ProfileResult profileResult = services.sessionService().fetchProfile(id, true); + // Paper start - fill player profile events -+ if (apiServices.sessionService() instanceof com.destroystokyo.paper.profile.PaperMinecraftSessionService paperService) { -+ final GameProfile profile = pair.getSecond() != null ? pair.getSecond() : new com.mojang.authlib.GameProfile(pair.getFirst(), ""); ++ if (services.sessionService() instanceof com.destroystokyo.paper.profile.PaperMinecraftSessionService paperService) { ++ final GameProfile profile = id.getSecond() != null ? id.getSecond() : new com.mojang.authlib.GameProfile(id.getFirst(), ""); + return Optional.ofNullable(paperService.fetchProfile(profile, true)).map(ProfileResult::profile); + } -+ ProfileResult profileResult = apiServices.sessionService().fetchProfile(pair.getFirst(), true); ++ ProfileResult profileResult = services.sessionService().fetchProfile(id.getFirst(), true); + // Paper end - fill player profile events return Optional.ofNullable(profileResult).map(ProfileResult::profile); } @@ -56,13 +56,13 @@ } public static void clear() { -@@ -210,9 +216,11 @@ +@@ -210,9 +_,11 @@ : CompletableFuture.completedFuture(Optional.empty()); } -- public static CompletableFuture> fetchGameProfile(UUID uuid) { +- public static CompletableFuture> fetchGameProfile(UUID profileUuid) { - LoadingCache>> loadingCache = profileCacheById; -- return loadingCache != null ? loadingCache.getUnchecked(uuid) : CompletableFuture.completedFuture(Optional.empty()); +- return loadingCache != null ? loadingCache.getUnchecked(profileUuid) : CompletableFuture.completedFuture(Optional.empty()); + // Paper start - player profile events + public static CompletableFuture> fetchGameProfile(UUID uuid, @Nullable String name) { + LoadingCache, CompletableFuture>> loadingCache = profileCacheById; diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java.patch b/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java.patch deleted file mode 100644 index 8438fd826a..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java.patch +++ /dev/null @@ -1,356 +0,0 @@ ---- a/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java -+++ b/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java -@@ -22,7 +22,6 @@ - import net.minecraft.world.ContainerHelper; - import net.minecraft.world.WorldlyContainer; - import net.minecraft.world.entity.ExperienceOrb; --import net.minecraft.world.entity.player.Player; - import net.minecraft.world.entity.player.StackedItemContents; - import net.minecraft.world.inventory.ContainerData; - import net.minecraft.world.inventory.RecipeCraftingHolder; -@@ -41,6 +40,20 @@ - import net.minecraft.world.level.block.Blocks; - import net.minecraft.world.level.block.state.BlockState; - import net.minecraft.world.phys.Vec3; -+// CraftBukkit start -+import org.bukkit.craftbukkit.block.CraftBlock; -+import org.bukkit.craftbukkit.entity.CraftHumanEntity; -+import org.bukkit.craftbukkit.inventory.CraftItemStack; -+import org.bukkit.craftbukkit.inventory.CraftItemType; -+import org.bukkit.entity.HumanEntity; -+import org.bukkit.entity.Player; -+import org.bukkit.event.block.BlockExpEvent; -+import org.bukkit.event.inventory.FurnaceBurnEvent; -+import org.bukkit.event.inventory.FurnaceExtractEvent; -+import org.bukkit.event.inventory.FurnaceSmeltEvent; -+import org.bukkit.event.inventory.FurnaceStartSmeltEvent; -+import org.bukkit.inventory.CookingRecipe; -+// CraftBukkit end - - public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntity implements WorldlyContainer, RecipeCraftingHolder, StackedContentsCompatible { - -@@ -65,6 +78,8 @@ - protected final ContainerData dataAccess; - public final Reference2IntOpenHashMap>> recipesUsed; - private final RecipeManager.CachedCheck quickCheck; -+ public final RecipeType recipeType; // Paper - cook speed multiplier API -+ public double cookSpeedMultiplier = 1.0; // Paper - cook speed multiplier API - - protected AbstractFurnaceBlockEntity(BlockEntityType blockEntityType, BlockPos pos, BlockState state, RecipeType recipeType) { - super(blockEntityType, pos, state); -@@ -110,9 +125,40 @@ - } - }; - this.recipesUsed = new Reference2IntOpenHashMap(); -- this.quickCheck = RecipeManager.createCheck(recipeType); -+ this.quickCheck = RecipeManager.createCheck((RecipeType) recipeType); // CraftBukkit - decompile error // Eclipse fail -+ this.recipeType = recipeType; // Paper - cook speed multiplier API - } - -+ // CraftBukkit start - add fields and methods -+ private int maxStack = MAX_STACK; -+ public List transaction = new java.util.ArrayList(); -+ -+ public List getContents() { -+ return this.items; -+ } -+ -+ public void onOpen(CraftHumanEntity who) { -+ this.transaction.add(who); -+ } -+ -+ public void onClose(CraftHumanEntity who) { -+ this.transaction.remove(who); -+ } -+ -+ public List getViewers() { -+ return this.transaction; -+ } -+ -+ @Override -+ public int getMaxStackSize() { -+ return this.maxStack; -+ } -+ -+ public void setMaxStackSize(int size) { -+ this.maxStack = size; -+ } -+ // CraftBukkit end -+ - private boolean isLit() { - return this.litTimeRemaining > 0; - } -@@ -132,9 +178,18 @@ - while (iterator.hasNext()) { - String s = (String) iterator.next(); - -- this.recipesUsed.put(ResourceKey.create(Registries.RECIPE, ResourceLocation.parse(s)), nbttagcompound1.getInt(s)); -+ // Paper start - Validate ResourceLocation -+ final ResourceLocation resourceLocation = ResourceLocation.tryParse(s); -+ if (resourceLocation != null) { -+ this.recipesUsed.put(ResourceKey.create(Registries.RECIPE, resourceLocation), nbttagcompound1.getInt(s)); -+ } - } - -+ // Paper start - cook speed multiplier API -+ if (nbt.contains("Paper.CookSpeedMultiplier")) { -+ this.cookSpeedMultiplier = nbt.getDouble("Paper.CookSpeedMultiplier"); -+ } -+ // Paper end - cook speed multiplier API - } - - @Override -@@ -144,6 +199,7 @@ - nbt.putShort("cooking_total_time", (short) this.cookingTotalTime); - nbt.putShort("lit_time_remaining", (short) this.litTimeRemaining); - nbt.putShort("lit_total_time", (short) this.litTotalTime); -+ nbt.putDouble("Paper.CookSpeedMultiplier", this.cookSpeedMultiplier); // Paper - cook speed multiplier API - ContainerHelper.saveAllItems(nbt, this.items, registries); - CompoundTag nbttagcompound1 = new CompoundTag(); - -@@ -175,7 +231,7 @@ - RecipeHolder recipeholder; - - if (flag2) { -- recipeholder = (RecipeHolder) blockEntity.quickCheck.getRecipeFor(singlerecipeinput, world).orElse((Object) null); -+ recipeholder = (RecipeHolder) blockEntity.quickCheck.getRecipeFor(singlerecipeinput, world).orElse(null); // CraftBukkit - decompile error - } else { - recipeholder = null; - } -@@ -183,11 +239,22 @@ - int i = blockEntity.getMaxStackSize(); - - if (!blockEntity.isLit() && AbstractFurnaceBlockEntity.canBurn(world.registryAccess(), recipeholder, singlerecipeinput, blockEntity.items, i)) { -- blockEntity.litTimeRemaining = blockEntity.getBurnDuration(world.fuelValues(), itemstack); -+ // CraftBukkit start -+ CraftItemStack fuel = CraftItemStack.asCraftMirror(itemstack); -+ -+ FurnaceBurnEvent furnaceBurnEvent = new FurnaceBurnEvent(CraftBlock.at(world, pos), fuel, blockEntity.getBurnDuration(world.fuelValues(), itemstack)); -+ world.getCraftServer().getPluginManager().callEvent(furnaceBurnEvent); -+ -+ if (furnaceBurnEvent.isCancelled()) { -+ return; -+ } -+ -+ blockEntity.litTimeRemaining = furnaceBurnEvent.getBurnTime(); - blockEntity.litTotalTime = blockEntity.litTimeRemaining; -- if (blockEntity.isLit()) { -+ if (blockEntity.isLit() && furnaceBurnEvent.isBurning()) { -+ // CraftBukkit end - flag1 = true; -- if (flag3) { -+ if (flag3 && furnaceBurnEvent.willConsumeFuel()) { // Paper - add consumeFuel to FurnaceBurnEvent - Item item = itemstack.getItem(); - - itemstack.shrink(1); -@@ -199,11 +266,23 @@ - } - - if (blockEntity.isLit() && AbstractFurnaceBlockEntity.canBurn(world.registryAccess(), recipeholder, singlerecipeinput, blockEntity.items, i)) { -+ // CraftBukkit start -+ if (recipeholder != null && blockEntity.cookingTimer == 0) { -+ CraftItemStack source = CraftItemStack.asCraftMirror(blockEntity.items.get(0)); -+ CookingRecipe recipe = (CookingRecipe) recipeholder.toBukkitRecipe(); -+ -+ FurnaceStartSmeltEvent event = new FurnaceStartSmeltEvent(CraftBlock.at(world, pos), source, recipe, AbstractFurnaceBlockEntity.getTotalCookTime(world, blockEntity, blockEntity.recipeType, blockEntity.cookSpeedMultiplier)); // Paper - cook speed multiplier API -+ world.getCraftServer().getPluginManager().callEvent(event); -+ -+ blockEntity.cookingTotalTime = event.getTotalCookTime(); -+ } -+ // CraftBukkit end -+ - ++blockEntity.cookingTimer; -- if (blockEntity.cookingTimer == blockEntity.cookingTotalTime) { -+ if (blockEntity.cookingTimer >= blockEntity.cookingTotalTime) { // Paper - cook speed multiplier API - blockEntity.cookingTimer = 0; -- blockEntity.cookingTotalTime = AbstractFurnaceBlockEntity.getTotalCookTime(world, blockEntity); -- if (AbstractFurnaceBlockEntity.burn(world.registryAccess(), recipeholder, singlerecipeinput, blockEntity.items, i)) { -+ blockEntity.cookingTotalTime = AbstractFurnaceBlockEntity.getTotalCookTime(world, blockEntity, blockEntity.recipeType, blockEntity.cookSpeedMultiplier); // Paper - cook speed multiplier API -+ if (AbstractFurnaceBlockEntity.burn(blockEntity.level, blockEntity.worldPosition, world.registryAccess(), recipeholder, singlerecipeinput, blockEntity.items, i)) { // CraftBukkit - blockEntity.setRecipeUsed(recipeholder); - } - -@@ -242,20 +321,47 @@ - } - } - -- private static boolean burn(RegistryAccess dynamicRegistryManager, @Nullable RecipeHolder recipe, SingleRecipeInput input, NonNullList inventory, int maxCount) { -- if (recipe != null && AbstractFurnaceBlockEntity.canBurn(dynamicRegistryManager, recipe, input, inventory, maxCount)) { -- ItemStack itemstack = (ItemStack) inventory.get(0); -- ItemStack itemstack1 = ((AbstractCookingRecipe) recipe.value()).assemble(input, dynamicRegistryManager); -- ItemStack itemstack2 = (ItemStack) inventory.get(2); -+ private static boolean burn(Level world, BlockPos blockposition, RegistryAccess iregistrycustom, @Nullable RecipeHolder recipeholder, SingleRecipeInput singlerecipeinput, NonNullList nonnulllist, int i) { // CraftBukkit -+ if (recipeholder != null && AbstractFurnaceBlockEntity.canBurn(iregistrycustom, recipeholder, singlerecipeinput, nonnulllist, i)) { -+ ItemStack itemstack = (ItemStack) nonnulllist.get(0); -+ ItemStack itemstack1 = ((AbstractCookingRecipe) recipeholder.value()).assemble(singlerecipeinput, iregistrycustom); -+ ItemStack itemstack2 = (ItemStack) nonnulllist.get(2); - -+ // CraftBukkit start - fire FurnaceSmeltEvent -+ CraftItemStack source = CraftItemStack.asCraftMirror(itemstack); -+ org.bukkit.inventory.ItemStack result = CraftItemStack.asBukkitCopy(itemstack1); -+ -+ FurnaceSmeltEvent furnaceSmeltEvent = new FurnaceSmeltEvent(CraftBlock.at(world, blockposition), source, result, (org.bukkit.inventory.CookingRecipe) recipeholder.toBukkitRecipe()); // Paper - Add recipe to cook events -+ world.getCraftServer().getPluginManager().callEvent(furnaceSmeltEvent); -+ -+ if (furnaceSmeltEvent.isCancelled()) { -+ return false; -+ } -+ -+ result = furnaceSmeltEvent.getResult(); -+ itemstack1 = CraftItemStack.asNMSCopy(result); -+ -+ if (!itemstack1.isEmpty()) { -+ if (itemstack2.isEmpty()) { -+ nonnulllist.set(2, itemstack1.copy()); -+ } else if (CraftItemStack.asCraftMirror(itemstack2).isSimilar(result)) { -+ itemstack2.grow(itemstack1.getCount()); -+ } else { -+ return false; -+ } -+ } -+ -+ /* - if (itemstack2.isEmpty()) { -- inventory.set(2, itemstack1.copy()); -+ nonnulllist.set(2, itemstack1.copy()); - } else if (ItemStack.isSameItemSameComponents(itemstack2, itemstack1)) { - itemstack2.grow(1); - } -+ */ -+ // CraftBukkit end - -- if (itemstack.is(Blocks.WET_SPONGE.asItem()) && !((ItemStack) inventory.get(1)).isEmpty() && ((ItemStack) inventory.get(1)).is(Items.BUCKET)) { -- inventory.set(1, new ItemStack(Items.WATER_BUCKET)); -+ if (itemstack.is(Blocks.WET_SPONGE.asItem()) && !((ItemStack) nonnulllist.get(1)).isEmpty() && ((ItemStack) nonnulllist.get(1)).is(Items.BUCKET)) { -+ nonnulllist.set(1, new ItemStack(Items.WATER_BUCKET)); - } - - itemstack.shrink(1); -@@ -269,12 +375,14 @@ - return fuelRegistry.burnDuration(stack); - } - -- public static int getTotalCookTime(ServerLevel world, AbstractFurnaceBlockEntity furnace) { -+ public static int getTotalCookTime(@Nullable ServerLevel world, AbstractFurnaceBlockEntity furnace, RecipeType recipeType, double cookSpeedMultiplier) { // Paper - cook speed multiplier API - SingleRecipeInput singlerecipeinput = new SingleRecipeInput(furnace.getItem(0)); - -- return (Integer) furnace.quickCheck.getRecipeFor(singlerecipeinput, world).map((recipeholder) -> { -- return ((AbstractCookingRecipe) recipeholder.value()).cookingTime(); -- }).orElse(200); -+ // Paper start - cook speed multiplier API -+ /* Scale the recipe's cooking time to the current cookSpeedMultiplier */ -+ int cookTime = world != null ? furnace.quickCheck.getRecipeFor(singlerecipeinput, world).map(holder -> holder.value().cookingTime()).orElse(200) : (net.minecraft.server.MinecraftServer.getServer().getRecipeManager().getRecipeFor(recipeType, singlerecipeinput, world /* passing a null level here is safe. world is only used for map extending recipes which won't happen here */).map(holder -> holder.value().cookingTime()).orElse(200)); -+ return (int) Math.ceil (cookTime / cookSpeedMultiplier); -+ // Paper end - cook speed multiplier API - } - - @Override -@@ -320,12 +428,11 @@ - if (world instanceof ServerLevel) { - ServerLevel worldserver = (ServerLevel) world; - -- this.cookingTotalTime = AbstractFurnaceBlockEntity.getTotalCookTime(worldserver, this); -+ this.cookingTotalTime = AbstractFurnaceBlockEntity.getTotalCookTime(worldserver, this, this.recipeType, this.cookSpeedMultiplier); // Paper - cook speed multiplier API - this.cookingTimer = 0; - this.setChanged(); - } - } -- - } - - @Override -@@ -358,19 +465,19 @@ - } - - @Override -- public void awardUsedRecipes(Player player, List ingredients) {} -+ public void awardUsedRecipes(net.minecraft.world.entity.player.Player player, List ingredients) {} - -- public void awardUsedRecipesAndPopExperience(ServerPlayer player) { -- List> list = this.getRecipesToAwardAndPopExperience(player.serverLevel(), player.position()); -+ public void awardUsedRecipesAndPopExperience(ServerPlayer entityplayer, ItemStack itemstack, int amount) { // CraftBukkit -+ List> list = this.getRecipesToAwardAndPopExperience(entityplayer.serverLevel(), entityplayer.position(), this.worldPosition, entityplayer, itemstack, amount); // CraftBukkit - -- player.awardRecipes(list); -+ entityplayer.awardRecipes(list); - Iterator iterator = list.iterator(); - - while (iterator.hasNext()) { - RecipeHolder recipeholder = (RecipeHolder) iterator.next(); - - if (recipeholder != null) { -- player.triggerRecipeCrafted(recipeholder, this.items); -+ entityplayer.triggerRecipeCrafted(recipeholder, this.items); - } - } - -@@ -378,41 +485,56 @@ - } - - public List> getRecipesToAwardAndPopExperience(ServerLevel world, Vec3 pos) { -+ // CraftBukkit start -+ return this.getRecipesToAwardAndPopExperience(world, pos, this.worldPosition, null, null, 0); -+ } -+ -+ public List> getRecipesToAwardAndPopExperience(ServerLevel worldserver, Vec3 vec3d, BlockPos blockposition, ServerPlayer entityplayer, ItemStack itemstack, int amount) { -+ // CraftBukkit end - List> list = Lists.newArrayList(); - ObjectIterator objectiterator = this.recipesUsed.reference2IntEntrySet().iterator(); - - while (objectiterator.hasNext()) { - Entry>> entry = (Entry) objectiterator.next(); - -- world.recipeAccess().byKey((ResourceKey) entry.getKey()).ifPresent((recipeholder) -> { -+ worldserver.recipeAccess().byKey(entry.getKey()).ifPresent((recipeholder) -> { // CraftBukkit - decompile error -+ if (!(recipeholder.value() instanceof AbstractCookingRecipe)) return; // Paper - don't process non-cooking recipes - list.add(recipeholder); -- AbstractFurnaceBlockEntity.createExperience(world, pos, entry.getIntValue(), ((AbstractCookingRecipe) recipeholder.value()).experience()); -+ AbstractFurnaceBlockEntity.createExperience(worldserver, vec3d, entry.getIntValue(), ((AbstractCookingRecipe) recipeholder.value()).experience(), blockposition, entityplayer, itemstack, amount); // CraftBukkit - }); - } - - return list; - } - -- private static void createExperience(ServerLevel world, Vec3 pos, int multiplier, float experience) { -- int j = Mth.floor((float) multiplier * experience); -- float f1 = Mth.frac((float) multiplier * experience); -+ private static void createExperience(ServerLevel worldserver, Vec3 vec3d, int i, float f, BlockPos blockposition, net.minecraft.world.entity.player.Player entityhuman, ItemStack itemstack, int amount) { // CraftBukkit -+ int j = Mth.floor((float) i * f); -+ float f1 = Mth.frac((float) i * f); - - if (f1 != 0.0F && Math.random() < (double) f1) { - ++j; - } - -- ExperienceOrb.award(world, pos, j); -+ // CraftBukkit start - fire FurnaceExtractEvent / BlockExpEvent -+ BlockExpEvent event; -+ if (amount != 0) { -+ event = new FurnaceExtractEvent((Player) entityhuman.getBukkitEntity(), CraftBlock.at(worldserver, blockposition), CraftItemType.minecraftToBukkit(itemstack.getItem()), amount, j); -+ } else { -+ event = new BlockExpEvent(CraftBlock.at(worldserver, blockposition), j); -+ } -+ worldserver.getCraftServer().getPluginManager().callEvent(event); -+ j = event.getExpToDrop(); -+ // CraftBukkit end -+ -+ ExperienceOrb.award(worldserver, vec3d, j, org.bukkit.entity.ExperienceOrb.SpawnReason.FURNACE, entityhuman); // Paper - } - - @Override - public void fillStackedContents(StackedItemContents finder) { -- Iterator iterator = this.items.iterator(); -+ // Paper start - don't account fuel stack (fixes MC-243057) -+ finder.accountStack(this.items.get(SLOT_INPUT)); -+ finder.accountStack(this.items.get(SLOT_RESULT)); -+ // Paper end - -- while (iterator.hasNext()) { -- ItemStack itemstack = (ItemStack) iterator.next(); -- -- finder.accountStack(itemstack); -- } -- - } - } diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BannerBlockEntity.java.patch b/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BannerBlockEntity.java.patch deleted file mode 100644 index 3b8ce9376c..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BannerBlockEntity.java.patch +++ /dev/null @@ -1,81 +0,0 @@ ---- a/net/minecraft/world/level/block/entity/BannerBlockEntity.java -+++ b/net/minecraft/world/level/block/entity/BannerBlockEntity.java -@@ -19,13 +19,17 @@ - import net.minecraft.world.level.block.state.BlockState; - import org.slf4j.Logger; - -+// CraftBukkit start -+import java.util.List; -+// CraftBukkit end -+ - public class BannerBlockEntity extends BlockEntity implements Nameable { - - private static final Logger LOGGER = LogUtils.getLogger(); - public static final int MAX_PATTERNS = 6; - private static final String TAG_PATTERNS = "patterns"; - @Nullable -- private Component name; -+ public Component name; // Paper - public - public DyeColor baseColor; - private BannerPatternLayers patterns; - -@@ -53,7 +57,7 @@ - @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()); - } - -@@ -74,7 +78,7 @@ - BannerPatternLayers.CODEC.parse(registries.createSerializationContext(NbtOps.INSTANCE), nbt.get("patterns")).resultOrPartial((s) -> { - BannerBlockEntity.LOGGER.error("Failed to parse banner patterns: '{}'", s); - }).ifPresent((bannerpatternlayers) -> { -- this.patterns = bannerpatternlayers; -+ this.setPatterns(bannerpatternlayers); // CraftBukkit - apply limits - }); - } - -@@ -85,9 +89,18 @@ - return ClientboundBlockEntityDataPacket.create(this); - } - -+ // Paper start - always send patterns to client -+ ThreadLocal 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() { -@@ -108,7 +121,7 @@ - @Override - protected void applyImplicitComponents(BlockEntity.DataComponentInput components) { - super.applyImplicitComponents(components); -- this.patterns = (BannerPatternLayers) components.getOrDefault(DataComponents.BANNER_PATTERNS, BannerPatternLayers.EMPTY); -+ this.setPatterns((BannerPatternLayers) components.getOrDefault(DataComponents.BANNER_PATTERNS, BannerPatternLayers.EMPTY)); // CraftBukkit - apply limits - this.name = (Component) components.get(DataComponents.CUSTOM_NAME); - } - -@@ -124,4 +137,13 @@ - nbt.remove("patterns"); - nbt.remove("CustomName"); - } -+ -+ // CraftBukkit start -+ public void setPatterns(BannerPatternLayers bannerpatternlayers) { -+ if (bannerpatternlayers.layers().size() > 20) { -+ bannerpatternlayers = new BannerPatternLayers(List.copyOf(bannerpatternlayers.layers().subList(0, 20))); -+ } -+ this.patterns = bannerpatternlayers; -+ } -+ // CraftBukkit end - } diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BeaconBlockEntity.java.patch b/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BeaconBlockEntity.java.patch deleted file mode 100644 index 736be5da6c..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BeaconBlockEntity.java.patch +++ /dev/null @@ -1,259 +0,0 @@ ---- a/net/minecraft/world/level/block/entity/BeaconBlockEntity.java -+++ b/net/minecraft/world/level/block/entity/BeaconBlockEntity.java -@@ -1,5 +1,6 @@ - package net.minecraft.world.level.block.entity; - -+import com.destroystokyo.paper.event.block.BeaconEffectEvent; - import com.google.common.collect.ImmutableList; - import com.google.common.collect.Lists; - import java.util.Collection; -@@ -45,6 +46,11 @@ - import net.minecraft.world.level.block.state.BlockState; - import net.minecraft.world.level.levelgen.Heightmap; - import net.minecraft.world.phys.AABB; -+// CraftBukkit start -+import org.bukkit.craftbukkit.event.CraftEventFactory; -+import org.bukkit.craftbukkit.potion.CraftPotionUtil; -+import org.bukkit.potion.PotionEffect; -+// CraftBukkit end - - public class BeaconBlockEntity extends BlockEntity implements MenuProvider, Nameable { - -@@ -71,7 +77,36 @@ - public Component name; - public LockCode lockKey; - private final ContainerData dataAccess; -+ // CraftBukkit start - add fields and methods -+ public PotionEffect getPrimaryEffect() { -+ return (this.primaryPower != null) ? CraftPotionUtil.toBukkit(new MobEffectInstance(this.primaryPower, BeaconBlockEntity.getLevel(this.levels), BeaconBlockEntity.getAmplification(this.levels, this.primaryPower, this.secondaryPower), true, true)) : null; -+ } - -+ public PotionEffect getSecondaryEffect() { -+ return (BeaconBlockEntity.hasSecondaryEffect(this.levels, this.primaryPower, this.secondaryPower)) ? CraftPotionUtil.toBukkit(new MobEffectInstance(this.secondaryPower, BeaconBlockEntity.getLevel(this.levels), BeaconBlockEntity.getAmplification(this.levels, this.primaryPower, this.secondaryPower), true, true)) : null; -+ } -+ // CraftBukkit end -+ // Paper start - Custom beacon ranges -+ private final String PAPER_RANGE_TAG = "Paper.Range"; -+ private double effectRange = -1; -+ -+ public double getEffectRange() { -+ if (this.effectRange < 0) { -+ return this.levels * 10 + 10; -+ } else { -+ return effectRange; -+ } -+ } -+ -+ public void setEffectRange(double range) { -+ this.effectRange = range; -+ } -+ -+ public void resetEffectRange() { -+ this.effectRange = -1; -+ } -+ // Paper end - Custom beacon ranges -+ - @Nullable - static Holder filterEffect(@Nullable Holder effect) { - return BeaconBlockEntity.VALID_EFFECTS.contains(effect) ? effect : null; -@@ -186,10 +221,19 @@ - } - - if (blockEntity.levels > 0 && !blockEntity.beamSections.isEmpty()) { -- BeaconBlockEntity.applyEffects(world, pos, blockEntity.levels, blockEntity.primaryPower, blockEntity.secondaryPower); -+ BeaconBlockEntity.applyEffects(world, pos, blockEntity.levels, blockEntity.primaryPower, blockEntity.secondaryPower, blockEntity); // Paper - Custom beacon ranges - BeaconBlockEntity.playSound(world, pos, SoundEvents.BEACON_AMBIENT); - } - } -+ // Paper start - beacon activation/deactivation events -+ if (i1 <= 0 && blockEntity.levels > 0) { -+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(world, pos); -+ new io.papermc.paper.event.block.BeaconActivatedEvent(block).callEvent(); -+ } else if (i1 > 0 && blockEntity.levels <= 0) { -+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(world, pos); -+ new io.papermc.paper.event.block.BeaconDeactivatedEvent(block).callEvent(); -+ } -+ // Paper end - beacon activation/deactivation events - - if (blockEntity.lastCheckY >= l) { - blockEntity.lastCheckY = world.getMinY() - 1; -@@ -247,43 +291,123 @@ - - @Override - public void setRemoved() { -+ // Paper start - beacon activation/deactivation events -+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, worldPosition); -+ new io.papermc.paper.event.block.BeaconDeactivatedEvent(block).callEvent(); -+ // Paper end - beacon activation/deactivation events -+ // Paper start - fix MC-153086 -+ if (this.levels > 0 && !this.beamSections.isEmpty()) { - BeaconBlockEntity.playSound(this.level, this.worldPosition, SoundEvents.BEACON_DEACTIVATE); -+ } -+ // Paper end - super.setRemoved(); - } - -- private static void applyEffects(Level world, BlockPos pos, int beaconLevel, @Nullable Holder primaryEffect, @Nullable Holder secondaryEffect) { -- if (!world.isClientSide && primaryEffect != null) { -- double d0 = (double) (beaconLevel * 10 + 10); -+ // CraftBukkit start - split into components -+ private static byte getAmplification(int i, @Nullable Holder holder, @Nullable Holder holder1) { -+ { - byte b0 = 0; - -- if (beaconLevel >= 4 && Objects.equals(primaryEffect, secondaryEffect)) { -+ if (i >= 4 && Objects.equals(holder, holder1)) { - b0 = 1; - } - -- int j = (9 + beaconLevel * 2) * 20; -- AABB axisalignedbb = (new AABB(pos)).inflate(d0).expandTowards(0.0D, (double) world.getHeight(), 0.0D); -- List list = world.getEntitiesOfClass(Player.class, axisalignedbb); -+ return b0; -+ } -+ } -+ -+ private static int getLevel(int i) { -+ { -+ int j = (9 + i * 2) * 20; -+ return j; -+ } -+ } -+ -+ public static List getHumansInRange(Level world, BlockPos blockposition, int i) { -+ // Paper start - Custom beacon ranges -+ return BeaconBlockEntity.getHumansInRange(world, blockposition, i, null); -+ } -+ public static List getHumansInRange(Level world, BlockPos blockposition, int i, @Nullable BeaconBlockEntity blockEntity) { -+ // Paper end - Custom beacon ranges -+ { -+ double d0 = blockEntity != null ? blockEntity.getEffectRange() : (i * 10 + 10); // Paper - Custom beacon ranges -+ -+ AABB axisalignedbb = (new AABB(blockposition)).inflate(d0).expandTowards(0.0D, (double) world.getHeight(), 0.0D); -+ // Paper start - Perf: optimize player lookup for beacons -+ List list; -+ if (d0 <= 128.0) { -+ list = world.getEntitiesOfClass(Player.class, axisalignedbb); -+ } else { -+ list = new java.util.ArrayList<>(); -+ for (Player player : world.players()) { -+ if (player.isSpectator()) { -+ continue; -+ } -+ if (player.getBoundingBox().intersects(axisalignedbb)) { -+ list.add(player); -+ } -+ } -+ } -+ // Paper end - Perf: optimize player lookup for beacons -+ -+ return list; -+ } -+ } -+ -+ private static void applyEffect(List list, @Nullable Holder holder, int j, int b0, boolean isPrimary, BlockPos worldPosition) { // Paper - BeaconEffectEvent -+ if (!list.isEmpty()) { // Paper - BeaconEffectEvent - Iterator iterator = list.iterator(); - - Player entityhuman; -+ // Paper start - BeaconEffectEvent -+ org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(((Player) list.get(0)).level(), worldPosition); -+ PotionEffect effect = CraftPotionUtil.toBukkit(new MobEffectInstance(holder, j, b0, true, true)); -+ // Paper end - BeaconEffectEvent - - while (iterator.hasNext()) { -- entityhuman = (Player) iterator.next(); -- entityhuman.addEffect(new MobEffectInstance(primaryEffect, j, b0, true, true)); -+ // Paper start - BeaconEffectEvent -+ entityhuman = (ServerPlayer) iterator.next(); -+ BeaconEffectEvent event = new BeaconEffectEvent(block, effect, (org.bukkit.entity.Player) entityhuman.getBukkitEntity(), isPrimary); -+ if (CraftEventFactory.callEvent(event).isCancelled()) continue; -+ entityhuman.addEffect(new MobEffectInstance(CraftPotionUtil.fromBukkit(event.getEffect())), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.BEACON); -+ // Paper end - BeaconEffectEvent - } -+ } -+ } - -- if (beaconLevel >= 4 && !Objects.equals(primaryEffect, secondaryEffect) && secondaryEffect != null) { -- iterator = list.iterator(); -- -- while (iterator.hasNext()) { -- entityhuman = (Player) iterator.next(); -- entityhuman.addEffect(new MobEffectInstance(secondaryEffect, j, 0, true, true)); -- } -+ private static boolean hasSecondaryEffect(int i, @Nullable Holder holder, @Nullable Holder holder1) { -+ { -+ if (i >= 4 && !Objects.equals(holder, holder1) && holder1 != null) { -+ return true; - } - -+ return false; - } - } - -+ private static void applyEffects(Level world, BlockPos pos, int beaconLevel, @Nullable Holder primaryEffect, @Nullable Holder secondaryEffect) { -+ // Paper start - Custom beacon ranges -+ BeaconBlockEntity.applyEffects(world, pos, beaconLevel, primaryEffect, secondaryEffect, null); -+ } -+ private static void applyEffects(Level world, BlockPos pos, int beaconLevel, @Nullable Holder primaryEffect, @Nullable Holder secondaryEffect, @Nullable BeaconBlockEntity blockEntity) { -+ // Paper end - Custom beacon ranges -+ if (!world.isClientSide && primaryEffect != null) { -+ double d0 = (double) (beaconLevel * 10 + 10); -+ byte b0 = BeaconBlockEntity.getAmplification(beaconLevel, primaryEffect, secondaryEffect); -+ -+ int j = BeaconBlockEntity.getLevel(beaconLevel); -+ List list = BeaconBlockEntity.getHumansInRange(world, pos, beaconLevel, blockEntity); // Paper - Custom beacon ranges -+ -+ BeaconBlockEntity.applyEffect(list, primaryEffect, j, b0, true, pos); // Paper - BeaconEffectEvent -+ -+ if (BeaconBlockEntity.hasSecondaryEffect(beaconLevel, primaryEffect, secondaryEffect)) { -+ BeaconBlockEntity.applyEffect(list, secondaryEffect, j, 0, false, pos); // Paper - BeaconEffectEvent -+ } -+ } -+ -+ } -+ // CraftBukkit end -+ - public static void playSound(Level world, BlockPos pos, SoundEvent sound) { - world.playSound((Player) null, pos, sound, SoundSource.BLOCKS, 1.0F, 1.0F); - } -@@ -316,7 +440,7 @@ - if (nbt.contains(key, 8)) { - ResourceLocation minecraftkey = ResourceLocation.tryParse(nbt.getString(key)); - -- return minecraftkey == null ? null : (Holder) BuiltInRegistries.MOB_EFFECT.get(minecraftkey).map(BeaconBlockEntity::filterEffect).orElse((Object) null); -+ return minecraftkey == null ? null : (Holder) BuiltInRegistries.MOB_EFFECT.get(minecraftkey).orElse(null); // CraftBukkit - persist manually set non-default beacon effects (SPIGOT-3598) - } else { - return null; - } -@@ -327,11 +451,13 @@ - super.loadAdditional(nbt, registries); - this.primaryPower = BeaconBlockEntity.loadEffect(nbt, "primary_effect"); - this.secondaryPower = BeaconBlockEntity.loadEffect(nbt, "secondary_effect"); -+ this.levels = nbt.getInt("Levels"); // CraftBukkit - SPIGOT-5053, use where available - if (nbt.contains("CustomName", 8)) { - this.name = parseCustomNameSafe(nbt.getString("CustomName"), registries); - } - - this.lockKey = LockCode.fromTag(nbt, registries); -+ this.effectRange = nbt.contains(PAPER_RANGE_TAG, 6) ? nbt.getDouble(PAPER_RANGE_TAG) : -1; // Paper - Custom beacon ranges - } - - @Override -@@ -345,6 +471,7 @@ - } - - this.lockKey.addToTag(nbt, registries); -+ nbt.putDouble(PAPER_RANGE_TAG, this.effectRange); // Paper - Custom beacon ranges - } - - public void setCustomName(@Nullable Component customName) { -@@ -360,7 +487,7 @@ - @Nullable - @Override - public AbstractContainerMenu createMenu(int syncId, Inventory playerInventory, Player player) { -- return BaseContainerBlockEntity.canUnlock(player, this.lockKey, this.getDisplayName()) ? new BeaconMenu(syncId, playerInventory, this.dataAccess, ContainerLevelAccess.create(this.level, this.getBlockPos())) : null; -+ return BaseContainerBlockEntity.canUnlock(player, this.lockKey, this.getDisplayName(), this) ? new BeaconMenu(syncId, playerInventory, this.dataAccess, ContainerLevelAccess.create(this.level, this.getBlockPos())) : null; // Paper - Add BlockLockCheckEvent - } - - @Override diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java.patch b/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java.patch deleted file mode 100644 index 8457946183..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java.patch +++ /dev/null @@ -1,306 +0,0 @@ ---- a/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java -+++ b/net/minecraft/world/level/block/entity/BeehiveBlockEntity.java -@@ -43,6 +43,10 @@ - import net.minecraft.world.level.gameevent.GameEvent; - import org.slf4j.Logger; - -+// CraftBukkit start -+import org.bukkit.event.entity.EntityRemoveEvent; -+// CraftBukkit end -+ - public class BeehiveBlockEntity extends BlockEntity { - - private static final Logger LOGGER = LogUtils.getLogger(); -@@ -56,6 +60,7 @@ - private List stored = Lists.newArrayList(); - @Nullable - public BlockPos savedFlowerPos; -+ public int maxBees = 3; // CraftBukkit - allow setting max amount of bees a hive can hold - - public BeehiveBlockEntity(BlockPos pos, BlockState state) { - super(BlockEntityType.BEEHIVE, pos, state); -@@ -95,7 +100,7 @@ - } - - public boolean isFull() { -- return this.stored.size() == 3; -+ return this.stored.size() == this.maxBees; // CraftBukkit - } - - public void emptyAllLivingFromHive(@Nullable Player player, BlockState state, BeehiveBlockEntity.BeeReleaseStatus beeState) { -@@ -112,7 +117,7 @@ - - if (player.position().distanceToSqr(entity.position()) <= 16.0D) { - if (!this.isSedated()) { -- entitybee.setTarget(player); -+ entitybee.setTarget(player, org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER, true); // CraftBukkit - } else { - entitybee.setStayOutOfHiveCountdown(400); - } -@@ -124,10 +129,16 @@ - } - - private List releaseAllOccupants(BlockState state, BeehiveBlockEntity.BeeReleaseStatus beeState) { -+ // CraftBukkit start - This allows us to bypass the night/rain/emergency check -+ return this.releaseBees(state, beeState, false); -+ } -+ -+ public List releaseBees(BlockState iblockdata, BeehiveBlockEntity.BeeReleaseStatus tileentitybeehive_releasestatus, boolean force) { - List list = Lists.newArrayList(); - - this.stored.removeIf((tileentitybeehive_hivebee) -> { -- return BeehiveBlockEntity.releaseOccupant(this.level, this.worldPosition, state, tileentitybeehive_hivebee.toOccupant(), list, beeState, this.savedFlowerPos); -+ return BeehiveBlockEntity.releaseOccupant(this.level, this.worldPosition, iblockdata, tileentitybeehive_hivebee.toOccupant(), list, tileentitybeehive_releasestatus, this.savedFlowerPos, force); -+ // CraftBukkit end - }); - if (!list.isEmpty()) { - super.setChanged(); -@@ -141,6 +152,11 @@ - return this.stored.size(); - } - -+ // Paper start - Add EntityBlockStorage clearEntities -+ public void clearBees() { -+ this.stored.clear(); -+ } -+ // Paper end - Add EntityBlockStorage clearEntities - public static int getHoneyLevel(BlockState state) { - return (Integer) state.getValue(BeehiveBlock.HONEY_LEVEL); - } -@@ -151,7 +167,17 @@ - } - - public void addOccupant(Bee entity) { -- if (this.stored.size() < 3) { -+ if (this.stored.size() < this.maxBees) { // CraftBukkit -+ // CraftBukkit start -+ if (this.level != null) { -+ org.bukkit.event.entity.EntityEnterBlockEvent event = new org.bukkit.event.entity.EntityEnterBlockEvent(entity.getBukkitEntity(), org.bukkit.craftbukkit.block.CraftBlock.at(this.level, this.getBlockPos())); -+ org.bukkit.Bukkit.getPluginManager().callEvent(event); -+ if (event.isCancelled()) { -+ entity.setStayOutOfHiveCountdown(400); -+ return; -+ } -+ } -+ // CraftBukkit end - entity.stopRiding(); - entity.ejectPassengers(); - entity.dropLeash(); -@@ -167,7 +193,7 @@ - this.level.gameEvent((Holder) GameEvent.BLOCK_CHANGE, blockposition, GameEvent.Context.of(entity, this.getBlockState())); - } - -- entity.discard(); -+ entity.discard(EntityRemoveEvent.Cause.ENTER_BLOCK); // CraftBukkit - add Bukkit remove cause - super.setChanged(); - } - } -@@ -177,32 +203,50 @@ - } - - private static boolean releaseOccupant(Level world, BlockPos pos, BlockState state, BeehiveBlockEntity.Occupant bee, @Nullable List entities, BeehiveBlockEntity.BeeReleaseStatus beeState, @Nullable BlockPos flowerPos) { -- if (Bee.isNightOrRaining(world) && beeState != BeehiveBlockEntity.BeeReleaseStatus.EMERGENCY) { -+ // CraftBukkit start - This allows us to bypass the night/rain/emergency check -+ return BeehiveBlockEntity.releaseOccupant(world, pos, state, bee, entities, beeState, flowerPos, false); -+ } -+ -+ private static boolean releaseOccupant(Level world, BlockPos blockposition, BlockState iblockdata, BeehiveBlockEntity.Occupant tileentitybeehive_c, @Nullable List list, BeehiveBlockEntity.BeeReleaseStatus tileentitybeehive_releasestatus, @Nullable BlockPos blockposition1, boolean force) { -+ if (!force && Bee.isNightOrRaining(world) && tileentitybeehive_releasestatus != BeehiveBlockEntity.BeeReleaseStatus.EMERGENCY) { -+ // CraftBukkit end - return false; - } else { -- Direction enumdirection = (Direction) state.getValue(BeehiveBlock.FACING); -- BlockPos blockposition2 = pos.relative(enumdirection); -+ Direction enumdirection = (Direction) iblockdata.getValue(BeehiveBlock.FACING); -+ BlockPos blockposition2 = blockposition.relative(enumdirection); - boolean flag = !world.getBlockState(blockposition2).getCollisionShape(world, blockposition2).isEmpty(); - -- if (flag && beeState != BeehiveBlockEntity.BeeReleaseStatus.EMERGENCY) { -+ if (flag && tileentitybeehive_releasestatus != BeehiveBlockEntity.BeeReleaseStatus.EMERGENCY) { - return false; - } else { -- Entity entity = bee.createEntity(world, pos); -+ Entity entity = tileentitybeehive_c.createEntity(world, blockposition); - - if (entity != null) { -+ // CraftBukkit start - if (entity instanceof Bee) { -+ float f = entity.getBbWidth(); -+ double d0 = flag ? 0.0D : 0.55D + (double) (f / 2.0F); -+ double d1 = (double) blockposition.getX() + 0.5D + d0 * (double) enumdirection.getStepX(); -+ double d2 = (double) blockposition.getY() + 0.5D - (double) (entity.getBbHeight() / 2.0F); -+ double d3 = (double) blockposition.getZ() + 0.5D + d0 * (double) enumdirection.getStepZ(); -+ -+ entity.moveTo(d1, d2, d3, entity.getYRot(), entity.getXRot()); -+ } -+ if (!world.addFreshEntity(entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BEEHIVE)) return false; // CraftBukkit - SpawnReason, moved from below -+ // CraftBukkit end -+ if (entity instanceof Bee) { - Bee entitybee = (Bee) entity; - -- if (flowerPos != null && !entitybee.hasSavedFlowerPos() && world.random.nextFloat() < 0.9F) { -- entitybee.setSavedFlowerPos(flowerPos); -+ if (blockposition1 != null && !entitybee.hasSavedFlowerPos() && world.random.nextFloat() < 0.9F) { -+ entitybee.setSavedFlowerPos(blockposition1); - } - -- if (beeState == BeehiveBlockEntity.BeeReleaseStatus.HONEY_DELIVERED) { -+ if (tileentitybeehive_releasestatus == BeehiveBlockEntity.BeeReleaseStatus.HONEY_DELIVERED) { - entitybee.dropOffNectar(); -- if (state.is(BlockTags.BEEHIVES, (blockbase_blockdata) -> { -+ if (iblockdata.is(BlockTags.BEEHIVES, (blockbase_blockdata) -> { - return blockbase_blockdata.hasProperty(BeehiveBlock.HONEY_LEVEL); - })) { -- int i = BeehiveBlockEntity.getHoneyLevel(state); -+ int i = BeehiveBlockEntity.getHoneyLevel(iblockdata); - - if (i < 5) { - int j = world.random.nextInt(100) == 0 ? 2 : 1; -@@ -211,27 +255,35 @@ - --j; - } - -- world.setBlockAndUpdate(pos, (BlockState) state.setValue(BeehiveBlock.HONEY_LEVEL, i + j)); -+ // Paper start - Fire EntityChangeBlockEvent in more places -+ BlockState newBlockState = iblockdata.setValue(BeehiveBlock.HONEY_LEVEL, i + j); -+ -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(entitybee, blockposition, newBlockState)) { -+ world.setBlockAndUpdate(blockposition, newBlockState); -+ } -+ // Paper end - Fire EntityChangeBlockEvent in more places - } - } - } - -- if (entities != null) { -- entities.add(entitybee); -+ if (list != null) { -+ list.add(entitybee); - } - -+ /* // CraftBukkit start - float f = entity.getBbWidth(); - double d0 = flag ? 0.0D : 0.55D + (double) (f / 2.0F); -- double d1 = (double) pos.getX() + 0.5D + d0 * (double) enumdirection.getStepX(); -- double d2 = (double) pos.getY() + 0.5D - (double) (entity.getBbHeight() / 2.0F); -- double d3 = (double) pos.getZ() + 0.5D + d0 * (double) enumdirection.getStepZ(); -+ double d1 = (double) blockposition.getX() + 0.5D + d0 * (double) enumdirection.getStepX(); -+ double d2 = (double) blockposition.getY() + 0.5D - (double) (entity.getBbHeight() / 2.0F); -+ double d3 = (double) blockposition.getZ() + 0.5D + d0 * (double) enumdirection.getStepZ(); - - entity.moveTo(d1, d2, d3, entity.getYRot(), entity.getXRot()); -+ */ // CraftBukkit end - } - -- world.playSound((Player) null, pos, SoundEvents.BEEHIVE_EXIT, SoundSource.BLOCKS, 1.0F, 1.0F); -- world.gameEvent((Holder) GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(entity, world.getBlockState(pos))); -- return world.addFreshEntity(entity); -+ world.playSound((Player) null, blockposition, SoundEvents.BEEHIVE_EXIT, SoundSource.BLOCKS, 1.0F, 1.0F); -+ world.gameEvent((Holder) GameEvent.BLOCK_CHANGE, blockposition, GameEvent.Context.of(entity, world.getBlockState(blockposition))); -+ return true; // return this.world.addFreshEntity(entity); // CraftBukkit - moved up - } else { - return false; - } -@@ -256,6 +308,10 @@ - if (BeehiveBlockEntity.releaseOccupant(world, pos, state, tileentitybeehive_hivebee.toOccupant(), (List) null, tileentitybeehive_releasestatus, flowerPos)) { - flag = true; - iterator.remove(); -+ // CraftBukkit start -+ } else { -+ tileentitybeehive_hivebee.exitTickCounter = tileentitybeehive_hivebee.occupant.minTicksInHive / 2; // Not strictly Vanilla behaviour in cases where bees cannot spawn but still reasonable // Paper - Fix bees aging inside hives; use exitTickCounter to keep actual bee life -+ // CraftBukkit end - } - } - } -@@ -282,7 +338,7 @@ - @Override - protected void loadAdditional(CompoundTag nbt, HolderLookup.Provider registries) { - super.loadAdditional(nbt, registries); -- this.stored.clear(); -+ this.stored = Lists.newArrayList(); // CraftBukkit - SPIGOT-7790: create new copy (may be modified in physics event triggered by honey change) - if (nbt.contains("bees")) { - BeehiveBlockEntity.Occupant.LIST_CODEC.parse(NbtOps.INSTANCE, nbt.get("bees")).resultOrPartial((s) -> { - BeehiveBlockEntity.LOGGER.error("Failed to parse bees: '{}'", s); -@@ -291,7 +347,12 @@ - }); - } - -- this.savedFlowerPos = (BlockPos) NbtUtils.readBlockPos(nbt, "flower_pos").orElse((Object) null); -+ this.savedFlowerPos = (BlockPos) NbtUtils.readBlockPos(nbt, "flower_pos").orElse(null); // CraftBukkit - decompile error -+ // CraftBukkit start -+ if (nbt.contains("Bukkit.MaxEntities")) { -+ this.maxBees = nbt.getInt("Bukkit.MaxEntities"); -+ } -+ // CraftBukkit end - } - - @Override -@@ -301,13 +362,14 @@ - if (this.hasSavedFlowerPos()) { - nbt.put("flower_pos", NbtUtils.writeBlockPos(this.savedFlowerPos)); - } -+ nbt.putInt("Bukkit.MaxEntities", this.maxBees); // CraftBukkit - - } - - @Override - protected void applyImplicitComponents(BlockEntity.DataComponentInput components) { - super.applyImplicitComponents(components); -- this.stored.clear(); -+ this.stored = Lists.newArrayList(); // CraftBukkit - SPIGOT-7790: create new copy (may be modified in physics event triggered by honey change) - List list = (List) components.getOrDefault(DataComponents.BEES, List.of()); - - list.forEach(this::storeBee); -@@ -348,7 +410,7 @@ - CompoundTag nbttagcompound = new CompoundTag(); - - entity.save(nbttagcompound); -- List list = BeehiveBlockEntity.IGNORED_BEE_TAGS; -+ List list = BeehiveBlockEntity.IGNORED_BEE_TAGS; // CraftBukkit - decompile error - - Objects.requireNonNull(nbttagcompound); - list.forEach(nbttagcompound::remove); -@@ -367,7 +429,7 @@ - @Nullable - public Entity createEntity(Level world, BlockPos pos) { - CompoundTag nbttagcompound = this.entityData.copyTag(); -- List list = BeehiveBlockEntity.IGNORED_BEE_TAGS; -+ List list = BeehiveBlockEntity.IGNORED_BEE_TAGS; // CraftBukkit - decompile error - - Objects.requireNonNull(nbttagcompound); - list.forEach(nbttagcompound::remove); -@@ -391,6 +453,7 @@ - } - - private static void setBeeReleaseData(int ticksInHive, Bee beeEntity) { -+ if (!beeEntity.ageLocked) { // Paper - Honor ageLock - int j = beeEntity.getAge(); - - if (j < 0) { -@@ -400,21 +463,25 @@ - } - - beeEntity.setInLoveTime(Math.max(0, beeEntity.getInLoveTime() - ticksInHive)); -+ } // Paper - Honor ageLock - } - } - - private static class BeeData { - - private final BeehiveBlockEntity.Occupant occupant; -+ private int exitTickCounter; // Paper - Fix bees aging inside hives; separate counter for checking if bee should exit to reduce exit attempts - private int ticksInHive; - - BeeData(BeehiveBlockEntity.Occupant data) { - this.occupant = data; - this.ticksInHive = data.ticksInHive(); -+ this.exitTickCounter = this.ticksInHive; // Paper - Fix bees aging inside hives - } - - public boolean tick() { -- return this.ticksInHive++ > this.occupant.minTicksInHive; -+ this.ticksInHive++; // Paper - Fix bees aging inside hives -+ return this.exitTickCounter++ > this.occupant.minTicksInHive; // Paper - Fix bees aging inside hives - } - - public BeehiveBlockEntity.Occupant toOccupant() { diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BlockEntity.java.patch b/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BlockEntity.java.patch deleted file mode 100644 index aca157f8aa..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BlockEntity.java.patch +++ /dev/null @@ -1,153 +0,0 @@ ---- a/net/minecraft/world/level/block/entity/BlockEntity.java -+++ b/net/minecraft/world/level/block/entity/BlockEntity.java -@@ -26,8 +26,18 @@ - import net.minecraft.world.level.block.state.BlockState; - import org.slf4j.Logger; - -+// CraftBukkit start -+import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer; -+import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry; -+import org.bukkit.inventory.InventoryHolder; -+// CraftBukkit end -+ - public abstract class BlockEntity { - -+ // CraftBukkit start - data containers -+ private static final CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new CraftPersistentDataTypeRegistry(); -+ public CraftPersistentDataContainer persistentDataContainer; -+ // CraftBukkit end - private static final Logger LOGGER = LogUtils.getLogger(); - private final BlockEntityType type; - @Nullable -@@ -43,6 +53,7 @@ - this.worldPosition = pos.immutable(); - this.validateBlockState(state); - this.blockState = state; -+ this.persistentDataContainer = new CraftPersistentDataContainer(DATA_TYPE_REGISTRY); // Paper - always init - } - - private void validateBlockState(BlockState state) { -@@ -74,7 +85,16 @@ - return this.level != null; - } - -- protected void loadAdditional(CompoundTag nbt, HolderLookup.Provider registries) {} -+ // CraftBukkit start - read container -+ protected void loadAdditional(CompoundTag nbt, HolderLookup.Provider registries) { -+ this.persistentDataContainer.clear(); // Paper - clear instead of init -+ -+ net.minecraft.nbt.Tag persistentDataTag = nbt.get("PublicBukkitValues"); -+ if (persistentDataTag instanceof CompoundTag) { -+ this.persistentDataContainer.putAll((CompoundTag) persistentDataTag); -+ } -+ } -+ // CraftBukkit end - - public final void loadWithComponents(CompoundTag nbt, HolderLookup.Provider registries) { - this.loadAdditional(nbt, registries); -@@ -114,6 +134,11 @@ - }).ifPresent((nbtbase) -> { - nbttagcompound.merge((CompoundTag) nbtbase); - }); -+ // CraftBukkit start - store container -+ if (this.persistentDataContainer != null && !this.persistentDataContainer.isEmpty()) { -+ nbttagcompound.put("PublicBukkitValues", this.persistentDataContainer.toTagCompound()); -+ } -+ // CraftBukkit end - return nbttagcompound; - } - -@@ -121,6 +146,11 @@ - CompoundTag nbttagcompound = new CompoundTag(); - - this.saveAdditional(nbttagcompound, registries); -+ // Paper start - store PDC here as well -+ if (this.persistentDataContainer != null && !this.persistentDataContainer.isEmpty()) { -+ nbttagcompound.put("PublicBukkitValues", this.persistentDataContainer.toTagCompound()); -+ } -+ // Paper end - return nbttagcompound; - } - -@@ -234,7 +264,12 @@ - public void fillCrashReportCategory(CrashReportCategory crashReportSection) { - crashReportSection.setDetail("Name", this::getNameForReporting); - if (this.level != null) { -- CrashReportCategory.populateBlockDetails(crashReportSection, this.level, this.worldPosition, this.getBlockState()); -+ // Paper start - Prevent block entity and entity crashes -+ BlockState block = this.getBlockState(); -+ if (block != null) { -+ CrashReportCategory.populateBlockDetails(crashReportSection, this.level, this.worldPosition, block); -+ } -+ // Paper end - Prevent block entity and entity crashes - CrashReportCategory.populateBlockDetails(crashReportSection, this.level, this.worldPosition, this.level.getBlockState(this.worldPosition)); - } - } -@@ -263,13 +298,19 @@ - } - - public final void applyComponents(DataComponentMap defaultComponents, DataComponentPatch components) { -+ // CraftBukkit start -+ this.applyComponentsSet(defaultComponents, components); -+ } -+ -+ public final Set> applyComponentsSet(DataComponentMap datacomponentmap, DataComponentPatch datacomponentpatch) { -+ // CraftBukkit end - final Set> set = new HashSet(); - - set.add(DataComponents.BLOCK_ENTITY_DATA); - set.add(DataComponents.BLOCK_STATE); -- final PatchedDataComponentMap patcheddatacomponentmap = PatchedDataComponentMap.fromPatch(defaultComponents, components); -+ final PatchedDataComponentMap patcheddatacomponentmap = PatchedDataComponentMap.fromPatch(datacomponentmap, datacomponentpatch); - -- this.applyImplicitComponents(new BlockEntity.DataComponentInput(this) { -+ this.applyImplicitComponents(new BlockEntity.DataComponentInput() { // CraftBukkit - decompile error - @Nullable - @Override - public T get(DataComponentType type) { -@@ -284,9 +325,13 @@ - } - }); - Objects.requireNonNull(set); -- DataComponentPatch datacomponentpatch1 = components.forget(set::contains); -+ DataComponentPatch datacomponentpatch1 = datacomponentpatch.forget(set::contains); - - this.components = datacomponentpatch1.split().added(); -+ // CraftBukkit start -+ set.remove(DataComponents.BLOCK_ENTITY_DATA); // Remove as never actually added by applyImplicitComponents -+ return set; -+ // CraftBukkit end - } - - protected void collectImplicitComponents(DataComponentMap.Builder builder) {} -@@ -321,6 +366,30 @@ - } - } - -+ // CraftBukkit start - add method -+ public InventoryHolder getOwner() { -+ // Paper start -+ return getOwner(true); -+ } -+ public InventoryHolder getOwner(boolean useSnapshot) { -+ // Paper end -+ if (this.level == null) return null; -+ org.bukkit.block.Block block = this.level.getWorld().getBlockAt(this.worldPosition.getX(), this.worldPosition.getY(), this.worldPosition.getZ()); -+ // if (block.getType() == org.bukkit.Material.AIR) return null; // Paper - actually get the tile entity if it still exists -+ org.bukkit.block.BlockState state = block.getState(useSnapshot); // Paper -+ if (state instanceof InventoryHolder) return (InventoryHolder) state; -+ return null; -+ } -+ // CraftBukkit end -+ -+ // Paper start - Sanitize sent data -+ public CompoundTag sanitizeSentNbt(CompoundTag tag) { -+ tag.remove("PublicBukkitValues"); -+ -+ return tag; -+ } -+ // Paper end - Sanitize sent data -+ - private static class ComponentHelper { - - public static final Codec COMPONENTS_CODEC = DataComponentMap.CODEC.optionalFieldOf("components", DataComponentMap.EMPTY).codec(); diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BlockEntityType.java.patch b/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BlockEntityType.java.patch deleted file mode 100644 index c2e5be1463..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BlockEntityType.java.patch +++ /dev/null @@ -1,20 +0,0 @@ ---- a/net/minecraft/world/level/block/entity/BlockEntityType.java -+++ b/net/minecraft/world/level/block/entity/BlockEntityType.java -@@ -66,7 +66,7 @@ - public static final BlockEntityType CRAFTER = BlockEntityType.register("crafter", CrafterBlockEntity::new, Blocks.CRAFTER); - public static final BlockEntityType TRIAL_SPAWNER = BlockEntityType.register("trial_spawner", TrialSpawnerBlockEntity::new, Blocks.TRIAL_SPAWNER); - public static final BlockEntityType VAULT = BlockEntityType.register("vault", VaultBlockEntity::new, Blocks.VAULT); -- private static final Set> OP_ONLY_CUSTOM_DATA = Set.of(BlockEntityType.COMMAND_BLOCK, BlockEntityType.LECTERN, BlockEntityType.SIGN, BlockEntityType.HANGING_SIGN, BlockEntityType.MOB_SPAWNER, BlockEntityType.TRIAL_SPAWNER); -+ private static final Set> OP_ONLY_CUSTOM_DATA = Set.of(BlockEntityType.COMMAND_BLOCK, BlockEntityType.LECTERN, BlockEntityType.SIGN, BlockEntityType.HANGING_SIGN, BlockEntityType.MOB_SPAWNER, BlockEntityType.TRIAL_SPAWNER); // CraftBukkit // Paper - Allow chests to be placed with NBT data - private final BlockEntityType.BlockEntitySupplier factory; - public final Set validBlocks; - private final Holder.Reference> builtInRegistryHolder; -@@ -110,7 +110,7 @@ - public T getBlockEntity(BlockGetter world, BlockPos pos) { - BlockEntity tileentity = world.getBlockEntity(pos); - -- return tileentity != null && tileentity.getType() == this ? tileentity : null; -+ return tileentity != null && tileentity.getType() == this ? (T) tileentity : null; // CraftBukkit - decompile error - } - - public boolean onlyOpCanSetNbt() { diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java.patch b/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java.patch deleted file mode 100644 index 39dec9ecc2..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java.patch +++ /dev/null @@ -1,232 +0,0 @@ ---- a/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java -+++ b/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java -@@ -8,7 +8,6 @@ - import net.minecraft.core.NonNullList; - import net.minecraft.nbt.CompoundTag; - import net.minecraft.network.chat.Component; --import net.minecraft.tags.ItemTags; - import net.minecraft.world.ContainerHelper; - import net.minecraft.world.Containers; - import net.minecraft.world.WorldlyContainer; -@@ -23,6 +22,20 @@ - import net.minecraft.world.level.Level; - import net.minecraft.world.level.block.BrewingStandBlock; - import net.minecraft.world.level.block.state.BlockState; -+// CraftBukkit start -+import java.util.ArrayList; -+import java.util.List; -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.tags.ItemTags; -+import org.bukkit.craftbukkit.block.CraftBlock; -+import org.bukkit.craftbukkit.entity.CraftHumanEntity; -+import org.bukkit.craftbukkit.inventory.CraftItemStack; -+import org.bukkit.entity.HumanEntity; -+import org.bukkit.event.block.BrewingStartEvent; -+import org.bukkit.event.inventory.BrewEvent; -+import org.bukkit.event.inventory.BrewingStandFuelEvent; -+import org.bukkit.inventory.InventoryHolder; -+// CraftBukkit end - - public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements WorldlyContainer { - -@@ -37,11 +50,42 @@ - public static final int NUM_DATA_VALUES = 2; - private NonNullList items; - public int brewTime; -+ public int recipeBrewTime = 400; // Paper - Add recipeBrewTime - private boolean[] lastPotionCount; - private Item ingredient; - public int fuel; - protected final ContainerData dataAccess; -+ // CraftBukkit start - add fields and methods -+ // private int lastTick = MinecraftServer.currentTick; // Paper - remove anti tick skipping measures / wall time -+ public List transaction = new java.util.ArrayList(); -+ private int maxStack = MAX_STACK; - -+ public void onOpen(CraftHumanEntity who) { -+ this.transaction.add(who); -+ } -+ -+ public void onClose(CraftHumanEntity who) { -+ this.transaction.remove(who); -+ } -+ -+ public List getViewers() { -+ return this.transaction; -+ } -+ -+ public List getContents() { -+ return this.items; -+ } -+ -+ @Override -+ public int getMaxStackSize() { -+ return this.maxStack; -+ } -+ -+ public void setMaxStackSize(int size) { -+ this.maxStack = size; -+ } -+ // CraftBukkit end -+ - public BrewingStandBlockEntity(BlockPos pos, BlockState state) { - super(BlockEntityType.BREWING_STAND, pos, state); - this.items = NonNullList.withSize(5, ItemStack.EMPTY); -@@ -57,6 +101,11 @@ - 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; - } -@@ -72,13 +121,18 @@ - 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 - } - }; - } -@@ -107,8 +161,19 @@ - ItemStack itemstack = (ItemStack) blockEntity.items.get(4); - - if (blockEntity.fuel <= 0 && itemstack.is(ItemTags.BREWING_FUEL)) { -- blockEntity.fuel = 20; -- itemstack.shrink(1); -+ // CraftBukkit start -+ BrewingStandFuelEvent event = new BrewingStandFuelEvent(CraftBlock.at(world, pos), CraftItemStack.asCraftMirror(itemstack), 20); -+ world.getCraftServer().getPluginManager().callEvent(event); -+ -+ if (event.isCancelled()) { -+ return; -+ } -+ -+ blockEntity.fuel = event.getFuelPower(); -+ if (blockEntity.fuel > 0 && event.isConsuming()) { -+ itemstack.shrink(1); -+ } -+ // CraftBukkit end - setChanged(world, pos, state); - } - -@@ -116,12 +181,15 @@ - boolean flag1 = blockEntity.brewTime > 0; - ItemStack itemstack1 = (ItemStack) blockEntity.items.get(3); - -+ // Paper - remove anti tick skipping measures / wall time -+ - if (flag1) { -- --blockEntity.brewTime; -- boolean flag2 = blockEntity.brewTime == 0; -+ --blockEntity.brewTime; // Paper - remove anti tick skipping measures / wall time - revert to vanilla -+ boolean flag2 = blockEntity.brewTime <= 0; // == -> <= -+ // CraftBukkit end - - if (flag2 && flag) { -- BrewingStandBlockEntity.doBrew(world, pos, blockEntity.items); -+ BrewingStandBlockEntity.doBrew(world, pos, blockEntity.items, blockEntity); // CraftBukkit - } else if (!flag || !itemstack1.is(blockEntity.ingredient)) { - blockEntity.brewTime = 0; - } -@@ -129,7 +197,12 @@ - setChanged(world, pos, state); - } else if (flag && blockEntity.fuel > 0) { - --blockEntity.fuel; -- blockEntity.brewTime = 400; -+ // CraftBukkit start -+ BrewingStartEvent event = new BrewingStartEvent(CraftBlock.at(world, pos), CraftItemStack.asCraftMirror(itemstack1), 400); -+ world.getCraftServer().getPluginManager().callEvent(event); -+ 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); - } -@@ -185,14 +258,36 @@ - } - } - -- private static void doBrew(Level world, BlockPos pos, NonNullList slots) { -- ItemStack itemstack = (ItemStack) slots.get(3); -+ private static void doBrew(Level world, BlockPos blockposition, NonNullList nonnulllist, BrewingStandBlockEntity tileentitybrewingstand) { // CraftBukkit -+ ItemStack itemstack = (ItemStack) nonnulllist.get(3); - PotionBrewing potionbrewer = world.potionBrewing(); - -+ // CraftBukkit start -+ InventoryHolder owner = tileentitybrewingstand.getOwner(); -+ List brewResults = new ArrayList<>(3); - for (int i = 0; i < 3; ++i) { -- slots.set(i, potionbrewer.mix(itemstack, (ItemStack) slots.get(i))); -+ brewResults.add(i, CraftItemStack.asCraftMirror(potionbrewer.mix(itemstack, (ItemStack) nonnulllist.get(i)))); - } - -+ if (owner != null) { -+ BrewEvent event = new BrewEvent(CraftBlock.at(world, blockposition), (org.bukkit.inventory.BrewerInventory) owner.getInventory(), brewResults, tileentitybrewingstand.fuel); -+ org.bukkit.Bukkit.getPluginManager().callEvent(event); -+ if (event.isCancelled()) { -+ return; -+ } -+ } -+ // CraftBukkit end -+ -+ for (int i = 0; i < 3; ++i) { -+ // CraftBukkit start - validate index in case it is cleared by plugins -+ if (i < brewResults.size()) { -+ nonnulllist.set(i, CraftItemStack.asNMSCopy(brewResults.get(i))); -+ } else { -+ nonnulllist.set(i, ItemStack.EMPTY); -+ } -+ // CraftBukkit end -+ } -+ - itemstack.shrink(1); - ItemStack itemstack1 = itemstack.getItem().getCraftingRemainder(); - -@@ -200,12 +295,12 @@ - if (itemstack.isEmpty()) { - itemstack = itemstack1; - } else { -- Containers.dropItemStack(world, (double) pos.getX(), (double) pos.getY(), (double) pos.getZ(), itemstack1); -+ Containers.dropItemStack(world, (double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ(), itemstack1); - } - } - -- slots.set(3, itemstack); -- world.levelEvent(1035, pos, 0); -+ nonnulllist.set(3, itemstack); -+ world.levelEvent(1035, blockposition, 0); - } - - @Override -@@ -231,12 +326,12 @@ - - @Override - public boolean canPlaceItem(int slot, ItemStack stack) { -+ PotionBrewing potionbrewer = this.level != null ? this.level.potionBrewing() : PotionBrewing.EMPTY; // Paper - move up - if (slot == 3) { -- PotionBrewing potionbrewer = this.level != null ? this.level.potionBrewing() : PotionBrewing.EMPTY; - - return potionbrewer.isIngredient(stack); - } else { -- return slot == 4 ? stack.is(ItemTags.BREWING_FUEL) : (stack.is(Items.POTION) || stack.is(Items.SPLASH_POTION) || stack.is(Items.LINGERING_POTION) || stack.is(Items.GLASS_BOTTLE)) && this.getItem(slot).isEmpty(); -+ return slot == 4 ? stack.is(ItemTags.BREWING_FUEL) : (stack.is(Items.POTION) || stack.is(Items.SPLASH_POTION) || stack.is(Items.LINGERING_POTION) || stack.is(Items.GLASS_BOTTLE) || potionbrewer.isCustomInput(stack)) && this.getItem(slot).isEmpty(); // Paper - Custom Potion Mixes - } - } - diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BrushableBlockEntity.java.patch b/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BrushableBlockEntity.java.patch deleted file mode 100644 index 57515e3834..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/BrushableBlockEntity.java.patch +++ /dev/null @@ -1,36 +0,0 @@ ---- a/net/minecraft/world/level/block/entity/BrushableBlockEntity.java -+++ b/net/minecraft/world/level/block/entity/BrushableBlockEntity.java -@@ -31,6 +31,12 @@ - import net.minecraft.world.phys.Vec3; - import org.slf4j.Logger; - -+// CraftBukkit start -+import java.util.Arrays; -+import org.bukkit.craftbukkit.block.CraftBlock; -+import org.bukkit.craftbukkit.event.CraftEventFactory; -+// CraftBukkit end -+ - public class BrushableBlockEntity extends BlockEntity { - - private static final Logger LOGGER = LogUtils.getLogger(); -@@ -151,7 +157,10 @@ - ItemEntity entityitem = new ItemEntity(world, d3, d4, d5, this.item.split(world.random.nextInt(21) + 10)); - - entityitem.setDeltaMovement(Vec3.ZERO); -- world.addFreshEntity(entityitem); -+ // CraftBukkit start -+ org.bukkit.block.Block bblock = CraftBlock.at(this.level, this.worldPosition); -+ CraftEventFactory.handleBlockDropItemEvent(bblock, bblock.getState(), (ServerPlayer) player, Arrays.asList(entityitem)); -+ // CraftBukkit end - this.item = ItemStack.EMPTY; - } - -@@ -185,7 +194,7 @@ - - private boolean tryLoadLootTable(CompoundTag nbt) { - if (nbt.contains("LootTable", 8)) { -- this.lootTable = ResourceKey.create(Registries.LOOT_TABLE, ResourceLocation.parse(nbt.getString("LootTable"))); -+ this.lootTable = net.minecraft.Optionull.map(ResourceLocation.tryParse(nbt.getString("LootTable")), rl -> ResourceKey.create(Registries.LOOT_TABLE, rl)); // Paper - Validate ResourceLocation - this.lootTableSeed = nbt.getLong("LootTableSeed"); - return true; - } else { diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/CampfireBlockEntity.java.patch b/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/CampfireBlockEntity.java.patch deleted file mode 100644 index aea8cb9be4..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/CampfireBlockEntity.java.patch +++ /dev/null @@ -1,121 +0,0 @@ ---- a/net/minecraft/world/level/block/entity/CampfireBlockEntity.java -+++ b/net/minecraft/world/level/block/entity/CampfireBlockEntity.java -@@ -31,6 +31,14 @@ - import net.minecraft.world.level.block.state.BlockState; - import net.minecraft.world.level.gameevent.GameEvent; - -+// CraftBukkit start -+import org.bukkit.craftbukkit.block.CraftBlock; -+import org.bukkit.craftbukkit.inventory.CraftItemStack; -+import org.bukkit.event.block.BlockCookEvent; -+import org.bukkit.event.block.CampfireStartEvent; -+import org.bukkit.inventory.CampfireRecipe; -+// CraftBukkit end -+ - public class CampfireBlockEntity extends BlockEntity implements Clearable { - - private static final int BURN_COOL_SPEED = 2; -@@ -38,12 +46,14 @@ - private final NonNullList items; - public final int[] cookingProgress; - public final int[] cookingTime; -+ public final boolean[] stopCooking; // Paper - Add more Campfire API - - public CampfireBlockEntity(BlockPos pos, BlockState state) { - super(BlockEntityType.CAMPFIRE, pos, state); - this.items = NonNullList.withSize(4, ItemStack.EMPTY); - this.cookingProgress = new int[4]; - this.cookingTime = new int[4]; -+ this.stopCooking = new boolean[4]; // Paper - Add more Campfire API - } - - public static void cookTick(ServerLevel world, BlockPos pos, BlockState state, CampfireBlockEntity blockEntity, RecipeManager.CachedCheck recipeMatchGetter) { -@@ -54,16 +64,42 @@ - - if (!itemstack.isEmpty()) { - flag = true; -+ if (!blockEntity.stopCooking[i]) { // Paper - Add more Campfire API - int j = blockEntity.cookingProgress[i]++; -+ } // Paper - Add more Campfire API - - if (blockEntity.cookingProgress[i] >= blockEntity.cookingTime[i]) { - SingleRecipeInput singlerecipeinput = new SingleRecipeInput(itemstack); -- ItemStack itemstack1 = (ItemStack) recipeMatchGetter.getRecipeFor(singlerecipeinput, world).map((recipeholder) -> { -+ // Paper start - add recipe to cook events -+ final Optional> recipeHolderOptional = recipeMatchGetter.getRecipeFor(singlerecipeinput, world); -+ ItemStack itemstack1 = (ItemStack) recipeHolderOptional.map((recipeholder) -> { -+ // Paper end - add recipe to cook events - return ((CampfireCookingRecipe) recipeholder.value()).assemble(singlerecipeinput, world.registryAccess()); - }).orElse(itemstack); - - if (itemstack1.isItemEnabled(world.enabledFeatures())) { -- Containers.dropItemStack(world, (double) pos.getX(), (double) pos.getY(), (double) pos.getZ(), itemstack1); -+ // CraftBukkit start - fire BlockCookEvent -+ CraftItemStack source = CraftItemStack.asCraftMirror(itemstack); -+ org.bukkit.inventory.ItemStack result = CraftItemStack.asBukkitCopy(itemstack1); -+ -+ BlockCookEvent blockCookEvent = new BlockCookEvent(CraftBlock.at(world, pos), source, result, (org.bukkit.inventory.CookingRecipe) recipeHolderOptional.map(RecipeHolder::toBukkitRecipe).orElse(null)); // Paper - Add recipe to cook events -+ world.getCraftServer().getPluginManager().callEvent(blockCookEvent); -+ -+ if (blockCookEvent.isCancelled()) { -+ return; -+ } -+ -+ result = blockCookEvent.getResult(); -+ itemstack1 = CraftItemStack.asNMSCopy(result); -+ // CraftBukkit end -+ // Paper start - Fix item locations dropped from campfires -+ double deviation = 0.05F * RandomSource.GAUSSIAN_SPREAD_FACTOR; -+ while (!itemstack1.isEmpty()) { -+ net.minecraft.world.entity.item.ItemEntity droppedItem = new net.minecraft.world.entity.item.ItemEntity(world, pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, itemstack1.split(world.random.nextInt(21) + 10)); -+ droppedItem.setDeltaMovement(world.random.triangle(0.0D, deviation), world.random.triangle(0.2D, deviation), world.random.triangle(0.0D, deviation)); -+ world.addFreshEntity(droppedItem); -+ } -+ // Paper end - Fix item locations dropped from campfires - blockEntity.items.set(i, ItemStack.EMPTY); - world.sendBlockUpdated(pos, state, state, 3); - world.gameEvent((Holder) GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(state)); -@@ -143,6 +179,16 @@ - System.arraycopy(aint, 0, this.cookingTime, 0, Math.min(this.cookingTime.length, aint.length)); - } - -+ // Paper start - Add more Campfire API -+ if (nbt.contains("Paper.StopCooking", org.bukkit.craftbukkit.util.CraftMagicNumbers.NBT.TAG_BYTE_ARRAY)) { -+ byte[] abyte = nbt.getByteArray("Paper.StopCooking"); -+ boolean[] cookingState = new boolean[4]; -+ for (int index = 0; index < abyte.length; index++) { -+ cookingState[index] = abyte[index] == 1; -+ } -+ System.arraycopy(cookingState, 0, this.stopCooking, 0, Math.min(this.stopCooking.length, abyte.length)); -+ } -+ // Paper end - Add more Campfire API - } - - @Override -@@ -151,6 +197,13 @@ - ContainerHelper.saveAllItems(nbt, this.items, true, registries); - nbt.putIntArray("CookingTimes", this.cookingProgress); - nbt.putIntArray("CookingTotalTimes", this.cookingTime); -+ // Paper start - Add more Campfire API -+ byte[] cookingState = new byte[4]; -+ for (int index = 0; index < cookingState.length; index++) { -+ cookingState[index] = (byte) (this.stopCooking[index] ? 1 : 0); -+ } -+ nbt.putByteArray("Paper.StopCooking", cookingState); -+ // Paper end - Add more Campfire API - } - - @Override -@@ -177,7 +230,11 @@ - return false; - } - -- this.cookingTime[i] = ((CampfireCookingRecipe) ((RecipeHolder) optional.get()).value()).cookingTime(); -+ // CraftBukkit start -+ CampfireStartEvent event = new CampfireStartEvent(CraftBlock.at(this.level,this.worldPosition), CraftItemStack.asCraftMirror(stack), (CampfireRecipe) optional.get().toBukkitRecipe()); -+ this.level.getCraftServer().getPluginManager().callEvent(event); -+ this.cookingTime[i] = event.getTotalCookTime(); // i -> event.getTotalCookTime() -+ // CraftBukkit end - this.cookingProgress[i] = 0; - this.items.set(i, stack.consumeAndReturn(1, entity)); - world.gameEvent((Holder) GameEvent.BLOCK_CHANGE, this.getBlockPos(), GameEvent.Context.of(entity, this.getBlockState())); diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/ConduitBlockEntity.java.patch b/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/ConduitBlockEntity.java.patch deleted file mode 100644 index 6dd6c8627e..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/ConduitBlockEntity.java.patch +++ /dev/null @@ -1,108 +0,0 @@ ---- a/net/minecraft/world/level/block/entity/ConduitBlockEntity.java -+++ b/net/minecraft/world/level/block/entity/ConduitBlockEntity.java -@@ -10,6 +10,7 @@ - import net.minecraft.core.particles.ParticleTypes; - import net.minecraft.nbt.CompoundTag; - import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; -+import net.minecraft.server.level.ServerLevel; - import net.minecraft.sounds.SoundEvent; - import net.minecraft.sounds.SoundEvents; - import net.minecraft.sounds.SoundSource; -@@ -187,11 +188,23 @@ - } - - private static void applyEffects(Level world, BlockPos pos, List activatingBlocks) { -- int i = activatingBlocks.size(); -+ // CraftBukkit start -+ ConduitBlockEntity.applyEffects(world, pos, ConduitBlockEntity.getRange(activatingBlocks)); -+ } -+ -+ public static int getRange(List list) { -+ // CraftBukkit end -+ int i = list.size(); - int j = i / 7 * 16; -- int k = pos.getX(); -- int l = pos.getY(); -- int i1 = pos.getZ(); -+ // CraftBukkit start -+ return j; -+ } -+ -+ private static void applyEffects(Level world, BlockPos blockposition, int j) { // j = effect range in blocks -+ // CraftBukkit end -+ int k = blockposition.getX(); -+ int l = blockposition.getY(); -+ int i1 = blockposition.getZ(); - AABB axisalignedbb = (new AABB((double) k, (double) l, (double) i1, (double) (k + 1), (double) (l + 1), (double) (i1 + 1))).inflate((double) j).expandTowards(0.0D, (double) world.getHeight(), 0.0D); - List list1 = world.getEntitiesOfClass(Player.class, axisalignedbb); - -@@ -201,8 +214,8 @@ - while (iterator.hasNext()) { - Player entityhuman = (Player) iterator.next(); - -- if (pos.closerThan(entityhuman.blockPosition(), (double) j) && entityhuman.isInWaterOrRain()) { -- entityhuman.addEffect(new MobEffectInstance(MobEffects.CONDUIT_POWER, 260, 0, true, true)); -+ if (blockposition.closerThan(entityhuman.blockPosition(), (double) j) && entityhuman.isInWaterOrRain()) { -+ entityhuman.addEffect(new MobEffectInstance(MobEffects.CONDUIT_POWER, 260, 0, true, true), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.CONDUIT); // CraftBukkit - } - } - -@@ -210,33 +223,42 @@ - } - - private static void updateDestroyTarget(Level world, BlockPos pos, BlockState state, List activatingBlocks, ConduitBlockEntity blockEntity) { -- LivingEntity entityliving = blockEntity.destroyTarget; -- int i = activatingBlocks.size(); -+ // CraftBukkit start - add "damageTarget" boolean -+ ConduitBlockEntity.updateDestroyTarget(world, pos, state, activatingBlocks, blockEntity, true); -+ } - -+ public static void updateDestroyTarget(Level world, BlockPos blockposition, BlockState iblockdata, List list, ConduitBlockEntity tileentityconduit, boolean damageTarget) { -+ // CraftBukkit end -+ LivingEntity entityliving = tileentityconduit.destroyTarget; -+ int i = list.size(); -+ - if (i < 42) { -- blockEntity.destroyTarget = null; -- } else if (blockEntity.destroyTarget == null && blockEntity.destroyTargetUUID != null) { -- blockEntity.destroyTarget = ConduitBlockEntity.findDestroyTarget(world, pos, blockEntity.destroyTargetUUID); -- blockEntity.destroyTargetUUID = null; -- } else if (blockEntity.destroyTarget == null) { -- List list1 = world.getEntitiesOfClass(LivingEntity.class, ConduitBlockEntity.getDestroyRangeAABB(pos), (entityliving1) -> { -+ tileentityconduit.destroyTarget = null; -+ } else if (tileentityconduit.destroyTarget == null && tileentityconduit.destroyTargetUUID != null) { -+ tileentityconduit.destroyTarget = ConduitBlockEntity.findDestroyTarget(world, blockposition, tileentityconduit.destroyTargetUUID); -+ tileentityconduit.destroyTargetUUID = null; -+ } else if (tileentityconduit.destroyTarget == null) { -+ List list1 = world.getEntitiesOfClass(LivingEntity.class, ConduitBlockEntity.getDestroyRangeAABB(blockposition), (entityliving1) -> { - return entityliving1 instanceof Enemy && entityliving1.isInWaterOrRain(); - }); - - if (!list1.isEmpty()) { -- blockEntity.destroyTarget = (LivingEntity) list1.get(world.random.nextInt(list1.size())); -+ tileentityconduit.destroyTarget = (LivingEntity) list1.get(world.random.nextInt(list1.size())); - } -- } else if (!blockEntity.destroyTarget.isAlive() || !pos.closerThan(blockEntity.destroyTarget.blockPosition(), 8.0D)) { -- blockEntity.destroyTarget = null; -+ } else if (!tileentityconduit.destroyTarget.isAlive() || !blockposition.closerThan(tileentityconduit.destroyTarget.blockPosition(), 8.0D)) { -+ tileentityconduit.destroyTarget = null; - } - -- if (blockEntity.destroyTarget != null) { -- world.playSound((Player) null, blockEntity.destroyTarget.getX(), blockEntity.destroyTarget.getY(), blockEntity.destroyTarget.getZ(), SoundEvents.CONDUIT_ATTACK_TARGET, SoundSource.BLOCKS, 1.0F, 1.0F); -- blockEntity.destroyTarget.hurt(world.damageSources().magic(), 4.0F); -+ // CraftBukkit start -+ if (damageTarget && tileentityconduit.destroyTarget != null) { -+ if (tileentityconduit.destroyTarget.hurtServer((ServerLevel) world, world.damageSources().magic().directBlock(world, blockposition), 4.0F)) { -+ world.playSound(null, tileentityconduit.destroyTarget.getX(), tileentityconduit.destroyTarget.getY(), tileentityconduit.destroyTarget.getZ(), SoundEvents.CONDUIT_ATTACK_TARGET, SoundSource.BLOCKS, 1.0F, 1.0F); -+ } -+ // CraftBukkit end - } - -- if (entityliving != blockEntity.destroyTarget) { -- world.sendBlockUpdated(pos, state, state, 2); -+ if (entityliving != tileentityconduit.destroyTarget) { -+ world.sendBlockUpdated(blockposition, iblockdata, iblockdata, 2); - } - - } diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/CrafterBlockEntity.java.patch b/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/CrafterBlockEntity.java.patch deleted file mode 100644 index e71ac50ca3..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/CrafterBlockEntity.java.patch +++ /dev/null @@ -1,68 +0,0 @@ ---- a/net/minecraft/world/level/block/entity/CrafterBlockEntity.java -+++ b/net/minecraft/world/level/block/entity/CrafterBlockEntity.java -@@ -22,6 +22,11 @@ - import net.minecraft.world.level.Level; - import net.minecraft.world.level.block.CrafterBlock; - import net.minecraft.world.level.block.state.BlockState; -+// CraftBukkit start -+import org.bukkit.Location; -+import org.bukkit.craftbukkit.entity.CraftHumanEntity; -+import org.bukkit.entity.HumanEntity; -+// CraftBukkit end - - public class CrafterBlockEntity extends RandomizableContainerBlockEntity implements CraftingContainer { - -@@ -35,12 +40,52 @@ - private NonNullList items; - public int craftingTicksRemaining; - protected final ContainerData containerData; -+ // CraftBukkit start - add fields and methods -+ public List transaction = new java.util.ArrayList<>(); -+ private int maxStack = MAX_STACK; - -+ @Override -+ public List getContents() { -+ return this.items; -+ } -+ -+ @Override -+ public void onOpen(CraftHumanEntity who) { -+ this.transaction.add(who); -+ } -+ -+ @Override -+ public void onClose(CraftHumanEntity who) { -+ this.transaction.remove(who); -+ } -+ -+ @Override -+ public List getViewers() { -+ return this.transaction; -+ } -+ -+ @Override -+ public int getMaxStackSize() { -+ return this.maxStack; -+ } -+ -+ @Override -+ public void setMaxStackSize(int size) { -+ this.maxStack = size; -+ } -+ -+ @Override -+ public Location getLocation() { -+ if (this.level == null) return null; -+ return new org.bukkit.Location(this.level.getWorld(), this.worldPosition.getX(), this.worldPosition.getY(), this.worldPosition.getZ()); -+ } -+ // CraftBukkit end -+ - public CrafterBlockEntity(BlockPos pos, BlockState state) { - super(BlockEntityType.CRAFTER, pos, state); - this.items = NonNullList.withSize(9, ItemStack.EMPTY); - this.craftingTicksRemaining = 0; -- this.containerData = new ContainerData(this) { -+ this.containerData = new ContainerData() { // CraftBukkit - decompile error - private final int[] slotStates = new int[9]; - private int triggered = 0; - diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/DispenserBlockEntity.java.patch b/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/DispenserBlockEntity.java.patch deleted file mode 100644 index ae1688f953..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/DispenserBlockEntity.java.patch +++ /dev/null @@ -1,50 +0,0 @@ ---- a/net/minecraft/world/level/block/entity/DispenserBlockEntity.java -+++ b/net/minecraft/world/level/block/entity/DispenserBlockEntity.java -@@ -13,12 +13,47 @@ - import net.minecraft.world.inventory.DispenserMenu; - import net.minecraft.world.item.ItemStack; - import net.minecraft.world.level.block.state.BlockState; -+// CraftBukkit start -+import java.util.List; -+import org.bukkit.craftbukkit.entity.CraftHumanEntity; -+import org.bukkit.entity.HumanEntity; -+// CraftBukkit end - - public class DispenserBlockEntity extends RandomizableContainerBlockEntity { - - public static final int CONTAINER_SIZE = 9; - private NonNullList items; - -+ // CraftBukkit start - add fields and methods -+ public List transaction = new java.util.ArrayList(); -+ private int maxStack = MAX_STACK; -+ -+ public List getContents() { -+ return this.items; -+ } -+ -+ public void onOpen(CraftHumanEntity who) { -+ this.transaction.add(who); -+ } -+ -+ public void onClose(CraftHumanEntity who) { -+ this.transaction.remove(who); -+ } -+ -+ public List getViewers() { -+ return this.transaction; -+ } -+ -+ @Override -+ public int getMaxStackSize() { -+ return this.maxStack; -+ } -+ -+ public void setMaxStackSize(int size) { -+ this.maxStack = size; -+ } -+ // CraftBukkit end -+ - protected DispenserBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) { - super(type, pos, state); - this.items = NonNullList.withSize(9, ItemStack.EMPTY); diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/LecternBlockEntity.java.patch b/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/LecternBlockEntity.java.patch deleted file mode 100644 index afaf4c6781..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/LecternBlockEntity.java.patch +++ /dev/null @@ -1,164 +0,0 @@ ---- a/net/minecraft/world/level/block/entity/LecternBlockEntity.java -+++ b/net/minecraft/world/level/block/entity/LecternBlockEntity.java -@@ -28,6 +28,17 @@ - import net.minecraft.world.level.block.state.BlockState; - import net.minecraft.world.phys.Vec2; - import net.minecraft.world.phys.Vec3; -+// CraftBukkit start -+import java.util.ArrayList; -+import java.util.Arrays; -+import java.util.List; -+import org.bukkit.Location; -+import org.bukkit.block.Lectern; -+import org.bukkit.craftbukkit.entity.CraftHumanEntity; -+import org.bukkit.craftbukkit.util.CraftLocation; -+import org.bukkit.entity.HumanEntity; -+import org.bukkit.inventory.InventoryHolder; -+// CraftBukkit end - - public class LecternBlockEntity extends BlockEntity implements Clearable, MenuProvider { - -@@ -35,8 +46,55 @@ - public static final int NUM_DATA = 1; - public static final int SLOT_BOOK = 0; - public static final int NUM_SLOTS = 1; -- public final Container bookAccess = new Container() { -+ // CraftBukkit start - add fields and methods -+ public final Container bookAccess = new LecternInventory(); -+ public class LecternInventory implements Container { -+ -+ public List transaction = new ArrayList<>(); -+ private int maxStack = 1; -+ - @Override -+ public List getContents() { -+ return Arrays.asList(LecternBlockEntity.this.book); -+ } -+ -+ @Override -+ public void onOpen(CraftHumanEntity who) { -+ this.transaction.add(who); -+ } -+ -+ @Override -+ public void onClose(CraftHumanEntity who) { -+ this.transaction.remove(who); -+ } -+ -+ @Override -+ public List getViewers() { -+ return this.transaction; -+ } -+ -+ @Override -+ public void setMaxStackSize(int i) { -+ this.maxStack = i; -+ } -+ -+ @Override -+ public Location getLocation() { -+ if (LecternBlockEntity.this.level == null) return null; -+ return CraftLocation.toBukkit(LecternBlockEntity.this.worldPosition, LecternBlockEntity.this.level.getWorld()); -+ } -+ -+ @Override -+ public InventoryHolder getOwner() { -+ return (Lectern) LecternBlockEntity.this.getOwner(); -+ } -+ -+ public LecternBlockEntity getLectern() { -+ return LecternBlockEntity.this; -+ } -+ // CraftBukkit end -+ -+ @Override - public int getContainerSize() { - return 1; - } -@@ -80,11 +138,20 @@ - } - - @Override -- public void setItem(int slot, ItemStack stack) {} -+ // CraftBukkit start -+ public void setItem(int slot, ItemStack stack) { -+ if (slot == 0) { -+ LecternBlockEntity.this.setBook(stack); -+ if (LecternBlockEntity.this.getLevel() != null) { -+ LecternBlock.resetBookState(null, LecternBlockEntity.this.getLevel(), LecternBlockEntity.this.getBlockPos(), LecternBlockEntity.this.getBlockState(), LecternBlockEntity.this.hasBook()); -+ } -+ } -+ } -+ // CraftBukkit end - - @Override - public int getMaxStackSize() { -- return 1; -+ return this.maxStack; // CraftBukkit - } - - @Override -@@ -164,7 +231,7 @@ - if (j != this.page) { - this.page = j; - this.setChanged(); -- LecternBlock.signalPageChange(this.getLevel(), this.getBlockPos(), this.getBlockState()); -+ if (this.level != null) LecternBlock.signalPageChange(this.getLevel(), this.getBlockPos(), this.getBlockState()); // CraftBukkit - } - - } -@@ -189,6 +256,35 @@ - return book; - } - -+ // CraftBukkit start -+ private final CommandSource commandSource = new CommandSource() { -+ -+ @Override -+ public void sendSystemMessage(Component message) { -+ } -+ -+ @Override -+ public org.bukkit.command.CommandSender getBukkitSender(CommandSourceStack wrapper) { -+ return wrapper.getEntity() != null ? wrapper.getEntity().getBukkitEntity() : new org.bukkit.craftbukkit.command.CraftBlockCommandSender(wrapper, LecternBlockEntity.this); -+ } -+ -+ @Override -+ public boolean acceptsSuccess() { -+ return false; -+ } -+ -+ @Override -+ public boolean acceptsFailure() { -+ return false; -+ } -+ -+ @Override -+ public boolean shouldInformAdmins() { -+ return false; -+ } -+ }; -+ // CraftBukkit end -+ - private CommandSourceStack createCommandSourceStack(@Nullable Player player, ServerLevel world) { - String s; - Object object; -@@ -203,7 +299,8 @@ - - Vec3 vec3d = Vec3.atCenterOf(this.worldPosition); - -- return new CommandSourceStack(CommandSource.NULL, vec3d, Vec2.ZERO, world, 2, s, (Component) object, world.getServer(), player); -+ // CraftBukkit - commandSource -+ return new CommandSourceStack(this.commandSource, vec3d, Vec2.ZERO, world, 2, s, (Component) object, world.getServer(), player); - } - - @Override -@@ -236,7 +333,7 @@ - - @Override - public AbstractContainerMenu createMenu(int syncId, Inventory playerInventory, Player player) { -- return new LecternMenu(syncId, this.bookAccess, this.dataAccess); -+ return new LecternMenu(syncId, this.bookAccess, this.dataAccess, playerInventory); // CraftBukkit - } - - @Override diff --git a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/SignBlockEntity.java.patch b/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/SignBlockEntity.java.patch deleted file mode 100644 index 788235f327..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/world/level/block/entity/SignBlockEntity.java.patch +++ /dev/null @@ -1,269 +0,0 @@ ---- a/net/minecraft/world/level/block/entity/SignBlockEntity.java -+++ b/net/minecraft/world/level/block/entity/SignBlockEntity.java -@@ -22,12 +22,12 @@ - import net.minecraft.network.chat.Style; - import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; - import net.minecraft.server.level.ServerLevel; -+import net.minecraft.server.level.ServerPlayer; - import net.minecraft.server.network.FilteredText; - import net.minecraft.sounds.SoundEvent; - import net.minecraft.sounds.SoundEvents; - import net.minecraft.util.Mth; - import net.minecraft.world.entity.Entity; --import net.minecraft.world.entity.player.Player; - import net.minecraft.world.level.Level; - import net.minecraft.world.level.block.Block; - import net.minecraft.world.level.block.SignBlock; -@@ -35,6 +35,12 @@ - import net.minecraft.world.phys.Vec2; - import net.minecraft.world.phys.Vec3; - import org.slf4j.Logger; -+import org.bukkit.block.sign.Side; -+import org.bukkit.craftbukkit.block.CraftBlock; -+import org.bukkit.craftbukkit.util.CraftChatMessage; -+import org.bukkit.entity.Player; -+import org.bukkit.event.block.SignChangeEvent; -+// CraftBukkit end - - public class SignBlockEntity extends BlockEntity { - -@@ -61,13 +67,18 @@ - return new SignText(); - } - -- public boolean isFacingFrontText(Player player) { -+ public boolean isFacingFrontText(net.minecraft.world.entity.player.Player player) { -+ // Paper start - More Sign Block API -+ return this.isFacingFrontText(player.getX(), player.getZ()); -+ } -+ public boolean isFacingFrontText(double x, double z) { -+ // Paper end - More Sign Block API - Block block = this.getBlockState().getBlock(); - - if (block instanceof SignBlock blocksign) { - Vec3 vec3d = blocksign.getSignHitboxCenterPosition(this.getBlockState()); -- double d0 = player.getX() - ((double) this.getBlockPos().getX() + vec3d.x); -- double d1 = player.getZ() - ((double) this.getBlockPos().getZ() + vec3d.z); -+ double d0 = x - ((double) this.getBlockPos().getX() + vec3d.x); // Paper - More Sign Block API -+ double d1 = z - ((double) this.getBlockPos().getZ() + vec3d.z); // Paper - More Sign Block API - float f = blocksign.getYRotationDegrees(this.getBlockState()); - float f1 = (float) (Mth.atan2(d1, d0) * 57.2957763671875D) - 90.0F; - -@@ -101,7 +112,7 @@ - protected void saveAdditional(CompoundTag nbt, HolderLookup.Provider registries) { - super.saveAdditional(nbt, registries); - DynamicOps dynamicops = registries.createSerializationContext(NbtOps.INSTANCE); -- DataResult dataresult = SignText.DIRECT_CODEC.encodeStart(dynamicops, this.frontText); -+ DataResult dataresult = SignText.DIRECT_CODEC.encodeStart(dynamicops, this.frontText); // CraftBukkit - decompile error - Logger logger = SignBlockEntity.LOGGER; - - Objects.requireNonNull(logger); -@@ -121,7 +132,7 @@ - protected void loadAdditional(CompoundTag nbt, HolderLookup.Provider registries) { - super.loadAdditional(nbt, registries); - DynamicOps dynamicops = registries.createSerializationContext(NbtOps.INSTANCE); -- DataResult dataresult; -+ DataResult dataresult; // CraftBukkit - decompile error - Logger logger; - - if (nbt.contains("front_text")) { -@@ -161,7 +172,7 @@ - - if (world instanceof ServerLevel worldserver) { - try { -- return ComponentUtils.updateForEntity(SignBlockEntity.createCommandSourceStack((Player) null, worldserver, this.worldPosition), text, (Entity) null, 0); -+ return ComponentUtils.updateForEntity(this.createCommandSourceStack((net.minecraft.world.entity.player.Player) null, worldserver, this.worldPosition), text, (Entity) null, 0); - } catch (CommandSyntaxException commandsyntaxexception) { - ; - } -@@ -170,15 +181,17 @@ - return text; - } - -- public void updateSignText(Player player, boolean front, List messages) { -+ public void updateSignText(net.minecraft.world.entity.player.Player player, boolean front, List messages) { - if (!this.isWaxed() && player.getUUID().equals(this.getPlayerWhoMayEdit()) && this.level != null) { - this.updateText((signtext) -> { -- return this.setMessages(player, messages, signtext); -+ return this.setMessages(player, messages, signtext, front); // CraftBukkit - }, front); - this.setAllowedPlayerEditor((UUID) null); - this.level.sendBlockUpdated(this.getBlockPos(), this.getBlockState(), this.getBlockState(), 3); - } else { - SignBlockEntity.LOGGER.warn("Player {} just tried to change non-editable sign", player.getName().getString()); -+ if (player.distanceToSqr(this.getBlockPos().getX(), this.getBlockPos().getY(), this.getBlockPos().getZ()) < 32 * 32) // Paper - Dont send far away sign update -+ ((ServerPlayer) player).connection.send(this.getUpdatePacket()); // CraftBukkit - } - } - -@@ -188,19 +201,43 @@ - return this.setText((SignText) textChanger.apply(signtext), front); - } - -- private SignText setMessages(Player player, List messages, SignText text) { -- for (int i = 0; i < messages.size(); ++i) { -- FilteredText filteredtext = (FilteredText) messages.get(i); -- Style chatmodifier = text.getMessage(i, player.isTextFilteringEnabled()).getStyle(); -+ private SignText setMessages(net.minecraft.world.entity.player.Player entityhuman, List list, SignText signtext, boolean front) { // CraftBukkit -+ SignText originalText = signtext; // CraftBukkit -+ for (int i = 0; i < list.size(); ++i) { -+ FilteredText filteredtext = (FilteredText) list.get(i); -+ Style chatmodifier = signtext.getMessage(i, entityhuman.isTextFilteringEnabled()).getStyle(); - -- if (player.isTextFilteringEnabled()) { -- text = text.setMessage(i, Component.literal(filteredtext.filteredOrEmpty()).setStyle(chatmodifier)); -+ if (entityhuman.isTextFilteringEnabled()) { -+ signtext = signtext.setMessage(i, Component.literal(net.minecraft.util.StringUtil.filterText(filteredtext.filteredOrEmpty())).setStyle(chatmodifier)); // Paper - filter sign text to chat only - } else { -- text = text.setMessage(i, Component.literal(filteredtext.raw()).setStyle(chatmodifier), Component.literal(filteredtext.filteredOrEmpty()).setStyle(chatmodifier)); -+ signtext = signtext.setMessage(i, Component.literal(net.minecraft.util.StringUtil.filterText(filteredtext.raw())).setStyle(chatmodifier), Component.literal(net.minecraft.util.StringUtil.filterText(filteredtext.filteredOrEmpty())).setStyle(chatmodifier)); // Paper - filter sign text to chat only - } - } - -- return text; -+ // CraftBukkit start -+ Player player = ((ServerPlayer) entityhuman).getBukkitEntity(); -+ List lines = new java.util.ArrayList<>(); // Paper - adventure -+ -+ for (int i = 0; i < list.size(); ++i) { -+ lines.add(io.papermc.paper.adventure.PaperAdventure.asAdventure(signtext.getMessage(i, entityhuman.isTextFilteringEnabled()))); // Paper - Adventure -+ } -+ -+ SignChangeEvent event = new SignChangeEvent(CraftBlock.at(this.level, this.worldPosition), player, new java.util.ArrayList<>(lines), (front) ? Side.FRONT : Side.BACK); // Paper - Adventure -+ entityhuman.level().getCraftServer().getPluginManager().callEvent(event); -+ -+ if (event.isCancelled()) { -+ return originalText; -+ } -+ -+ Component[] components = org.bukkit.craftbukkit.block.CraftSign.sanitizeLines(event.lines()); // Paper - Adventure -+ for (int i = 0; i < components.length; i++) { -+ if (!Objects.equals(lines.get(i), event.line(i))) { // Paper - Adventure -+ signtext = signtext.setMessage(i, components[i]); -+ } -+ } -+ // CraftBukkit end -+ -+ return signtext; - } - - public boolean setText(SignText text, boolean front) { -@@ -227,11 +264,11 @@ - } - } - -- public boolean canExecuteClickCommands(boolean front, Player player) { -+ public boolean canExecuteClickCommands(boolean front, net.minecraft.world.entity.player.Player player) { - return this.isWaxed() && this.getText(front).hasAnyClickCommands(player); - } - -- public boolean executeClickCommandsIfPresent(Player player, Level world, BlockPos pos, boolean front) { -+ public boolean executeClickCommandsIfPresent(net.minecraft.world.entity.player.Player player, Level world, BlockPos pos, boolean front) { - boolean flag1 = false; - Component[] aichatbasecomponent = this.getText(front).getMessages(player.isTextFilteringEnabled()); - int i = aichatbasecomponent.length; -@@ -242,7 +279,17 @@ - ClickEvent chatclickable = chatmodifier.getClickEvent(); - - if (chatclickable != null && chatclickable.getAction() == ClickEvent.Action.RUN_COMMAND) { -- player.getServer().getCommands().performPrefixedCommand(SignBlockEntity.createCommandSourceStack(player, world, pos), chatclickable.getValue()); -+ // Paper start - Fix commands from signs not firing command events -+ String command = chatclickable.getValue().startsWith("/") ? chatclickable.getValue() : "/" + chatclickable.getValue(); -+ if (org.spigotmc.SpigotConfig.logCommands) { -+ LOGGER.info("{} issued server command: {}", player.getScoreboardName(), command); -+ } -+ io.papermc.paper.event.player.PlayerSignCommandPreprocessEvent event = new io.papermc.paper.event.player.PlayerSignCommandPreprocessEvent((org.bukkit.entity.Player) player.getBukkitEntity(), command, new org.bukkit.craftbukkit.util.LazyPlayerSet(player.getServer()), (org.bukkit.block.Sign) CraftBlock.at(this.level, this.worldPosition).getState(), front ? Side.FRONT : Side.BACK); -+ if (!event.callEvent()) { -+ return false; -+ } -+ player.getServer().getCommands().performPrefixedCommand(this.createCommandSourceStack(((org.bukkit.craftbukkit.entity.CraftPlayer) event.getPlayer()).getHandle(), world, pos), event.getMessage()); -+ // Paper end - Fix commands from signs not firing command events - flag1 = true; - } - } -@@ -250,11 +297,55 @@ - return flag1; - } - -- private static CommandSourceStack createCommandSourceStack(@Nullable Player player, Level world, BlockPos pos) { -+ // CraftBukkit start -+ private final CommandSource commandSource = new CommandSource() { -+ -+ @Override -+ public void sendSystemMessage(Component message) {} -+ -+ @Override -+ public org.bukkit.command.CommandSender getBukkitSender(CommandSourceStack wrapper) { -+ return wrapper.getEntity() != null ? wrapper.getEntity().getBukkitEntity() : new org.bukkit.craftbukkit.command.CraftBlockCommandSender(wrapper, SignBlockEntity.this); -+ } -+ -+ @Override -+ public boolean acceptsSuccess() { -+ return false; -+ } -+ -+ @Override -+ public boolean acceptsFailure() { -+ return false; -+ } -+ -+ @Override -+ public boolean shouldInformAdmins() { -+ return false; -+ } -+ }; -+ -+ private CommandSourceStack createCommandSourceStack(@Nullable net.minecraft.world.entity.player.Player player, Level world, BlockPos pos) { -+ // CraftBukkit end - String s = player == null ? "Sign" : player.getName().getString(); - Object object = player == null ? Component.literal("Sign") : player.getDisplayName(); - -- return new CommandSourceStack(CommandSource.NULL, Vec3.atCenterOf(pos), Vec2.ZERO, (ServerLevel) world, 2, s, (Component) object, world.getServer(), player); -+ // Paper start - Fix commands from signs not firing command events -+ CommandSource commandSource = this.level.paperConfig().misc.showSignClickCommandFailureMsgsToPlayer ? new io.papermc.paper.commands.DelegatingCommandSource(this.commandSource) { -+ @Override -+ public void sendSystemMessage(Component message) { -+ if (player instanceof final ServerPlayer serverPlayer) { -+ serverPlayer.sendSystemMessage(message); -+ } -+ } -+ -+ @Override -+ public boolean acceptsFailure() { -+ return true; -+ } -+ } : this.commandSource; -+ // Paper end - Fix commands from signs not firing command events -+ // CraftBukkit - this -+ return new CommandSourceStack(commandSource, Vec3.atCenterOf(pos), Vec2.ZERO, (ServerLevel) world, 2, s, (Component) object, world.getServer(), player); // Paper - Fix commands from signs not firing command events - } - - @Override -@@ -273,12 +364,17 @@ - - @Nullable - public UUID getPlayerWhoMayEdit() { -+ // CraftBukkit start - unnecessary sign ticking removed, so do this lazily -+ if (this.level != null && this.playerWhoMayEdit != null) { -+ this.clearInvalidPlayerWhoMayEdit(this, this.level, this.playerWhoMayEdit); -+ } -+ // CraftBukkit end - return this.playerWhoMayEdit; - } - - private void markUpdated() { - this.setChanged(); -- this.level.sendBlockUpdated(this.getBlockPos(), this.getBlockState(), this.getBlockState(), 3); -+ if (this.level != null) this.level.sendBlockUpdated(this.getBlockPos(), this.getBlockState(), this.getBlockState(), 3); // CraftBukkit - skip notify if world is null (SPIGOT-5122) - } - - public boolean isWaxed() { -@@ -296,7 +392,7 @@ - } - - public boolean playerIsTooFarAwayToEdit(UUID uuid) { -- Player entityhuman = this.level.getPlayerByUUID(uuid); -+ net.minecraft.world.entity.player.Player entityhuman = this.level.getPlayerByUUID(uuid); - - return entityhuman == null || !entityhuman.canInteractWithBlock(this.getBlockPos(), 4.0D); - }