Skip to content

Inventory Support on InventoryViewBuilders #12518

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions menu-support.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
AnvilMenu -> No
BeaconMenu -> No
BlastFurnaceMenu -> Yes
BrewingStandMenu -> Yes
CartographyTableMenu -> No
ChestMenu -> Yes
CrafterMenu -> No
CraftingMenu -> No
DispenserMenu -> Yes
EnchantmentMenu -> No
FuranceMenu -> Yes
GrindstoneMenu -> No
HopperMenu -> Yes
LecternMenu -> No (Possible)
LoomMenu -> No
MerchantMenu -> No
ShulkerBoxMenu -> Yes
SmithingMenu -> No
SmokerMenu -> Yes
StonecutterMenu -> No

--- Possible ---
BlastFuranceMenu | BlockEntity | Implemented
BrewingStandMenu | BlockEntity | Implemented
ChestMenu | DoubleChest, BlockEntity, Standard | partial impl
DispenserMenu | BlockEntity | Implemented
FurnaceMenu | BlockEntity | Implemented
HopperMenu | BlockEntity | Implemented
ShulkerBoxMenu | BlockEntity | Implemented
SmokerMenu | BlockEntity | Implemented
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.bukkit.inventory.view.builder;

import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;

/**
* Capability for InventoryViewBuilders to support adding Inventories directly.
*
* @param <V> the type of view produced by the builder
*/
public interface InventorySupport<V extends InventoryView> {

/**
* Sets the inventory to be used by the inventory supporting builder
* <p>
* Note setting an Inventory will clear all other settings besides the title.
*
* @param inventory the inventory
* @return the builder instance
*/
InventoryViewBuilder<V> inventory(Inventory inventory);
}
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ public void openInventory(InventoryView inventory) {
}

