Allow For Default Titles in InventoryView Builders (#12013)

This commit is contained in:
Miles
2025-02-16 22:07:00 +00:00
committed by GitHub
parent 84609dc046
commit 8eb8e44ac3
18 changed files with 201 additions and 70 deletions

View File

@@ -1,5 +1,6 @@
package org.bukkit.inventory; package org.bukkit.inventory;
import net.kyori.adventure.text.Component;
import org.bukkit.Keyed; import org.bukkit.Keyed;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.Registry; import org.bukkit.Registry;
@@ -18,12 +19,14 @@ import org.bukkit.inventory.view.builder.InventoryViewBuilder;
import org.bukkit.inventory.view.builder.LocationInventoryViewBuilder; import org.bukkit.inventory.view.builder.LocationInventoryViewBuilder;
import org.bukkit.inventory.view.builder.MerchantInventoryViewBuilder; import org.bukkit.inventory.view.builder.MerchantInventoryViewBuilder;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
/** /**
* Represents different kinds of views, also known as menus, which can be * Represents different kinds of views, also known as menus, which can be
* created and viewed by the player. * created and viewed by the player.
*/ */
@NullMarked
@ApiStatus.Experimental @ApiStatus.Experimental
public interface MenuType extends Keyed, io.papermc.paper.world.flag.FeatureDependant { // Paper - make FeatureDependant public interface MenuType extends Keyed, io.papermc.paper.world.flag.FeatureDependant { // Paper - make FeatureDependant
@@ -138,6 +141,20 @@ public interface MenuType extends Keyed, io.papermc.paper.world.flag.FeatureDepe
*/ */
interface Typed<V extends InventoryView, B extends InventoryViewBuilder<V>> extends MenuType { interface Typed<V extends InventoryView, B extends InventoryViewBuilder<V>> extends MenuType {
/**
* Creates a view of the specified menu type.
* <p>
* The player provided to create this view must be the player the view
* is opened for. See {@link HumanEntity#openInventory(InventoryView)}
* for more information.
*
* @param player the player the view belongs to
* @return the created {@link InventoryView}
*/
default V create(HumanEntity player) {
return create(player, (Component) null);
}
/** /**
* Creates a view of the specified menu type. * Creates a view of the specified menu type.
* <p> * <p>
@@ -148,11 +165,10 @@ public interface MenuType extends Keyed, io.papermc.paper.world.flag.FeatureDepe
* @param player the player the view belongs to * @param player the player the view belongs to
* @param title the title of the view * @param title the title of the view
* @return the created {@link InventoryView} * @return the created {@link InventoryView}
* @deprecated Use {@link #create(HumanEntity, net.kyori.adventure.text.Component)} instead. * @deprecated Use {@link #create(HumanEntity, Component)} instead.
*/ */
@NotNull
@Deprecated(since = "1.21") // Paper - adventure @Deprecated(since = "1.21") // Paper - adventure
V create(@NotNull HumanEntity player, @NotNull String title); V create(HumanEntity player, @Nullable String title);
// Paper start - adventure // Paper start - adventure
/** /**
@@ -166,11 +182,9 @@ public interface MenuType extends Keyed, io.papermc.paper.world.flag.FeatureDepe
* @param title the title of the view * @param title the title of the view
* @return the created {@link InventoryView} * @return the created {@link InventoryView}
*/ */
@NotNull V create(HumanEntity player, @Nullable Component title);
V create(@NotNull HumanEntity player, @NotNull net.kyori.adventure.text.Component title);
// Paper end - adventure // Paper end - adventure
@NotNull
B builder(); B builder();
} }
@@ -186,8 +200,7 @@ public interface MenuType extends Keyed, io.papermc.paper.world.flag.FeatureDepe
* @param title the title of the view * @param title the title of the view
* @return the created {@link InventoryView} * @return the created {@link InventoryView}
*/ */
@NotNull InventoryView create(HumanEntity player, @Nullable Component title);
InventoryView create(@NotNull HumanEntity player, @NotNull net.kyori.adventure.text.Component title);
// Paper end - adventure // Paper end - adventure
/** /**
@@ -196,7 +209,6 @@ public interface MenuType extends Keyed, io.papermc.paper.world.flag.FeatureDepe
* *
* @return the typed MenuType. * @return the typed MenuType.
*/ */
@NotNull
MenuType.Typed<InventoryView, InventoryViewBuilder<InventoryView>> typed(); MenuType.Typed<InventoryView, InventoryViewBuilder<InventoryView>> typed();
/** /**
@@ -213,19 +225,16 @@ public interface MenuType extends Keyed, io.papermc.paper.world.flag.FeatureDepe
* @throws IllegalArgumentException if the provided viewClass cannot be * @throws IllegalArgumentException if the provided viewClass cannot be
* typed to this MenuType * typed to this MenuType
*/ */
@NotNull <V extends InventoryView, B extends InventoryViewBuilder<V>> MenuType.Typed<V, B> typed(final Class<V> viewClass) throws IllegalArgumentException;
<V extends InventoryView, B extends InventoryViewBuilder<V>> MenuType.Typed<V, B> typed(@NotNull final Class<V> viewClass) throws IllegalArgumentException;
/** /**
* Gets the {@link InventoryView} class of this MenuType. * Gets the {@link InventoryView} class of this MenuType.
* *
* @return the {@link InventoryView} class of this MenuType * @return the {@link InventoryView} class of this MenuType
*/ */
@NotNull
Class<? extends InventoryView> getInventoryViewClass(); Class<? extends InventoryView> getInventoryViewClass();
@NotNull private static <T extends MenuType> T get(final String key) {
private static <T extends MenuType> T get(@NotNull final String key) {
return (T) Registry.MENU.getOrThrow(NamespacedKey.minecraft(key)); return (T) Registry.MENU.getOrThrow(NamespacedKey.minecraft(key));
} }
} }

