Loft implementations to shared library plugin WizCompat

This commit is contained in:
Gabriel Tofvesson 2021-06-25 15:49:01 +02:00
parent 4082e51aa2
commit eb5b2beb41
20 changed files with 58 additions and 750 deletions

9
.idea/libraries/SpigotWizCompat.xml generated Normal file
View File

@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="SpigotWizCompat">
<CLASSES>
<root url="jar://$PROJECT_DIR$/lib/SpigotWizCompat.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

View File

@ -9,5 +9,6 @@
<orderEntry type="jdk" jdkName="16" jdkType="JavaSDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="spigot-1.15.5-R0.1" level="project" />
<orderEntry type="library" name="SpigotWizCompat" level="project" />
</component>
</module>

View File

@ -3,6 +3,8 @@ version: 1.3.3
author: IKEA_Jesus
main: dev.w1zzrd.invtweaks.InvTweaksPlugin
api-version: 1.13
depend:
- WizCompat
commands:
sort:
description: Sort chest you are looking at

View File

@ -1,93 +0,0 @@
package dev.w1zzrd.invtweaks;
import org.bukkit.Bukkit;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.plugin.Plugin;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Manager for persistent data storage for a plugin
*/
public class DataStore {
private static final Logger logger = Bukkit.getLogger();
private final File storeFile;
private final FileConfiguration config;
/**
* Create a data store with the given name. This will attempt to load a yaml file named after the store
* @param storeName Name of the store to load. File will be named storeName + ".yml"
* @param plugin Plugin to associate the data store with
*/
public DataStore(final String storeName, final Plugin plugin) {
storeFile = new File(plugin.getDataFolder(), storeName + ".yml");
config = YamlConfiguration.loadConfiguration(storeFile);
// Save config in case it doesn't exist
saveData();
}
/**
* Load a value from the data store
* @param path Path in the file to load the data from
* @param defaultValue Getter for a default value, in case the data does not exist in the store
* @param <T> Type of the data to load
* @return Data at the given path, if available, else the default value
*/
public <T extends ConfigurationSerializable> T loadData(final String path, final DefaultGetter<T> defaultValue) {
final T value = (T) config.get(path);
return value == null ? defaultValue.get() : value;
}
/**
* Save data at a given path in the store
* @param path Path to store data at
* @param value Data to store
* @param <T> Type of {@link ConfigurationSerializable} data to store
*/
public <T extends ConfigurationSerializable> void storeData(final String path, final T value) {
config.set(path, value);
}
/**
* Save the current data store in memory to persistent memory
*/
public void saveData() {
try {
config.save(storeFile);
} catch (IOException e) {
logger.log(Level.SEVERE, Logger.GLOBAL_LOGGER_NAME + " Could not save data due to an I/O error", e);
}
}
/**
* Reload data store from persistent memory, overwriting any current state
*/
public void loadData() {
try {
config.load(storeFile);
} catch (IOException | InvalidConfigurationException e) {
logger.log(Level.SEVERE, Logger.GLOBAL_LOGGER_NAME + " Could not load data due to an I/O error", e);
}
}
/**
* Functional interface for constructing default values
* @param <T> Type to construct
*/
public interface DefaultGetter<T> {
/**
* Instantiate default value
* @return Default value that was instantiated
*/
T get();
}
}

View File

