diff --git a/src/dev/w1zzrd/invtweaks/InvTweaksPlugin.java b/src/dev/w1zzrd/invtweaks/InvTweaksPlugin.java index 807a14c..465b1af 100644 --- a/src/dev/w1zzrd/invtweaks/InvTweaksPlugin.java +++ b/src/dev/w1zzrd/invtweaks/InvTweaksPlugin.java @@ -2,9 +2,11 @@ package dev.w1zzrd.invtweaks; import dev.w1zzrd.invtweaks.command.MagnetCommandExecutor; import dev.w1zzrd.invtweaks.command.SortCommandExecutor; +import dev.w1zzrd.invtweaks.config.MagnetConfig; import dev.w1zzrd.invtweaks.listener.SortListener; import dev.w1zzrd.invtweaks.listener.StackReplaceListener; import org.bukkit.Bukkit; +import org.bukkit.configuration.serialization.ConfigurationSerialization; import org.bukkit.event.HandlerList; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; @@ -22,11 +24,6 @@ public final class InvTweaksPlugin extends JavaPlugin { */ public static final String LOG_PLUGIN_NAME = "[InventoryTweaks]"; - // TODO: Magic values: make a config - private static final double MAGNET_DISTANCE = 8.0; - private static final long MAGNET_INTERVAL = 5; - private static final int MAGNET_SUBDIVIDE = 2; - private final Logger logger = Bukkit.getLogger(); // Command executor references in case I need them or something idk @@ -37,6 +34,12 @@ public final class InvTweaksPlugin extends JavaPlugin { public void onEnable() { logger.fine(LOG_PLUGIN_NAME + " Plugin enabled"); + registerSerializers(); + + getConfig().options().copyDefaults(true); + + saveConfig(); + initCommands(); initEvents(); } @@ -47,15 +50,28 @@ public final class InvTweaksPlugin extends JavaPlugin { disableEvents(); disableCommands(); + + saveConfig(); + + unregisterSerializers(); } + @Override + public void reloadConfig() { + super.reloadConfig(); + + getConfig().options().copyDefaults(true); + + if (magnetCommandExecutor != null) + magnetCommandExecutor.reloadConfig(); + } /** * Initialize commands registered by this plugin */ private void initCommands() { sortCommandExecutor = new SortCommandExecutor(); - magnetCommandExecutor = new MagnetCommandExecutor(this, MAGNET_DISTANCE, MAGNET_INTERVAL, MAGNET_SUBDIVIDE); + magnetCommandExecutor = new MagnetCommandExecutor(this); // TODO: Bind command by annotation Objects.requireNonNull(getCommand("sort")).setExecutor(sortCommandExecutor); @@ -68,7 +84,6 @@ public final class InvTweaksPlugin extends JavaPlugin { private void initEvents() { final PluginManager pluginManager = getServer().getPluginManager(); - // TODO: Register listeners by annotation pluginManager.registerEvents(new StackReplaceListener(), this); pluginManager.registerEvents(new SortListener(), this); } @@ -88,4 +103,12 @@ public final class InvTweaksPlugin extends JavaPlugin { // Un-register all listeners HandlerList.unregisterAll(this); } + + private void registerSerializers() { + ConfigurationSerialization.registerClass(MagnetConfig.class); + } + + private void unregisterSerializers() { + ConfigurationSerialization.unregisterClass(MagnetConfig.class); + } } diff --git a/src/dev/w1zzrd/invtweaks/command/MagnetCommandExecutor.java b/src/dev/w1zzrd/invtweaks/command/MagnetCommandExecutor.java index ccee5b6..10ed52c 100644 --- a/src/dev/w1zzrd/invtweaks/command/MagnetCommandExecutor.java +++ b/src/dev/w1zzrd/invtweaks/command/MagnetCommandExecutor.java @@ -1,5 +1,6 @@ package dev.w1zzrd.invtweaks.command; +import dev.w1zzrd.invtweaks.config.MagnetConfig; import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -26,6 +27,8 @@ public class MagnetCommandExecutor implements CommandExecutor { private static final Logger logger = Bukkit.getLogger(); + private static final String CONFIG_PATH = "magnet"; + /** * List of players with magnet mode active */ @@ -33,10 +36,7 @@ public class MagnetCommandExecutor implements CommandExecutor { private final List activeMagnetsView = Collections.unmodifiableList(activeMagnets); private final Plugin plugin; - private final long interval; - private final int subdivide; - - private final double sqRadius; + private MagnetConfig config; private int divIndex = 0; @@ -46,18 +46,20 @@ public class MagnetCommandExecutor implements CommandExecutor { /** * Initialize the magnet executor and manger * @param plugin Owner plugin for this executor - * @param sqRadius Radius of the cube to search for items in (half side length) - * @param interval Interval (in ticks) between magnetism checks for active magnets - * @param subdivide What fraction of the list of active magnets should be iterated over during a magnetism check. - * Set to 1 to check the whole list each iteration */ - public MagnetCommandExecutor(final Plugin plugin, final double sqRadius, final long interval, final int subdivide) { + public MagnetCommandExecutor(final Plugin plugin) { this.plugin = plugin; - this.sqRadius = sqRadius; - this.interval = interval; - this.subdivide = subdivide; + + // Don't call reloadConfig to ensure we don't leak `this` during construction (a bit pedantic) + config = loadConfig(plugin); } + /** + * Reload magnet command configuration + */ + public void reloadConfig() { + config = loadConfig(plugin); + } @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { @@ -217,7 +219,7 @@ public class MagnetCommandExecutor implements CommandExecutor { */ private void updateMagnetismTask() { if (refreshTask == null && activeMagnets.size() > 0 && plugin.isEnabled()) { - refreshTask = Bukkit.getScheduler().runTaskTimer(plugin, this::taskApplyMagnetism, 0, interval); + refreshTask = Bukkit.getScheduler().runTaskTimer(plugin, this::taskApplyMagnetism, 0, config.getInterval()); logger.info(LOG_PLUGIN_NAME + " Activated magnetism check task"); } else if (refreshTask != null && (activeMagnets.size() == 0 || !plugin.isEnabled())) { @@ -239,6 +241,9 @@ public class MagnetCommandExecutor implements CommandExecutor { final List toRemove = new ArrayList<>(); + final int subdivide = config.getSubdivide(); + final double sqRadius = config.getRadius(); + // Iterate over a subdivision of the active magnets for (int index = divIndex; index < size; index += subdivide) { final UUID uuid = activeMagnets.get(index); @@ -265,4 +270,14 @@ public class MagnetCommandExecutor implements CommandExecutor { // Update subdivision to check next iteration divIndex = (divIndex + 1) % subdivide; } + + + + + private static MagnetConfig loadConfig(final Plugin plugin) { + return (MagnetConfig) plugin.getConfig().get( + CONFIG_PATH, + MagnetConfig.getDefault(plugin, CONFIG_PATH) + ); + } } diff --git a/src/dev/w1zzrd/invtweaks/config/MagnetConfig.java b/src/dev/w1zzrd/invtweaks/config/MagnetConfig.java new file mode 100644 index 0000000..d8b004d --- /dev/null +++ b/src/dev/w1zzrd/invtweaks/config/MagnetConfig.java @@ -0,0 +1,44 @@ +package dev.w1zzrd.invtweaks.config; + +import org.bukkit.plugin.Plugin; + +import java.util.Map; +import java.util.Objects; + +public class MagnetConfig extends SimpleReflectiveConfigItem { + private double radius; + private int interval; + private int subdivide; + + public MagnetConfig(final Map mappings) { + super(mappings); + } + + public double getRadius() { + return radius; + } + + public int getInterval() { + return interval; + } + + public int getSubdivide() { + return subdivide; + } + + public void setRadius(double radius) { + this.radius = radius; + } + + public void setInterval(int interval) { + this.interval = interval; + } + + public void setSubdivide(int subdivide) { + this.subdivide = subdivide; + } + + public static MagnetConfig getDefault(final Plugin plugin, final String path) { + return (MagnetConfig) Objects.requireNonNull(plugin.getConfig().getDefaults()).get(path); + } +} diff --git a/src/dev/w1zzrd/invtweaks/config/SimpleReflectiveConfigItem.java b/src/dev/w1zzrd/invtweaks/config/SimpleReflectiveConfigItem.java new file mode 100644 index 0000000..104fd32 --- /dev/null +++ b/src/dev/w1zzrd/invtweaks/config/SimpleReflectiveConfigItem.java @@ -0,0 +1,97 @@ +package dev.w1zzrd.invtweaks.config; + +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.Arrays; +import java.util.HashMap; +import java.util.Map; + +public class SimpleReflectiveConfigItem implements ConfigurationSerializable { + + public SimpleReflectiveConfigItem(final Map mappings) { + deserializeMapped(mappings); + } + + @Override + public Map serialize() { + final HashMap 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; + } + + private void deserializeMapped(final Map mappings) { + for (final Field field : getClass().getDeclaredFields()) { + if (Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) + continue; + + try { + if (mappings.containsKey(field.getName())) + parse(mappings.get(field.getName()), field, this); + } catch (IllegalAccessException | InvocationTargetException e) { + // This shouldn't happen + e.printStackTrace(); + } + } + } + + 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)); + } + + 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; + } + + 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()) && + 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; + } +}