Jump to content

[1.18.2] Change value in BlockEntity using Button on MenuScreen


Skyriis

Recommended Posts

Hey Guys,

i'm trying to create a custom Jukebox which allows starting and stopping the current disc. I've added a play and stop button in my Menu Screen to controll the play state but for some reason nothing happens if i click my buttons.

Menu:

Spoiler
public class JukeBoxMenu extends AbstractContainerMenu {
    private final ContainerLevelAccess levelAccess;
    private final InvWrapper playerInvWrapper;
    private final ItemStackHandler container;
    private int lastHotBarIndex, lastInventoryIndex, lastJukeBoxIndex, discPlayerSlotIndex;
    private final JukeboxBlockEntity blockEntity;

    public JukeBoxMenu(int containerId, Inventory inventory, JukeboxBlockEntity blockEntity, ContainerLevelAccess levelAccess) {
        super(Vanilla_PlusMenuTypes.JUKE_BOX_MENU, containerId);
        this.levelAccess = levelAccess;
        this.playerInvWrapper = new InvWrapper(inventory);
        this.container = blockEntity.getContainer();
        this.blockEntity = blockEntity;

        setupJukeBoxSlots();
        setupPlayerSlots();
    }

    private void setupJukeBoxSlots() {
        int slotIndex = 0;
        slotIndex = placeCol(8, 3, 4, slotIndex);
        slotIndex = placeCol(8 + 18, 3, 4, slotIndex);
        this.discPlayerSlotIndex = slotIndex;
        addSlot(new SlotItemHandler(this.container, slotIndex++, 80, 30));
        slotIndex = placeCol(134, 3, 4, slotIndex);
        placeCol(134 + 18, 3, 4, slotIndex);
        lastJukeBoxIndex = slots.size();
    }

    private int placeCol(int x, int y, int amount, int startIndex) {
        for (int i = 0; i < amount; i++) {
            addSlot(new SlotItemHandler(this.container, startIndex++, x, y + 18 * i));
        }

        return startIndex;
    }

    private void setupPlayerSlots() {
        int index = 0;

        //Hot bar
        for (int col = 0; col < 9; col++) {
            addSlot(new SlotItemHandler(this.playerInvWrapper, index++, 8 + 18 * col, 83 + 18 * 3));
        }
        lastHotBarIndex = slots.size();

        //Inventory
        for (int row = 0; row < 3; row++) {
            for (int col = 0; col < 9; col++) {
                addSlot(new SlotItemHandler(this.playerInvWrapper, index++, 8 + 18 * col, 79 + 18 * row));
            }
        }
        lastInventoryIndex = slots.size();
    }

    @Override
    public ItemStack quickMoveStack(Player pPlayer, int pIndex) {
        ItemStack slotItemStack = slots.get(pIndex).getItem();
        if (slotItemStack.isEmpty()) return ItemStack.EMPTY;

        if (pIndex < lastJukeBoxIndex) {
            //Handle Jukebox Inventory Shift-Click
            if (pIndex == this.discPlayerSlotIndex) {
                //Try to move it into the Jukebox storage
                slotItemStack = moveToJukeBoxInventory(slotItemStack);
                //Move it to the player inventory if the jukebox storage is full
                if (!slotItemStack.isEmpty()) {
                    slotItemStack = moveToPlayerInventory(slotItemStack);
                }
            } else {
                slotItemStack = moveToPlayerInventory(slotItemStack);
            }
        } else {
            slotItemStack = moveToJukeBoxInventory(slotItemStack);
        }

        Slot slot = slots.get(pIndex);
        slot.set(slotItemStack);
        slot.setChanged();
        return ItemStack.EMPTY;
    }

    private ItemStack moveToPlayerInventory(int slotIndex, ItemStack itemStack) {
        Slot currentCurrentSlot = getSlot(slotIndex);
        //Check if slot exist
        if (currentCurrentSlot == null) return itemStack;
        //Try to place Item into the Player Slot
        if (currentCurrentSlot.mayPlace(itemStack)) {
            itemStack = currentCurrentSlot.safeInsert(itemStack);
            currentCurrentSlot.setChanged();
        }

        return itemStack;
    }

    @Override
    public boolean stillValid(Player pPlayer) {
        return stillValid(this.levelAccess, pPlayer, Blocks.JUKEBOX);
    }

    private ItemStack moveToPlayerInventory(ItemStack stack) {
        //Check Space in hot-bar
        for (int i = lastJukeBoxIndex; i < lastHotBarIndex; i++) {
            stack = moveToPlayerInventory(i, stack);
            //Check if there are no items left
            if (stack.isEmpty()) {
                break;
            }
        }

        //Check Space in Inventory
        if (!stack.isEmpty()) {
            for (int i = lastHotBarIndex; i < lastInventoryIndex; i++) {
                stack = moveToPlayerInventory(i, stack);
                //Check if there are no items left
                if (stack.isEmpty()) {
                    break;
                }
            }
        }

        return stack;
    }

