Вопрос Регистрация кастомных зачарований на PaperMC 1.21.1

Версия Minecraft
1.20.X

KotenokDev

Пользователь
Сообщения
133
Решения
1
Ядро: Paper-1.21.1

До этого мне приходилось работать с кастомными зачарованиями только на 1.16.5 но недавно я решил перенести сервер на версию 1.21.1 и у меня возникла проблема с регистрацией кастомных зачарований. Я нашел туториал на spigotmc org и по нему написал небольшую библиотеку для плагина для регистрации чар:


Java:
package ru.kotenokdev.kenchantments.utils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.enchantments.CraftEnchantment;
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.inventory.ItemType;

import io.papermc.paper.registry.TypedKey;
import net.kyori.adventure.text.Component;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderSet;
import net.minecraft.core.MappedRegistry;
import net.minecraft.core.Registry;
import net.minecraft.core.component.DataComponentMap;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.tags.EnchantmentTags;
import net.minecraft.tags.TagKey;
import net.minecraft.world.entity.EquipmentSlotGroup;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.enchantment.Enchantment;
import net.minecraft.world.item.enchantment.Enchantment.Cost;
import net.minecraft.world.item.enchantment.Enchantment.EnchantmentDefinition;

public class Registration {
   
    private static final MinecraftServer             SERVER;
    private static final MappedRegistry<org.bukkit.enchantments.Enchantment> ENCHANTS;
    private static final MappedRegistry<Item>        ITEMS;

    private static final String REGISTRY_FROZEN_TAGS_FIELD = "j"; // frozenTags
    private static final String REGISTRY_ALL_TAGS_FIELD    = "k"; // allTags
    private static final String TAG_SET_UNBOUND_METHOD     = "a"; // .unbound()
    private static final String TAG_SET_MAP_FIELD          = "a"; // val$map for PaperMC

    static {
        SERVER = ((CraftServer) Bukkit.getServer()).getServer();
        ITEMS = (MappedRegistry<Item>) SERVER.registryAccess().lookup(Registries.ITEM).orElseThrow();
        ENCHANTS = (MappedRegistry<org.bukkit.enchantments.Enchantment>) SERVER.registryAccess().lookup(Registries.ENCHANTMENT).orElseThrow();
    }

    private static <T> ResourceKey<T> getResourceKey(Registry<T> registry, String name) {
        return ResourceKey.create(registry.key(), ResourceLocation.withDefaultNamespace(name));
    }

    private static <T> TagKey<T> getTagKey( Registry<T> registry, String name) {
        return TagKey.create(registry.key(), ResourceLocation.withDefaultNamespace(name));
    }

    private static <T> Map<TagKey<T>, HolderSet.Named<T>> getFrozenTags( MappedRegistry<T> registry) {
        return (Map<TagKey<T>, HolderSet.Named<T>>) Reflex.getFieldValue(registry, REGISTRY_FROZEN_TAGS_FIELD);
    }

    private static <T> Object getAllTags( MappedRegistry<T> registry) {
        return Reflex.getFieldValue(registry, REGISTRY_ALL_TAGS_FIELD);
    }

    private static <T> Map<TagKey<T>, HolderSet.Named<T>> getTagsMap( Object tagSet) {
        return new HashMap<>((Map<TagKey<T>, HolderSet.Named<T>>) Reflex.getFieldValue(tagSet, TAG_SET_MAP_FIELD));
    }
   
    public void unfreezeRegistry() {
        unfreeze(ENCHANTS);
        unfreeze(ITEMS);
    }

    private static <T> void unfreeze(MappedRegistry<T> registry) {
        Reflex.setFieldValue(registry, "l", false);             // MappedRegistry#frozen
        Reflex.setFieldValue(registry, "m", new IdentityHashMap<>()); // MappedRegistry#unregisteredIntrusiveHolders
    }
   
