Помогите Запись любых данных в ItemMeta

  • Автор темы Автор темы kapehh
  • Дата начала Дата начала

kapehh

Ньюби
Пользователь
Баллы
1
Здравствуйте )
Стандартно в ItemMeta нельзя записывать что-то своё. Вариант использования Lore отпадает из-за того что там только String, оно в виде списка, и вся инфа доступна в описании предмета.
Гуру, посоветуйте как сделать задуманное.
 
А что именно надо записывать? Пример, если можно.
 
Например числовое значение. Например что-то типа такого: meta.putInt("key", val);
 
Например числовое значение. Например что-то типа такого: meta.putInt("key", val);
На сколько знаю, такого нету, но можно записывать в лор, просто Integer.toString и подставить в начале ChatColor.DARK_GRAY, тогда будет не так заметно. Правда минус в том что много туда не запихнешь.
 
можно записывать в лор
Сразу отпадает : ( Писал в первом посте
Да и это не корректно, писать всю инфу игроку на дисплей )
Вариант использования Lore отпадает из-за того что там только String, оно в виде списка, и вся инфа доступна в описании предмета.
 
По-другому даже не знаю как. Это, скорее всего, невозможно.
Ну... так-то вроде можно... но я не знаю, правильно ли это.

Когда я целый день гуглил по этому вопросу, я наткнулся на одно решение:
Код:
ItemMeta itemMeta = itemStack.getItemMeta();
        itemMeta.setLore(Arrays.asList("One", ChatColor.BOLD + "NYAN"));
        itemStack.setItemMeta(itemMeta);

        net.minecraft.server.v1_7_R3.ItemStack nms = CraftItemStack.asNMSCopy(itemStack);
        NBTTagCompound tag;
        if(nms.tag != null)
            tag = nms.tag;
        else
        {
            nms.tag = new NBTTagCompound();
            tag = nms.tag;
        }
        tag.setInt("MyKek", 546);
        itemStack = CraftItemStack.asCraftMirror(nms);
После чего я кинулся на гитхабе изучать код CraftBukkit'а, а если точнее, именно эти классы.
И решение было почти получено, но есть одно "но". В коде выше, мы не изменяем существующий ItemStack, а "пересоздаем" новый. А это не всегда удобно, особенно если нам надо изменить ItemStack в каком-нибудь событии, которое передано в аргументы события.

Но CraftBukkit очень гадкий. Он не дает расширить CraftItemMeta чтоб написать своё кастомное Meta поле.
Плюс ко всему, класс CraftItemStack (реализующий интерфейс ItemStack) тоже не дает никак получить meta информацию в виде Map<String, Object> (хотя сам где-то внутри себя хранит...).

Со временем, мне пришла в голову идея следующего кода:
Код:
CraftItemStack craftItemStack = (CraftItemStack) itemStack;

            try {
                Class c = craftItemStack.getClass();
                Field nameField = c.getDeclaredField("handle");
                nameField.setAccessible(true);
                net.minecraft.server.v1_7_R3.ItemStack handle = (net.minecraft.server.v1_7_R3.ItemStack) nameField.get(craftItemStack);

                NBTTagCompound tag;
                if(handle.tag != null)
                    tag = handle.tag;
                else
                {
                    handle.tag = new NBTTagCompound();
                    tag = handle.tag;
                }

                if (tag.hasKey("MySuperLevel")) {
                    player.sendMessage("HAS KEY: " + tag.getInt("MySuperLevel"));
                }
                tag.setInt("MySuperLevel", 228);
                player.sendMessage(craftItemStack.toString());
            } catch (Exception e) {
                e.printStackTrace();
            }
Так как поле handle было приватным, я решил сделать его публичным. А затем получаю его и изменяю NBTTagCompound как хочу. И это тоже к счастью работает, без пересоздания ItemStack'а.

Я нашел только один минус :DD Если мета-данные разные, то нельзя стакануть вещи. Например, если у меня есть 2 земли которые подверглись влиянию предыдущего кода, то они не смогут стакануться с другой землей, пока та другая земля тоже не будет иметь такой-же набор meta-данных.

Интересует больше всего правильность этого подхода. На сколько это корректно.
Кто-нибудь имел опыт с мета-данными вещей?
 