// Now open the window
MenuType<?> windowType = CraftContainer.getNotchInventoryType(inventory.getTopInventory());
MenuType<?> windowType = container.getType();
// we can open these now, delegate for now
if (windowType == MenuType.MERCHANT) {
CraftMenus.openMerchantMenu(player, (MerchantMenu) container);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,16 @@
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.npc.Villager;
import net.minecraft.world.inventory.BlastFurnaceMenu;
import net.minecraft.world.inventory.BrewingStandMenu;
import net.minecraft.world.inventory.ChestMenu;
import net.minecraft.world.inventory.DispenserMenu;
import net.minecraft.world.inventory.FurnaceMenu;
import net.minecraft.world.inventory.HopperMenu;
import net.minecraft.world.inventory.MerchantMenu;
import net.minecraft.world.inventory.ShulkerBoxMenu;
import net.minecraft.world.inventory.SimpleContainerData;
import net.minecraft.world.inventory.SmokerMenu;
import net.minecraft.world.item.trading.Merchant;
import net.minecraft.world.item.trading.MerchantOffers;
import net.minecraft.world.level.block.Blocks;
Expand All @@ -24,6 +33,7 @@
import org.bukkit.craftbukkit.inventory.view.builder.CraftBlockEntityInventoryViewBuilder;
import org.bukkit.craftbukkit.inventory.view.builder.CraftDoubleChestInventoryViewBuilder;
import org.bukkit.craftbukkit.inventory.view.builder.CraftEnchantmentInventoryViewBuilder;
import org.bukkit.craftbukkit.inventory.view.builder.CraftInventorySupportBlockEntityInventoryViewBuilder;
import org.bukkit.craftbukkit.inventory.view.builder.CraftMerchantInventoryViewBuilder;
import org.bukkit.craftbukkit.inventory.view.builder.CraftStandardInventoryViewBuilder;
import org.bukkit.inventory.InventoryView;
Expand All @@ -43,6 +53,8 @@

import java.util.function.Supplier;

import static org.bukkit.craftbukkit.inventory.view.builder.CraftInventorySupportBlockEntityInventoryViewBuilder.SimpleMenuBuilder.chest;

@NullMarked
public final class CraftMenus {

Expand Down Expand Up @@ -78,16 +90,28 @@ public static void openMerchantMenu(final ServerPlayer player, final MerchantMen
public static <V extends InventoryView, B extends InventoryViewBuilder<V>> MenuTypeData<V, B> getMenuTypeData(final CraftMenuType<?, ?> menuType) {
final net.minecraft.world.inventory.MenuType<?> handle = menuType.getHandle();
// this sucks horribly but it should work for now
if (menuType == MenuType.GENERIC_9X6) {
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftDoubleChestInventoryViewBuilder<>(handle)));
if (menuType == MenuType.GENERIC_9X1) {
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftStandardInventoryViewBuilder<>(handle, chest(1, handle))));
}
if (menuType == MenuType.GENERIC_9X2) {
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftStandardInventoryViewBuilder<>(handle, chest(2, handle))));
}
if (menuType == MenuType.GENERIC_9X3) {
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.CHEST, ChestBlockEntity::new, false)));
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftInventorySupportBlockEntityInventoryViewBuilder<>(handle, Blocks.CHEST, ChestBlockEntity::new, (id, pi, container) -> new ChestMenu(net.minecraft.world.inventory.MenuType.GENERIC_9x3, id, pi, container, 3), false)));
}
if (menuType == MenuType.GENERIC_9X4) {
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftStandardInventoryViewBuilder<>(handle, chest(4, handle))));
}
if (menuType == MenuType.GENERIC_9X5) {
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftStandardInventoryViewBuilder<>(handle, chest(5, handle))));
}
if (menuType == MenuType.GENERIC_9X6) {
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftDoubleChestInventoryViewBuilder<>(handle)));
}
// 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
if (menuType == MenuType.GENERIC_3X3) {
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.DISPENSER, DispenserBlockEntity::new)));
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftInventorySupportBlockEntityInventoryViewBuilder<>(handle, Blocks.DISPENSER, DispenserBlockEntity::new, DispenserMenu::new)));
}
if (menuType == MenuType.CRAFTER_3X3) {
return asType(new MenuTypeData<>(CrafterView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.CRAFTER, CrafterBlockEntity::new)));
Expand All @@ -99,10 +123,10 @@ public static <V extends InventoryView, B extends InventoryViewBuilder<V>> MenuT
return asType(new MenuTypeData<>(BeaconView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.BEACON, BeaconBlockEntity::new)));
}
if (menuType == MenuType.BLAST_FURNACE) {
return asType(new MenuTypeData<>(FurnaceView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.BLAST_FURNACE, BlastFurnaceBlockEntity::new)));
return asType(new MenuTypeData<>(FurnaceView.class, () -> new CraftInventorySupportBlockEntityInventoryViewBuilder<>(handle, Blocks.BLAST_FURNACE, BlastFurnaceBlockEntity::new, (id, pi, container) -> new BlastFurnaceMenu(id ,pi ,container, new SimpleContainerData(4)))));
}
if (menuType == MenuType.BREWING_STAND) {
return asType(new MenuTypeData<>(BrewingStandView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.BREWING_STAND, BrewingStandBlockEntity::new)));
return asType(new MenuTypeData<>(BrewingStandView.class, () -> new CraftInventorySupportBlockEntityInventoryViewBuilder<>(handle, Blocks.BREWING_STAND, BrewingStandBlockEntity::new, (id, pi, c) -> new BrewingStandMenu(id, pi, c, new SimpleContainerData(5)))));
}
if (menuType == MenuType.CRAFTING) {
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, Blocks.CRAFTING_TABLE)));
Expand All @@ -111,15 +135,15 @@ public static <V extends InventoryView, B extends InventoryViewBuilder<V>> MenuT
return asType(new MenuTypeData<>(EnchantmentView.class, () -> new CraftEnchantmentInventoryViewBuilder(handle)));
}
if (menuType == MenuType.FURNACE) {
return asType(new MenuTypeData<>(FurnaceView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.FURNACE, FurnaceBlockEntity::new)));
return asType(new MenuTypeData<>(FurnaceView.class, () -> new CraftInventorySupportBlockEntityInventoryViewBuilder<>(handle, Blocks.FURNACE, FurnaceBlockEntity::new, (id, pi ,c) -> new FurnaceMenu(id, pi, c, new SimpleContainerData(4)))));
}
if (menuType == MenuType.GRINDSTONE) {
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, Blocks.GRINDSTONE)));
}
// We really don't need to be creating a block entity for hopper but currently InventoryType doesn't have capacity
// to understand otherwise
if (menuType == MenuType.HOPPER) {
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.HOPPER, HopperBlockEntity::new)));
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftInventorySupportBlockEntityInventoryViewBuilder<>(handle, Blocks.HOPPER, HopperBlockEntity::new, HopperMenu::new)));
}
// We also don't need to create a block entity for lectern, but again InventoryType isn't smart enough to know any better
if (menuType == MenuType.LECTERN) {
Expand All @@ -132,13 +156,13 @@ public static <V extends InventoryView, B extends InventoryViewBuilder<V>> MenuT
return asType(new MenuTypeData<>(MerchantView.class, () -> new CraftMerchantInventoryViewBuilder<>(handle)));
}
if (menuType == MenuType.SHULKER_BOX) {
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.SHULKER_BOX, ShulkerBoxBlockEntity::new)));
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftInventorySupportBlockEntityInventoryViewBuilder<>(handle, Blocks.SHULKER_BOX, ShulkerBoxBlockEntity::new, ShulkerBoxMenu::new)));
}
if (menuType == MenuType.SMITHING) {
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, Blocks.SMITHING_TABLE)));
}
if (menuType == MenuType.SMOKER) {
return asType(new MenuTypeData<>(FurnaceView.class, () -> new CraftBlockEntityInventoryViewBuilder<>(handle, Blocks.SMOKER, SmokerBlockEntity::new)));
return asType(new MenuTypeData<>(FurnaceView.class, () -> new CraftInventorySupportBlockEntityInventoryViewBuilder<>(handle, Blocks.SMOKER, SmokerBlockEntity::new, (id, pi, c) -> new SmokerMenu(id, pi, c, new SimpleContainerData(4)))));
}
if (menuType == MenuType.CARTOGRAPHY_TABLE) {
return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, Blocks.CARTOGRAPHY_TABLE)));
Expand All @@ -147,7 +171,7 @@ public static <V extends InventoryView, B extends InventoryViewBuilder<V>> MenuT
return asType(new MenuTypeData<>(StonecutterView.class, () -> new CraftAccessLocationInventoryViewBuilder<>(handle, Blocks.STONECUTTER)));
}

