Finish block entity

This commit is contained in:
Bjarne Koll 2024-12-14 05:05:32 +01:00
parent 368d2116ba
commit f25c1a33a0
No known key found for this signature in database
GPG key ID: 9576DAF3FDDB088F
41 changed files with 1904 additions and 2396 deletions

View file

@ -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<ResourceKey<Recipe<?>>> recipesUsed = new Reference2IntOpenHashMap<>();
private final RecipeManager.CachedCheck<SingleRecipeInput, ? extends AbstractCookingRecipe> quickCheck;
+ public final RecipeType<? extends AbstractCookingRecipe> 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<? extends AbstractCookingRecipe> 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<org.bukkit.entity.HumanEntity> transaction = new java.util.ArrayList<>();
+
+ public List<ItemStack> 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<org.bukkit.entity.HumanEntity> 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<? extends AbstractCookingRecipe> recipe,
SingleRecipeInput recipeInput,
NonNullList<ItemStack> 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<? extends AbstractCookingRecipe> 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<ItemStack> items) {
+ public void awardUsedRecipes(net.minecraft.world.entity.player.Player player, List<ItemStack> items) {
}
- public void awardUsedRecipesAndPopExperience(ServerPlayer player) {
- List<RecipeHolder<?>> recipesToAwardAndPopExperience = this.getRecipesToAwardAndPopExperience(player.serverLevel(), player.position());
+ public void awardUsedRecipesAndPopExperience(ServerPlayer player, ItemStack itemstack, int amount) { // CraftBukkit
+ List<RecipeHolder<?>> 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<RecipeHolder<?>> getRecipesToAwardAndPopExperience(ServerLevel level, Vec3 popVec) {
+ // CraftBukkit start
+ return this.getRecipesToAwardAndPopExperience(level, popVec, this.worldPosition, null, null, 0);
+ }
+ public List<RecipeHolder<?>> getRecipesToAwardAndPopExperience(ServerLevel level, Vec3 popVec, BlockPos blockPos, ServerPlayer serverPlayer, ItemStack itemStack, int amount) {
+ // CraftBukkit end
List<RecipeHolder<?>> list = Lists.newArrayList();
for (Entry<ResourceKey<Recipe<?>>> 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

View file

@ -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<Boolean> serialisingForNetwork = ThreadLocal.withInitial(() -> Boolean.FALSE);
@Override
public CompoundTag getUpdateTag(HolderLookup.Provider registries) {
+ final Boolean wasSerialisingForNetwork = serialisingForNetwork.get();
+ try {
+ serialisingForNetwork.set(Boolean.TRUE);
return this.saveWithoutMetadata(registries);
+ } finally {
+ serialisingForNetwork.set(wasSerialisingForNetwork);
+ }
+ // Paper end - always send patterns to client
}
public BannerPatternLayers getPatterns() {
@@ -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
}

View file

@ -24,7 +24,7 @@
+ }
+
+ @Override
+ public java.util.List<org.bukkit.entity.HumanEntity> getViewers() {
+ public List<HumanEntity> getViewers() {
+ return this.transaction;
+ }
+

View file

@ -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<MobEffect> filterEffect(@Nullable Holder<MobEffect> 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<MobEffect> primaryEffect, @Nullable Holder<MobEffect> 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<MobEffect> primaryEffect, @Nullable Holder<MobEffect> 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<Player> 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<Player> 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<MobEffect> primaryEffect, @Nullable Holder<MobEffect> 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<Player> 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<Player> 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<MobEffect> primaryEffect, final @Nullable Holder<MobEffect> 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<Player> 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<MobEffect> 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;
}

View file

@ -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<BeehiveBlockEntity.BeeData> 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<Entity> 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<Entity> releaseBees(BlockState state, BeehiveBlockEntity.BeeReleaseStatus releaseStatus, boolean force) {
+ // CraftBukkit end - This allows us to bypass t he night/rain/emergecny check
List<Entity> 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<Entity> 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<BeehiveBlockEntity.Occupant> 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
}
}
}

View file

@ -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<DataComponentType<?>> applyComponentsSet(DataComponentMap components, DataComponentPatch patch) {
+ // CraftBukkit end
final Set<DataComponentType<?>> 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<DataComponentMap> COMPONENTS_CODEC = DataComponentMap.CODEC.optionalFieldOf("components", DataComponentMap.EMPTY).codec();

View file

@ -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<ItemStack> 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<org.bukkit.entity.HumanEntity> 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<HumanEntity> getViewers() {
+ return this.transaction;
+ }
+
+ public java.util.List<net.minecraft.world.item.ItemStack> 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<ItemStack> items) {
+ private static void doBrew(Level level, BlockPos pos, NonNullList<ItemStack> 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<org.bukkit.inventory.ItemStack> 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();
}
}

View file

@ -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 {

View file

@ -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() {

View file

@ -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<ItemStack> 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()));

View file

@ -20,7 +20,7 @@
+ this.transaction.remove(who);
+ }
+
+ public java.util.List<org.bukkit.entity.HumanEntity> getViewers() {
+ public List<HumanEntity> getViewers() {
+ return this.transaction;
+ }
+

View file

@ -24,7 +24,7 @@
+ }
+
+ @Override
+ public java.util.List<org.bukkit.entity.HumanEntity> getViewers() {
+ public List<HumanEntity> getViewers() {
+ return this.transaction;
+ }
+

View file

@ -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<BlockPos> positions) {
+ // CraftBukkit start
+ ConduitBlockEntity.applyEffects(level, pos, ConduitBlockEntity.getRange(positions));
+ }
+
+ public static int getRange(List<BlockPos> 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<BlockPos> 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<BlockPos> 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) {

View file

@ -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;

View file

@ -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<org.bukkit.entity.HumanEntity> transaction = new java.util.ArrayList<>();
+ private int maxStack = MAX_STACK;
+
+ @Override
+ public java.util.List<net.minecraft.world.item.ItemStack> 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<org.bukkit.entity.HumanEntity> 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);
}

View file

@ -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<HumanEntity> transaction = new ArrayList<>();
+ public List<org.bukkit.entity.HumanEntity> transaction = new java.util.ArrayList<>();
+ private int maxStack = MAX_STACK;
+
+ @Override
+ public List<ItemStack> 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<HumanEntity> getViewers() {
+ public java.util.List<HumanEntity> 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
+

View file

@ -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<ItemStack> items = NonNullList.withSize(9, ItemStack.EMPTY);
+ // CraftBukkit start - add fields and methods
+ public java.util.List<org.bukkit.entity.HumanEntity> transaction = new java.util.ArrayList<>();
+ private int maxStack = MAX_STACK;
+
+ public java.util.List<net.minecraft.world.item.ItemStack> 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<HumanEntity> 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);
}