Дело в том что CraftItemMeta - фейк. Она нигде не хранится а при попытке получить её она анврапится из NBT, и собственно при применении к итемстаку мета записывает некоторые нбт теги и всё.
Но при этом баккит сам очищает лишние nbt тэги из итемстака, так хранить что-либо в итемстаке - очень сложная задача. В 1.7 правда есть AttributeStorage, там можно незаметно хранить какую-нибудь информацию. Правда кастомный обжект туда тоже не запишеш, в нбт правда тоже.
 
Дело в том что CraftItemMeta - фейк.
Почему? Вот реализация интерфейса https://github.com/Bukkit/CraftBukk...kkit/craftbukkit/inventory/CraftMetaItem.java


Но при этом баккит сам очищает лишние nbt тэги из итемстака
хмм... в коде выше, все работает. Инфа сохраняется даже после перезагрузки сервера или при передачи ItemStack другому игроку
Вот ещё немного нагуглил кое-что:
https://github.com/aadnk/AttributeS.../java/com/comphenix/attribute/NbtFactory.java

Если даже там в коде так же изменяют доступ к полю, значит этот способ нормальный :D
Спасибо всем что отписались. Если будут ещё предложения по теме, пишите. Меня эта тема очень интересует)
Если кому интересно:
Java:
import net.minecraft.server.v1_7_R3.ItemStack;
import net.minecraft.server.v1_7_R3.NBTTagCompound;
import org.bukkit.craftbukkit.v1_7_R3.inventory.CraftItemStack;

import java.lang.reflect.Field;

/**
* Created by Karen on 31.08.2014.
*/
public class NBTItemMeta {

    private static Field fieldHandle;
    private static boolean initialized = false;

    private static void initialize() {
        if (initialized) {
            return;
        }

        try {
            fieldHandle = CraftItemStack.class.getDeclaredField("handle");
            fieldHandle.setAccessible(true);
        } catch (Exception e) {
            fieldHandle = null;
            e.printStackTrace();
        }

        initialized = true;
    }

    private NBTTagCompound tag;

