Вопрос Как сохранить инвентарь или массив предметов в блок?

q20w26a

Разработчик
Инструктор
Пользователь
Сообщения
575
Решения
27
Мне нужно сохранить предметы в блок, который изначально для этого не предназначен. GUI у меня уже есть, осталось сохранение предметов.
Я рассматривал несколько вариантов:

1) Сохранение инвентаря в отдельный файл
Это самый простой вариант, но мне он не нравится.
Во-первых, на каждый блок будет создан новый файл, а это не очень хорошо, во-вторых, это не очень быстро будет работать.
2) Сохранение инвентарей в отдельный файл
Суть в том, что для каждого чанка с нужными мне блоками будет создаваться файл, в котором уже хранятся нужные предметы.
Что-то вроде:
YAML:
block-1:
    location:
        x: 0
        z: 19
        y: 23
    inventory:
        1:
            ==: org.bukkit.inventory.ItemStack
              v: <data version>
              type: <item type name>
              amount: <stack size>
            ...
        2:
            ...
block-2:
    location:
        ...
Скорее всего это будет еще медленнее из-за возможных размеров файлов
3) Сохранение инвентарей в базу данных
Мне кажется это не сильно отличается от файлов, разве что тут все более или менее структурировано.
4) Использование MetadataValue и Metadatable
Это казалось мне наиболее подходящим решением.
Во-первых, интерфейс Block наследует Metadatable;
во-вторых, с этим всем не трудно работать;
в-третьих, мне вроде как не нужны никакие лишние файлы.
Это казалось бы идеальное решение проблемы обломалось на одном не идеальном моменте - ItemStack не реализует MetadataValue. Значит, он не может быть сохранен никак. Только примитивы и строки.
5) NBT
Если память мне не изменяет - Minecraft позволяет сохранять NBT в предметы, блоки и существ. В новых версиях CompoundTag даже содержит метод для сохранения и чтения самих ItemStack'ов. Осталось разве что как-то сохранять и читать NBT блока в Bukkit, желательно без сторонних библиотек, ну или с минимумом рефлексии и NMS, дабы не терять поддержку нескольких версий.
 
Решение
Java:
public class InventorySerializerUtil {

    /**
     * Converts the player inventory to a String array of Base64 strings. First string is the content and second string is the armor.
     *
     * @param playerInventory to turn into an array of strings.
     * @return Array of strings: [ main content, armor content ]
     */
    public static String[] playerInventoryToBase64(PlayerInventory playerInventory) throws IllegalStateException {
        String content = toBase64(playerInventory);
        String armor = itemStackArrayToBase64(playerInventory.getArmorContents());

        return new String[] { content, armor };
    }

    /**
     * A method to serialize an {@link ItemStack} array to Base64 String.
     *
     * @param items to...
Java:
public class InventorySerializerUtil {

    /**
     * Converts the player inventory to a String array of Base64 strings. First string is the content and second string is the armor.
     *
     * @param playerInventory to turn into an array of strings.
     * @return Array of strings: [ main content, armor content ]
     */
    public static String[] playerInventoryToBase64(PlayerInventory playerInventory) throws IllegalStateException {
        String content = toBase64(playerInventory);
        String armor = itemStackArrayToBase64(playerInventory.getArmorContents());

        return new String[] { content, armor };
    }

    /**
     * A method to serialize an {@link ItemStack} array to Base64 String.
     *
     * @param items to turn into a Base64 String.
     * @return Base64 string of the items.
     */
    public static String itemStackArrayToBase64(ItemStack[] items) throws IllegalStateException {
        try {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream);

            dataOutput.writeInt(items.length);

            for (ItemStack item : items) {
                dataOutput.writeObject(item);
            }

            dataOutput.close();
            return Base64Coder.encodeLines(outputStream.toByteArray());
        } catch (Exception e) {
            throw new IllegalStateException("Unable to save item stacks.", e);
        }
    }

    /**
     * A method to serialize an inventory to Base64 string.
     *
     * @param inventory to serialize
     * @return Base64 string of the provided inventory
     */
    public static String toBase64(Inventory inventory) throws IllegalStateException {
        try {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream);

            dataOutput.writeInt(inventory.getSize());

            for (int i = 0; i < inventory.getSize(); i++) {
                dataOutput.writeObject(inventory.getItem(i));
            }

            dataOutput.close();
            return Base64Coder.encodeLines(outputStream.toByteArray());
        } catch (Exception e) {
            throw new IllegalStateException("Unable to save item stacks.", e);
        }
    }

    /**
     *
     * A method to get an {@link Inventory} from an encoded, Base64, string.
     *
     * @param data Base64 string of data containing an inventory.
     * @return Inventory created from the Base64 string.
     */
    public static Inventory fromBase64(String data) throws IOException {
        try {
            ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(data));
            BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream);
            Inventory inventory = Bukkit.getServer().createInventory(null, dataInput.readInt());

            for (int i = 0; i < inventory.getSize(); i++) {
                inventory.setItem(i, (ItemStack) dataInput.readObject());
            }

            dataInput.close();
            return inventory;
        } catch (ClassNotFoundException e) {
            throw new IOException("Unable to decode class type.", e);
        }
    }

}
Продолжая твои рассуждения в теме - в новых версиях есть PersistentDataContainer.

Я без понятия, какая изначальная цель у всего этого, но хранить довольно большие данные в Metadata / PersistentDataContainer - я бы не советовал. БД банально быстрее и эффективнее в разы, к тому же, в таком варианте всегда есть возможность менеджмента данными - перемещение / синхронизация / обновление и удаление.
 
Последнее редактирование:
Xezard, спасибо, выглядит просто как в реализации, так и в использовании. Пойдет с Metadatable?
Кстати, по поводу Metadatable - я кое-где читал что значения тут живут до первой отгрузки чанков, не знаешь, так ли это?
 
Xezard, спасибо, выглядит просто как в реализации, так и в использовании. Пойдет с Metadatable?
Кстати, по поводу Metadatable - я кое-где читал что значения тут живут до первой отгрузки чанков, не знаешь, так ли это?
Честно говоря, особо не баловался с Metadatable, чтобы что-то утверждать. Точно знаю что PersistentDataContainer сохраняет данные на всё время, сколько сервер не перезагружай. Всё ещё не рекомендую хранить довольно большой объём данных напрямую в блоках / предметах.
 
Metadatable - я кое-где читал что значения тут живут до первой отгрузки чанков, не знаешь, так ли это?
Конечно, после первой перезагрузки все стирается.
PersistentDataContainer - мапа с кастомными тэгами. Ничего особенного в ней нет, но для хранения инвентарей не подходит, т.к. не рассчитана на хранение больших данных, тупо лагать будет. Если уж надо хранить данные в блоках - то только в базах данных.
 
Сундуки тоже лагают?

Иногда мне кажется что стоит кончать с плагинами и Bukkit в частности, и идти дальше писать серверные моды на Fabric.
Чем так хорош Fabric если не секрет?
 
Сундуки тоже лагают?
Вся суть в реализации. Я очень сомневаюсь что хранение достаточно большого объёма данных "в блоках" не будет вызывать какие-нибудь побочки. Нужно тщательно тестировать этот момент.

Чем так хорош Fabric если не секрет?
Тем, что всякие анархисты в темах не оффтопят не по делу. Точно такое же сообщение ты можешь спокойно написать ТС в ЛС.
 
Назад
Сверху Снизу