    public void freezeRegistry() {
        freeze(ITEMS);
        freeze(ENCHANTS);
    }

    private static <T> void freeze(MappedRegistry<T> registry) {
        Object tagSet = getAllTags(registry);

        Map<TagKey<T>, HolderSet.Named<T>> tagsMap = getTagsMap(tagSet);
        Map<TagKey<T>, HolderSet.Named<T>> frozenTags = getFrozenTags(registry);

        tagsMap.forEach(frozenTags::putIfAbsent);

        unbound(registry);
        registry.freeze();

        frozenTags.forEach(tagsMap::putIfAbsent);
        Reflex.setFieldValue(tagSet, TAG_SET_MAP_FIELD, tagsMap);
        Reflex.setFieldValue(registry, REGISTRY_ALL_TAGS_FIELD, tagSet);
    }

    private static <T> void unbound(MappedRegistry<T> registry) {
        Class<?> tagSetClass = Reflex.getInnerClass(MappedRegistry.class.getName(), "a"); // TagSet for PaperMC

        Method unboundMethod = Reflex.getMethod(tagSetClass, TAG_SET_UNBOUND_METHOD);
        Object unboundTagSet = Reflex.invokeMethod(unboundMethod, registry); // new TagSet object.

        Reflex.setFieldValue(registry, REGISTRY_ALL_TAGS_FIELD, unboundTagSet);
    }
   
    private static HolderSet.Named<Item> createItemsSet(String prefix, String enchantId, Set<Material> materials) {
        TagKey<Item> customKey = getTagKey(ITEMS, prefix + "/" + enchantId);
        List<Holder<Item>> holders = new ArrayList<>();

        materials.forEach(material -> {
            ResourceLocation location = CraftNamespacedKey.toMinecraft(material.getKey());
            Holder.Reference<Item> holder = ITEMS.get(location).builtInRegistryHolder();
            if (holder == null) return;

            holders.add(holder);
        });
       
        Map<TagKey<Item>, List<Holder<Item>>> customKeys = new HashMap<>();
        customKeys.put(customKey, holders);
        ITEMS.bindTags(customKeys);

        return getFrozenTags(ITEMS).get(customKey);
    }
   
    public void register(org.bukkit.enchantments.Enchantment ench) {
        Component display = ench.displayName(1);
        HolderSet.Named<Item> supportedItems = createItemsSet("enchant_supported", ench.getName(), Set.of(Material.DIAMOND_HELMET));;
        HolderSet.Named<Item> primaryItems = createItemsSet("enchant_primary", ench.getName(), Set.of(Material.DIAMOND_HELMET));;
        Cost minCost = new Cost(ench.getMinModifiedCost(1), ench.getMinModifiedCost(1));
        Cost maxCost = new Cost(ench.getMaxModifiedCost(1), ench.getMaxModifiedCost(1));
        EquipmentSlotGroup[] slots = ench.getActiveSlotGroups().toArray(new EquipmentSlotGroup[] {});
        DataComponentMap effects = DataComponentMap.builder().build();
       
        EnchantmentDefinition definition = Enchantment.definition(supportedItems, primaryItems,
                ench.getWeight(), ench.getMaxLevel(), minCost, maxCost, ench.getAnvilCost(), slots);
        Registry.register(ENCHANTS, ench.getName(), ench);
    }

}


Java:
package ru.kotenokdev.kenchantments.utils;