View File

@@ -4,6 +4,7 @@ import net.kyori.adventure.text.Component;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryView; import org.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.Nullable;
/** /**
* Generic Builder for InventoryView's with no special attributes or parameters * Generic Builder for InventoryView's with no special attributes or parameters
@@ -23,10 +24,10 @@ public interface InventoryViewBuilder<V extends InventoryView> {
/** /**
* Sets the title of the builder * Sets the title of the builder
* *
* @param title the title * @param title the title, or null for a default title
* @return this builder * @return this builder
*/ */
InventoryViewBuilder<V> title(final Component title); InventoryViewBuilder<V> title(@Nullable final Component title);
/** /**
* Builds this builder into a InventoryView * Builds this builder into a InventoryView

View File

@@ -4,7 +4,7 @@ import net.kyori.adventure.text.Component;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.inventory.InventoryView; import org.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jspecify.annotations.Nullable;
/** /**
* An InventoryViewBuilder that can be bound by location within the world * An InventoryViewBuilder that can be bound by location within the world
@@ -18,7 +18,7 @@ public interface LocationInventoryViewBuilder<V extends InventoryView> extends I
LocationInventoryViewBuilder<V> copy(); LocationInventoryViewBuilder<V> copy();
@Override @Override
LocationInventoryViewBuilder<V> title(final @NotNull Component title); LocationInventoryViewBuilder<V> title(final @Nullable Component title);
/** /**
* Determines whether or not the server should check if the player can reach * Determines whether or not the server should check if the player can reach

View File

@@ -5,7 +5,7 @@ import org.bukkit.Server;
import org.bukkit.inventory.InventoryView; import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.Merchant; import org.bukkit.inventory.Merchant;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jspecify.annotations.Nullable;
/** /**
* An InventoryViewBuilder for creating merchant views * An InventoryViewBuilder for creating merchant views
@@ -19,7 +19,7 @@ public interface MerchantInventoryViewBuilder<V extends InventoryView> extends I
MerchantInventoryViewBuilder<V> copy(); MerchantInventoryViewBuilder<V> copy();
@Override @Override
MerchantInventoryViewBuilder<V> title(final @NotNull Component title); MerchantInventoryViewBuilder<V> title(final @Nullable Component title);
/** /**
* Adds a merchant to this builder * Adds a merchant to this builder

View File

@@ -9,8 +9,12 @@
@Nullable @Nullable
@Override @Override
public AbstractContainerMenu createMenu(int containerId, Inventory playerInventory, Player player) { public AbstractContainerMenu createMenu(int containerId, Inventory playerInventory, Player player) {
@@ -106,7 +_,7 @@ @@ -103,10 +_,10 @@
return (Component)(second.hasCustomName() ? second.getDisplayName() : Component.translatable("container.chestDouble")); if (first.hasCustomName()) {
return first.getDisplayName();
} else {
- return (Component)(second.hasCustomName() ? second.getDisplayName() : Component.translatable("container.chestDouble"));
+ return (Component)(second.hasCustomName() ? second.getDisplayName() : Component.translatable("container.chestDouble")); // Paper - diff on change - CraftDoubleChestInventoryViewBuilder.defaultTitle
} }
} }
- }); - });

View File

@@ -37,3 +37,12 @@
protected ChestBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState blockState) { protected ChestBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState blockState) {
super(type, pos, blockState); super(type, pos, blockState);
} }
@@ -71,7 +_,7 @@
@Override
protected Component getDefaultName() {
- return Component.translatable("container.chest");
+ return Component.translatable("container.chest"); // Paper - diff on change - CraftStandardInventoryViewBuilder.defaultTitle
}
@Override

View File

@@ -2479,7 +2479,7 @@ public final class CraftServer implements Server {
@Override @Override
public @NotNull Merchant createMerchant() { public @NotNull Merchant createMerchant() {
return new CraftMerchantCustom(net.kyori.adventure.text.Component.empty()); return new CraftMerchantCustom();
} }
@Override @Override

View File

@@ -38,7 +38,7 @@ public class CraftMenuType<V extends InventoryView, B extends InventoryViewBuild
@Override @Override
public V create(final HumanEntity player, final String title) { public V create(final HumanEntity player, final String title) {
// Paper start - adventure // Paper start - adventure
return builder().title(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(title)).build(player); return builder().title(title != null ? net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(title) : null).build(player);
} }
@Override @Override
public V create(final HumanEntity player, final net.kyori.adventure.text.Component title) { public V create(final HumanEntity player, final net.kyori.adventure.text.Component title) {

View File

@@ -4,6 +4,7 @@ import com.google.common.base.Preconditions;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundEvents;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.trading.Merchant; import net.minecraft.world.item.trading.Merchant;
@@ -25,6 +26,11 @@ public class CraftMerchantCustom implements CraftMerchant {
this.merchant = new MinecraftMerchant(title); this.merchant = new MinecraftMerchant(title);
getMerchant().craftMerchant = this; getMerchant().craftMerchant = this;
} }
public CraftMerchantCustom() {
this.merchant = new MinecraftMerchant();
getMerchant().craftMerchant = this;
}
// Paper end // Paper end
@Override @Override
@@ -54,6 +60,10 @@ public class CraftMerchantCustom implements CraftMerchant {
Preconditions.checkArgument(title != null, "Title cannot be null"); Preconditions.checkArgument(title != null, "Title cannot be null");
this.title = io.papermc.paper.adventure.PaperAdventure.asVanilla(title); this.title = io.papermc.paper.adventure.PaperAdventure.asVanilla(title);
} }
public MinecraftMerchant() {
this.title = EntityType.VILLAGER.getDescription();
}
// Paper end // Paper end
@Override @Override

View File

@@ -17,6 +17,7 @@ import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BeaconBlockEntity; import net.minecraft.world.level.block.entity.BeaconBlockEntity;
import net.minecraft.world.level.block.entity.BlastFurnaceBlockEntity; import net.minecraft.world.level.block.entity.BlastFurnaceBlockEntity;
import net.minecraft.world.level.block.entity.BrewingStandBlockEntity; import net.minecraft.world.level.block.entity.BrewingStandBlockEntity;
import net.minecraft.world.level.block.entity.ChestBlockEntity;
import net.minecraft.world.level.block.entity.CrafterBlockEntity; import net.minecraft.world.level.block.entity.CrafterBlockEntity;
import net.minecraft.world.level.block.entity.DispenserBlockEntity; import net.minecraft.world.level.block.entity.DispenserBlockEntity;
import net.minecraft.world.level.block.entity.FurnaceBlockEntity; import net.minecraft.world.level.block.entity.FurnaceBlockEntity;
@@ -29,6 +30,7 @@ import org.bukkit.craftbukkit.inventory.CraftMerchant;
import org.bukkit.craftbukkit.inventory.view.builder.CraftAccessLocationInventoryViewBuilder; import org.bukkit.craftbukkit.inventory.view.builder.CraftAccessLocationInventoryViewBuilder;
import org.bukkit.craftbukkit.inventory.view.builder.CraftBlockEntityInventoryViewBuilder; import org.bukkit.craftbukkit.inventory.view.builder.CraftBlockEntityInventoryViewBuilder;
import org.bukkit.craftbukkit.inventory.view.builder.CraftDoubleChestInventoryViewBuilder; import org.bukkit.craftbukkit.inventory.view.builder.CraftDoubleChestInventoryViewBuilder;
import org.bukkit.craftbukkit.inventory.view.builder.CraftEnchantmentInventoryViewBuilder;
import org.bukkit.craftbukkit.inventory.view.builder.CraftMerchantInventoryViewBuilder; import org.bukkit.craftbukkit.inventory.view.builder.CraftMerchantInventoryViewBuilder;
import org.bukkit.craftbukkit.inventory.view.builder.CraftStandardInventoryViewBuilder; import org.bukkit.craftbukkit.inventory.view.builder.CraftStandardInventoryViewBuilder;
import org.bukkit.inventory.InventoryView; import org.bukkit.inventory.InventoryView;
@@ -87,7 +89,7 @@ public final class CraftMenus {
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftDoubleChestInventoryViewBuilder<>(handle))); return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftDoubleChestInventoryViewBuilder<>(handle)));
} }
if (menuType == MenuType.GENERIC_9X3) { if (menuType == MenuType.GENERIC_9X3) {
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.CHEST, null))); return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.CHEST, ChestBlockEntity::new, false)));
} }
// this isn't ideal as both dispenser and dropper are 3x3, InventoryType can't currently handle generic 3x3s with size 9 // this isn't ideal as both dispenser and dropper are 3x3, InventoryType can't currently handle generic 3x3s with size 9
// this needs to be removed when inventory creation is overhauled // this needs to be removed when inventory creation is overhauled
@@ -98,7 +100,7 @@ public final class CraftMenus {
return asType(new MenuTypeData<>(CrafterView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.CRAFTER, CrafterBlockEntity::new))); return asType(new MenuTypeData<>(CrafterView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.CRAFTER, CrafterBlockEntity::new)));
} }
if (menuType == MenuType.ANVIL) { if (menuType == MenuType.ANVIL) {
return asType(new MenuTypeData<>(AnvilView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, AnvilMenu::new))); return asType(new MenuTypeData<>(AnvilView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, Blocks.ANVIL)));
} }
if (menuType == MenuType.BEACON) { if (menuType == MenuType.BEACON) {
return asType(new MenuTypeData<>(BeaconView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.BEACON, BeaconBlockEntity::new))); return asType(new MenuTypeData<>(BeaconView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.BEACON, BeaconBlockEntity::new)));
@@ -110,16 +112,16 @@ public final class CraftMenus {
return asType(new MenuTypeData<>(BrewingStandView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.BREWING_STAND, BrewingStandBlockEntity::new))); return asType(new MenuTypeData<>(BrewingStandView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.BREWING_STAND, BrewingStandBlockEntity::new)));
} }
if (menuType == MenuType.CRAFTING) { if (menuType == MenuType.CRAFTING) {
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, CraftingMenu::new))); return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, Blocks.CRAFTING_TABLE)));
} }
if (menuType == MenuType.ENCHANTMENT) { if (menuType == MenuType.ENCHANTMENT) {
return asType(new MenuTypeData<>(EnchantmentView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, EnchantmentMenu::new))); return asType(new MenuTypeData<>(EnchantmentView.class, () -> new CraftEnchantmentInventoryViewBuilder(handle)));
} }
if (menuType == MenuType.FURNACE) { if (menuType == MenuType.FURNACE) {
return asType(new MenuTypeData<>(FurnaceView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.FURNACE, FurnaceBlockEntity::new))); return asType(new MenuTypeData<>(FurnaceView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.FURNACE, FurnaceBlockEntity::new)));
} }
if (menuType == MenuType.GRINDSTONE) { if (menuType == MenuType.GRINDSTONE) {
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, GrindstoneMenu::new))); return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, Blocks.GRINDSTONE)));
} }
// We really don't need to be creating a tile entity for hopper but currently InventoryType doesn't have capacity // We really don't need to be creating a tile entity for hopper but currently InventoryType doesn't have capacity
// to understand otherwise // to understand otherwise
@@ -131,7 +133,7 @@ public final class CraftMenus {
return asType(new MenuTypeData<>(LecternView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.LECTERN, LecternBlockEntity::new))); return asType(new MenuTypeData<>(LecternView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.LECTERN, LecternBlockEntity::new)));
} }
if (menuType == MenuType.LOOM) { if (menuType == MenuType.LOOM) {
return asType(new MenuTypeData<>(LoomView.class, () -> new CraftStandardInventoryViewBuilder<>(handle))); return asType(new MenuTypeData<>(LoomView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, Blocks.LOOM)));
} }
if (menuType == MenuType.MERCHANT) { if (menuType == MenuType.MERCHANT) {
return asType(new MenuTypeData<>(MerchantView.class, () -> new CraftMerchantInventoryViewBuilder<>(handle))); return asType(new MenuTypeData<>(MerchantView.class, () -> new CraftMerchantInventoryViewBuilder<>(handle)));
@@ -140,16 +142,16 @@ public final class CraftMenus {
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.SHULKER_BOX, ShulkerBoxBlockEntity::new))); return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.SHULKER_BOX, ShulkerBoxBlockEntity::new)));
} }
if (menuType == MenuType.SMITHING) { if (menuType == MenuType.SMITHING) {
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, SmithingMenu::new))); return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, Blocks.SMITHING_TABLE)));
} }
if (menuType == MenuType.SMOKER) { if (menuType == MenuType.SMOKER) {
return asType(new MenuTypeData<>(FurnaceView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.SMOKER, SmokerBlockEntity::new))); return asType(new MenuTypeData<>(FurnaceView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.SMOKER, SmokerBlockEntity::new)));
} }
if (menuType == MenuType.CARTOGRAPHY_TABLE) { if (menuType == MenuType.CARTOGRAPHY_TABLE) {
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, CartographyTableMenu::new))); return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, Blocks.CARTOGRAPHY_TABLE)));
} }
if (menuType == MenuType.STONECUTTER) { if (menuType == MenuType.STONECUTTER) {
return asType(new MenuTypeData<>(StonecutterView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, StonecutterMenu::new))); return asType(new MenuTypeData<>(StonecutterView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, Blocks.STONECUTTER)));
} }
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftStandardInventoryViewBuilder<>(handle))); return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftStandardInventoryViewBuilder<>(handle)));

View File

@@ -10,21 +10,22 @@ import org.bukkit.craftbukkit.entity.CraftHumanEntity;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryView; import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.view.builder.InventoryViewBuilder; import org.bukkit.inventory.view.builder.InventoryViewBuilder;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.jspecify.annotations.Nullable;
public abstract class CraftAbstractInventoryViewBuilder<V extends InventoryView> implements InventoryViewBuilder<V> { public abstract class CraftAbstractInventoryViewBuilder<V extends InventoryView> implements InventoryViewBuilder<V> {
protected final MenuType<?> handle; protected final MenuType<?> handle;
protected boolean checkReachable = false; protected boolean checkReachable = false;
protected @MonotonicNonNull Component title = null; protected @Nullable Component title = null;
protected net.minecraft.network.chat.Component defaultTitle = null;
public CraftAbstractInventoryViewBuilder(final MenuType<?> handle) { public CraftAbstractInventoryViewBuilder(final MenuType<?> handle) {
this.handle = handle; this.handle = handle;
} }
@Override @Override
public InventoryViewBuilder<V> title(final Component title) { public InventoryViewBuilder<V> title(final @Nullable Component title) {
this.title = title; this.title = title;
return this; return this;
} }
@@ -33,14 +34,14 @@ public abstract class CraftAbstractInventoryViewBuilder<V extends InventoryView>
@Override @Override
public V build(final HumanEntity player) { public V build(final HumanEntity player) {
Preconditions.checkArgument(player != null, "The given player must not be null"); Preconditions.checkArgument(player != null, "The given player must not be null");
Preconditions.checkArgument(this.title != null, "The given title must not be null");
Preconditions.checkArgument(player instanceof CraftHumanEntity, "The given player must be a CraftHumanEntity"); Preconditions.checkArgument(player instanceof CraftHumanEntity, "The given player must be a CraftHumanEntity");
final CraftHumanEntity craftHuman = (CraftHumanEntity) player; final CraftHumanEntity craftHuman = (CraftHumanEntity) player;
Preconditions.checkArgument(craftHuman.getHandle() instanceof ServerPlayer, "The given player must be an EntityPlayer"); Preconditions.checkArgument(craftHuman.getHandle() instanceof ServerPlayer, "The given player must be an ServerPlayer");
final ServerPlayer serverPlayer = (ServerPlayer) craftHuman.getHandle(); final ServerPlayer serverPlayer = (ServerPlayer) craftHuman.getHandle();
final AbstractContainerMenu container = buildContainer(serverPlayer); final AbstractContainerMenu container = buildContainer(serverPlayer);
container.checkReachable = this.checkReachable; container.checkReachable = this.checkReachable;
container.setTitle(PaperAdventure.asVanilla(this.title)); container.setTitle(this.title != null ? PaperAdventure.asVanilla(this.title) : this.defaultTitle);
return (V) container.getBukkitView(); return (V) container.getBukkitView();
} }

View File

@@ -22,7 +22,7 @@ public abstract class CraftAbstractLocationInventoryViewBuilder<V extends Invent
} }
@Override @Override
public LocationInventoryViewBuilder<V> title(final Component title) { public LocationInventoryViewBuilder<V> title(final @Nullable Component title) {
return (LocationInventoryViewBuilder<V>) super.title(title); return (LocationInventoryViewBuilder<V>) super.title(title);
} }

View File

@@ -1,37 +1,50 @@
package org.bukkit.craftbukkit.inventory.view.builder; package org.bukkit.craftbukkit.inventory.view.builder;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.ContainerLevelAccess; import net.minecraft.world.inventory.ContainerLevelAccess;
import net.minecraft.world.inventory.MenuType; import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.inventory.InventoryView; import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.view.builder.LocationInventoryViewBuilder; import org.bukkit.inventory.view.builder.LocationInventoryViewBuilder;
public class CraftAccessLocationInventoryViewBuilder<V extends InventoryView> extends CraftAbstractLocationInventoryViewBuilder<V> { public class CraftAccessLocationInventoryViewBuilder<V extends InventoryView> extends CraftAbstractLocationInventoryViewBuilder<V> {
private final CraftAccessContainerObjectBuilder containerBuilder; private final Block block;
public CraftAccessLocationInventoryViewBuilder(final MenuType<?> handle, final CraftAccessContainerObjectBuilder containerBuilder) { public CraftAccessLocationInventoryViewBuilder(final MenuType<?> handle, final Block block) {
super(handle); super(handle);
this.containerBuilder = containerBuilder; this.block = block;
} }
@Override @Override
protected AbstractContainerMenu buildContainer(final ServerPlayer player) { protected AbstractContainerMenu buildContainer(final ServerPlayer player) {
final ContainerLevelAccess access; final BlockState effectiveBlockState;
if (super.position == null) { final BlockPos effectiveBlockPos;
access = ContainerLevelAccess.create(player.level(), player.blockPosition()); final Level effectiveLevel;
if (super.position != null) {
effectiveBlockPos = super.position;
effectiveLevel = super.world;
effectiveBlockState = super.world.getBlockState(position);
} else { } else {
access = ContainerLevelAccess.create(super.world, super.position); effectiveBlockPos = player.blockPosition();
effectiveLevel = player.level();
effectiveBlockState = block.defaultBlockState();
} }
return this.containerBuilder.build(player.nextContainerCounter(), player.getInventory(), access); final MenuProvider provider = block.getMenuProvider(effectiveBlockState, effectiveLevel, effectiveBlockPos);
super.defaultTitle = provider.getDisplayName();
return provider.createMenu(player.nextContainerCounter(), player.getInventory(), player);
} }
@Override @Override
public LocationInventoryViewBuilder<V> copy() { public LocationInventoryViewBuilder<V> copy() {
final CraftAccessLocationInventoryViewBuilder<V> copy = new CraftAccessLocationInventoryViewBuilder<>(this.handle, this.containerBuilder); final CraftAccessLocationInventoryViewBuilder<V> copy = new CraftAccessLocationInventoryViewBuilder<>(this.handle, this.block);
copy.world = super.world; copy.world = super.world;
copy.position = super.position; copy.position = super.position;
copy.checkReachable = super.checkReachable; copy.checkReachable = super.checkReachable;

View File

@@ -16,10 +16,25 @@ import org.jspecify.annotations.Nullable;
public class CraftBlockEntityInventoryViewBuilder<V extends InventoryView> extends CraftAbstractLocationInventoryViewBuilder<V> { public class CraftBlockEntityInventoryViewBuilder<V extends InventoryView> extends CraftAbstractLocationInventoryViewBuilder<V> {
private final Block block; private final Block block;
private final @Nullable CraftTileInventoryBuilder builder; private final boolean useFakeBlockEntity;
private final @Nullable CraftBlockInventoryBuilder builder;
public CraftBlockEntityInventoryViewBuilder(final MenuType<?> handle, final Block block, final @Nullable CraftTileInventoryBuilder builder) { public CraftBlockEntityInventoryViewBuilder(
final MenuType<?> handle,
final Block block,
final @Nullable CraftBlockInventoryBuilder builder
) {
this(handle, block, builder, true);
}
public CraftBlockEntityInventoryViewBuilder(
final MenuType<?> handle,
final Block block,
final @Nullable CraftBlockInventoryBuilder builder,
final boolean useFakeBlockEntity
) {
super(handle); super(handle);
this.useFakeBlockEntity = useFakeBlockEntity;
this.block = block; this.block = block;
this.builder = builder; this.builder = builder;
} }
@@ -32,35 +47,44 @@ public class CraftBlockEntityInventoryViewBuilder<V extends InventoryView> exten
if (this.position == null) { if (this.position == null) {
this.position = player.blockPosition(); this.position = player.blockPosition();
return buildFakeBlockEntity(player);
} }
final BlockEntity entity = this.world.getBlockEntity(position); final BlockEntity entity = this.world.getBlockEntity(position);
if (!(entity instanceof final MenuConstructor container)) { if (!(entity instanceof final MenuConstructor container)) {
return buildFakeTile(player); return buildFakeBlockEntity(player);
} }
final AbstractContainerMenu atBlock = container.createMenu(player.nextContainerCounter(), player.getInventory(), player); final AbstractContainerMenu atBlock = container.createMenu(player.nextContainerCounter(), player.getInventory(), player);
if (atBlock.getType() != super.handle) { if (atBlock.getType() != super.handle) {
return buildFakeTile(player); return buildFakeBlockEntity(player);
} }
if (!(entity instanceof final MenuProvider provider)) {
throw new IllegalStateException("Provided blockEntity during MenuType creation can not find a default title! This is a bug!");
}
super.defaultTitle = provider.getDisplayName();
return atBlock; return atBlock;
} }
private AbstractContainerMenu buildFakeTile(final ServerPlayer player) { private AbstractContainerMenu buildFakeBlockEntity(final ServerPlayer player) {
if (this.builder == null) { final MenuProvider inventory = this.builder.build(this.position, this.block.defaultBlockState());
if (inventory instanceof final BlockEntity blockEntity) {
blockEntity.setLevel(this.world);
super.defaultTitle = inventory.getDisplayName();
}
if (!this.useFakeBlockEntity) { // gets around open noise for chest
return handle.create(player.nextContainerCounter(), player.getInventory()); return handle.create(player.nextContainerCounter(), player.getInventory());
} }
final MenuProvider inventory = this.builder.build(this.position, this.block.defaultBlockState());
if (inventory instanceof final BlockEntity tile) {
tile.setLevel(this.world);
}
return inventory.createMenu(player.nextContainerCounter(), player.getInventory(), player); return inventory.createMenu(player.nextContainerCounter(), player.getInventory(), player);
} }
@Override @Override
public LocationInventoryViewBuilder<V> copy() { public LocationInventoryViewBuilder<V> copy() {
final CraftBlockEntityInventoryViewBuilder<V> copy = new CraftBlockEntityInventoryViewBuilder<>(super.handle, this.block, this.builder); final CraftBlockEntityInventoryViewBuilder<V> copy = new CraftBlockEntityInventoryViewBuilder<>(super.handle, this.block, this.builder, this.useFakeBlockEntity);
copy.world = this.world; copy.world = this.world;
copy.position = this.position; copy.position = this.position;
copy.checkReachable = super.checkReachable; copy.checkReachable = super.checkReachable;
@@ -68,7 +92,7 @@ public class CraftBlockEntityInventoryViewBuilder<V extends InventoryView> exten
return copy; return copy;
} }
public interface CraftTileInventoryBuilder { public interface CraftBlockInventoryBuilder {
MenuProvider build(BlockPos blockPosition, BlockState blockData); MenuProvider build(BlockPos blockPosition, BlockState blockData);
} }
} }

View File

@@ -1,5 +1,6 @@
package org.bukkit.craftbukkit.inventory.view.builder; package org.bukkit.craftbukkit.inventory.view.builder;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.MenuProvider; import net.minecraft.world.MenuProvider;
import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.AbstractContainerMenu;
@@ -15,6 +16,7 @@ public class CraftDoubleChestInventoryViewBuilder<V extends InventoryView> exten
public CraftDoubleChestInventoryViewBuilder(final MenuType<?> handle) { public CraftDoubleChestInventoryViewBuilder(final MenuType<?> handle) {
super(handle); super(handle);
super.defaultTitle = Component.translatable("container.chestDouble");
} }
@Override @Override
@@ -24,7 +26,9 @@ public class CraftDoubleChestInventoryViewBuilder<V extends InventoryView> exten
} }
final ChestBlock chest = (ChestBlock) Blocks.CHEST; final ChestBlock chest = (ChestBlock) Blocks.CHEST;
final DoubleBlockCombiner.NeighborCombineResult<? extends ChestBlockEntity> result = chest.combine(super.world.getBlockState(super.position), super.world, super.position, false); final DoubleBlockCombiner.NeighborCombineResult<? extends ChestBlockEntity> result = chest.combine(
super.world.getBlockState(super.position), super.world, super.position, false
);
if (result instanceof DoubleBlockCombiner.NeighborCombineResult.Single<? extends ChestBlockEntity>) { if (result instanceof DoubleBlockCombiner.NeighborCombineResult.Single<? extends ChestBlockEntity>) {
return handle.create(player.nextContainerCounter(), player.getInventory()); return handle.create(player.nextContainerCounter(), player.getInventory());
} }
@@ -33,6 +37,7 @@ public class CraftDoubleChestInventoryViewBuilder<V extends InventoryView> exten
if (combined == null) { if (combined == null) {
return handle.create(player.nextContainerCounter(), player.getInventory()); return handle.create(player.nextContainerCounter(), player.getInventory());
} }
return combined.createMenu(player.nextContainerCounter(), player.getInventory(), player); return combined.createMenu(player.nextContainerCounter(), player.getInventory(), player);
} }

View File

@@ -0,0 +1,40 @@
package org.bukkit.craftbukkit.inventory.view.builder;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.ContainerLevelAccess;
import net.minecraft.world.inventory.EnchantmentMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.EnchantingTableBlockEntity;
import org.bukkit.inventory.view.EnchantmentView;
public class CraftEnchantmentInventoryViewBuilder extends CraftAbstractLocationInventoryViewBuilder<EnchantmentView> {
public CraftEnchantmentInventoryViewBuilder(final MenuType<?> handle) {
super(handle);
}
@Override
protected AbstractContainerMenu buildContainer(final ServerPlayer player) {
if (this.world == null) {
this.world = player.level();
}
if (this.position == null) {
this.position = player.blockPosition();
super.defaultTitle = new EnchantingTableBlockEntity(this.position, Blocks.ENCHANTING_TABLE.defaultBlockState()).getDisplayName();
return new EnchantmentMenu(player.nextContainerCounter(), player.getInventory(), ContainerLevelAccess.create(this.world, this.position));
}
final BlockEntity entity = this.world.getBlockEntity(position);
if (entity instanceof final EnchantingTableBlockEntity enchantingBlockEntity) {
super.defaultTitle = enchantingBlockEntity.getDisplayName();
} else {
super.defaultTitle = new EnchantingTableBlockEntity(this.position, Blocks.ENCHANTING_TABLE.defaultBlockState()).getDisplayName();
}
return new EnchantmentMenu(player.nextContainerCounter(), player.getInventory(), ContainerLevelAccess.create(this.world, this.position));
}
}

View File

@@ -4,6 +4,7 @@ import com.google.common.base.Preconditions;
import io.papermc.paper.adventure.PaperAdventure; import io.papermc.paper.adventure.PaperAdventure;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.npc.AbstractVillager;
import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType; import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.inventory.MerchantMenu; import net.minecraft.world.inventory.MerchantMenu;
@@ -25,7 +26,7 @@ public class CraftMerchantInventoryViewBuilder<V extends InventoryView> extends
} }
@Override @Override
public MerchantInventoryViewBuilder<V> title(final Component title) { public MerchantInventoryViewBuilder<V> title(final @Nullable Component title) {
return (MerchantInventoryViewBuilder<V>) super.title(title); return (MerchantInventoryViewBuilder<V>) super.title(title);
} }
@@ -44,24 +45,34 @@ public class CraftMerchantInventoryViewBuilder<V extends InventoryView> extends
@Override @Override
public V build(final HumanEntity player) { public V build(final HumanEntity player) {
Preconditions.checkArgument(player != null, "The given player must not be null"); Preconditions.checkArgument(player != null, "The given player must not be null");
Preconditions.checkArgument(this.title != null, "The given title must not be null");
Preconditions.checkArgument(player instanceof CraftHumanEntity, "The given player must be a CraftHumanEntity"); Preconditions.checkArgument(player instanceof CraftHumanEntity, "The given player must be a CraftHumanEntity");
final CraftHumanEntity craftHuman = (CraftHumanEntity) player; final CraftHumanEntity craftHuman = (CraftHumanEntity) player;
Preconditions.checkArgument(craftHuman.getHandle() instanceof ServerPlayer, "The given player must be an EntityPlayer"); Preconditions.checkArgument(craftHuman.getHandle() instanceof ServerPlayer, "The given player must be an ServerPlayer");
final ServerPlayer serverPlayer = (ServerPlayer) craftHuman.getHandle(); final ServerPlayer serverPlayer = (ServerPlayer) craftHuman.getHandle();
final MerchantMenu container; final MerchantMenu container;
if (this.merchant == null) { if (this.merchant == null) {
container = new MerchantMenu(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), new CraftMerchantCustom(title).getMerchant()); this.merchant = this.title == null ? new CraftMerchantCustom().getMerchant() : new CraftMerchantCustom(title).getMerchant();
} else {
container = new MerchantMenu(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), this.merchant);
} }
container = new MerchantMenu(serverPlayer.nextContainerCounter(), serverPlayer.getInventory(), this.merchant);
container.checkReachable = super.checkReachable; container.checkReachable = super.checkReachable;
container.setTitle(PaperAdventure.asVanilla(this.title)); setDefaultTitle(this.merchant);
container.setTitle(super.title != null ? PaperAdventure.asVanilla(this.title) : super.defaultTitle);
return (V) container.getBukkitView(); return (V) container.getBukkitView();
} }
private void setDefaultTitle(final net.minecraft.world.item.trading.Merchant merchant) {
if (merchant instanceof final AbstractVillager villager) {
super.defaultTitle = villager.getDisplayName();
} else if (merchant instanceof final CraftMerchantCustom.MinecraftMerchant custom) {
super.defaultTitle = custom.getScoreboardDisplayName();
} else {
throw new IllegalStateException("Provided merchant during MenuType creation can not find a default title! This is a bug!");
}
}
@Override @Override
protected AbstractContainerMenu buildContainer(final ServerPlayer player) { protected AbstractContainerMenu buildContainer(final ServerPlayer player) {
throw new UnsupportedOperationException("buildContainer is not supported for CraftMerchantInventoryViewBuilder"); throw new UnsupportedOperationException("buildContainer is not supported for CraftMerchantInventoryViewBuilder");

View File

@@ -1,5 +1,6 @@
package org.bukkit.craftbukkit.inventory.view.builder; package org.bukkit.craftbukkit.inventory.view.builder;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType; import net.minecraft.world.inventory.MenuType;
@@ -10,6 +11,7 @@ public class CraftStandardInventoryViewBuilder<V extends InventoryView> extends
public CraftStandardInventoryViewBuilder(final MenuType<?> handle) { public CraftStandardInventoryViewBuilder(final MenuType<?> handle) {
super(handle); super(handle);
super.defaultTitle = Component.translatable("container.chest");
} }
@Override @Override