    public NBTItemMeta(org.*********ventory.ItemStack itemStack) {
        initialize();

        try {
            ItemStack handle = (ItemStack) fieldHandle.get(itemStack);
            if (handle == null) {
                return;
            }
            if(handle.tag != null) {
                tag = handle.tag;
            } else {
                tag = new NBTTagCompound();
                handle.tag = tag;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public boolean isValid() {
        return tag != null;
    }

    public boolean hasKey(String key) {
        return tag.hasKey(key);
    }

    public void setInt(String key, int val) {
        tag.setInt(key, val);
    }

    public int getInt(String key) {
        return tag.getInt(key);
    }
}

Использование:
Java:
            NBTItemMeta nbtItemMeta = new NBTItemMeta(itemStack);
            if (nbtItemMeta.isValid()) {
                if (nbtItemMeta.hasKey("BecTestLevel")) {
                    player.sendMessage("Has BecTestLevel: " + nbtItemMeta.getInt("BecTestLevel"));
                } else {
                    Random random = new Random();
                    int r = random.nextInt(1000);
                    nbtItemMeta.setInt("BecTestLevel", r);
                    player.sendMessage("Set BecTestLevel: " + r);
                }
            }
 
Если что-либо попробует получить итеммету предмета то все незнакомые баккиту нбт теги сотрутся. Так что лучше таки хранить всё в AttributeStorage.
 
Если что-либо попробует получить итеммету предмета то все незнакомые баккиту нбт теги сотрутся. Так что лучше таки хранить всё в AttributeStorage.
Нет. Вот кусок кода CraftItemStack:
Java:
// [B]CraftItemStack[/B]
    public static ItemMeta getItemMeta(net.minecraft.server.ItemStack item) {
        if (!hasItemMeta(item)) {
            return CraftItemFactory.instance().getItemMeta(getType(item));
        }
        switch (getType(item)) {
            case WRITTEN_BOOK:
            case BOOK_AND_QUILL:
                return new CraftMetaBook(item.tag);
            case SKULL_ITEM:
                return new CraftMetaSkull(item.tag);
            case LEATHER_HELMET:
            case LEATHER_CHESTPLATE:
            case LEATHER_LEGGINGS:
            case LEATHER_BOOTS:
                return new CraftMetaLeatherArmor(item.tag);
            case POTION:
                return new CraftMetaPotion(item.tag);
            case MAP:
                return new CraftMetaMap(item.tag);
            case FIREWORK:
                return new CraftMetaFirework(item.tag);
            case FIREWORK_CHARGE:
                return new CraftMetaCharge(item.tag);
            case ENCHANTED_BOOK:
                return new CraftMetaEnchantedBook(item.tag);
            default:
                return new CraftMetaItem(item.tag);
        }
    }

Но вот если что-то попытается записать в мету....
Java:
// [B]CraftItemStack[/B]
    public static boolean setItemMeta(net.minecraft.server.ItemStack item, ItemMeta itemMeta) {
        if (item == null) {
            return false;
        }
        if (CraftItemFactory.instance().equals(itemMeta, null)) {
            item.tag = null;
            return true;
        }
        if (!CraftItemFactory.instance().isApplicable(itemMeta, getType(item))) {
            return false;
        }

        NBTTagCompound tag = new NBTTagCompound();
        item.setTag(tag); // Во всем виновата эта строчка : (

        ((CraftMetaItem) itemMeta).applyToItem(tag);
        return true;
    }
Java:
// [B]CraftMetaItem[/B]
    @Overridden
    void applyToItem(NBTTagCompound itemTag) {
        if (hasDisplayName()) {
            setDisplayTag(itemTag, NAME.NBT, new NBTTagString(displayName));
        }

        if (hasLore()) {
            setDisplayTag(itemTag, LORE.NBT, createStringList(lore));
        }

        applyEnchantments(enchantments, itemTag, ENCHANTMENTS);

        if (hasRepairCost()) {
            itemTag.setInt(REPAIR.NBT, repairCost);
        }

        if (attributes != null) {
            itemTag.set(ATTRIBUTES.NBT, attributes); // Какие-то аттрибуты? о_О
        }
    }

То да : ( Данные потеряются.
Это что, получается, что мне теперь надо делать поле private final NBTTagList attributes; в CraftMetaItem публичным и пихать туда данные ?))
 
Именно так. Не знаю почему они убрали систему которая валидировала нбт, но они могут впихнуть её снова.
 
Ещё есть плагин powerNBT. Можно его API заюзать.
 
Именно так. Не знаю почему они убрали систему которая валидировала нбт, но они могут впихнуть её снова.
А разработчики не уберут поле attributes ?) А то мало-ли xDD

Там тот же алгоритм действий что и у меня, делается поле handle публичным.
Но обнаружилась проблема, описанная выше.
Если другой плагин что-то начнет писать в мету, например в Lore (что они любят делать) то моя инфа сотрется.
Вот собираюсь попробовать писать в NBTTagList attributes, судя по коду, он сохраняется тоже. Проверю и отпишусь.
Ещё есть плагин powerNBT. Можно его API заюзать.
Посмотрел код:
https://github.com/DPOH-VAR/PowerNB...e/dpohvar/powernbt/utils/ReflectionUtils.java

Думаю, что тут тоже будет проблема, если другой плагин попробует обновить инфу в Meta.
Попытаюсь проверить, мало ли, вдруг в этом PowerNBT реализован какой-нибудь перехват метода, которого я не заметил :D
 
Самый простой и банальный способ - каждому предмету дать уникальный идентификатор, зашифровать его цветовым кодом и хранить в имени итема, а уже на основе уникального идентификатора опазновать, что этот итем обладает какими-то свойствами и всё, и не нужно копаться в рефлексии.

Успешно это использую в мини играх.
 
Самый простой и банальный способ - каждому предмету дать уникальный идентификатор, зашифровать его цветовым кодом и хранить в имени итема, а уже на основе уникального идентификатора опазновать, что этот итем обладает какими-то свойствами и всё, и не нужно копаться в рефлексии.

Успешно это использую в мини играх.
Угу... и внезапно, другой плагин тоже так будет хранить информацию... произойдет коллизия.
Так-что это вообще не вариант)
 
Угу... и внезапно, другой плагин тоже так будет хранить информацию... произойдет коллизия.
Так-что это вообще не вариант)
Подобных плагинов я не видел, хотя на каком-то буржуйском сервере видел в имени номера уникальные. Хочешь секретные данные в lore храни, хочешь - в конец имени добавляй.
Зато здесь нет никакого гемороя с тем, что ядро сотрёт данные из NBT при любом удобном случае.
Ничего не мешает сделать несколько секретных идентификаторов в имени.
 
Подобных плагинов я не видел, хотя на каком-то буржуйском сервере видел в имени номера уникальные. Хочешь секретные данные в lore храни, хочешь - в конец имени добавляй.
Зато здесь нет никакого гемороя с тем, что ядро сотрёт данные из NBT при любом удобном случае.
Ничего не мешает сделать несколько секретных идентификаторов в имени.
Lore тоже не вариант))
 
Назад
Верх