mirror of
https://github.com/weidaoli/betterenderchests.git
synced 2026-03-31 19:32:32 +08:00
update
This commit is contained in:
17
README.md
17
README.md
@@ -7,23 +7,18 @@ A Minecraft mod that adds tiered Ender Chests with massive storage capacity!
|
||||
### Tiered Ender Chests
|
||||
| Tier | Storage Slots | Rows |
|
||||
|------|---------------|------|
|
||||
| Copper | 27 | 1 |
|
||||
| Iron | 54 | 2 |
|
||||
| Gold | 81 | 3 |
|
||||
| Diamond | 108 | 4 |
|
||||
| Netherite | 135 | 5 |
|
||||
| Dragon | 2000 | ~222 |
|
||||
| Copper | 27 | 1 |
|
||||
| Iron | 54 | 2 |
|
||||
| Gold | 81 | 3 |
|
||||
| Diamond | 108 | 4 |
|
||||
| Netherite | 135 | 5 |
|
||||
| Dragon | 1998 | ~222 |
|
||||
|
||||
### Portable Versions
|
||||
All tiers have handheld versions crafted with 2 sticks + the chest block.
|
||||
|
||||
## Building
|
||||
|
||||
### Using Docker (Recommended)
|
||||
```bash
|
||||
docker build -t betterenderchests .
|
||||
docker run --rm -v $(pwd)/build:/app/build betterenderchests
|
||||
```
|
||||
|
||||
### Using Gradle
|
||||
```bash
|
||||
|
||||
@@ -56,9 +56,10 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "net.neoforged:neoforge:${neo_version}"
|
||||
implementation 'net.neoforged:neoforge:' + neo_version
|
||||
}
|
||||
|
||||
|
||||
tasks.withType(ProcessResources).configureEach {
|
||||
var replaceProperties = [
|
||||
minecraft_version : minecraft_version,
|
||||
|
||||
@@ -35,7 +35,7 @@ public class ModBlocks {
|
||||
|
||||
// Dragon Breath Ender Chest - Massive storage
|
||||
public static final DeferredBlock<Block> DRAGON_ENDER_CHEST = BLOCKS.registerBlock("dragon_ender_chest",
|
||||
props -> new TieredEnderChestBlock(props, 2000), // 10^1024 concept - using large GUI instead
|
||||
props -> new TieredEnderChestBlock(props, 1998),
|
||||
BlockBehaviour.Properties.ofFullCopy(Blocks.OBSIDIAN).strength(50f, 1200f).sound(SoundType.STONE));
|
||||
|
||||
public static void register(IEventBus eventBus) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.betterenderchests.block;
|
||||
|
||||
import com.betterenderchests.menu.TieredEnderChestMenu;
|
||||
import com.betterenderchests.storage.PlayerSharedEnderStorage;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
@@ -59,7 +60,7 @@ public class TieredEnderChestBlock extends BaseEntityBlock {
|
||||
|
||||
@Override
|
||||
protected RenderShape getRenderShape(BlockState state) {
|
||||
return RenderShape.ENTITYBLOCK_ANIMATED;
|
||||
return RenderShape.MODEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -71,11 +72,26 @@ public class TieredEnderChestBlock extends BaseEntityBlock {
|
||||
protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hit) {
|
||||
if (level.isClientSide) {
|
||||
return InteractionResult.SUCCESS;
|
||||
} else {
|
||||
player.openMenu(state.getMenuProvider(level, pos));
|
||||
player.awardStat(Stats.OPEN_ENDERCHEST);
|
||||
return InteractionResult.CONSUME;
|
||||
}
|
||||
|
||||
if (player instanceof ServerPlayer serverPlayer) {
|
||||
int normalizedSize = TieredEnderChestMenu.normalizeSize(inventorySize);
|
||||
serverPlayer.openMenu(
|
||||
new SimpleMenuProvider(
|
||||
(containerId, playerInventory, p) -> new TieredEnderChestMenu(
|
||||
containerId,
|
||||
playerInventory,
|
||||
PlayerSharedEnderStorage.getContainer(serverPlayer),
|
||||
normalizedSize
|
||||
),
|
||||
containerTitle
|
||||
),
|
||||
buffer -> buffer.writeVarInt(normalizedSize)
|
||||
);
|
||||
player.awardStat(Stats.OPEN_ENDERCHEST);
|
||||
}
|
||||
|
||||
return InteractionResult.CONSUME;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
19
src/main/java/com/betterenderchests/client/ClientSetup.java
Normal file
19
src/main/java/com/betterenderchests/client/ClientSetup.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package com.betterenderchests.client;
|
||||
|
||||
import com.betterenderchests.BetterEnderChests;
|
||||
import com.betterenderchests.menu.ModMenus;
|
||||
import net.neoforged.api.distmarker.Dist;
|
||||
import net.neoforged.bus.api.SubscribeEvent;
|
||||
import net.neoforged.fml.common.EventBusSubscriber;
|
||||
import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent;
|
||||
|
||||
@EventBusSubscriber(modid = BetterEnderChests.MOD_ID, value = Dist.CLIENT, bus = EventBusSubscriber.Bus.MOD)
|
||||
public final class ClientSetup {
|
||||
private ClientSetup() {
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onRegisterMenuScreens(RegisterMenuScreensEvent event) {
|
||||
event.register(ModMenus.TIERED_ENDER_CHEST.get(), TieredEnderChestScreen::new);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package com.betterenderchests.client;
|
||||
|
||||
import com.betterenderchests.menu.TieredEnderChestMenu;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
|
||||
public class TieredEnderChestScreen extends AbstractContainerScreen<TieredEnderChestMenu> {
|
||||
private static final ResourceLocation CONTAINER_BACKGROUND =
|
||||
ResourceLocation.withDefaultNamespace("textures/gui/container/generic_54.png");
|
||||
|
||||
private static final int SCROLL_X_OFFSET = 176;
|
||||
private static final int SCROLL_Y_OFFSET = 18;
|
||||
private static final int SCROLL_THUMB_HEIGHT = 15;
|
||||
|
||||
private final int visibleRows;
|
||||
|
||||
private boolean scrolling;
|
||||
|
||||
public TieredEnderChestScreen(TieredEnderChestMenu menu, Inventory playerInventory, Component title) {
|
||||
super(menu, playerInventory, title);
|
||||
this.visibleRows = menu.getRowCount();
|
||||
this.imageHeight = 114 + this.visibleRows * 18;
|
||||
this.inventoryLabelY = this.imageHeight - 94;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderBg(GuiGraphics guiGraphics, float partialTick, int mouseX, int mouseY) {
|
||||
int x = this.leftPos;
|
||||
int y = this.topPos;
|
||||
|
||||
guiGraphics.blit(CONTAINER_BACKGROUND, x, y, 0, 0, this.imageWidth, this.visibleRows * 18 + 17);
|
||||
guiGraphics.blit(CONTAINER_BACKGROUND, x, y + this.visibleRows * 18 + 17, 0, 126, this.imageWidth, 96);
|
||||
|
||||
if (this.menu.getMaxScrollRows() > 0) {
|
||||
int scrollX = x + SCROLL_X_OFFSET;
|
||||
int scrollY = y + SCROLL_Y_OFFSET;
|
||||
guiGraphics.fill(scrollX, scrollY, scrollX + 6, scrollY + getScrollTrackHeight(), 0xFF3F3F3F);
|
||||
int thumbY = getThumbY();
|
||||
guiGraphics.fill(scrollX, thumbY, scrollX + 6, thumbY + SCROLL_THUMB_HEIGHT, 0xFFCFCFCF);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderLabels(GuiGraphics guiGraphics, int mouseX, int mouseY) {
|
||||
super.renderLabels(guiGraphics, mouseX, mouseY);
|
||||
if (this.menu.getMaxScrollRows() > 0) {
|
||||
String page = (this.menu.getScrollRow() + 1) + "/" + (this.menu.getMaxScrollRows() + 1);
|
||||
guiGraphics.drawString(this.font, page, this.imageWidth - 44, 6, 0x404040, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseClicked(double mouseX, double mouseY, int button) {
|
||||
if (button == 0 && this.menu.getMaxScrollRows() > 0 && isOverScrollbar(mouseX, mouseY)) {
|
||||
this.scrolling = true;
|
||||
updateScrollFromMouse(mouseY);
|
||||
return true;
|
||||
}
|
||||
return super.mouseClicked(mouseX, mouseY, button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseDragged(double mouseX, double mouseY, int button, double dragX, double dragY) {
|
||||
if (this.scrolling) {
|
||||
updateScrollFromMouse(mouseY);
|
||||
return true;
|
||||
}
|
||||
return super.mouseDragged(mouseX, mouseY, button, dragX, dragY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseReleased(double mouseX, double mouseY, int button) {
|
||||
this.scrolling = false;
|
||||
return super.mouseReleased(mouseX, mouseY, button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseScrolled(double mouseX, double mouseY, double scrollX, double scrollY) {
|
||||
if (this.menu.getMaxScrollRows() > 0) {
|
||||
int target = this.menu.getScrollRow() - (int) Math.signum(scrollY);
|
||||
sendScrollRow(target);
|
||||
return true;
|
||||
}
|
||||
return super.mouseScrolled(mouseX, mouseY, scrollX, scrollY);
|
||||
}
|
||||
|
||||
private boolean isOverScrollbar(double mouseX, double mouseY) {
|
||||
int x = this.leftPos + SCROLL_X_OFFSET;
|
||||
int y = this.topPos + SCROLL_Y_OFFSET;
|
||||
return mouseX >= x && mouseX < x + 6 && mouseY >= y && mouseY < y + getScrollTrackHeight();
|
||||
}
|
||||
|
||||
private int getScrollTrackHeight() {
|
||||
return this.visibleRows * 18;
|
||||
}
|
||||
|
||||
private int getThumbY() {
|
||||
int x = this.topPos + SCROLL_Y_OFFSET;
|
||||
int range = getScrollTrackHeight() - SCROLL_THUMB_HEIGHT;
|
||||
if (this.menu.getMaxScrollRows() <= 0) {
|
||||
return x;
|
||||
}
|
||||
float fraction = (float) this.menu.getScrollRow() / (float) this.menu.getMaxScrollRows();
|
||||
return x + Mth.floor(fraction * range);
|
||||
}
|
||||
|
||||
private void updateScrollFromMouse(double mouseY) {
|
||||
int top = this.topPos + SCROLL_Y_OFFSET;
|
||||
int range = getScrollTrackHeight() - SCROLL_THUMB_HEIGHT;
|
||||
float fraction = (float) ((mouseY - top) - (SCROLL_THUMB_HEIGHT / 2.0)) / (float) range;
|
||||
int target = Math.round(Mth.clamp(fraction, 0.0F, 1.0F) * this.menu.getMaxScrollRows());
|
||||
sendScrollRow(target);
|
||||
}
|
||||
|
||||
private void sendScrollRow(int targetRow) {
|
||||
int clamped = Mth.clamp(targetRow, 0, this.menu.getMaxScrollRows());
|
||||
if (clamped == this.menu.getScrollRow()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.menu.setScrollRow(clamped);
|
||||
Minecraft minecraft = this.minecraft;
|
||||
if (minecraft != null && minecraft.gameMode != null) {
|
||||
minecraft.gameMode.handleInventoryButtonClick(this.menu.containerId, clamped);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) {
|
||||
this.renderBackground(guiGraphics, mouseX, mouseY, partialTick);
|
||||
super.render(guiGraphics, mouseX, mouseY, partialTick);
|
||||
this.renderTooltip(guiGraphics, mouseX, mouseY);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
package com.betterenderchests.item;
|
||||
|
||||
import com.betterenderchests.BetterEnderChests;
|
||||
import com.betterenderchests.block.ModBlocks;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.item.CreativeModeTab;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.neoforged.bus.api.IEventBus;
|
||||
import net.neoforged.neoforge.registries.DeferredRegister;
|
||||
|
||||
@@ -18,23 +16,23 @@ public class ModCreativeTabs {
|
||||
public static final Supplier<CreativeModeTab> BETTER_ENDER_CHESTS_TAB = CREATIVE_MODE_TABS.register("betterenderchests_tab",
|
||||
() -> CreativeModeTab.builder()
|
||||
.title(Component.translatable("itemGroup.betterenderchests"))
|
||||
.icon(() -> new ItemStack(ModBlocks.DIAMOND_ENDER_CHEST.get()))
|
||||
.icon(() -> ModItems.DIAMOND_ENDER_CHEST.toStack())
|
||||
.displayItems((parameters, output) -> {
|
||||
// Blocks
|
||||
output.accept(ModBlocks.COPPER_ENDER_CHEST.get());
|
||||
output.accept(ModBlocks.IRON_ENDER_CHEST.get());
|
||||
output.accept(ModBlocks.GOLD_ENDER_CHEST.get());
|
||||
output.accept(ModBlocks.DIAMOND_ENDER_CHEST.get());
|
||||
output.accept(ModBlocks.NETHERITE_ENDER_CHEST.get());
|
||||
output.accept(ModBlocks.DRAGON_ENDER_CHEST.get());
|
||||
// Placeable chest blocks
|
||||
output.accept(ModItems.COPPER_ENDER_CHEST);
|
||||
output.accept(ModItems.IRON_ENDER_CHEST);
|
||||
output.accept(ModItems.GOLD_ENDER_CHEST);
|
||||
output.accept(ModItems.DIAMOND_ENDER_CHEST);
|
||||
output.accept(ModItems.NETHERITE_ENDER_CHEST);
|
||||
output.accept(ModItems.DRAGON_ENDER_CHEST);
|
||||
|
||||
// Portable Items
|
||||
output.accept(ModItems.PORTABLE_COPPER_ENDER_CHEST.get());
|
||||
output.accept(ModItems.PORTABLE_IRON_ENDER_CHEST.get());
|
||||
output.accept(ModItems.PORTABLE_GOLD_ENDER_CHEST.get());
|
||||
output.accept(ModItems.PORTABLE_DIAMOND_ENDER_CHEST.get());
|
||||
output.accept(ModItems.PORTABLE_NETHERITE_ENDER_CHEST.get());
|
||||
output.accept(ModItems.PORTABLE_DRAGON_ENDER_CHEST.get());
|
||||
// Portable chest items
|
||||
output.accept(ModItems.PORTABLE_COPPER_ENDER_CHEST);
|
||||
output.accept(ModItems.PORTABLE_IRON_ENDER_CHEST);
|
||||
output.accept(ModItems.PORTABLE_GOLD_ENDER_CHEST);
|
||||
output.accept(ModItems.PORTABLE_DIAMOND_ENDER_CHEST);
|
||||
output.accept(ModItems.PORTABLE_NETHERITE_ENDER_CHEST);
|
||||
output.accept(ModItems.PORTABLE_DRAGON_ENDER_CHEST);
|
||||
})
|
||||
.build());
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.betterenderchests.item;
|
||||
|
||||
import com.betterenderchests.BetterEnderChests;
|
||||
import com.betterenderchests.block.ModBlocks;
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
import net.neoforged.bus.api.IEventBus;
|
||||
import net.neoforged.neoforge.registries.DeferredItem;
|
||||
import net.neoforged.neoforge.registries.DeferredRegister;
|
||||
@@ -8,6 +10,31 @@ import net.neoforged.neoforge.registries.DeferredRegister;
|
||||
public class ModItems {
|
||||
public static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(BetterEnderChests.MOD_ID);
|
||||
|
||||
// Block items for placeable chests
|
||||
public static final DeferredItem<BlockItem> COPPER_ENDER_CHEST = ITEMS.registerItem(
|
||||
"copper_ender_chest",
|
||||
props -> new BlockItem(ModBlocks.COPPER_ENDER_CHEST.get(), props));
|
||||
|
||||
public static final DeferredItem<BlockItem> IRON_ENDER_CHEST = ITEMS.registerItem(
|
||||
"iron_ender_chest",
|
||||
props -> new BlockItem(ModBlocks.IRON_ENDER_CHEST.get(), props));
|
||||
|
||||
public static final DeferredItem<BlockItem> GOLD_ENDER_CHEST = ITEMS.registerItem(
|
||||
"gold_ender_chest",
|
||||
props -> new BlockItem(ModBlocks.GOLD_ENDER_CHEST.get(), props));
|
||||
|
||||
public static final DeferredItem<BlockItem> DIAMOND_ENDER_CHEST = ITEMS.registerItem(
|
||||
"diamond_ender_chest",
|
||||
props -> new BlockItem(ModBlocks.DIAMOND_ENDER_CHEST.get(), props));
|
||||
|
||||
public static final DeferredItem<BlockItem> NETHERITE_ENDER_CHEST = ITEMS.registerItem(
|
||||
"netherite_ender_chest",
|
||||
props -> new BlockItem(ModBlocks.NETHERITE_ENDER_CHEST.get(), props.fireResistant()));
|
||||
|
||||
public static final DeferredItem<BlockItem> DRAGON_ENDER_CHEST = ITEMS.registerItem(
|
||||
"dragon_ender_chest",
|
||||
props -> new BlockItem(ModBlocks.DRAGON_ENDER_CHEST.get(), props.fireResistant()));
|
||||
|
||||
// Portable Ender Chests (Handheld versions)
|
||||
public static final DeferredItem<PortableEnderChestItem> PORTABLE_COPPER_ENDER_CHEST = ITEMS.registerItem(
|
||||
"portable_copper_ender_chest",
|
||||
@@ -31,7 +58,7 @@ public class ModItems {
|
||||
|
||||
public static final DeferredItem<PortableEnderChestItem> PORTABLE_DRAGON_ENDER_CHEST = ITEMS.registerItem(
|
||||
"portable_dragon_ender_chest",
|
||||
props -> new PortableEnderChestItem(props, 2000).fireResistant());
|
||||
props -> new PortableEnderChestItem(props, 1998).fireResistant());
|
||||
|
||||
public static void register(IEventBus eventBus) {
|
||||
ITEMS.register(eventBus);
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
package com.betterenderchests.item;
|
||||
|
||||
import com.betterenderchests.menu.TieredEnderChestMenu;
|
||||
import com.betterenderchests.storage.PlayerSharedEnderStorage;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.stats.Stats;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResultHolder;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.SimpleMenuProvider;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
|
||||
public class PortableEnderChestItem extends Item {
|
||||
private final int inventorySize;
|
||||
@@ -30,11 +28,19 @@ public class PortableEnderChestItem extends Item {
|
||||
ItemStack stack = player.getItemInHand(hand);
|
||||
|
||||
if (!level.isClientSide && player instanceof ServerPlayer serverPlayer) {
|
||||
player.openMenu(new SimpleMenuProvider(
|
||||
(containerId, playerInventory, p) ->
|
||||
new TieredEnderChestMenu(containerId, playerInventory, inventorySize),
|
||||
containerTitle
|
||||
));
|
||||
int normalizedSize = TieredEnderChestMenu.normalizeSize(inventorySize);
|
||||
serverPlayer.openMenu(
|
||||
new SimpleMenuProvider(
|
||||
(containerId, playerInventory, p) -> new TieredEnderChestMenu(
|
||||
containerId,
|
||||
playerInventory,
|
||||
PlayerSharedEnderStorage.getContainer(serverPlayer),
|
||||
normalizedSize
|
||||
),
|
||||
containerTitle
|
||||
),
|
||||
buffer -> buffer.writeVarInt(normalizedSize)
|
||||
);
|
||||
player.awardStat(Stats.OPEN_ENDERCHEST);
|
||||
}
|
||||
|
||||
@@ -48,4 +54,7 @@ public class PortableEnderChestItem extends Item {
|
||||
public PortableEnderChestItem fireResistant() {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ public class ModMenus {
|
||||
public static final Supplier<MenuType<TieredEnderChestMenu>> TIERED_ENDER_CHEST = MENUS.register(
|
||||
"tiered_ender_chest",
|
||||
() -> IMenuTypeExtension.create((containerId, playerInventory, buf) ->
|
||||
new TieredEnderChestMenu(containerId, playerInventory)));
|
||||
new TieredEnderChestMenu(containerId, playerInventory, buf)));
|
||||
|
||||
public static void register(IEventBus eventBus) {
|
||||
MENUS.register(eventBus);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.betterenderchests.menu;
|
||||
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.SimpleContainer;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
@@ -10,53 +11,104 @@ import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
public class TieredEnderChestMenu extends AbstractContainerMenu {
|
||||
public static final int VISIBLE_ROWS = 6;
|
||||
public static final int VISIBLE_SLOTS = VISIBLE_ROWS * 9;
|
||||
public static final int MAX_LOGICAL_SIZE = 1998;
|
||||
|
||||
private final Container container;
|
||||
private final int containerRows;
|
||||
private final int containerCols = 9;
|
||||
private final int logicalSize;
|
||||
private final int logicalRows;
|
||||
private final int visibleRows;
|
||||
private final int visibleSlots;
|
||||
private final int maxScrollRows;
|
||||
private int scrollRow;
|
||||
|
||||
public TieredEnderChestMenu(int containerId, Inventory playerInventory) {
|
||||
this(containerId, playerInventory, 27); // Default 3 rows
|
||||
this(containerId, playerInventory, 27);
|
||||
}
|
||||
|
||||
public TieredEnderChestMenu(int containerId, RegistryFriendlyByteBuf buffer) {
|
||||
this(containerId, null, 27); // Network sync constructor - will be filled from client
|
||||
this(containerId, null, readSize(buffer));
|
||||
}
|
||||
|
||||
public TieredEnderChestMenu(int containerId, Inventory playerInventory, RegistryFriendlyByteBuf buffer) {
|
||||
this(containerId, playerInventory, readSize(buffer));
|
||||
}
|
||||
|
||||
public static int normalizeSize(int requestedSize) {
|
||||
int clamped = Mth.clamp(requestedSize, 9, MAX_LOGICAL_SIZE);
|
||||
return clamped - (clamped % 9);
|
||||
}
|
||||
|
||||
private static int readSize(RegistryFriendlyByteBuf buffer) {
|
||||
return normalizeSize(buffer.readVarInt());
|
||||
}
|
||||
|
||||
public TieredEnderChestMenu(int containerId, Inventory playerInventory, int size) {
|
||||
this(containerId, playerInventory, new SimpleContainer(size), size);
|
||||
this(containerId, playerInventory, new SimpleContainer(normalizeSize(size)), normalizeSize(size));
|
||||
}
|
||||
|
||||
public TieredEnderChestMenu(int containerId, Inventory playerInventory, Container container, int size) {
|
||||
super(ModMenus.TIERED_ENDER_CHEST.get(), containerId);
|
||||
this.logicalSize = normalizeSize(size);
|
||||
this.logicalRows = this.logicalSize / 9;
|
||||
this.visibleRows = Math.min(VISIBLE_ROWS, this.logicalRows);
|
||||
this.visibleSlots = this.visibleRows * 9;
|
||||
this.maxScrollRows = Math.max(0, this.logicalRows - this.visibleRows);
|
||||
this.container = container;
|
||||
this.containerRows = size / 9;
|
||||
|
||||
checkContainerSize(container, size);
|
||||
container.startOpen(playerInventory.player);
|
||||
if (container.getContainerSize() < this.logicalSize) {
|
||||
throw new IllegalArgumentException("Container is smaller than logical size: " + container.getContainerSize() + " < " + this.logicalSize);
|
||||
}
|
||||
|
||||
if (playerInventory != null) {
|
||||
container.startOpen(playerInventory.player);
|
||||
}
|
||||
|
||||
// Container inventory slots
|
||||
int slotY = 17;
|
||||
for (int row = 0; row < containerRows; row++) {
|
||||
for (int row = 0; row < this.visibleRows; row++) {
|
||||
for (int col = 0; col < 9; col++) {
|
||||
this.addSlot(new Slot(container, col + row * 9, 8 + col * 18, slotY + row * 18));
|
||||
int visibleIndex = col + row * 9;
|
||||
this.addSlot(new ScrollingSlot(container, visibleIndex, 8 + col * 18, slotY + row * 18));
|
||||
}
|
||||
}
|
||||
|
||||
// Player inventory slots
|
||||
int playerInventoryY = slotY + containerRows * 18 + 14;
|
||||
for (int row = 0; row < 3; row++) {
|
||||
int playerInventoryY = slotY + this.visibleRows * 18 + 14;
|
||||
if (playerInventory != null) {
|
||||
for (int row = 0; row < 3; row++) {
|
||||
for (int col = 0; col < 9; col++) {
|
||||
this.addSlot(new Slot(playerInventory, col + row * 9 + 9, 8 + col * 18, playerInventoryY + row * 18));
|
||||
}
|
||||
}
|
||||
|
||||
int hotbarY = playerInventoryY + 58;
|
||||
for (int col = 0; col < 9; col++) {
|
||||
this.addSlot(new Slot(playerInventory, col + row * 9 + 9, 8 + col * 18, playerInventoryY + row * 18));
|
||||
this.addSlot(new Slot(playerInventory, col, 8 + col * 18, hotbarY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Player hotbar slots
|
||||
int hotbarY = playerInventoryY + 58;
|
||||
for (int col = 0; col < 9; col++) {
|
||||
this.addSlot(new Slot(playerInventory, col, 8 + col * 18, hotbarY));
|
||||
private int toLogicalIndex(int visibleSlot) {
|
||||
return visibleSlot + this.scrollRow * 9;
|
||||
}
|
||||
|
||||
public void setScrollRow(int row) {
|
||||
int clamped = Mth.clamp(row, 0, this.maxScrollRows);
|
||||
if (this.scrollRow != clamped) {
|
||||
this.scrollRow = clamped;
|
||||
this.broadcastChanges();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean clickMenuButton(Player player, int id) {
|
||||
if (id >= 0 && id <= this.maxScrollRows) {
|
||||
setScrollRow(id);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return this.container.stillValid(player);
|
||||
@@ -71,11 +123,11 @@ public class TieredEnderChestMenu extends AbstractContainerMenu {
|
||||
ItemStack slotStack = slot.getItem();
|
||||
itemstack = slotStack.copy();
|
||||
|
||||
if (index < this.containerRows * 9) {
|
||||
if (!this.moveItemStackTo(slotStack, this.containerRows * 9, this.slots.size(), true)) {
|
||||
if (index < this.visibleSlots) {
|
||||
if (!this.moveItemStackTo(slotStack, this.visibleSlots, this.slots.size(), true)) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
} else if (!this.moveItemStackTo(slotStack, 0, this.containerRows * 9, false)) {
|
||||
} else if (!this.moveItemStackTo(slotStack, 0, this.visibleSlots, false)) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@@ -95,11 +147,68 @@ public class TieredEnderChestMenu extends AbstractContainerMenu {
|
||||
this.container.stopOpen(player);
|
||||
}
|
||||
|
||||
public Container getContainer() {
|
||||
return container;
|
||||
public int getRowCount() {
|
||||
return this.visibleRows;
|
||||
}
|
||||
|
||||
public int getRowCount() {
|
||||
return containerRows;
|
||||
public int getLogicalRows() {
|
||||
return this.logicalRows;
|
||||
}
|
||||
|
||||
public int getMaxScrollRows() {
|
||||
return this.maxScrollRows;
|
||||
}
|
||||
|
||||
public int getScrollRow() {
|
||||
return this.scrollRow;
|
||||
}
|
||||
|
||||
private class ScrollingSlot extends Slot {
|
||||
private final int visibleIndex;
|
||||
|
||||
private ScrollingSlot(Container container, int visibleIndex, int x, int y) {
|
||||
super(container, visibleIndex, x, y);
|
||||
this.visibleIndex = visibleIndex;
|
||||
}
|
||||
|
||||
private int logicalIndex() {
|
||||
return toLogicalIndex(this.visibleIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSlot() {
|
||||
return logicalIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem() {
|
||||
return container.getItem(logicalIndex());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasItem() {
|
||||
return !getItem().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack stack) {
|
||||
container.setItem(logicalIndex(), stack);
|
||||
setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack remove(int amount) {
|
||||
return container.removeItem(logicalIndex(), amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack stack) {
|
||||
return container.canPlaceItem(logicalIndex(), stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {
|
||||
container.setChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.betterenderchests.storage;
|
||||
|
||||
import com.betterenderchests.menu.TieredEnderChestMenu;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.SimpleContainer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public final class PlayerSharedEnderStorage {
|
||||
private static final String STORAGE_TAG = "betterenderchests_shared_storage";
|
||||
private static final Map<UUID, PersistentContainer> CACHE = new HashMap<>();
|
||||
|
||||
private PlayerSharedEnderStorage() {
|
||||
}
|
||||
|
||||
public static SimpleContainer getContainer(ServerPlayer player) {
|
||||
return CACHE.computeIfAbsent(player.getUUID(), ignored -> createAndLoad(player));
|
||||
}
|
||||
|
||||
private static PersistentContainer createAndLoad(ServerPlayer player) {
|
||||
PersistentContainer container = new PersistentContainer(player);
|
||||
load(container, player);
|
||||
return container;
|
||||
}
|
||||
|
||||
private static void load(PersistentContainer container, ServerPlayer player) {
|
||||
CompoundTag tag = player.getPersistentData().getCompound(STORAGE_TAG);
|
||||
NonNullList<ItemStack> items = NonNullList.withSize(TieredEnderChestMenu.MAX_LOGICAL_SIZE, ItemStack.EMPTY);
|
||||
ContainerHelper.loadAllItems(tag, items, player.registryAccess());
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
container.setItem(i, items.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
private static void save(PersistentContainer container, ServerPlayer player) {
|
||||
NonNullList<ItemStack> items = NonNullList.withSize(TieredEnderChestMenu.MAX_LOGICAL_SIZE, ItemStack.EMPTY);
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
items.set(i, container.getItem(i));
|
||||
}
|
||||
CompoundTag tag = new CompoundTag();
|
||||
ContainerHelper.saveAllItems(tag, items, player.registryAccess());
|
||||
player.getPersistentData().put(STORAGE_TAG, tag);
|
||||
}
|
||||
|
||||
private static class PersistentContainer extends SimpleContainer {
|
||||
private final ServerPlayer owner;
|
||||
|
||||
private PersistentContainer(ServerPlayer owner) {
|
||||
super(TieredEnderChestMenu.MAX_LOGICAL_SIZE);
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {
|
||||
super.setChanged();
|
||||
save(this, owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"pattern": [
|
||||
" S ",
|
||||
"SES",
|
||||
"SCS",
|
||||
" S "
|
||||
],
|
||||
"key": {
|
||||
"S": {
|
||||
"item": "minecraft:stick"
|
||||
},
|
||||
"E": {
|
||||
"C": {
|
||||
"item": "betterenderchests:copper_ender_chest"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"pattern": [
|
||||
" S ",
|
||||
"SES",
|
||||
"SCS",
|
||||
" S "
|
||||
],
|
||||
"key": {
|
||||
"S": {
|
||||
"item": "minecraft:stick"
|
||||
},
|
||||
"E": {
|
||||
"C": {
|
||||
"item": "betterenderchests:diamond_ender_chest"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"pattern": [
|
||||
" S ",
|
||||
"SES",
|
||||
"SCS",
|
||||
" S "
|
||||
],
|
||||
"key": {
|
||||
"S": {
|
||||
"item": "minecraft:stick"
|
||||
},
|
||||
"E": {
|
||||
"C": {
|
||||
"item": "betterenderchests:dragon_ender_chest"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"pattern": [
|
||||
" S ",
|
||||
"SES",
|
||||
"SCS",
|
||||
" S "
|
||||
],
|
||||
"key": {
|
||||
"S": {
|
||||
"item": "minecraft:stick"
|
||||
},
|
||||
"E": {
|
||||
"C": {
|
||||
"item": "betterenderchests:gold_ender_chest"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"pattern": [
|
||||
" S ",
|
||||
"SES",
|
||||
"SCS",
|
||||
" S "
|
||||
],
|
||||
"key": {
|
||||
"S": {
|
||||
"item": "minecraft:stick"
|
||||
},
|
||||
"E": {
|
||||
"C": {
|
||||
"item": "betterenderchests:iron_ender_chest"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"pattern": [
|
||||
" S ",
|
||||
"SES",
|
||||
"SCS",
|
||||
" S "
|
||||
],
|
||||
"key": {
|
||||
"S": {
|
||||
"item": "minecraft:stick"
|
||||
},
|
||||
"E": {
|
||||
"C": {
|
||||
"item": "betterenderchests:netherite_ender_chest"
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user