@ -6,17 +6,16 @@ import dev.w1zzrd.invtweaks.listener.*;
import dev.w1zzrd.invtweaks.serialization.MagnetConfig;
import dev.w1zzrd.invtweaks.serialization.MagnetData;
import dev.w1zzrd.invtweaks.serialization.SearchConfig;
import dev.w1zzrd.invtweaks.serialization.UUIDList;
import dev.w1zzrd.spigot.wizcompat.enchantment.EnchantmentRegistryEntry;
import dev.w1zzrd.spigot.wizcompat.enchantment.ServerEnchantmentRegistry;
import dev.w1zzrd.spigot.wizcompat.serialization.PersistentData;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.serialization.ConfigurationSerialization;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.event.HandlerList;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.Objects;
import java.util.logging.Logger;
@ -41,9 +40,8 @@ public final class InvTweaksPlugin extends JavaPlugin {
private SearchCommandExecutor searchCommandExecutor;
private NamedChestCommand namedChestCommandExecutor;
private CapitatorCommand capitatorCommand;
private DataStore data;
private NamespacedKey capitatorEnchantmentKey;
private Enchantment capitatorEnchantment;
private PersistentData data;
private EnchantmentRegistryEntry<CapitatorEnchantment> capitatorEnchantment = null;
@Override
public void onEnable() {
@ -80,59 +78,28 @@ public final class InvTweaksPlugin extends JavaPlugin {
/**
* Get a reference to the persistent data store object for this plugin
* @return An instance of {@link DataStore} for this plugin
* @return An instance of {@link PersistentData} for this plugin
*/
public DataStore getPersistentData() {
public PersistentData getPersistentData() {
return data;
}
private void initEnchantments() {
final boolean activateCapitator = getConfig().getBoolean("capitator", true);
if (activateCapitator) {
capitatorEnchantmentKey = new NamespacedKey(this, ENCHANTMENT_CAPITATOR_NAME);
capitatorEnchantment = new CapitatorEnchantment(ENCHANTMENT_CAPITATOR_NAME, capitatorEnchantmentKey);
}
try {
final Field acceptingField = Enchantment.class.getDeclaredField("acceptingNew");
acceptingField.setAccessible(true);
acceptingField.set(null, true);
if (activateCapitator)
Enchantment.registerEnchantment(capitatorEnchantment);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
if (getConfig().getBoolean("capitator", true))
capitatorEnchantment = ServerEnchantmentRegistry.registerEnchantment(
this,
new CapitatorEnchantment(
ENCHANTMENT_CAPITATOR_NAME,
new NamespacedKey(this, ENCHANTMENT_CAPITATOR_NAME)
)
);
}
private void disableEnchantments() {
final boolean activateCapitator = getConfig().getBoolean("capitator", true);
try {
final Field byKeyField = Enchantment.class.getDeclaredField("byKey");
final Field byNameField = Enchantment.class.getDeclaredField("byName");
byKeyField.setAccessible(true);
byNameField.setAccessible(true);
final Object byKey = byKeyField.get(null);
final Object byName = byNameField.get(null);
if (byKey instanceof final Map<?, ?> byKeyMap && byName instanceof final Map<?, ?> byNameMap) {
if (activateCapitator) {
byKeyMap.remove(capitatorEnchantmentKey);
byNameMap.remove(ENCHANTMENT_CAPITATOR_NAME);
}
}
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
if (getConfig().getBoolean("capitator", true))
ServerEnchantmentRegistry.unRegisterEnchantment(this, capitatorEnchantment);
capitatorEnchantment = null;
capitatorEnchantmentKey = null;
}
/**
@ -150,7 +117,7 @@ public final class InvTweaksPlugin extends JavaPlugin {
pluginManager.registerEvents(new SortListener(), this);
pluginManager.registerEvents(new MagnetismListener(magnetCommandExecutor), this);
pluginManager.registerEvents(new TabCompletionListener(), this);
pluginManager.registerEvents(new TreeCapitatorListener(activateCapitator ? capitatorEnchantment : null), this);
pluginManager.registerEvents(new TreeCapitatorListener(activateCapitator ? capitatorEnchantment.getEnchantment() : null), this);
}
/**
@ -173,7 +140,7 @@ public final class InvTweaksPlugin extends JavaPlugin {
namedChestCommandExecutor = new NamedChestCommand(this);
if (activateCapitator)
capitatorCommand = new CapitatorCommand(capitatorEnchantment);
capitatorCommand = new CapitatorCommand(capitatorEnchantment.getEnchantment());
// TODO: Bind command by annotation
Objects.requireNonNull(getCommand("sort")).setExecutor(sortCommandExecutor);
@ -206,7 +173,6 @@ public final class InvTweaksPlugin extends JavaPlugin {
private void registerSerializers() {
ConfigurationSerialization.registerClass(MagnetConfig.class);
ConfigurationSerialization.registerClass(MagnetData.class);
ConfigurationSerialization.registerClass(UUIDList.class);
ConfigurationSerialization.registerClass(SearchConfig.class);
}
@ -218,7 +184,6 @@ public final class InvTweaksPlugin extends JavaPlugin {
private void unregisterSerializers() {
ConfigurationSerialization.unregisterClass(MagnetConfig.class);
ConfigurationSerialization.unregisterClass(MagnetData.class);
ConfigurationSerialization.unregisterClass(UUIDList.class);
ConfigurationSerialization.unregisterClass(SearchConfig.class);
}
@ -233,7 +198,7 @@ public final class InvTweaksPlugin extends JavaPlugin {
saveConfig();
// Implicit load
data = new DataStore(PERSISTENT_DATA_NAME, this);
data = new PersistentData(PERSISTENT_DATA_NAME, this);
}
/**

View File

@ -1,5 +1,6 @@
package dev.w1zzrd.invtweaks.command;
import dev.w1zzrd.spigot.wizcompat.command.CommandUtils;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;

View File

@ -1,32 +0,0 @@
package dev.w1zzrd.invtweaks.command;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.command.CommandSender;
public final class CommandUtils {
private CommandUtils() { throw new UnsupportedOperationException("Functional class"); }
public static boolean assertTrue(final boolean condition, final String message, final CommandSender sender) {
if (!condition) {
final TextComponent errorMessage = new TextComponent(message);
errorMessage.setColor(ChatColor.DARK_RED);
sender.spigot().sendMessage(errorMessage);
}
return !condition;
}
public static BaseComponent errorMessage(final String message) {
final BaseComponent component = new TextComponent(message);
component.setColor(ChatColor.DARK_RED);
return component;
}
public static BaseComponent successMessage(final String message) {
final BaseComponent component = new TextComponent(message);
component.setColor(ChatColor.GREEN);
return component;
}
}

View File

@ -1,32 +0,0 @@
package dev.w1zzrd.invtweaks.command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.plugin.Plugin;
public abstract class ConfigurableCommandExecutor<T extends ConfigurationSerializable> implements CommandExecutor {
private final Plugin plugin;
private final String path;
private T config;
public ConfigurableCommandExecutor(
final Plugin plugin,
final String path
) {
this.plugin = plugin;
this.path = path;
reloadConfig();
}
public void reloadConfig() {
config = (T) plugin.getConfig().get(path, plugin.getConfig().getDefaults().get(path));
}
protected T getConfig() {
return config;
}
protected Plugin getPlugin() {
return plugin;
}
}

View File

@ -1,13 +1,13 @@
package dev.w1zzrd.invtweaks.command;
import dev.w1zzrd.invtweaks.DataStore;
import dev.w1zzrd.invtweaks.serialization.MagnetConfig;
import dev.w1zzrd.invtweaks.serialization.MagnetData;
import dev.w1zzrd.spigot.wizcompat.command.ConfigurableCommandExecutor;
import dev.w1zzrd.spigot.wizcompat.serialization.PersistentData;
import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Item;
import org.bukkit.entity.Player;
@ -28,7 +28,7 @@ public class MagnetCommandExecutor extends ConfigurableCommandExecutor<MagnetCon
private static final Logger logger = Bukkit.getLogger();
private final DataStore data;
private final PersistentData data;
private final MagnetData magnetData;
private int divIndex = 0;
@ -39,7 +39,7 @@ public class MagnetCommandExecutor extends ConfigurableCommandExecutor<MagnetCon
* Initialize the magnet executor and manger
* @param plugin Owner plugin for this executor
*/
public MagnetCommandExecutor(final Plugin plugin, final String path, final DataStore data) {
public MagnetCommandExecutor(final Plugin plugin, final String path, final PersistentData data) {
super(plugin, path);
this.data = data;
this.magnetData = data.loadData("magnets", MagnetData::blank);

View File

@ -15,8 +15,8 @@ import org.bukkit.plugin.Plugin;
import java.util.Objects;
import static dev.w1zzrd.invtweaks.command.CommandUtils.assertTrue;
import static dev.w1zzrd.invtweaks.entity.EntityCreator.*;
import static dev.w1zzrd.spigot.wizcompat.command.CommandUtils.*;
import static dev.w1zzrd.spigot.wizcompat.packet.EntityCreator.*;
public class NamedChestCommand implements CommandExecutor {
@ -34,7 +34,7 @@ public class NamedChestCommand implements CommandExecutor {
if (assertTrue(args.length == 0, "Expected a name for the chest", sender))
return true;
if (assertTrue(args.length > 1, "Too many arguments for command", sender))
if (assertTrue(args.length > 2, "Too many arguments for command", sender))
return true;
assert sender instanceof Player;

View File

@ -2,7 +2,11 @@ package dev.w1zzrd.invtweaks.command;
import dev.w1zzrd.invtweaks.InvTweaksPlugin;
import dev.w1zzrd.invtweaks.serialization.SearchConfig;
import org.bukkit.*;
import dev.w1zzrd.spigot.wizcompat.command.ConfigurableCommandExecutor;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.*;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
@ -13,11 +17,14 @@ import org.bukkit.plugin.Plugin;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.logging.Logger;
import static dev.w1zzrd.invtweaks.command.CommandUtils.assertTrue;
import static dev.w1zzrd.invtweaks.listener.TabCompletionListener.getMaterialMatching;
import static dev.w1zzrd.spigot.wizcompat.command.CommandUtils.assertTrue;
/**
* Handler for executions of /search command

View File

@ -19,8 +19,8 @@ import java.util.Optional;
import java.util.logging.Logger;
import static dev.w1zzrd.invtweaks.InvTweaksPlugin.LOG_PLUGIN_NAME;
import static dev.w1zzrd.spigot.wizcompat.command.CommandUtils.assertTrue;
import static org.bukkit.Material.*;
import static dev.w1zzrd.invtweaks.command.CommandUtils.*;
/**
* Handler for executions of /sort command

View File

@ -1,153 +0,0 @@
package dev.w1zzrd.invtweaks.entity;
import org.bukkit.entity.Player;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import static dev.w1zzrd.invtweaks.entity.Reflect.*;
public final class EntityCreator {
private EntityCreator() { throw new UnsupportedOperationException("Functional class"); }
private static Package getNativeMonsterPackage(final Player from) {
// Given player wll be an instance of CraftPlayer
final Package bukkitEntityPackage = from.getClass().getPackage();
final Class<?> craftShulker = loadClass(bukkitEntityPackage, "CraftShulker");
assert craftShulker != null;
// CraftShulker constructor accepts minecraft EntityShulker instance as second argument
final Class<?> nativeEntityShulker = craftShulker.getDeclaredConstructors()[0].getParameterTypes()[1];
// EntityShulker is classified squarely as a monster, so it should be grouped with all other hostiles
return nativeEntityShulker.getPackage();
}
private static Package getNativePacketPackage(final Player from) {
final Method sendPacket = findDeclaredMethod(
reflectGetField(reflectGetField(from, "entity"), "playerConnection", "networkManager").getClass(),
new String[]{ "sendPacket" },
new Object[]{ null }
);
return sendPacket.getParameterTypes()[0].getPackage();
}
private static Object getMinecraftServerFromWorld(final Object worldServer) {
return reflectGetField(worldServer, "server", "D");
}
private static Object getWorldServerFromPlayer(final Player from) {
return reflectGetField(from.getWorld(), "world");
}
private static Object getMonsterEntityType(final Class<?> entityClass) {
final Class<?> type_EntityTypes = entityClass.getDeclaredConstructors()[0].getParameterTypes()[0];
return reflectGetGenericStaticField(type_EntityTypes, type_EntityTypes, entityClass);
}
private static Object createFakeMonster(final Player target, final String entityClassName) {
final Package versionPackage = getNativeMonsterPackage(target);
final Class<?> type_Entity = loadClass(versionPackage, entityClassName);
final Object nativeWorld = getWorldServerFromPlayer(target);
assert type_Entity != null;
final Object entityType = getMonsterEntityType(type_Entity);
return reflectConstruct(type_Entity, entityType, nativeWorld);
}
public static Object createFakeSlime(final Player target) {
return createFakeMonster(target, "EntitySlime");
}
public static Object createFakeShulker(final Player target) {
return createFakeMonster(target, "EntityShulker");
}
public static void sendPacket(final Player target, final Object packet) {
reflectInvoke(reflectGetField(reflectGetField(target, "entity"), "playerConnection", "networkManager"), new String[]{ "sendPacket" }, packet);
}
public static void sendEntitySpawnPacket(final Player target, final Object entity) {
final Package versionPackage = getNativePacketPackage(target);
sendPacket(target, reflectConstruct(loadClass(versionPackage, "PacketPlayOutSpawnEntityLiving", "game.PacketPlayOutSpawnEntityLiving"), entity));
}
public static void sendEntityMetadataPacket(final Player target, final Object entity) {
final Package versionPackage = getNativePacketPackage(target);
Object constr1;
try {
constr1 = reflectConstruct(
loadClass(versionPackage, "PacketPlayOutEntityMetadata", "game.PacketPlayOutEntityMetadata"),
getEntityID(entity),
reflectGetField(entity, "dataWatcher", "Y")
);
} catch (Throwable t) {
constr1 = reflectConstruct(
loadClass(versionPackage, "PacketPlayOutEntityMetadata", "game.PacketPlayOutEntityMetadata"),
getEntityID(entity),
reflectGetField(entity, "dataWatcher", "Y"),
true
);
}
sendPacket(
target,
constr1
);
}
public static void sendEntityDespawnPacket(final Player target, final int entityID) {
final Package versionPackage = getNativePacketPackage(target);
sendPacket(target, reflectConstruct(loadClass(versionPackage, "PacketPlayOutEntityDestroy", "game.PacketPlayOutEntityDestroy"), entityID));
}
public static int getEntityID(final Object entity) {
return (Integer)reflectInvoke(entity, new String[]{ "getId" });
}
public static void setEntityInvisible(final Object entity, final boolean invisible) {
reflectInvoke(entity, new String[]{ "setInvisible" }, invisible);
}
public static void setEntityInvulnerable(final Object entity, final boolean invulnerable) {
reflectInvoke(entity, new String[]{ "setInvulnerable" }, invulnerable);
}
public static void setEntityGlowing(final Object entity, final boolean isGlowing) {
reflectInvoke(entity, new String[]{ "setGlowingTag", "i" }, isGlowing);
}
public static void setEntityLocation(final Object entity, final double x, final double y, final double z, final float yaw, final float pitch) {
reflectInvoke(entity, new String[]{ "setLocation" }, x, y, z, yaw, pitch);
}
public static void setEntityCollision(final Object entity, final boolean collision) {
reflectSetField(entity, boolean.class, collision, "collides");
}
public static void setEntityCustomName(final Object entity, final String name) {
final Package versionPackage = entity.getClass().getPackage();
final Method setCustomName = findDeclaredMethod(entity.getClass(), new String[]{ "setCustomName" }, new Object[]{ null });
final Package chatPackage = setCustomName.getParameterTypes()[0].getPackage();
setEntityCustomName(entity, reflectConstruct(loadClass(chatPackage, "ChatComponentText"), name));
}
public static void setEntityCustomName(final Object entity, final Object chatBaseComponent) {
reflectInvoke(entity, new String[]{ "setCustomName" }, chatBaseComponent);
}
public static void setEntityCustomNameVisible(final Object entity, final boolean visible) {
reflectInvoke(entity, new String[] { "setCustomNameVisible" }, visible);
}
public static void setSlimeSize(final Object slime, final int size) {
reflectInvoke(slime, new String[]{ "setSize" }, size, true);
}
}

View File

@ -1,201 +0,0 @@
package dev.w1zzrd.invtweaks.entity;
import java.lang.reflect.*;
import java.util.Objects;
public final class Reflect {
private Reflect() { throw new UnsupportedOperationException("Functional class"); }
public static boolean contains(final String[] array, final String find) {
for (final String check : array)
if (check.equals(find))
return true;
return false;
}
public static <T> Method findDeclaredMethod(final Class<T> rootType, final String[] methodNames, final Object[] args) {
Class<? super T> current = rootType;
do {
for (final Method check : current.getDeclaredMethods())
if (contains(methodNames, check.getName()) && argsMatch(check.getParameterTypes(), args))
return check;
current = current.getSuperclass();
} while (true);
}
public static <T> Field findDeclaredField(final Class<T> rootType, final Class<?> expectedType, final String... fieldNames) {
Class<? super T> current = rootType;
do {
for (final Field check : current.getDeclaredFields())
if (contains(fieldNames, check.getName()) && (expectedType == null || check.getType().equals(expectedType)))
return check;
current = current.getSuperclass();
} while (true);
}
public static <T> Constructor<T> findDeclaredConstructor(final Class<T> type, final Object[] args) {
for (final Constructor<?> check : type.getDeclaredConstructors())
if (argsMatch(check.getParameterTypes(), args))
return (Constructor<T>) check;
return null;
}
public static Object reflectInvoke(final Object target, final String[] methodNames, final Object... args) {
final Method targetMethod = findDeclaredMethod(target.getClass(), methodNames, args);
targetMethod.setAccessible(true);
try {
return targetMethod.invoke(target, args);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
public static void reflectSetStaticField(final Class<?> target, final Object value, final String... fieldNames) {
reflectSetStaticField(target, null, value, fieldNames);
}
public static void reflectSetStaticField(final Class<?> target, final Class<?> expectedType, final Object value, final String... fieldNames) {
final Field targetField = findDeclaredField(target, expectedType, fieldNames);
targetField.setAccessible(true);
try {
targetField.set(null, value);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public static <T> T reflectGetStaticField(final Class<?> target, final String... fieldNames) {
return (T) reflectGetStaticField(target, null, fieldNames);
}
public static <T, R> T reflectGetGenericStaticField(final Class<?> target, final Class<T> fieldType, final Class<R> genericType) {
for (final Field check : target.getDeclaredFields()) {
if (fieldType.isAssignableFrom(check.getType())) {
final Type checkFieldType = check.getGenericType();
if (checkFieldType instanceof final ParameterizedType pCFT && pCFT.getActualTypeArguments().length != 0) {
for (final Type typeArg : pCFT.getActualTypeArguments()) {
if (typeArg == genericType) {
try {
return (T)check.get(null);
} catch (IllegalAccessException e) {
e.printStackTrace();
return null;
}
}
}
}
}
}
return null;
}
public static <T> T reflectGetStaticField(final Class<?> target, final Class<T> expectedType, final String... fieldNames) {
final Field targetField = findDeclaredField(target, expectedType, fieldNames);
targetField.setAccessible(true);
try {
return (T)targetField.get(null);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
public static void reflectSetField(final Object target, final Object value, final String... fieldNames) {
reflectSetField(target, null, value, fieldNames);
}
public static void reflectSetField(final Object target, final Class<?> expectedType, final Object value, final String... fieldNames) {
final Field targetField = findDeclaredField(target.getClass(), expectedType, fieldNames);
targetField.setAccessible(true);
try {
targetField.set(target, value);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public static Object reflectGetField(final Object target, final String... fieldNames) {
return reflectGetField(target, null, fieldNames);
}
public static <T> T reflectGetField(final Object target, final Class<T> expectedType, final String... fieldNames) {
final Field targetField = findDeclaredField(target.getClass(), expectedType, fieldNames);
targetField.setAccessible(true);
try {
return (T)targetField.get(target);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
public static <T> T reflectConstruct(final Class<T> targetType, final Object... args) {
final Constructor<T> targetConstructor = findDeclaredConstructor(targetType, args);
assert targetConstructor != null;
targetConstructor.setAccessible(true);
try {
return targetConstructor.newInstance(args);
} catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
public static Class<?> loadClass(final Package from, final String... names) {
for (final String possibleName : names)
try {
return Class.forName(from.getName() + "." + possibleName);
} catch (ClassNotFoundException e) {
}
return null;
}
private static boolean argsMatch(final Class<?>[] types, final Object[] args) {
if (types.length != args.length)
return false;
for (int i = 0; i < args.length; ++i)
if (isNotSoftAssignable(types[i], args[i]))
return false;
return true;
}
private static boolean isNotSoftAssignable(final Class<?> type, final Object arg) {
return (arg == null && type.isPrimitive()) || (arg != null && !type.isAssignableFrom(arg.getClass()) && !isBoxedPrimitive(type, arg.getClass()));
}
private static boolean isBoxedPrimitive(final Class<?> primitive, final Class<?> objectType) {
return (primitive == boolean.class && objectType == Boolean.class) ||
(primitive == byte.class && objectType == Byte.class) ||
(primitive == short.class && objectType == Short.class) ||
(primitive == int.class && objectType == Integer.class) ||
(primitive == long.class && objectType == Long.class) ||
(primitive == float.class && objectType == Float.class) ||
(primitive == double.class && objectType == Double.class);
}
private interface DeclarationGetter<T, R> {
R[] getDeclared(final Class<? super T> t);
}
private interface NameGetter<T> {
String getName(final T t);
}
}

View File

@ -1,5 +1,7 @@
package dev.w1zzrd.invtweaks.serialization;
import dev.w1zzrd.spigot.wizcompat.serialization.SimpleReflectiveConfigItem;
import java.util.Map;
public class ChestNameConfig extends SimpleReflectiveConfigItem {

View File

@ -1,5 +1,6 @@
package dev.w1zzrd.invtweaks.serialization;
import dev.w1zzrd.spigot.wizcompat.serialization.SimpleReflectiveConfigItem;
import org.bukkit.plugin.Plugin;
import java.util.Map;

View File

@ -1,5 +1,7 @@
package dev.w1zzrd.invtweaks.serialization;
import dev.w1zzrd.spigot.wizcompat.serialization.SimpleReflectiveConfigItem;
import dev.w1zzrd.spigot.wizcompat.serialization.UUIDList;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;

View File

@ -1,5 +1,7 @@
package dev.w1zzrd.invtweaks.serialization;
import dev.w1zzrd.spigot.wizcompat.serialization.SimpleReflectiveConfigItem;
import java.util.Map;
public class SearchConfig extends SimpleReflectiveConfigItem {

View File

@ -1,126 +0,0 @@
package dev.w1zzrd.invtweaks.serialization;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
/**
* Configuration serializable type for automatically serializing/deserializing fields
*/
public class SimpleReflectiveConfigItem implements ConfigurationSerializable {
/**
* Required constructor for deserializing data
* @param mappings Data to deserialize
*/
public SimpleReflectiveConfigItem(final Map<String, Object> mappings) {
deserializeMapped(mappings);
}
@Override
public Map<String, Object> serialize() {
final HashMap<String, Object> values = new HashMap<>();
Arrays.stream(getClass().getDeclaredFields())
.filter(it -> !Modifier.isTransient(it.getModifiers()) && !Modifier.isStatic(it.getModifiers()))
.forEach(it -> {
try {
it.setAccessible(true);
values.put(it.getName(), it.get(this));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
});
return values;
}
/**
* Deserialize mapped data by name
* @param mappings Data to deserialize
*/
private void deserializeMapped(final Map<String, Object> mappings) {
for (final Field field : getClass().getDeclaredFields()) {
if (Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers()))
continue;
try {
// Try to find mappings by field name
if (mappings.containsKey(field.getName()))
parse(mappings.get(field.getName()), field, this);
} catch (IllegalAccessException | InvocationTargetException e) {
// This shouldn't happen
e.printStackTrace();
}
}
}
/**
* Attempt to parse a value such that it can be stored in the given field
* @param value Value to parse
* @param field Field to store value in
* @param instance Configuration object for which to parse the vaue
* @throws IllegalAccessException Should never be thrown
* @throws InvocationTargetException Should never be thrown
*/
private static void parse(final Object value, final Field field, final Object instance) throws IllegalAccessException, InvocationTargetException {
field.setAccessible(true);
if (field.getType().isPrimitive() && value == null)
throw new NullPointerException("Attempt to assign null to a primitive field");
final Class<?> boxed = getBoxedType(field.getType());
if (boxed.isAssignableFrom(value.getClass())) {
field.set(instance, value);
return;
}
if (value instanceof String) {
final Method parser = locateParser(boxed, field.getType().isPrimitive() ? field.getType() : null);
if (parser != null)
field.set(instance, parser.invoke(null, value));
}
throw new IllegalArgumentException(String.format("No defined parser for value \"%s\"", value));
}
/**
* Converter for boxed primitives
* @param cls Primitive type
* @return Boxed type for primitive type, else the given type
*/
private static Class<?> getBoxedType(final Class<?> cls) {
if (cls == int.class) return Integer.class;
else if (cls == double.class) return Double.class;
else if (cls == long.class) return Long.class;
else if (cls == float.class) return Float.class;
else if (cls == char.class) return Character.class;
else if (cls == byte.class) return Byte.class;
else if (cls == short.class) return Short.class;
else if (cls == boolean.class) return Boolean.class;
else return cls;
}
/**
* Attempt to find a parser method for the given type
* @param cls Type to find parser for
* @param prim Primitive type find parser for (if applicable)
* @return Static method which accepts a {@link String} argument and returns the desired type
*/
private static Method locateParser(final Class<?> cls, final Class<?> prim) {
for (final Method method : cls.getDeclaredMethods()) {
final Class<?>[] params = method.getParameterTypes();
if ((method.getName().startsWith("parse" + cls.getSimpleName()) || method.getName().equals("fromString")) &&
Modifier.isStatic(method.getModifiers()) &&
method.getReturnType().equals(prim != null ? prim : cls) &&
params.length == 1 && params[0].equals(String.class))
method.setAccessible(true);
return method;
}
return null;
}
}

View File

@ -1,47 +0,0 @@
package dev.w1zzrd.invtweaks.serialization;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import java.util.*;
import java.util.stream.Collectors;
/**
* Serializable dataclass holding a collection of {@link UUID} objects
*/
public class UUIDList implements ConfigurationSerializable {
/**
* This is public to decrease performance overhead
*/
public final List<UUID> uuids;
/**
* Wrap a backing list of {@link UUID} objects to enable configuration serialization
* @param backingList Modifiable, backing list of {@link UUID} objects
*/
public UUIDList(final List<UUID> backingList) {
uuids = backingList;
}
/**
* Create a blank list of {@link UUID} objects
*/
public UUIDList() {
this(new ArrayList<>());
}
/**
* Deserialize serialized UUID strings
* @param values Data to deserialize
*/
public UUIDList(final Map<String, Object> values) {
this();
if (values.containsKey("values"))
uuids.addAll(((Collection<String>)values.get("values")).stream().map(UUID::fromString).collect(Collectors.toSet()));
}
@Override
public Map<String, Object> serialize() {
return Collections.singletonMap("values", uuids.stream().map(UUID::toString).collect(Collectors.toList()));
}
}