View file

@ -20,7 +20,7 @@
+ this.transaction.remove(who);
+ }
+
+ public List<org.bukkit.entity.HumanEntity> getViewers() {
+ public java.util.List<HumanEntity> getViewers() {
+ return this.transaction;
+ }
+

View file

@ -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<StructureTemplatePool> registry = world.registryAccess().lookupOrThrow(Registries.TEMPLATE_POOL);
- Holder<StructureTemplatePool> holder = registry.getOrThrow(this.pool);
Registry<StructureTemplatePool> registry = level.registryAccess().lookupOrThrow(Registries.TEMPLATE_POOL);
- Holder<StructureTemplatePool> orThrow = registry.getOrThrow(this.pool);
+ // Paper start - Replace getHolderOrThrow with a null check
+ Holder<StructureTemplatePool> holder = registry.get(this.pool).orElse(null);
+ if (holder == null) {
+ Holder<StructureTemplatePool> 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);
}

View file

@ -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<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>();
+ public java.util.List<org.bukkit.entity.HumanEntity> transaction = new java.util.ArrayList<>();
+ private int maxStack = MAX_STACK;
+ public boolean opened;
+
+ @Override
+ public List<ItemStack> getContents() {
+ public java.util.List<net.minecraft.world.item.ItemStack> 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<JukeboxSong>)holder, 0L));
- this.level.updateNeighborsAt(this.getBlockPos(), this.getBlockState().getBlock());
+ // CraftBukkit start - add null check for level
+ if (this.level != null) {

View file

@ -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<org.bukkit.entity.HumanEntity> transaction = new java.util.ArrayList<>();
+ private int maxStack = 1;
+
+ @Override
+ public java.util.List<net.minecraft.world.item.ItemStack> 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<org.bukkit.entity.HumanEntity> 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

View file

@ -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

View file

@ -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() {

View file

@ -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();
}

View file

@ -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<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>();
+ public List<org.bukkit.entity.HumanEntity> 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<HumanEntity> getViewers() {
+ public List<org.bukkit.entity.HumanEntity> 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);

View file

@ -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> 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> filteredText, SignText text) {
+ private SignText setMessages(Player player, List<FilteredText> 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<net.kyori.adventure.text.Component> 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() {

View file

@ -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<String, CompletableFuture<Optional<GameProfile>>> 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<UUID, CompletableFuture<Optional<GameProfile>>>() {
+ .build(new CacheLoader<>() { // Paper - player profile events
@Override
- public CompletableFuture<Optional<GameProfile>> load(UUID uUID) {
+ public CompletableFuture<Optional<GameProfile>> load(com.mojang.datafixers.util.Pair<java.util.UUID, @org.jetbrains.annotations.Nullable GameProfile> uUID) { // Paper - player profile events
return SkullBlockEntity.fetchProfileById(uUID, apiServices, booleanSupplier);
- public CompletableFuture<Optional<GameProfile>> load(UUID id) {
+ public CompletableFuture<Optional<GameProfile>> load(com.mojang.datafixers.util.Pair<java.util.UUID, @org.jetbrains.annotations.Nullable GameProfile> id) { // Paper - player profile events
return SkullBlockEntity.fetchProfileById(id, services, booleanSupplier);
}
});
@@ -89,23 +89,29 @@
@@ -89,23 +_,29 @@
.getAsync(name)
.thenCompose(
optional -> {
- LoadingCache<UUID, CompletableFuture<Optional<GameProfile>>> loadingCache = profileCacheById;
+ LoadingCache<com.mojang.datafixers.util.Pair<java.util.UUID, @org.jetbrains.annotations.Nullable GameProfile>, CompletableFuture<Optional<GameProfile>>> 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<Optional<GameProfile>> fetchProfileById(UUID uuid, Services apiServices, BooleanSupplier booleanSupplier) {
+ static CompletableFuture<Optional<GameProfile>> fetchProfileById(com.mojang.datafixers.util.Pair<java.util.UUID, @org.jetbrains.annotations.Nullable GameProfile> pair, Services apiServices, BooleanSupplier booleanSupplier) { // Paper
- static CompletableFuture<Optional<GameProfile>> fetchProfileById(UUID id, Services services, BooleanSupplier cacheUninitialized) {
+ static CompletableFuture<Optional<GameProfile>> fetchProfileById(com.mojang.datafixers.util.Pair<java.util.UUID, @org.jetbrains.annotations.Nullable GameProfile> 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<Optional<GameProfile>> fetchGameProfile(UUID uuid) {
- public static CompletableFuture<Optional<GameProfile>> fetchGameProfile(UUID profileUuid) {
- LoadingCache<UUID, CompletableFuture<Optional<GameProfile>>> 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<Optional<GameProfile>> fetchGameProfile(UUID uuid, @Nullable String name) {
+ LoadingCache<com.mojang.datafixers.util.Pair<java.util.UUID, @org.jetbrains.annotations.Nullable GameProfile>, CompletableFuture<Optional<GameProfile>>> loadingCache = profileCacheById;

View file

@ -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<ResourceKey<Recipe<?>>> recipesUsed;
private final RecipeManager.CachedCheck<SingleRecipeInput, ? extends AbstractCookingRecipe> quickCheck;
+ public final RecipeType<? extends AbstractCookingRecipe> 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<? extends AbstractCookingRecipe> recipeType) {
super(blockEntityType, pos, state);
@@ -110,9 +125,40 @@
}
};
this.recipesUsed = new Reference2IntOpenHashMap();
- this.quickCheck = RecipeManager.createCheck(recipeType);
+ this.quickCheck = RecipeManager.createCheck((RecipeType<AbstractCookingRecipe>) 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<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>();
+
+ public List<ItemStack> getContents() {
+ return this.items;
+ }
+
+ public void onOpen(CraftHumanEntity who) {
+ this.transaction.add(who);
+ }
+
+ public void onClose(CraftHumanEntity who) {
+ this.transaction.remove(who);
+ }
+
+ public List<HumanEntity> 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<? extends AbstractCookingRecipe> recipe, SingleRecipeInput input, NonNullList<ItemStack> 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<? extends AbstractCookingRecipe> recipeholder, SingleRecipeInput singlerecipeinput, NonNullList<ItemStack> 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<? extends AbstractCookingRecipe> 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<ItemStack> ingredients) {}
+ public void awardUsedRecipes(net.minecraft.world.entity.player.Player player, List<ItemStack> ingredients) {}
- public void awardUsedRecipesAndPopExperience(ServerPlayer player) {
- List<RecipeHolder<?>> list = this.getRecipesToAwardAndPopExperience(player.serverLevel(), player.position());
+ public void awardUsedRecipesAndPopExperience(ServerPlayer entityplayer, ItemStack itemstack, int amount) { // CraftBukkit
+ List<RecipeHolder<?>> 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<RecipeHolder<?>> getRecipesToAwardAndPopExperience(ServerLevel world, Vec3 pos) {
+ // CraftBukkit start
+ return this.getRecipesToAwardAndPopExperience(world, pos, this.worldPosition, null, null, 0);
+ }
+
+ public List<RecipeHolder<?>> getRecipesToAwardAndPopExperience(ServerLevel worldserver, Vec3 vec3d, BlockPos blockposition, ServerPlayer entityplayer, ItemStack itemstack, int amount) {
+ // CraftBukkit end
List<RecipeHolder<?>> list = Lists.newArrayList();
ObjectIterator objectiterator = this.recipesUsed.reference2IntEntrySet().iterator();
while (objectiterator.hasNext()) {
Entry<ResourceKey<Recipe<?>>> 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);
- }
-
}
}

View file

@ -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<Boolean> serialisingForNetwork = ThreadLocal.withInitial(() -> Boolean.FALSE);
@Override
public CompoundTag getUpdateTag(HolderLookup.Provider registries) {
+ final Boolean wasSerialisingForNetwork = serialisingForNetwork.get();
+ try {
+ serialisingForNetwork.set(Boolean.TRUE);
return this.saveWithoutMetadata(registries);
+ } finally {
+ serialisingForNetwork.set(wasSerialisingForNetwork);
+ }
+ // Paper end - always send patterns to client
}
public BannerPatternLayers getPatterns() {
@@ -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
}

View file

@ -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<MobEffect> filterEffect(@Nullable Holder<MobEffect> 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<MobEffect> primaryEffect, @Nullable Holder<MobEffect> 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<MobEffect> holder, @Nullable Holder<MobEffect> 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<Player> 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<Player> 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<MobEffect> 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<MobEffect> holder, @Nullable Holder<MobEffect> 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<MobEffect> primaryEffect, @Nullable Holder<MobEffect> 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<MobEffect> primaryEffect, @Nullable Holder<MobEffect> 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

View file

@ -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<BeehiveBlockEntity.BeeData> 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<Entity> 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<Entity> releaseBees(BlockState iblockdata, BeehiveBlockEntity.BeeReleaseStatus tileentitybeehive_releasestatus, boolean force) {
List<Entity> 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<Entity> 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<Entity> 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<BeehiveBlockEntity.Occupant> 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<String> 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<String> 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() {

View file

@ -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<DataComponentType<?>> applyComponentsSet(DataComponentMap datacomponentmap, DataComponentPatch datacomponentpatch) {
+ // CraftBukkit end
final Set<DataComponentType<?>> 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> T get(DataComponentType<T> 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<DataComponentMap> COMPONENTS_CODEC = DataComponentMap.CODEC.optionalFieldOf("components", DataComponentMap.EMPTY).codec();

View file

@ -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<CrafterBlockEntity> CRAFTER = BlockEntityType.register("crafter", CrafterBlockEntity::new, Blocks.CRAFTER);
public static final BlockEntityType<TrialSpawnerBlockEntity> TRIAL_SPAWNER = BlockEntityType.register("trial_spawner", TrialSpawnerBlockEntity::new, Blocks.TRIAL_SPAWNER);
public static final BlockEntityType<VaultBlockEntity> VAULT = BlockEntityType.register("vault", VaultBlockEntity::new, Blocks.VAULT);
- private static final Set<BlockEntityType<?>> 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<BlockEntityType<?>> 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<? extends T> factory;
public final Set<Block> validBlocks;
private final Holder.Reference<BlockEntityType<?>> 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() {

View file

@ -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<ItemStack> 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<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>();
+ 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<HumanEntity> getViewers() {
+ return this.transaction;
+ }
+
+ public List<ItemStack> 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<ItemStack> slots) {
- ItemStack itemstack = (ItemStack) slots.get(3);
+ private static void doBrew(Level world, BlockPos blockposition, NonNullList<ItemStack> nonnulllist, BrewingStandBlockEntity tileentitybrewingstand) { // CraftBukkit
+ ItemStack itemstack = (ItemStack) nonnulllist.get(3);
PotionBrewing potionbrewer = world.potionBrewing();
+ // CraftBukkit start
+ InventoryHolder owner = tileentitybrewingstand.getOwner();
+ List<org.bukkit.inventory.ItemStack> 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
}
}

View file

@ -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 {

View file

@ -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<ItemStack> items;
public final int[] cookingProgress;
public final int[] cookingTime;
+ public final boolean[] stopCooking; // Paper - Add more Campfire API
public CampfireBlockEntity(BlockPos pos, BlockState state) {
super(BlockEntityType.CAMPFIRE, pos, state);
this.items = NonNullList.withSize(4, ItemStack.EMPTY);
this.cookingProgress = new int[4];
this.cookingTime = new int[4];
+ this.stopCooking = new boolean[4]; // Paper - Add more Campfire API
}
public static void cookTick(ServerLevel world, BlockPos pos, BlockState state, CampfireBlockEntity blockEntity, RecipeManager.CachedCheck<SingleRecipeInput, CampfireCookingRecipe> recipeMatchGetter) {
@@ -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<RecipeHolder<CampfireCookingRecipe>> recipeHolderOptional = recipeMatchGetter.getRecipeFor(singlerecipeinput, world);
+ ItemStack itemstack1 = (ItemStack) recipeHolderOptional.map((recipeholder) -> {
+ // Paper end - add recipe to cook events
return ((CampfireCookingRecipe) recipeholder.value()).assemble(singlerecipeinput, world.registryAccess());
}).orElse(itemstack);
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()));

View file

@ -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<BlockPos> activatingBlocks) {
- int i = activatingBlocks.size();
+ // CraftBukkit start
+ ConduitBlockEntity.applyEffects(world, pos, ConduitBlockEntity.getRange(activatingBlocks));
+ }
+
+ public static int getRange(List<BlockPos> 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<Player> 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<BlockPos> 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<BlockPos> 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<LivingEntity> 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<LivingEntity> 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);
}
}

View file

@ -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<ItemStack> items;
public int craftingTicksRemaining;
protected final ContainerData containerData;
+ // CraftBukkit start - add fields and methods
+ public List<HumanEntity> transaction = new java.util.ArrayList<>();
+ private int maxStack = MAX_STACK;
+ @Override
+ public List<ItemStack> 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<HumanEntity> 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;

View file

@ -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<ItemStack> items;
+ // CraftBukkit start - add fields and methods
+ public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>();
+ private int maxStack = MAX_STACK;
+
+ public List<ItemStack> getContents() {
+ return this.items;
+ }
+
+ public void onOpen(CraftHumanEntity who) {
+ this.transaction.add(who);
+ }
+
+ public void onClose(CraftHumanEntity who) {
+ this.transaction.remove(who);
+ }
+
+ public List<HumanEntity> 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);

View file

@ -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<HumanEntity> transaction = new ArrayList<>();
+ private int maxStack = 1;
+
@Override
+ public List<ItemStack> 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<HumanEntity> 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

View file

@ -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<Tag> dynamicops = registries.createSerializationContext(NbtOps.INSTANCE);
- DataResult dataresult = SignText.DIRECT_CODEC.encodeStart(dynamicops, this.frontText);
+ DataResult<Tag> 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<Tag> dynamicops = registries.createSerializationContext(NbtOps.INSTANCE);
- DataResult dataresult;
+ DataResult<SignText> 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<FilteredText> messages) {
+ public void updateSignText(net.minecraft.world.entity.player.Player player, boolean front, List<FilteredText> 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<FilteredText> 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<FilteredText> 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<net.kyori.adventure.text.Component> 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);
}