return asType(new MenuTypeData<>(InventoryView.class, () -> new CraftStandardInventoryViewBuilder<>(handle)));
throw new IllegalArgumentException("No menu type data allocated for " + menuType.getHandle());
}

@SuppressWarnings("unchecked")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@

public class CraftBlockEntityInventoryViewBuilder<V extends InventoryView> extends CraftAbstractLocationInventoryViewBuilder<V> {

private final Block block;
private final boolean useFakeBlockEntity;
private final @Nullable CraftBlockInventoryBuilder builder;
protected final Block block;
protected final boolean useFakeBlockEntity;
protected final @Nullable CraftBlockInventoryBuilder builder;

public CraftBlockEntityInventoryViewBuilder(
final MenuType<?> handle,
Expand Down Expand Up @@ -68,7 +68,7 @@ protected AbstractContainerMenu buildContainer(final ServerPlayer player) {
return atBlock;
}

private AbstractContainerMenu buildFakeBlockEntity(final ServerPlayer player) {
protected AbstractContainerMenu buildFakeBlockEntity(final ServerPlayer player) {
final MenuProvider inventory = this.builder.build(this.position, this.block.defaultBlockState());
if (inventory instanceof final BlockEntity blockEntity) {
blockEntity.setLevel(this.world);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,47 @@
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.ChestMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.ChestBlock;
import net.minecraft.world.level.block.DoubleBlockCombiner;
import net.minecraft.world.level.block.entity.ChestBlockEntity;
import org.bukkit.craftbukkit.inventory.CraftInventory;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.view.builder.InventorySupport;
import org.bukkit.inventory.view.builder.InventoryViewBuilder;
import org.bukkit.inventory.view.builder.LocationInventoryViewBuilder;

public class CraftDoubleChestInventoryViewBuilder<V extends InventoryView> extends CraftAbstractLocationInventoryViewBuilder<V> {
public class CraftDoubleChestInventoryViewBuilder<V extends InventoryView> extends CraftAbstractLocationInventoryViewBuilder<V> implements InventorySupport<V> {

private Inventory inventory;

public CraftDoubleChestInventoryViewBuilder(final MenuType<?> handle) {
super(handle);
super.defaultTitle = Component.translatable("container.chestDouble");
}

@Override
public InventoryViewBuilder<V> inventory(final Inventory inventory) {
this.inventory = inventory;
return this;
}

@Override
protected AbstractContainerMenu buildContainer(final ServerPlayer player) {
if (inventory != null) {
return ChestMenu.sixRows(player.nextContainerCounter(), player.getInventory(), ((CraftInventory) this.inventory).getInventory());
}

if (super.world == null) {
return handle.create(player.nextContainerCounter(), player.getInventory());
}

final ChestBlock chest = (ChestBlock) Blocks.CHEST;
final DoubleBlockCombiner.NeighborCombineResult<? extends ChestBlockEntity> result = chest.combine(
super.world.getBlockState(super.position), super.world, super.position, false
super.world.getBlockState(super.position), super.world, super.position, false
);
if (result instanceof DoubleBlockCombiner.NeighborCombineResult.Single<? extends ChestBlockEntity>) {
return handle.create(player.nextContainerCounter(), player.getInventory());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package org.bukkit.craftbukkit.inventory.view.builder;

import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.ChestMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import org.bukkit.craftbukkit.inventory.CraftInventory;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.view.builder.InventorySupport;
import org.bukkit.inventory.view.builder.LocationInventoryViewBuilder;
import org.jspecify.annotations.Nullable;

// This name SUCKS big time
public class CraftInventorySupportBlockEntityInventoryViewBuilder<V extends InventoryView> extends CraftBlockEntityInventoryViewBuilder<V> implements InventorySupport<V> {
private final SimpleMenuBuilder smb;

private @Nullable Inventory inventory;

public CraftInventorySupportBlockEntityInventoryViewBuilder(final MenuType<?> handle, final Block block, final @Nullable CraftBlockInventoryBuilder builder, final SimpleMenuBuilder smb, final boolean useFakeBlockEntity) {
super(handle, block, builder, useFakeBlockEntity);
this.smb = smb;
}

public CraftInventorySupportBlockEntityInventoryViewBuilder(final MenuType<?> handle, final Block block, final @Nullable CraftBlockInventoryBuilder builder, final SimpleMenuBuilder smb) {
super(handle, block, builder);
this.smb = smb;
}

@Override
public LocationInventoryViewBuilder<V> inventory(final Inventory inventory) {
this.inventory = inventory;
return this;
}

@Override
protected AbstractContainerMenu buildContainer(final ServerPlayer player) {
if (inventory == null) {
return super.buildContainer(player);
}

if (this.world == null) {
this.world = player.level();
}

if (this.position == null) {
this.position = player.blockPosition();
}

// this is for default title
final MenuProvider provider = this.builder.build(this.position, this.block.defaultBlockState());
if (provider instanceof final BlockEntity blockEntity) {
blockEntity.setLevel(this.world);
super.defaultTitle = provider.getDisplayName();
}

return this.smb.build(player.nextContainerCounter(), player.getInventory(), ((CraftInventory) this.inventory).getInventory());
}

@Override
public LocationInventoryViewBuilder<V> copy() {
final CraftInventorySupportBlockEntityInventoryViewBuilder<V> copy = new CraftInventorySupportBlockEntityInventoryViewBuilder<>(super.handle, this.block, this.builder, this.smb, this.useFakeBlockEntity);
copy.world = this.world;
copy.position = this.position;
copy.checkReachable = super.checkReachable;
copy.title = title;
return copy;
}

public interface SimpleMenuBuilder {
static SimpleMenuBuilder chest(int rows, MenuType<?> type) {
return (id, pi, c) -> new ChestMenu(type, id, pi, c, rows);
}

AbstractContainerMenu build(int containerId, net.minecraft.world.entity.player.Inventory playerInventory, Container container);
}
}
Loading
Loading