    private ItemStack moveToJukeBoxInventory(ItemStack stack) {
        //Handle Player Inventory Shift-Click
        for (int i = 0; i < lastJukeBoxIndex; i++) {
            if (i == discPlayerSlotIndex) continue;
            Slot currentCurrentSlot = getSlot(i);
            //Check if slot exist
            if (currentCurrentSlot == null) break;

            //Try to place Item into the Player Slot
            if (currentCurrentSlot.mayPlace(stack)) {
                stack = currentCurrentSlot.safeInsert(stack);
                currentCurrentSlot.setChanged();

            }
        }
        return stack;
    }

    public boolean isPlaying() {
        return this.blockEntity.isPlaying();
    }

    public BlockPos getPosition() {
        return this.blockEntity.getBlockPos();
    }

    public void startPlaying() {
        this.blockEntity.play();
    }

    public void stopPlaying() {
        this.blockEntity.stop();
    }

    public Level getLevel() {
        return this.blockEntity.getLevel();
    }

    public void update() {
        levelAccess.execute((level, pos) -> {
            level.sendBlockUpdated(pos, blockEntity.getBlockState(), blockEntity.getBlockState(), 2);
        });
    }

    public boolean hasDisc() {
        return !blockEntity.getContainer().getCurrentRecord().isEmpty();
    }
}

 

MenuScreen:

Spoiler
public class JukeBoxScreen extends AbstractContainerScreen<JukeBoxMenu> {
    private final ImagePredicateButton playButton, stopButton;
    private static final ResourceLocation BACKGROUND = new ResourceLocation(Vanilla_Plus.MOD_ID, "textures/gui/jukebox.png");
    private static final ResourceLocation PLAY_BUTTON = new ResourceLocation(Vanilla_Plus.MOD_ID, "textures/gui/play_button.png");
    private static final ResourceLocation STOP_BUTTON = new ResourceLocation(Vanilla_Plus.MOD_ID, "textures/gui/stop_button.png");

    public JukeBoxScreen(JukeBoxMenu pMenu, Inventory pPlayerInventory, Component pTitle) {
        super(pMenu, pPlayerInventory, pTitle);
        this.playButton = new ImagePredicateButton(0, 0, 12, 12, PLAY_BUTTON, pButton -> {
            Vanilla_PlusNetwork.getInstance().sendToServer(new RequestJukeboxUpdate(true));
        }, (pButton, pPoseStack, pMouseX, pMouseY) -> {
            renderTooltip(pPoseStack, Translation.of("jukebox.button.play"), pMouseX, pMouseY);
        }, () -> !pMenu.isPlaying() && pMenu.hasDisc());
        this.stopButton = new ImagePredicateButton(0, 0, 12, 12, STOP_BUTTON, pButton -> {
            Vanilla_PlusNetwork.getInstance().sendToServer(new RequestJukeboxUpdate(false));
        }, (pButton, pPoseStack, pMouseX, pMouseY) -> {
            renderTooltip(pPoseStack, Translation.of("jukebox.button.stop"), pMouseX, pMouseY);
        }, pMenu::isPlaying);
    }

    @Override
    protected void init() {
        super.init();
        titleLabelX = (imageWidth - font.width(title)) / 2;
        titleLabelY = 4;
        inventoryLabelX = (imageWidth - font.width(playerInventoryTitle)) / 2;
        inventoryLabelY = titleLabelY + 60;

        removeWidget(this.playButton);
        this.playButton.x = getGuiLeft() + imageWidth / 2 - 26;
        this.playButton.y = getGuiTop() + 32;
        addRenderableWidget(this.playButton);
        removeWidget(this.stopButton);
        this.stopButton.x = getGuiLeft() + imageWidth / 2 + 14;
        this.stopButton.y = getGuiTop() + 32;
        addRenderableWidget(this.stopButton);
    }

    @Override
    protected void renderBg(PoseStack pPoseStack, float pPartialTick, int pMouseX, int pMouseY) {
        RenderSystem.setShader(GameRenderer::getPositionTexShader);
        RenderSystem.setShaderColor(1F, 1F, 1F, 1F);
        RenderSystem.setShaderTexture(0, BACKGROUND);

        int x = (width - imageWidth) / 2;
        int y = (height - imageWidth) / 2;
        blit(pPoseStack, x, y, 0, 0, imageWidth, imageHeight, 256, 256);
    }
}

 

Packet:

Spoiler
@RequiredArgsConstructor
public class RequestJukeboxUpdate {
    private final boolean playingState;


    public RequestJukeboxUpdate(FriendlyByteBuf buf) {
        this.playingState = buf.readBoolean();
    }

    public void toBytes(FriendlyByteBuf buf) {
        buf.writeBoolean(this.playingState);
    }

    public void handle(Supplier<NetworkEvent.Context> ctx) {
        ctx.get().enqueueWork(() -> {
            if (ctx.get().getSender().containerMenu instanceof JukeBoxMenu menu) {
                if (!menu.hasDisc()) return;

                if (this.playingState) {
                    menu.startPlaying();
                } else {
                    menu.stopPlaying();
                }
                menu.update();
            }
        });
        ctx.get().setPacketHandled(true);
    }
}

 

