001package com.mrivanplays.commandworker.bukkit.argtypes; 002 003import com.mojang.brigadier.arguments.ArgumentType; 004import com.mrivanplays.commandworker.bukkit.registry.CmdRegistryHandler; 005import java.lang.reflect.Constructor; 006import java.lang.reflect.Field; 007import java.lang.reflect.InvocationTargetException; 008import java.lang.reflect.Method; 009import java.util.Arrays; 010import org.bukkit.Bukkit; 011import org.bukkit.NamespacedKey; 012 013/** 014 * Represents a utility class for accessing {@link ArgumentType}s provided by mojang in the server's 015 * internals. 016 */ 017public class MinecraftArgumentTypesAccessor { 018 019 private MinecraftArgumentTypesAccessor() {} 020 021 private static Constructor<?> MINECRAFT_KEY_CONSTRUCTOR; 022 private static Method ARGUMENT_REGISTRY_GET_BY_KEY_METHOD; 023 private static Field ARGUMENT_REGISTRY_ENTRY_CLASS_FIELD; 024 025 static { 026 if (CmdRegistryHandler.isSupported()) { 027 String nmsVersion = 028 Bukkit.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3]; 029 try { 030 Class<?> minecraftKey = 031 Class.forName("net.minecraft.server." + nmsVersion + ".MinecraftKey"); 032 MINECRAFT_KEY_CONSTRUCTOR = minecraftKey.getConstructor(String.class, String.class); 033 MINECRAFT_KEY_CONSTRUCTOR.setAccessible(true); 034 035 Class<?> argumentRegistry = 036 Class.forName("net.minecraft.server." + nmsVersion + ".ArgumentRegistry"); 037 ARGUMENT_REGISTRY_GET_BY_KEY_METHOD = 038 Arrays.stream(argumentRegistry.getDeclaredMethods()) 039 .filter(method -> method.getParameterCount() == 1) 040 .filter(method -> minecraftKey.equals(method.getParameterTypes()[0])) 041 .findFirst() 042 .orElseThrow(NoSuchMethodException::new); 043 ARGUMENT_REGISTRY_GET_BY_KEY_METHOD.setAccessible(true); 044 045 Class<?> argumentRegistryEntry = ARGUMENT_REGISTRY_GET_BY_KEY_METHOD.getReturnType(); 046 ARGUMENT_REGISTRY_ENTRY_CLASS_FIELD = 047 Arrays.stream(argumentRegistryEntry.getDeclaredFields()) 048 .filter(field -> field.getType() == Class.class) 049 .findFirst() 050 .orElseThrow(NoSuchFieldException::new); 051 ARGUMENT_REGISTRY_ENTRY_CLASS_FIELD.setAccessible(true); 052 } catch (NoSuchMethodException | NoSuchFieldException | ClassNotFoundException e) { 053 throw new ExceptionInInitializerError(e); 054 } 055 } 056 } 057 058 public static void ensureSetup() { 059 // do nothing; this is enough to trigger the static initializer 060 } 061 062 /** 063 * Retrieves the registered argument type's class, matching the key specified, if brigadier is 064 * supported. 065 * 066 * @param key the key of the argument type you want to retrieve 067 * @return argument type class 068 */ 069 @SuppressWarnings("unchecked") 070 public static Class<? extends ArgumentType<?>> getArgumentClass(NamespacedKey key) { 071 if (CmdRegistryHandler.isSupported()) { 072 try { 073 Object minecraftKey = 074 MINECRAFT_KEY_CONSTRUCTOR.newInstance(key.getNamespace(), key.getKey()); 075 Object entry = ARGUMENT_REGISTRY_GET_BY_KEY_METHOD.invoke(null, minecraftKey); 076 if (entry == null) { 077 throw new IllegalArgumentException( 078 "No such ArgumentType with key '" + key.toString() + "'"); 079 } 080 081 Class<?> argument = (Class<?>) ARGUMENT_REGISTRY_ENTRY_CLASS_FIELD.get(entry); 082 return (Class<? extends ArgumentType<?>>) argument; 083 } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) { 084 return null; 085 } 086 } 087 return null; 088 } 089 090 /** 091 * Retrieves the registered argument type, matching the key specified, if brigadier is supported. 092 * 093 * @param key the key of the argument type you want to retrieve 094 * @return argument type 095 */ 096 public static ArgumentType<?> getByKey(NamespacedKey key) { 097 if (CmdRegistryHandler.isSupported()) { 098 try { 099 Constructor<? extends ArgumentType<?>> argumentConstructor = 100 getArgumentClass(key).getDeclaredConstructor(); 101 argumentConstructor.setAccessible(true); 102 return argumentConstructor.newInstance(); 103 } catch (NullPointerException 104 | InstantiationException 105 | InvocationTargetException 106 | NoSuchMethodException 107 | IllegalAccessException e) { 108 return null; 109 } 110 } 111 return null; 112 } 113}