Полки из версии 1.21.10

Версия Minecraft
1.20.1
API
Fabric
Всем привет, создаю мод на маинкрафт версии 1.20.1, хотел сделать такие же полки как и в новой версии 1.21.10, но не получается рендер состояния полки, то есть когда забираю предмет из полки, то текстура все равно там остается
Java:
package farllands.farllands_deco.blockentity;

import farllands.farllands_deco.blockentity.ModBlockEntities;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.Inventories;
import net.minecraft.inventory.Inventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.collection.DefaultedList;
import net.minecraft.util.math.BlockPos;
import org.jetbrains.annotations.Nullable;
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import farllands.farllands_deco.Farllands_deco;

/**
 * Блок-сущность полки: 3 слота, ванильная синхронизация на клиент.
 * Требования:
 *  - В ModBlockEntities должен быть зарегистрирован тип SHELF.
 *  - ShelfBlock#createBlockEntity(...) возвращает new ShelfBlockEntity(pos, state).
 *  - В BER рендер читать stacks через getStack(i) (без кэшей).
 */
public class ShelfBlockEntity extends BlockEntity implements Inventory {

    public final DefaultedList<ItemStack> items = DefaultedList.ofSize(3, ItemStack.EMPTY);

    public ShelfBlockEntity(BlockPos pos, BlockState state) {
        super(ModBlockEntities.SHELF, pos, state);
    }

    /* ------------------------- Inventory ------------------------- */

    @Override
    public int size() {
        return items.size();
    }

    @Override
    public boolean isEmpty() {
        for (ItemStack stack : items) {
            if (!stack.isEmpty()) return false;
        }
        return true;
    }

    @Override
    public ItemStack getStack(int slot) {
        return items.get(slot);
    }

    @Override
    public ItemStack removeStack(int slot, int amount) {
        ItemStack out = Inventories.splitStack(items, slot, amount);
        // Всегда синхронизируем, даже если результат пустой, чтобы обновить визуально
        markDirtyAndSync();
        return out;
    }

    @Override
    public ItemStack removeStack(int slot) {
        ItemStack out = Inventories.removeStack(items, slot);
        // Всегда синхронизируем, даже если результат пустой, чтобы обновить визуально
        markDirtyAndSync();
        return out;
    }

    @Override
    public void setStack(int slot, ItemStack stack) {
        items.set(slot, stack);
        if (stack.getCount() > getMaxCountPerStack()) {
            stack.setCount(getMaxCountPerStack());
        }
        markDirtyAndSync();
    }

    @Override
    public void clear() {
        items.clear();
        markDirtyAndSync();
    }

    @Override
    public boolean canPlayerUse(PlayerEntity player) {
        if (world == null || world.getBlockEntity(pos) != this) return false;
        return player.squaredDistanceTo(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5) < 64.0;
    }

    @Override
    public int getMaxCountPerStack() {
        return 1;
    }

    /** Возвращает индекс первого пустого слота или -1. */
    public int firstEmptySlot() {
        for (int i = 0; i < items.size(); i++) {
            if (items.get(i).isEmpty()) return i;
        }
        return -1;
    }

    /* --------------------------- NBT ----------------------------- */

    @Override
    protected void writeNbt(NbtCompound nbt) {
        super.writeNbt(nbt);
        Inventories.writeNbt(nbt, items);
    }

    @Override
    public void readNbt(NbtCompound nbt) {
        super.readNbt(nbt);
        Inventories.readNbt(nbt, items);
        if (world != null && world.isClient) {
            // Принудительно обновляем рендер на клиенте
            world.updateListeners(pos, getCachedState(), getCachedState(), Block.NOTIFY_LISTENERS | Block.REDRAW_ON_MAIN_THREAD);
        }
    }

    /* --------------------- Client <-> Server --------------------- */

    @Nullable
    @Override
    public BlockEntityUpdateS2CPacket toUpdatePacket() {
        return BlockEntityUpdateS2CPacket.create(this, be -> be.toInitialChunkDataNbt());
    }

    @Override
    public NbtCompound toInitialChunkDataNbt() {
        NbtCompound nbt = new NbtCompound();
        Inventories.writeNbt(nbt, items);
        return nbt;
    }

    /**
     * Вызывать ПОСЛЕ любого изменения содержимого.
     * - Помечает сохранение
     * - Шлёт пакет на клиент
     * - Перерисовывает блок (BER)
     */
    public void markDirtyAndSync() {
        if (world == null || world.isClient()) return;

        markDirty();
        
        // Отправляем обновление через стандартный механизм
        ((ServerWorld) world).getChunkManager().markForUpdate(pos);

        // Отправляем кастомный пакет каждому игроку отдельно
        ServerWorld serverWorld = (ServerWorld) world;
        for (ServerPlayerEntity player : serverWorld.getPlayers()) {
            if (player.squaredDistanceTo(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5) <= 64 * 64) {
                // Создаём новый буфер для каждого игрока
                PacketByteBuf buf = PacketByteBufs.create();
                buf.writeBlockPos(pos);
                NbtCompound nbt = new NbtCompound();
                Inventories.writeNbt(nbt, items);
                buf.writeNbt(nbt);
                ServerPlayNetworking.send(player, Farllands_deco.SHELF_UPDATE_PACKET, buf);
            }
        }
    }
}

Может подскажет кто нмбудь, что не так
 
Назад
Сверху