BlockEntity:

Spoiler
public class JukeboxBlockEntity extends BlockEntity implements Clearable {
    private static final int PLAY_RECORD_EVENT = 1010;
    @Getter
    private final JukeboxContainer container = new JukeboxContainer();
    @Getter
    private boolean isPlaying;
    private ItemStack lastDisc = ItemStack.EMPTY;
    private boolean firstTickDone = false;

    public JukeboxBlockEntity(BlockPos pWorldPosition, BlockState pBlockState) {
        super(Vanilla_PlusBlockEntities.JUKEBOX_BLOCK_ENTITY, pWorldPosition, pBlockState);
        container.addListener((handler, slot) -> {
            if (level != null && !level.isClientSide()) {
                if (slot == 8) {
                    if (!lastDisc.equals(handler.getStackInSlot(slot))) {
                        stop();
                    }
                }
                setChanged();
            }
        });
        isPlaying = pBlockState.getValue(JukeboxBlock.HAS_RECORD);
        container.getCurrentRecord();
    }

    public void load(CompoundTag pTag) {
        super.load(pTag);
        this.isPlaying = pTag.getBoolean("isPlaying");
        container.deserializeNBT(pTag.getCompound("Inventory"));
        lastDisc = container.getCurrentRecord();
    }

    protected void saveAdditional(CompoundTag pTag) {
        super.saveAdditional(pTag);
        pTag.putBoolean("isPlaying", this.isPlaying);
        pTag.put("Inventory", container.serializeNBT());
    }

    public ItemStack getRecord() {
        return this.container.getCurrentRecord();
    }

    public void setRecord(ItemStack pRecord) {
        this.container.setCurrentRecord(pRecord);
        this.setChanged();
    }


    @Override
    public void clearContent() {
        this.container.clear();
    }

    public void play() {
        if (!getRecord().isEmpty()) {
            this.isPlaying = true;
            setChanged();
            if (hasLevel()) {
                level.setBlock(worldPosition, getBlockState().setValue(JukeboxBlock.HAS_RECORD, true), 2);
                level.levelEvent(PLAY_RECORD_EVENT, worldPosition, Item.getId(getRecord().getItem()));
            }
        }
    }

    public void stop() {
        this.isPlaying = false;
        setChanged();
        if (hasLevel()) {
            level.setBlock(worldPosition, getBlockState().setValue(JukeboxBlock.HAS_RECORD, false), 2);
            level.levelEvent(PLAY_RECORD_EVENT, worldPosition, 0);
        }
    }

    @Nullable
    @Override
    public Packet<ClientGamePacketListener> getUpdatePacket() {
        return ClientboundBlockEntityDataPacket.create(this);
    }

    @Override
    public CompoundTag getUpdateTag() {
        CompoundTag tag = super.getUpdateTag();
        saveAdditional(tag);
        return tag;
    }

    public static <T extends BlockEntity> void tick(Level level, BlockPos pos, BlockState blockState, T be) {
        if (be instanceof JukeboxBlockEntity blockEntity) {
            if (!blockEntity.firstTickDone && blockEntity.isPlaying()) {
                //Start playing again when the blockEntity ticks the first time
                blockEntity.play();
            }
        }
    }
}

 

Edited by Skyriis
Link to comment
Share on other sites

10 hours ago, diesieben07 said:

These fields should not be in the packet. At most they allow the user to cheat. The server already knows this information.

They where unused anyway. I've removed them from the Packet and updated the code above. My Problem still remains :(

Link to comment
Share on other sites

28 minutes ago, diesieben07 said:

I don't see anything that would even happen if the packet is received, except the server setting the block state. Is that all that you expected

It should change "isPlaying" variable too. (in the play / stop method of the blockentity)

Link to comment
Share on other sites

Just now, diesieben07 said:

How have you checked whether it does or not?

If i check it in the Packet (using the menu.isPlaying method) it'll show the correct value but if i check it in the menu screen it shows the old / default value.

Link to comment
Share on other sites

i guess that thing get's invoked when i open my container but why?

container.addListener((handler, slot) -> {
            if (slot == 8) {
                stop();
            }
            setChanged();
        });

it should only get invoked if "onContentsChanged" get's called

Link to comment
Share on other sites

Okay next problem. the blockentity doesn't play anything when i log out and log in again. I guess i have to run the 

level.levelEvent(PLAY_RECORD_EVENT, worldPosition, Item.getId(getRecord().getItem()));

when the blockentity gets loaded but is there a method for that?

Edited by Skyriis
Link to comment
Share on other sites

12 minutes ago, diesieben07 said:

You have to do it in your BlockEntity's ticker

i should run

level.levelEvent(PLAY_RECORD_EVENT, worldPosition, Item.getId(getRecord().getItem()));

every tick? Wouldn't that cause the disc to go crasy?

Edited by Skyriis
Link to comment
Share on other sites

14 minutes ago, diesieben07 said:

You make a boolean.

Okay i made a boolean and it started the disc but i can't stop it anymore. (i've updated the code above).

 

EDIT

nvm i forgot to update the boolean

Edited by Skyriis
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.