import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class Reflex {

   
    public static Class<?> getClass(  String path,   String name) {
        return getClass(path + "." + name);
    }

   
    public static Class<?> getInnerClass(  String path,   String name) {
        return getClass(path + "$" + name);
    }

   
    public static Class<?> getNMSClass(  String path,   String realName) {
        return getNMSClass(path, realName, null);
    }

   
    public static Class<?> getNMSClass(  String path,   String realName,   String obfName) {
        Class<?> byRealName = getClass(path + "." + realName, false);
        if (byRealName != null) {
            return byRealName;
        }

        if (obfName != null) {
            return getClass(path + "." + obfName, false);
        }

        return null;
    }

    private static Class<?> getClass(  String path) {
        return getClass(path, true);
    }

    private static Class<?> getClass(  String path, boolean printError) {
        try {
            return Class.forName(path);
        }
        catch (ClassNotFoundException exception) {
            if (printError) exception.printStackTrace();
            return null;
        }
    }

    public static Constructor<?> getConstructor(  Class<?> source, Class<?>... types) {
        try {
            Constructor<?> constructor = source.getDeclaredConstructor(types);
            constructor.setAccessible(true);
            return constructor;
        }
        catch (ReflectiveOperationException exception) {
            exception.printStackTrace();
        }
        return null;
    }

    public static Object invokeConstructor(  Constructor<?> constructor, Object... obj) {
        try {
            return constructor.newInstance(obj);
        }
        catch (ReflectiveOperationException exception) {
            exception.printStackTrace();
        }
        return null;
    }

   
    public static <T> List<T> getFields(  Class<?> source,   Class<T> type) {
        List<T> list = new ArrayList<>();

        for (Field field : Reflex.getFields(source)) {
            if (!field.getDeclaringClass().equals(source)) continue;
            //if (!field.canAccess(null)) continue;
            if (!Modifier.isStatic(field.getModifiers())) continue;
            if (!Modifier.isFinal(field.getModifiers())) continue;
            if (!type.isAssignableFrom(field.getType())) continue;
            if (!field.trySetAccessible()) continue;

            try {
                list.add(type.cast(field.get(null)));
            }
            catch (IllegalArgumentException | IllegalAccessException exception) {
                exception.printStackTrace();
            }
        }

        return list;
    }

   
    public static List<Field> getFields(  Class<?> source) {
        List<Field> result = new ArrayList<>();

        Class<?> clazz = source;
        while (clazz != null && clazz != Object.class) {
            if (!result.isEmpty()) {
                result.addAll(0, Arrays.asList(clazz.getDeclaredFields()));
            }
            else {
                Collections.addAll(result, clazz.getDeclaredFields());
            }
            clazz = clazz.getSuperclass();
        }

        return result;
    }

    public static Field getField(  Class<?> source,   String name) {
        try {
            return source.getDeclaredField(name);
        }
        catch (NoSuchFieldException exception) {
            Class<?> superClass = source.getSuperclass();
            return superClass == null ? null : getField(superClass, name);
        }
    }

    public static Object getFieldValue(  Object source,   String realName,   String obfName) {
        Object byName = getFieldValue(source, realName);
        return byName == null ? getFieldValue(source, obfName) : byName;
    }

    public static Object getFieldValue(  Object source,   String name) {
        try {
            Class<?> clazz = source instanceof Class<?> ? (Class<?>) source : source.getClass();
            Field field = getField(clazz, name);
            if (field == null) return null;

            field.setAccessible(true);
            return field.get(source);
        }
        catch (IllegalAccessException exception) {
            exception.printStackTrace();
        }
        return null;
    }

    public static boolean setFieldValue(  Object source,   String name,   Object value) {
        try {
            boolean isStatic = source instanceof Class;
            Class<?> clazz = isStatic ? (Class<?>) source : source.getClass();

            Field field = getField(clazz, name);
            if (field == null) return false;

            field.setAccessible(true);
            field.set(isStatic ? null : source, value);
            return true;
        }
        catch (IllegalAccessException exception) {
            exception.printStackTrace();
        }
        return false;
    }

    public static Method getMethod(  Class<?> source,   String realName,   String obfName,   Class<?>... params) {
        Method byName = getMethod(source, realName, params);
        return byName == null ? getMethod(source, obfName, params) : byName;
    }

    public static Method getMethod(  Class<?> source,   String name,   Class<?>... params) {
        try {
            return source.getDeclaredMethod(name, params);
        }
        catch (NoSuchMethodException exception) {
            Class<?> superClass = source.getSuperclass();
            return superClass == null ? null : getMethod(superClass, name);
        }
    }

    public static Object invokeMethod(  Method method,   Object by,   Object... param) {
        method.setAccessible(true);
        try {
            return method.invoke(by, param);
        }
        catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException exception) {
            exception.printStackTrace();
        }
        return null;
    }
}

