Хостинг серверов Minecraft playvds.com
  1. Вы находитесь в русском сообществе Bukkit. Мы - администраторы серверов Minecraft, разрабатываем собственные плагины и переводим на русский язык плагины наших собратьев из других стран.
    Скрыть объявление

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

Тема в разделе "Разработка плагинов для новичков", создана пользователем kapehh, 30 авг 2014.

  1. Автор темы
    kapehh

    kapehh Ньюби Пользователь

    Баллы:
    1
    Здравствуйте )
    Стандартно в ItemMeta нельзя записывать что-то своё. Вариант использования Lore отпадает из-за того что там только String, оно в виде списка, и вся инфа доступна в описании предмета.
    Гуру, посоветуйте как сделать задуманное.
     
  2. Хостинг MineCraft
    <
  3. makssof

    makssof Старожил Девелопер Пользователь

    Баллы:
    103
    Skype:
    makssofez
    А что именно надо записывать? Пример, если можно.
     
  4. Автор темы
    kapehh

    kapehh Ньюби Пользователь

    Баллы:
    1
    Например числовое значение. Например что-то типа такого: meta.putInt("key", val);
     
  5. CraftCoder

    CraftCoder Старожил Пользователь

    Баллы:
    108
    Имя в Minecraft:
    CraftCoderr
    На сколько знаю, такого нету, но можно записывать в лор, просто Integer.toString и подставить в начале ChatColor.DARK_GRAY, тогда будет не так заметно. Правда минус в том что много туда не запихнешь.
     
  6. makssof

    makssof Старожил Девелопер Пользователь

    Баллы:
    103
    Skype:
    makssofez
    Сохранять это в ХМ?..
     
  7. Автор темы
    kapehh

    kapehh Ньюби Пользователь

    Баллы:
    1
    Сразу отпадает : ( Писал в первом посте
    Да и это не корректно, писать всю инфу игроку на дисплей )
     
  8. CraftCoder

    CraftCoder Старожил Пользователь

    Баллы:
    108
    Имя в Minecraft:
    CraftCoderr
    По-другому даже не знаю как. Это, скорее всего, невозможно.
     
  9. Автор темы
    kapehh

    kapehh Ньюби Пользователь

    Баллы:
    1
    Ну... так-то вроде можно... но я не знаю, правильно ли это.

    Когда я целый день гуглил по этому вопросу, я наткнулся на одно решение:
    Код:
    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-данных.

    Интересует больше всего правильность этого подхода. На сколько это корректно.
    Кто-нибудь имел опыт с мета-данными вещей?
     
  10. Shevchik

    Shevchik Старожил Пользователь

    Баллы:
    173
    Имя в Minecraft:
    _Shevchik_
    Дело в том что CraftItemMeta - фейк. Она нигде не хранится а при попытке получить её она анврапится из NBT, и собственно при применении к итемстаку мета записывает некоторые нбт теги и всё.
    Но при этом баккит сам очищает лишние nbt тэги из итемстака, так хранить что-либо в итемстаке - очень сложная задача. В 1.7 правда есть AttributeStorage, там можно незаметно хранить какую-нибудь информацию. Правда кастомный обжект туда тоже не запишеш, в нбт правда тоже.
     
  11. Автор темы
    kapehh

    kapehh Ньюби Пользователь

    Баллы:
    1
    Почему? Вот реализация интерфейса https://github.com/Bukkit/CraftBukk...kkit/craftbukkit/inventory/CraftMetaItem.java


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

    Если даже там в коде так же изменяют доступ к полю, значит этот способ нормальный :D
    Спасибо всем что отписались. Если будут ещё предложения по теме, пишите. Меня эта тема очень интересует)[DOUBLEPOST=1409476450][/DOUBLEPOST]Если кому интересно:
    Код:
    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.bukkit.inventory.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);
        }
    }
    
    Использование:
    Код:
                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);
                    }
                }
     
  12. Shevchik

    Shevchik Старожил Пользователь

    Баллы:
    173
    Имя в Minecraft:
    _Shevchik_
    Если что-либо попробует получить итеммету предмета то все незнакомые баккиту нбт теги сотрутся. Так что лучше таки хранить всё в AttributeStorage.
     
  13. Автор темы
    kapehh

    kapehh Ньюби Пользователь

    Баллы:
    1
    Нет. Вот кусок кода CraftItemStack:
    Код:
    // [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);
            }
        }
    Но вот если что-то попытается записать в мету....
    Код:
    // [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;
        }
    
    Код:
    // [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 публичным и пихать туда данные ?))
     
  14. Shevchik

    Shevchik Старожил Пользователь

    Баллы:
    173
    Имя в Minecraft:
    _Shevchik_
    Именно так. Не знаю почему они убрали систему которая валидировала нбт, но они могут впихнуть её снова.
     
  15. BeYkeR

    BeYkeR Старожил Девелопер Пользователь

    Баллы:
    173
  16. saharin94

    saharin94 Старожил Пользователь Заблокирован

    Баллы:
    173
    Skype:
    RikkiLooh
    Имя в Minecraft:
    RubukkitDniwe
    Ещё есть плагин powerNBT. Можно его API заюзать.
     
  17. Автор темы
    kapehh

    kapehh Ньюби Пользователь

    Баллы:
    1
    А разработчики не уберут поле attributes ?) А то мало-ли xDD

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

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

    ptnk Старожил Пользователь

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

    Успешно это использую в мини играх.
     
  19. Автор темы
    kapehh

    kapehh Ньюби Пользователь

    Баллы:
    1
    Угу... и внезапно, другой плагин тоже так будет хранить информацию... произойдет коллизия.
    Так-что это вообще не вариант)
     
  20. ptnk

    ptnk Старожил Пользователь

    Баллы:
    173
    Подобных плагинов я не видел, хотя на каком-то буржуйском сервере видел в имени номера уникальные. Хочешь секретные данные в lore храни, хочешь - в конец имени добавляй.
    Зато здесь нет никакого гемороя с тем, что ядро сотрёт данные из NBT при любом удобном случае.
    Ничего не мешает сделать несколько секретных идентификаторов в имени.
     
  21. Автор темы
    kapehh

    kapehh Ньюби Пользователь

    Баллы:
    1
    Lore тоже не вариант))
     

Поделиться этой страницей