Finish block entity
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -24,7 +24,7 @@
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public java.util.List<org.bukkit.entity.HumanEntity> getViewers() {
|
||||
+ public List<HumanEntity> getViewers() {
|
||||
+ return this.transaction;
|
||||
+ }
|
||||
+
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
@@ -0,0 +1,23 @@
|
||||
--- a/net/minecraft/world/level/block/entity/CalibratedSculkSensorBlockEntity.java
|
||||
+++ b/net/minecraft/world/level/block/entity/CalibratedSculkSensorBlockEntity.java
|
||||
@@ -20,6 +_,12 @@
|
||||
public VibrationSystem.User createVibrationUser() {
|
||||
return new CalibratedSculkSensorBlockEntity.VibrationUser(this.getBlockPos());
|
||||
}
|
||||
+ // Paper start - Configurable sculk sensor listener range
|
||||
+ @Override
|
||||
+ protected void saveRangeOverride(final net.minecraft.nbt.CompoundTag nbt) {
|
||||
+ if (this.rangeOverride != null && this.rangeOverride != 16) nbt.putInt(PAPER_LISTENER_RANGE_NBT_KEY, this.rangeOverride); // only save if it's different from the default
|
||||
+ }
|
||||
+ // Paper end - Configurable sculk sensor listener range
|
||||
|
||||
protected class VibrationUser extends SculkSensorBlockEntity.VibrationUser {
|
||||
public VibrationUser(final BlockPos pos1) {
|
||||
@@ -28,6 +_,7 @@
|
||||
|
||||
@Override
|
||||
public int getListenerRadius() {
|
||||
+ if (CalibratedSculkSensorBlockEntity.this.rangeOverride != null) return CalibratedSculkSensorBlockEntity.this.rangeOverride; // Paper - Configurable sculk sensor listener range
|
||||
return 16;
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
@@ -20,7 +20,7 @@
|
||||
+ this.transaction.remove(who);
|
||||
+ }
|
||||
+
|
||||
+ public java.util.List<org.bukkit.entity.HumanEntity> getViewers() {
|
||||
+ public List<HumanEntity> getViewers() {
|
||||
+ return this.transaction;
|
||||
+ }
|
||||
+
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public java.util.List<org.bukkit.entity.HumanEntity> getViewers() {
|
||||
+ public List<HumanEntity> getViewers() {
|
||||
+ return this.transaction;
|
||||
+ }
|
||||
+
|
||||
|
||||
@@ -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) {
|
||||
@@ -0,0 +1,76 @@
|
||||
--- a/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java
|
||||
+++ b/net/minecraft/world/level/block/entity/ContainerOpenersCounter.java
|
||||
@@ -13,6 +_,7 @@
|
||||
private static final int CHECK_TICK_DELAY = 5;
|
||||
private int openCount;
|
||||
private double maxInteractionRange;
|
||||
+ public boolean opened; // CraftBukki
|
||||
|
||||
protected abstract void onOpen(Level level, BlockPos pos, BlockState state);
|
||||
|
||||
@@ -20,10 +_,36 @@
|
||||
|
||||
protected abstract void openerCountChanged(Level level, BlockPos pos, BlockState state, int count, int openCount);
|
||||
|
||||
+ // CraftBukkit start
|
||||
+ public void onAPIOpen(Level level, BlockPos blockPos, BlockState blockState) {
|
||||
+ this.onOpen(level, blockPos, blockState);
|
||||
+ }
|
||||
+
|
||||
+ public void onAPIClose(Level level, BlockPos blockPos, BlockState blockState) {
|
||||
+ this.onClose(level, blockPos, blockState);
|
||||
+ }
|
||||
+
|
||||
+ public void openerAPICountChanged(Level world, BlockPos blockPos, BlockState blockState, int count, int openCount) {
|
||||
+ this.openerCountChanged(world, blockPos, blockState, count, openCount);
|
||||
+ }
|
||||
+ // CraftBukkit en
|
||||
+
|
||||
protected abstract boolean isOwnContainer(Player player);
|
||||
|
||||
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 (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(level, pos, oldPower, newPower);
|
||||
+ }
|
||||
+ }
|
||||
+ // CraftBukkit en
|
||||
+
|
||||
if (i == 0) {
|
||||
this.onOpen(level, pos, state);
|
||||
level.gameEvent(player, GameEvent.CONTAINER_OPEN, pos);
|
||||
@@ -35,7 +_,20 @@
|
||||
}
|
||||
|
||||
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 (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(level, pos, oldPower, newPower);
|
||||
+ }
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
if (this.openCount == 0) {
|
||||
this.onClose(level, pos, state);
|
||||
level.gameEvent(player, GameEvent.CONTAINER_CLOSE, pos);
|
||||
@@ -60,6 +_,7 @@
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
--- a/net/minecraft/world/level/block/entity/DecoratedPotBlockEntity.java
|
||||
+++ b/net/minecraft/world/level/block/entity/DecoratedPotBlockEntity.java
|
||||
@@ -20,6 +_,48 @@
|
||||
import net.minecraft.world.ticks.ContainerSingleItem;
|
||||
|
||||
public class DecoratedPotBlockEntity extends BlockEntity implements RandomizableContainer, ContainerSingleItem.BlockContainerSingleItem {
|
||||
+
|
||||
+ // CraftBukkit start - add fields and methods
|
||||
+ public List<org.bukkit.entity.HumanEntity> transaction = new java.util.ArrayList<>();
|
||||
+ private int maxStack = MAX_STACK;
|
||||
+
|
||||
+ @Override
|
||||
+ public List<ItemStack> getContents() {
|
||||
+ return java.util.List.of(this.item);
|
||||
+ }
|
||||
+
|
||||
+ @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<HumanEntity> getViewers() {
|
||||
+ return this.transaction;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public int getMaxStackSize() {
|
||||
+ return this.maxStack;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void setMaxStackSize(int i) {
|
||||
+ this.maxStack = i;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public org.bukkit.Location getLocation() {
|
||||
+ if (this.level == null) return null;
|
||||
+ return org.bukkit.craftbukkit.util.CraftLocation.toBukkit(this.worldPosition, this.level.getWorld());
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
+
|
||||
public static final String TAG_SHERDS = "sherds";
|
||||
public static final String TAG_ITEM = "item";
|
||||
public static final int EVENT_POT_WOBBLES = 1;
|
||||
@@ -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);
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
+ this.transaction.remove(who);
|
||||
+ }
|
||||
+
|
||||
+ public List<org.bukkit.entity.HumanEntity> getViewers() {
|
||||
+ public java.util.List<HumanEntity> getViewers() {
|
||||
+ return this.transaction;
|
||||
+ }
|
||||
+
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
--- a/net/minecraft/world/level/block/entity/JigsawBlockEntity.java
|
||||
+++ b/net/minecraft/world/level/block/entity/JigsawBlockEntity.java
|
||||
@@ -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 = level.registryAccess().lookupOrThrow(Registries.TEMPLATE_POOL);
|
||||
- Holder<StructureTemplatePool> orThrow = registry.getOrThrow(this.pool);
|
||||
+ // Paper start - Replace getHolderOrThrow with a null check
|
||||
+ Holder<StructureTemplatePool> orThrow = registry.get(this.pool).orElse(null);
|
||||
+ if (orThrow == null) {
|
||||
+ return;
|
||||
+ }
|
||||
+ // Paper end - Replace getHolderOrThrow with a null check
|
||||
JigsawPlacement.generateJigsaw(level, orThrow, this.target, maxDepth, blockPos, keepJigsaws);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
--- a/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java
|
||||
+++ b/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java
|
||||
@@ -20,6 +_,44 @@
|
||||
import net.minecraft.world.ticks.ContainerSingleItem;
|
||||
|
||||
public class JukeboxBlockEntity extends BlockEntity implements ContainerSingleItem.BlockContainerSingleItem {
|
||||
+
|
||||
+ // 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 boolean opened;
|
||||
+
|
||||
+ @Override
|
||||
+ public java.util.List<net.minecraft.world.item.ItemStack> getContents() {
|
||||
+ return Collections.singletonList(this.item);
|
||||
+ }
|
||||
+
|
||||
+ @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 List<HumanEntity> getViewers() {
|
||||
+ return this.transaction;
|
||||
+ }
|
||||
+
|
||||
+ @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 end
|
||||
+
|
||||
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() {
|
||||
- return 1;
|
||||
+ return this.maxStack; // CraftBukkit
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -147,9 +_,14 @@
|
||||
@VisibleForTesting
|
||||
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(), 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) {
|
||||
+ this.level.updateNeighborsAt(this.getBlockPos(), this.getBlockState().getBlock());
|
||||
+ }
|
||||
+ // CraftBukkit end
|
||||
this.setChanged();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,16 @@
|
||||
--- a/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
|
||||
+++ b/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
|
||||
@@ -115,4 +_,13 @@
|
||||
tag.remove("LootTable");
|
||||
tag.remove("LootTableSeed");
|
||||
}
|
||||
+
|
||||
+ // Paper start - LootTable API
|
||||
+ final com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData = new com.destroystokyo.paper.loottable.PaperLootableInventoryData(); // Paper
|
||||
+
|
||||
+ @Override
|
||||
+ public com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData() {
|
||||
+ return this.lootableData;
|
||||
+ }
|
||||
+ // Paper end - LootTable API
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
--- a/net/minecraft/world/level/block/entity/SculkSensorBlockEntity.java
|
||||
+++ b/net/minecraft/world/level/block/entity/SculkSensorBlockEntity.java
|
||||
@@ -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 blockState) {
|
||||
super(type, pos, blockState);
|
||||
@@ -52,8 +_,16 @@
|
||||
.resultOrPartial(string -> LOGGER.error("Failed to parse vibration listener for Sculk Sensor: '{}'", string))
|
||||
.ifPresent(data -> this.vibrationData = data);
|
||||
}
|
||||
+ // Paper start - Configurable sculk sensor listener range
|
||||
+ if (tag.contains(PAPER_LISTENER_RANGE_NBT_KEY)) {
|
||||
+ this.rangeOverride = tag.getInt(PAPER_LISTENER_RANGE_NBT_KEY);
|
||||
+ } else {
|
||||
+ this.rangeOverride = null;
|
||||
+ }
|
||||
+ // Paper end - Configurable sculk sensor listener range
|
||||
}
|
||||
|
||||
+ protected static final String PAPER_LISTENER_RANGE_NBT_KEY = "Paper.ListenerRange"; // Paper - Configurable sculk sensor listener range
|
||||
@Override
|
||||
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(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 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 +_,7 @@
|
||||
|
||||
@Override
|
||||
public int getListenerRadius() {
|
||||
+ if (SculkSensorBlockEntity.this.rangeOverride != null) return SculkSensorBlockEntity.this.rangeOverride; // Paper - Configurable sculk sensor listener range
|
||||
return 8;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
--- a/net/minecraft/world/level/block/entity/SculkShriekerBlockEntity.java
|
||||
+++ b/net/minecraft/world/level/block/entity/SculkShriekerBlockEntity.java
|
||||
@@ -105,6 +_,13 @@
|
||||
|
||||
@Nullable
|
||||
public static ServerPlayer tryGetPlayer(@Nullable Entity entity) {
|
||||
+ // Paper start - check global player list where appropriate; ensure level is the same for sculk events
|
||||
+ final ServerPlayer player = tryGetPlayer0(entity);
|
||||
+ return player != null && player.level() == entity.level() ? player : null;
|
||||
+ }
|
||||
+ @Nullable
|
||||
+ private static ServerPlayer tryGetPlayer0(@Nullable Entity entity) {
|
||||
+ // Paper end - check global player list where appropriate
|
||||
if (entity instanceof ServerPlayer) {
|
||||
return (ServerPlayer)entity;
|
||||
} else {
|
||||
@@ -190,7 +_,7 @@
|
||||
private boolean trySummonWarden(ServerLevel level) {
|
||||
return this.warningLevel >= 4
|
||||
&& SpawnUtil.trySpawnMob(
|
||||
- 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();
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
--- a/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java
|
||||
+++ b/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java
|
||||
@@ -49,6 +_,37 @@
|
||||
@Nullable
|
||||
private final DyeColor color;
|
||||
|
||||
+ // CraftBukkit start - add fields and methods
|
||||
+ public List<org.bukkit.entity.HumanEntity> transaction = new java.util.ArrayList<>();
|
||||
+ private int maxStack = MAX_STACK;
|
||||
+ public boolean opened;
|
||||
+
|
||||
+ public List<ItemStack> getContents() {
|
||||
+ return this.itemStacks;
|
||||
+ }
|
||||
+
|
||||
+ 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
|
||||
+
|
||||
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.level.blockEvent(this.worldPosition, this.getBlockState().getBlock(), 1, this.openCount);
|
||||
if (this.openCount == 1) {
|
||||
this.level.gameEvent(player, GameEvent.CONTAINER_OPEN, this.worldPosition);
|
||||
@@ -180,6 +_,7 @@
|
||||
public void stopOpen(Player player) {
|
||||
if (!this.remove && !player.isSpectator()) {
|
||||
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(player, GameEvent.CONTAINER_CLOSE, this.worldPosition);
|
||||
@@ -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() {
|
||||
@@ -0,0 +1,73 @@
|
||||
--- a/net/minecraft/world/level/block/entity/SkullBlockEntity.java
|
||||
+++ b/net/minecraft/world/level/block/entity/SkullBlockEntity.java
|
||||
@@ -41,7 +_,7 @@
|
||||
@Nullable
|
||||
private static LoadingCache<String, CompletableFuture<Optional<GameProfile>>> profileCacheByName;
|
||||
@Nullable
|
||||
- private static LoadingCache<UUID, CompletableFuture<Optional<GameProfile>>> profileCacheById;
|
||||
+ private static LoadingCache<com.mojang.datafixers.util.Pair<java.util.UUID, @org.jetbrains.annotations.Nullable GameProfile>, CompletableFuture<Optional<GameProfile>>> profileCacheById; // Paper - player profile events
|
||||
public static final Executor CHECKED_MAIN_THREAD_EXECUTOR = runnable -> {
|
||||
Executor executor = mainThreadExecutor;
|
||||
if (executor != null) {
|
||||
@@ -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 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 +_,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(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 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 (cacheUninitialized.getAsBoolean()) {
|
||||
return Optional.empty();
|
||||
} else {
|
||||
- ProfileResult profileResult = services.sessionService().fetchProfile(id, true);
|
||||
+ // Paper start - fill player profile events
|
||||
+ 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 = services.sessionService().fetchProfile(id.getFirst(), true);
|
||||
+ // Paper end - fill player profile events
|
||||
return Optional.ofNullable(profileResult).map(ProfileResult::profile);
|
||||
}
|
||||
- }, Util.backgroundExecutor().forName("fetchProfile"));
|
||||
+ }, Util.PROFILE_EXECUTOR); // Paper - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
@@ -210,9 +_,11 @@
|
||||
: CompletableFuture.completedFuture(Optional.empty());
|
||||
}
|
||||
|
||||
- public static CompletableFuture<Optional<GameProfile>> fetchGameProfile(UUID profileUuid) {
|
||||
- LoadingCache<UUID, CompletableFuture<Optional<GameProfile>>> loadingCache = profileCacheById;
|
||||
- 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;
|
||||
+ return loadingCache != null ? loadingCache.getUnchecked(new com.mojang.datafixers.util.Pair<>(uuid, name != null ? new com.mojang.authlib.GameProfile(uuid, name) : null)) : CompletableFuture.completedFuture(Optional.empty());
|
||||
+ // Paper end - player profile events
|
||||
}
|
||||
|
||||
@Override
|
||||
Reference in New Issue
Block a user