KotenokDev
Пользователь
- Сообщения
- 133
- Решения
- 1
Ядро: Paper-1.21.1
До этого мне приходилось работать с кастомными зачарованиями только на 1.16.5 но недавно я решил перенести сервер на версию 1.21.1 и у меня возникла проблема с регистрацией кастомных зачарований. Я нашел туториал на spigotmc org и по нему написал небольшую библиотеку для плагина для регистрации чар:
и получил ошибку:
Есть способы ее исправить или есть аналогичные методы регистрации кастомных чар?
До этого мне приходилось работать с кастомными зачарованиями только на 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
Есть способы ее исправить или есть аналогичные методы регистрации кастомных чар?