и получил ошибку:

Форматирование (BB-код):
[17:29:17 ERROR]: Error occurred while enabling KotenokEnchantments v1.0 (Is it up to date?)
java.lang.ExceptionInInitializerError: null
        at KotenokEnchantments.jar/ru.kotenokdev.kenchantments.KotenokEnchantments.onEnable(KotenokEnchantments.java:29) ~[KotenokEnchantments.jar:?]
        at org.bukkit.plugin.java.JavaPlugin.setEnabled(JavaPlugin.java:288) ~[paper-mojangapi-1.21.1-R0.1-SNAPSHOT.jar:?]
        at io.papermc.paper.plugin.manager.PaperPluginInstanceManager.enablePlugin(PaperPluginInstanceManager.java:202) ~[paper-1.21.1.jar:1.21.1-133-3cb8529]
        at io.papermc.paper.plugin.manager.PaperPluginManagerImpl.enablePlugin(PaperPluginManagerImpl.java:109) ~[paper-1.21.1.jar:1.21.1-133-3cb8529]
        at org.bukkit.plugin.SimplePluginManager.enablePlugin(SimplePluginManager.java:520) ~[paper-mojangapi-1.21.1-R0.1-SNAPSHOT.jar:?]
        at org.bukkit.craftbukkit.CraftServer.enablePlugin(CraftServer.java:641) ~[paper-1.21.1.jar:1.21.1-133-3cb8529]
        at org.bukkit.craftbukkit.CraftServer.enablePlugins(CraftServer.java:590) ~[paper-1.21.1.jar:1.21.1-133-3cb8529]
        at net.minecraft.server.dedicated.DedicatedServer.initServer(DedicatedServer.java:293) ~[paper-1.21.1.jar:1.21.1-133-3cb8529]
        at net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:1214) ~[paper-1.21.1.jar:1.21.1-133-3cb8529]
        at net.minecraft.server.MinecraftServer.lambda$spin$0(MinecraftServer.java:329) ~[paper-1.21.1.jar:1.21.1-133-3cb8529]
        at java.base/java.lang.Thread.run(Thread.java:1583) ~[?:?]
Caused by: java.lang.ClassCastException: class net.minecraft.core.MappedRegistry$1 cannot be cast to class net.minecraft.core.MappedRegistry (net.minecraft.core.MappedRegistry$1 and net.minecraft.core.MappedRegistry are in unnamed module of loader java.net.URLClassLoader @1b9e1916)
        at KotenokEnchantments.jar/ru.kotenokdev.kenchantments.utils.Registration.<clinit>(Registration.java:50) ~[KotenokEnchantments.jar:?]
        ... 11 more

Есть способы ее исправить или есть аналогичные методы регистрации кастомных чар?
 
Java:
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.TypedKey;
import net.minecraft.resources.ResourceLocation;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;

public class CustomEnchant extends Enchantment {
  
    public CustomEnchant(String namespace, String key) {
        super(TypedKey.create(
            RegistryKey.ENCHANTMENT,
            ResourceLocation.of(namespace + ":" + key)
        ));
    }

    @Override
    public boolean canEnchantItem(ItemStack item) {
        return item.getType() == Material.DIAMOND_SWORD;
    }

    @Override
    public int getMaxLevel() {
        return 3;
    }
}
попробуй так, выше не так отправил ( на проверке )
 
Последнее редактирование:
Назад
Сверху Снизу