diff --git a/paper-api/pom.xml b/paper-api/pom.xml
index e1fbb2adbb..26f4c38a6d 100644
--- a/paper-api/pom.xml
+++ b/paper-api/pom.xml
@@ -107,5 +107,11 @@
+ * If no source {@link Configuration} was provided as a default collection, + * then a new {@link MemoryConfiguration} will be created to hold the new default + * value. + *
+ * If value is null, the value will be removed from the default Configuration source. + * + * @param path Path of the value to set. + * @param value Value to set the default to. + * @throws IllegalArgumentException Thrown if path is null. + */ + public void addDefault(String path, Object value); + + /** + * Sets the default values of the given paths as provided. + *
+ * If no source {@link Configuration} was provided as a default collection,
+ * then a new {@link MemoryConfiguration} will be created to hold the new default
+ * values.
+ *
+ * @param defaults A map of Path->Values to add to defaults.
+ * @throws IllegalArgumentException Thrown if defaults is null.
+ */
+ public void addDefaults(Map
+ * If no source {@link Configuration} was provided as a default collection,
+ * then a new {@link MemoryConfiguration} will be created to hold the new default
+ * value.
+ *
+ * This method will not hold a reference to the specified Configuration, nor will it
+ * automatically update if that Configuration ever changes. If you require this,
+ * you should set the default source with {@link #setDefaults(org.bukkit.configuration.Configuration)}.
+ *
+ * @param defaults A configuration holding a list of defaults to copy.
+ * @throws IllegalArgumentException Thrown if defaults is null or this.
+ */
+ public void addDefaults(Configuration defaults);
+
+ /**
+ * Sets the source of all default values for this {@link Configuration}.
+ *
+ * If a previous source was set, or previous default values were defined, then they will
+ * not be copied to the new source.
+ *
+ * @param defaults New source of default values for this configuration.
+ * @throws IllegalArgumentException Thrown if defaults is null or this.
+ */
+ public void setDefaults(Configuration defaults);
+
+ /**
+ * Gets the source {@link Configuration} for this configuration.
+ *
+ * If no configuration source was set, but default values were added, then a
+ * {@link MemoryConfiguration} will be returned. If no source was set and no
+ * defaults were set, then this method will return null.
+ *
+ * @return Configuration source for default values, or null if none exist.
+ */
+ public Configuration getDefaults();
+
+ /**
+ * Gets the {@link ConfigurationOptions} for this {@link Configuration}.
+ *
+ * All setters through this method are chainable.
+ *
+ * @return Options for this configuration
+ */
+ public ConfigurationOptions options();
+}
diff --git a/paper-api/src/main/java/org/bukkit/configuration/ConfigurationOptions.java b/paper-api/src/main/java/org/bukkit/configuration/ConfigurationOptions.java
new file mode 100644
index 0000000000..5392700f23
--- /dev/null
+++ b/paper-api/src/main/java/org/bukkit/configuration/ConfigurationOptions.java
@@ -0,0 +1,81 @@
+package org.bukkit.configuration;
+
+/**
+ * Various settings for controlling the input and output of a {@link Configuration}
+ */
+public class ConfigurationOptions {
+ private char pathSeparator = '.';
+ private boolean copyDefaults = false;
+ private final Configuration configuration;
+
+ protected ConfigurationOptions(Configuration configuration) {
+ this.configuration = configuration;
+ }
+
+ /**
+ * Returns the {@link Configuration} that this object is responsible for.
+ *
+ * @return Parent configuration
+ */
+ public Configuration configuration() {
+ return configuration;
+ }
+
+ /**
+ * Gets the char that will be used to separate {@link ConfigurationSection}s
+ *
+ * This value does not affect how the {@link Configuration} is stored, only in
+ * how you access the data. The default value is '.'.
+ *
+ * @return Path separator
+ */
+ public char pathSeparator() {
+ return pathSeparator;
+ }
+
+ /**
+ * Sets the char that will be used to separate {@link ConfigurationSection}s
+ *
+ * This value does not affect how the {@link Configuration} is stored, only in
+ * how you access the data. The default value is '.'.
+ *
+ * @param value Path separator
+ * @return This object, for chaining
+ */
+ public ConfigurationOptions pathSeparator(char value) {
+ this.pathSeparator = value;
+ return this;
+ }
+
+ /**
+ * Checks if the {@link Configuration} should copy values from its default {@link Configuration} directly.
+ *
+ * If this is true, all values in the default Configuration will be directly copied,
+ * making it impossible to distinguish between values that were set and values that
+ * are provided by default. As a result, {@link ConfigurationSection#contains(java.lang.String)} will always
+ * return the same value as {@link ConfigurationSection#isSet(java.lang.String)}.
+ * The default value is false.
+ *
+ * @return Whether or not defaults are directly copied
+ */
+ public boolean copyDefaults() {
+ return copyDefaults;
+ }
+
+ /**
+ * Sets if the {@link Configuration} should copy values from its default {@link Configuration} directly.
+ *
+ * If this is true, all values in the default Configuration will be directly copied,
+ * making it impossible to distinguish between values that were set and values that
+ * are provided by default. As a result, {@link ConfigurationSection#contains(java.lang.String)} will always
+ * return the same value as {@link ConfigurationSection#isSet(java.lang.String)}.
+ * The default value is false.
+ *
+ * @param value Whether or not defaults are directly copied
+ * @return This object, for chaining
+ */
+ public ConfigurationOptions copyDefaults(boolean value) {
+ this.copyDefaults = value;
+ return this;
+ }
+}
diff --git a/paper-api/src/main/java/org/bukkit/configuration/ConfigurationSection.java b/paper-api/src/main/java/org/bukkit/configuration/ConfigurationSection.java
new file mode 100644
index 0000000000..739a33598d
--- /dev/null
+++ b/paper-api/src/main/java/org/bukkit/configuration/ConfigurationSection.java
@@ -0,0 +1,560 @@
+package org.bukkit.configuration;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.List;
+import org.bukkit.OfflinePlayer;
+import org.bukkit.util.Vector;
+import org.bukkit.inventory.ItemStack;
+
+/**
+ * Represents a section of a {@link Configuration}
+ */
+public interface ConfigurationSection {
+ /**
+ * Gets a set containing all keys in this section.
+ *
+ * If deep is set to true, then this will contain all the keys within any child
+ * {@link ConfigurationSection}s (and their children, etc). These will be in a
+ * valid path notation for you to use.
+ *
+ * If deep is set to false, then this will contain only the keys of any direct children,
+ * and not their own children.
+ *
+ * @param deep Whether or not to get a deep list, as opposed to a shallow list.
+ * @return Set of keys contained within this ConfigurationSection.
+ */
+ public Set
+ * If deep is set to true, then this will contain all the keys and values within
+ * any child {@link ConfigurationSection}s (and their children, etc). These
+ * keys will be in a valid path notation for you to use.
+ *
+ * If deep is set to false, then this will contain only the keys and values of any
+ * direct children, and not their own children.
+ *
+ * @param deep Whether or not to get a deep list, as opposed to a shallow list.
+ * @return Map of keys and values of this section.
+ */
+ public Map
+ * If the value for the requested path does not exist but a default value has
+ * been specified, this will return true.
+ *
+ * @param path Path to check for existence.
+ * @return True if this section contains the requested path, either via default or being set.
+ * @throws IllegalArgumentException Thrown when path is null.
+ */
+ public boolean contains(String path);
+
+ /**
+ * Checks if this {@link ConfigurationSection} has a value set for the given path.
+ *
+ * If the value for the requested path does not exist but a default value has
+ * been specified, this will still return false.
+ *
+ * @param path Path to check for existence.
+ * @return True if this section contains the requested path, regardless of having a default.
+ * @throws IllegalArgumentException Thrown when path is null.
+ */
+ public boolean isSet(String path);
+
+ /**
+ * Gets the path of this {@link ConfigurationSection} from its root {@link Configuration}
+ *
+ * For any {@link Configuration} themselves, this will return an empty string.
+ *
+ * If the section is no longer contained within its root for any reason, such as
+ * being replaced with a different value, this may return null.
+ *
+ * To retrieve the single name of this section, that is, the final part of the path
+ * returned by this method, you may use {@link #getName()}.
+ *
+ * @return Path of this section relative to its root
+ */
+ public String getCurrentPath();
+
+ /**
+ * Gets the name of this individual {@link ConfigurationSection}, in the path.
+ *
+ * This will always be the final part of {@link #getCurrentPath()}, unless the
+ * section is orphaned.
+ *
+ * @return Name of this section
+ */
+ public String getName();
+
+ /**
+ * Gets the root {@link Configuration} that contains this {@link ConfigurationSection}
+ *
+ * For any {@link Configuration} themselves, this will return its own object.
+ *
+ * If the section is no longer contained within its root for any reason, such as
+ * being replaced with a different value, this may return null.
+ *
+ * @return Root configuration containing this section.
+ */
+ public Configuration getRoot();
+
+ /**
+ * Gets the parent {@link ConfigurationSection} that directly contains this
+ * {@link ConfigurationSection}.
+ *
+ * For any {@link Configuration} themselves, this will return null.
+ *
+ * If the section is no longer contained within its parent for any reason, such as
+ * being replaced with a different value, this may return null.
+ *
+ * @return Parent section containing this section.
+ */
+ public ConfigurationSection getParent();
+
+ /**
+ * Gets the requested Object by path.
+ *
+ * If the Object does not exist but a default value has been specified, this
+ * will return the default value. If the Object does not exist and no default
+ * value was specified, this will return null.
+ *
+ * @param path Path of the Object to get.
+ * @return Requested Object.
+ */
+ public Object get(String path);
+
+ /**
+ * Gets the requested Object by path, returning a default value if not found.
+ *
+ * If the Object does not exist then the specified default value will returned
+ * regardless of if a default has been identified in the root {@link Configuration}.
+ *
+ * @param path Path of the Object to get.
+ * @return Requested Object.
+ */
+ public Object get(String path, Object def);
+
+ /**
+ * Sets the specified path to the given value.
+ *
+ * If value is null, the entry will be removed. Any existing entry will be
+ * replaced, regardless of what the new value is.
+ *
+ * Some implementations may have limitations on what you may store. See their
+ * individual javadocs for details. No implementations should allow you to store
+ * {@link Configuration}s or {@link ConfigurationSection}s, please use
+ * {@link #createSection(java.lang.String)} for that.
+ *
+ * @param path Path of the object to set.
+ * @param value New value to set the path to.
+ */
+ public void set(String path, Object value);
+
+ /**
+ * Creates an empty {@link ConfigurationSection} at the specified path.
+ *
+ * Any value that was previously set at this path will be overwritten. If the
+ * previous value was itself a {@link ConfigurationSection}, it will be orphaned.
+ *
+ * @param path Path to create the section at.
+ * @return Newly created section
+ */
+ public ConfigurationSection createSection(String path);
+
+ // Primitives
+ /**
+ * Gets the requested String by path.
+ *
+ * If the String does not exist but a default value has been specified, this
+ * will return the default value. If the String does not exist and no default
+ * value was specified, this will return null.
+ *
+ * @param path Path of the String to get.
+ * @return Requested String.
+ */
+ public String getString(String path);
+
+ /**
+ * Gets the requested String by path, returning a default value if not found.
+ *
+ * If the String does not exist then the specified default value will returned
+ * regardless of if a default has been identified in the root {@link Configuration}.
+ *
+ * @param path Path of the String to get.
+ * @return Requested String.
+ */
+ public String getString(String path, String def);
+
+ /**
+ * Checks if the specified path is a String.
+ *
+ * If the path exists but is not a String, this will return false. If the path does not
+ * exist, this will return false. If the path does not exist but a default value
+ * has been specified, this will check if that default value is a String and return
+ * appropriately.
+ *
+ * @param path Path of the String to check.
+ * @return Whether or not the specified path is a String.
+ */
+ public boolean isString(String path);
+
+
+ /**
+ * Gets the requested int by path.
+ *
+ * If the int does not exist but a default value has been specified, this
+ * will return the default value. If the int does not exist and no default
+ * value was specified, this will return null.
+ *
+ * @param path Path of the int to get.
+ * @return Requested int.
+ */
+ public int getInt(String path);
+
+ /**
+ * Gets the requested int by path, returning a default value if not found.
+ *
+ * If the int does not exist then the specified default value will returned
+ * regardless of if a default has been identified in the root {@link Configuration}.
+ *
+ * @param path Path of the int to get.
+ * @return Requested int.
+ */
+ public int getInt(String path, int def);
+
+ /**
+ * Checks if the specified path is an int.
+ *
+ * If the path exists but is not a int, this will return false. If the path does not
+ * exist, this will return false. If the path does not exist but a default value
+ * has been specified, this will check if that default value is a int and return
+ * appropriately.
+ *
+ * @param path Path of the int to check.
+ * @return Whether or not the specified path is an int.
+ */
+ public boolean isInt(String path);
+
+
+ /**
+ * Gets the requested boolean by path.
+ *
+ * If the boolean does not exist but a default value has been specified, this
+ * will return the default value. If the boolean does not exist and no default
+ * value was specified, this will return false.
+ *
+ * @param path Path of the boolean to get.
+ * @return Requested boolean.
+ */
+ public boolean getBoolean(String path);
+
+ /**
+ * Gets the requested boolean by path, returning a default value if not found.
+ *
+ * If the boolean does not exist then the specified default value will returned
+ * regardless of if a default has been identified in the root {@link Configuration}.
+ *
+ * @param path Path of the boolean to get.
+ * @return Requested boolean.
+ */
+ public boolean getBoolean(String path, boolean def);
+
+ /**
+ * Checks if the specified path is a boolean.
+ *
+ * If the path exists but is not a boolean, this will return false. If the path does not
+ * exist, this will return false. If the path does not exist but a default value
+ * has been specified, this will check if that default value is a boolean and return
+ * appropriately.
+ *
+ * @param path Path of the boolean to check.
+ * @return Whether or not the specified path is a boolean.
+ */
+ public boolean isBoolean(String path);
+
+
+ /**
+ * Gets the requested double by path.
+ *
+ * If the double does not exist but a default value has been specified, this
+ * will return the default value. If the double does not exist and no default
+ * value was specified, this will return null.
+ *
+ * @param path Path of the double to get.
+ * @return Requested double.
+ */
+ public double getDouble(String path);
+
+ /**
+ * Gets the requested double by path, returning a default value if not found.
+ *
+ * If the double does not exist then the specified default value will returned
+ * regardless of if a default has been identified in the root {@link Configuration}.
+ *
+ * @param path Path of the double to get.
+ * @return Requested double.
+ */
+ public double getDouble(String path, double def);
+
+ /**
+ * Checks if the specified path is a double.
+ *
+ * If the path exists but is not a double, this will return false. If the path does not
+ * exist, this will return false. If the path does not exist but a default value
+ * has been specified, this will check if that default value is a double and return
+ * appropriately.
+ *
+ * @param path Path of the double to check.
+ * @return Whether or not the specified path is a double.
+ */
+ public boolean isDouble(String path);
+
+
+ /**
+ * Gets the requested long by path.
+ *
+ * If the long does not exist but a default value has been specified, this
+ * will return the default value. If the long does not exist and no default
+ * value was specified, this will return null.
+ *
+ * @param path Path of the long to get.
+ * @return Requested long.
+ */
+ public long getLong(String path);
+
+ /**
+ * Gets the requested long by path, returning a default value if not found.
+ *
+ * If the long does not exist then the specified default value will returned
+ * regardless of if a default has been identified in the root {@link Configuration}.
+ *
+ * @param path Path of the long to get.
+ * @return Requested long.
+ */
+ public long getLong(String path, long def);
+
+ /**
+ * Checks if the specified path is a long.
+ *
+ * If the path exists but is not a long, this will return false. If the path does not
+ * exist, this will return false. If the path does not exist but a default value
+ * has been specified, this will check if that default value is a long and return
+ * appropriately.
+ *
+ * @param path Path of the long to check.
+ * @return Whether or not the specified path is a long.
+ */
+ public boolean isLong(String path);
+
+
+
+ // Java
+ /**
+ * Gets the requested List by path.
+ *
+ * If the List does not exist but a default value has been specified, this
+ * will return the default value. If the List does not exist and no default
+ * value was specified, this will return null.
+ *
+ * @param path Path of the List to get.
+ * @return Requested List.
+ */
+ public List getList(String path);
+
+ /**
+ * Gets the requested List by path, returning a default value if not found.
+ *
+ * If the List does not exist then the specified default value will returned
+ * regardless of if a default has been identified in the root {@link Configuration}.
+ *
+ * @param path Path of the List to get.
+ * @return Requested List.
+ */
+ public List getList(String path, List def);
+
+ /**
+ * Checks if the specified path is a List.
+ *
+ * If the path exists but is not a List, this will return false. If the path does not
+ * exist, this will return false. If the path does not exist but a default value
+ * has been specified, this will check if that default value is a List and return
+ * appropriately.
+ *
+ * @param path Path of the List to check.
+ * @return Whether or not the specified path is a List.
+ */
+ public boolean isList(String path);
+
+
+
+ // Bukkit
+ /**
+ * Gets the requested Vector by path.
+ *
+ * If the Vector does not exist but a default value has been specified, this
+ * will return the default value. If the Vector does not exist and no default
+ * value was specified, this will return null.
+ *
+ * @param path Path of the Vector to get.
+ * @return Requested Vector.
+ */
+ public Vector getVector(String path);
+
+ /**
+ * Gets the requested Vector by path, returning a default value if not found.
+ *
+ * If the Vector does not exist then the specified default value will returned
+ * regardless of if a default has been identified in the root {@link Configuration}.
+ *
+ * @param path Path of the Vector to get.
+ * @return Requested Vector.
+ */
+ public Vector getVector(String path, Vector def);
+
+ /**
+ * Checks if the specified path is a Vector.
+ *
+ * If the path exists but is not a Vector, this will return false. If the path does not
+ * exist, this will return false. If the path does not exist but a default value
+ * has been specified, this will check if that default value is a Vector and return
+ * appropriately.
+ *
+ * @param path Path of the Vector to check.
+ * @return Whether or not the specified path is a Vector.
+ */
+ public boolean isVector(String path);
+
+
+ /**
+ * Gets the requested OfflinePlayer by path.
+ *
+ * If the OfflinePlayer does not exist but a default value has been specified, this
+ * will return the default value. If the OfflinePlayer does not exist and no default
+ * value was specified, this will return null.
+ *
+ * @param path Path of the OfflinePlayer to get.
+ * @return Requested OfflinePlayer.
+ */
+ public OfflinePlayer getOfflinePlayer(String path);
+
+ /**
+ * Gets the requested OfflinePlayer by path, returning a default value if not found.
+ *
+ * If the OfflinePlayer does not exist then the specified default value will returned
+ * regardless of if a default has been identified in the root {@link Configuration}.
+ *
+ * @param path Path of the OfflinePlayer to get.
+ * @return Requested OfflinePlayer.
+ */
+ public OfflinePlayer getOfflinePlayer(String path, OfflinePlayer def);
+
+ /**
+ * Checks if the specified path is an OfflinePlayer.
+ *
+ * If the path exists but is not a OfflinePlayer, this will return false. If the path does not
+ * exist, this will return false. If the path does not exist but a default value
+ * has been specified, this will check if that default value is a OfflinePlayer and return
+ * appropriately.
+ *
+ * @param path Path of the OfflinePlayer to check.
+ * @return Whether or not the specified path is an OfflinePlayer.
+ */
+ public boolean isOfflinePlayer(String path);
+
+
+ /**
+ * Gets the requested ItemStack by path.
+ *
+ * If the ItemStack does not exist but a default value has been specified, this
+ * will return the default value. If the ItemStack does not exist and no default
+ * value was specified, this will return null.
+ *
+ * @param path Path of the ItemStack to get.
+ * @return Requested ItemStack.
+ */
+ public ItemStack getItemStack(String path);
+
+ /**
+ * Gets the requested ItemStack by path, returning a default value if not found.
+ *
+ * If the ItemStack does not exist then the specified default value will returned
+ * regardless of if a default has been identified in the root {@link Configuration}.
+ *
+ * @param path Path of the ItemStack to get.
+ * @return Requested ItemStack.
+ */
+ public ItemStack getItemStack(String path, ItemStack def);
+
+ /**
+ * Checks if the specified path is an ItemStack.
+ *
+ * If the path exists but is not a ItemStack, this will return false. If the path does not
+ * exist, this will return false. If the path does not exist but a default value
+ * has been specified, this will check if that default value is a ItemStack and return
+ * appropriately.
+ *
+ * @param path Path of the ItemStack to check.
+ * @return Whether or not the specified path is an ItemStack.
+ */
+ public boolean isItemStack(String path);
+
+
+ /**
+ * Gets the requested ConfigurationSection by path.
+ *
+ * If the ConfigurationSection does not exist but a default value has been specified, this
+ * will return the default value. If the ConfigurationSection does not exist and no default
+ * value was specified, this will return null.
+ *
+ * @param path Path of the ConfigurationSection to get.
+ * @return Requested ConfigurationSection.
+ */
+ public ConfigurationSection getConfigurationSection(String path);
+
+ /**
+ * Checks if the specified path is a ConfigurationSection.
+ *
+ * If the path exists but is not a ConfigurationSection, this will return false. If the path does not
+ * exist, this will return false. If the path does not exist but a default value
+ * has been specified, this will check if that default value is a ConfigurationSection and return
+ * appropriately.
+ *
+ * @param path Path of the ConfigurationSection to check.
+ * @return Whether or not the specified path is a ConfigurationSection.
+ */
+ public boolean isConfigurationSection(String path);
+
+ /**
+ * Gets the equivalent {@link ConfigurationSection} from the default {@link Configuration} defined in {@link #getRoot()}.
+ *
+ * If the root contains no defaults, or the defaults doesn't contain a value
+ * for this path, or the value at this path is not a {@link ConfigurationSection} then
+ * this will return null.
+ *
+ * @return Equivalent section in root configuration
+ */
+ public ConfigurationSection getDefaultSection();
+
+ /**
+ * Sets the default value in the root at the given path as provided.
+ *
+ * If no source {@link Configuration} was provided as a default collection,
+ * then a new {@link MemoryConfiguration} will be created to hold the new default
+ * value.
+ *
+ * If value is null, the value will be removed from the default Configuration source.
+ *
+ * If the value as returned by {@link #getDefaultSection()} is null,
+ * then this will create a new section at the path, replacing anything that
+ * may have existed there previously.
+ *
+ * @param path Path of the value to set.
+ * @param value Value to set the default to.
+ * @throws IllegalArgumentException Thrown if path is null.
+ */
+ public void addDefault(String path, Object value);
+}
diff --git a/paper-api/src/main/java/org/bukkit/configuration/InvalidConfigurationException.java b/paper-api/src/main/java/org/bukkit/configuration/InvalidConfigurationException.java
new file mode 100644
index 0000000000..3057712f79
--- /dev/null
+++ b/paper-api/src/main/java/org/bukkit/configuration/InvalidConfigurationException.java
@@ -0,0 +1,39 @@
+package org.bukkit.configuration;
+
+/**
+ * Exception thrown when attempting to load an invalid {@link Configuration}
+ */
+public class InvalidConfigurationException extends Exception {
+ /**
+ * Creates a new instance of InvalidConfigurationException without a message or cause.
+ */
+ public InvalidConfigurationException() {}
+
+ /**
+ * Constructs an instance of InvalidConfigurationException with the specified message.
+ *
+ * @param msg The details of the exception.
+ */
+ public InvalidConfigurationException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructs an instance of InvalidConfigurationException with the specified cause.
+ *
+ * @param cause The cause of the exception.
+ */
+ public InvalidConfigurationException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Constructs an instance of InvalidConfigurationException with the specified message and cause.
+ *
+ * @param cause The cause of the exception.
+ * @param msg The details of the exception.
+ */
+ public InvalidConfigurationException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+}
diff --git a/paper-api/src/main/java/org/bukkit/configuration/MemoryConfiguration.java b/paper-api/src/main/java/org/bukkit/configuration/MemoryConfiguration.java
new file mode 100644
index 0000000000..5fffc593b5
--- /dev/null
+++ b/paper-api/src/main/java/org/bukkit/configuration/MemoryConfiguration.java
@@ -0,0 +1,85 @@
+package org.bukkit.configuration;
+
+import java.util.Map;
+
+/**
+ * This is a {@link Configuration} implementation that does not save or load
+ * from any source, and stores all values in memory only.
+ * This is useful for temporary Configurations for providing defaults.
+ */
+public class MemoryConfiguration extends MemorySection implements Configuration {
+ protected Configuration defaults;
+ protected MemoryConfigurationOptions options;
+
+ /**
+ * Creates an empty {@link MemoryConfiguration} with no default values.
+ */
+ public MemoryConfiguration() {}
+
+ /**
+ * Creates an empty {@link MemoryConfiguration} using the specified {@link Configuration}
+ * as a source for all default values.
+ *
+ * @param defaults Default value provider
+ * @throws IllegalArgumentException Thrown if defaults is null
+ */
+ public MemoryConfiguration(Configuration defaults) {
+ this.defaults = defaults;
+ }
+
+ @Override
+ public void addDefault(String path, Object value) {
+ if (path == null) {
+ throw new IllegalArgumentException("Path may not be null");
+ }
+
+ if (defaults == null) {
+ defaults = new MemoryConfiguration();
+ }
+
+ defaults.set(path, value);
+ }
+
+ public void addDefaults(Map
+ * Note that calling this without being yourself a {@link Configuration} will throw an
+ * exception!
+ *
+ * @throws IllegalStateException Thrown if this is not a {@link Configuration} root.
+ */
+ protected MemorySection() {
+ if (!(this instanceof Configuration)) {
+ throw new IllegalStateException("Cannot contruct a root MemorySection when not a Configuration");
+ }
+
+ this.path = "";
+ this.fullPath = "";
+ this.parent = null;
+ this.root = (Configuration)this;
+ }
+
+ /**
+ * Creates an empty MemorySection with the specified parent and path.
+ *
+ * @param parent Parent section that contains this own section.
+ * @param path Path that you may access this section from via the root {@link Configuration}.
+ * @throws IllegalArgumentException Thrown is parent or path is null, or if parent contains no root Configuration.
+ */
+ protected MemorySection(ConfigurationSection parent, String path) {
+ if (parent == null) {
+ throw new IllegalArgumentException("Parent cannot be null");
+ }
+ if (path == null) {
+ throw new IllegalArgumentException("Path cannot be null");
+ }
+
+ this.path = path;
+ this.parent = parent;
+ this.root = parent.getRoot();
+
+ if (root == null) {
+ throw new IllegalArgumentException("Path cannot be orphaned");
+ }
+
+ this.fullPath = createPath(parent, path);
+ }
+
+ public Set
+ * You may use this method for any given {@link ConfigurationSection}, not only {@link MemorySection}.
+ *
+ * @param section Section to create a path for.
+ * @param key Name of the specified section.
+ * @return Full path of the section from its root.
+ */
+ public static String createPath(ConfigurationSection section, String key) {
+ return createPath(section, key, (section == null) ? null : section.getRoot());
+ }
+
+
+ /**
+ * Creates a relative path to the given {@link ConfigurationSection} from the given relative section.
+ *
+ * You may use this method for any given {@link ConfigurationSection}, not only {@link MemorySection}.
+ *
+ * @param section Section to create a path for.
+ * @param key Name of the specified section.
+ * @param relativeTo Section to create the path relative to.
+ * @return Full path of the section from its root.
+ */
+ public static String createPath(ConfigurationSection section, String key, ConfigurationSection relativeTo) {
+ StringBuilder builder = new StringBuilder();
+
+ if (section != null) {
+ for (ConfigurationSection parent = section; (parent != null) && (parent != relativeTo); parent = parent.getParent()) {
+ if (builder.length() > 0) {
+ builder.insert(0, section.getRoot().options().pathSeparator());
+ }
+
+ builder.insert(0, parent.getName());
+ }
+ }
+
+ if ((key != null) && (key.length() > 0)) {
+ if (builder.length() > 0) {
+ builder.append(section.getRoot().options().pathSeparator());
+ }
+
+ builder.append(key);
+ }
+
+ return builder.toString();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+
+ builder.append(getClass().getSimpleName());
+ builder.append("[path='");
+ builder.append(getCurrentPath());
+ builder.append("', root='");
+ builder.append(root.getClass().getSimpleName());
+ builder.append("']");
+
+ return builder.toString();
+ }
+}
diff --git a/paper-api/src/main/java/org/bukkit/configuration/file/FileConfiguration.java b/paper-api/src/main/java/org/bukkit/configuration/file/FileConfiguration.java
new file mode 100644
index 0000000000..41ec8a0d8b
--- /dev/null
+++ b/paper-api/src/main/java/org/bukkit/configuration/file/FileConfiguration.java
@@ -0,0 +1,191 @@
+package org.bukkit.configuration.file;
+
+import com.google.common.io.Files;
+import org.bukkit.configuration.InvalidConfigurationException;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import org.bukkit.configuration.Configuration;
+import org.bukkit.configuration.MemoryConfiguration;
+
+/**
+ * This is a base class for all File based implementations of {@link Configuration}
+ */
+public abstract class FileConfiguration extends MemoryConfiguration {
+ /**
+ * Creates an empty {@link FileConfiguration} with no default values.
+ */
+ public FileConfiguration() {
+ super();
+ }
+
+ /**
+ * Creates an empty {@link FileConfiguration} using the specified {@link Configuration}
+ * as a source for all default values.
+ *
+ * @param defaults Default value provider
+ */
+ public FileConfiguration(Configuration defaults) {
+ super(defaults);
+ }
+
+ /**
+ * Saves this {@link FileConfiguration} to the specified location.
+ *
+ * If the file does not exist, it will be created. If already exists, it will
+ * be overwritten. If it cannot be overwritten or created, an exception will be thrown.
+ *
+ * @param file File to save to.
+ * @throws IOException Thrown when the given file cannot be written to for any reason.
+ * @throws IllegalArgumentException Thrown when file is null.
+ */
+ public void save(File file) throws IOException {
+ if (file == null) {
+ throw new IllegalArgumentException("File cannot be null");
+ }
+
+ Files.createParentDirs(file);
+
+ String data = saveToString();
+
+ FileWriter writer = new FileWriter(file);
+
+ try {
+ writer.write(data);
+ } finally {
+ writer.close();
+ }
+ }
+
+ /**
+ * Saves this {@link FileConfiguration} to the specified location.
+ *
+ * If the file does not exist, it will be created. If already exists, it will
+ * be overwritten. If it cannot be overwritten or created, an exception will be thrown.
+ *
+ * @param file File to save to.
+ * @throws IOException Thrown when the given file cannot be written to for any reason.
+ * @throws IllegalArgumentException Thrown when file is null.
+ */
+ public void save(String file) throws IOException {
+ if (file == null) {
+ throw new IllegalArgumentException("File cannot be null");
+ }
+
+ save(new File(file));
+ }
+
+ /**
+ * Saves this {@link FileConfiguration} to a string, and returns it.
+ *
+ * @return String containing this configuration.
+ */
+ public abstract String saveToString();
+
+ /**
+ * Loads this {@link FileConfiguration} from the specified location.
+ *
+ * All the values contained within this configuration will be removed, leaving
+ * only settings and defaults, and the new values will be loaded from the given file.
+ *
+ * If the file cannot be loaded for any reason, an exception will be thrown.
+ *
+ * @param file File to load from.
+ * @throws FileNotFoundException Thrown when the given file cannot be opened.
+ * @throws IOException Thrown when the given file cannot be read.
+ * @throws InvalidConfigurationException Thrown when the given file is not a valid Configuration.
+ * @throws IllegalArgumentException Thrown when file is null.
+ */
+ public void load(File file) throws FileNotFoundException, IOException, InvalidConfigurationException {
+ if (file == null) {
+ throw new IllegalArgumentException("File cannot be null");
+ }
+
+ load(new FileInputStream(file));
+ }
+
+ /**
+ * Loads this {@link FileConfiguration} from the specified stream.
+ *
+ * All the values contained within this configuration will be removed, leaving
+ * only settings and defaults, and the new values will be loaded from the given stream.
+ *
+ * @param stream Stream to load from
+ * @throws IOException Thrown when the given file cannot be read.
+ * @throws InvalidConfigurationException Thrown when the given file is not a valid Configuration.
+ * @throws IllegalArgumentException Thrown when stream is null.
+ */
+ public void load(InputStream stream) throws IOException, InvalidConfigurationException {
+ if (stream == null) {
+ throw new IllegalArgumentException("Stream cannot be null");
+ }
+
+ InputStreamReader reader = new InputStreamReader(stream);
+ StringBuilder builder = new StringBuilder();
+ BufferedReader input = new BufferedReader(reader);
+
+ try {
+ String line;
+
+ while ((line = input.readLine()) != null) {
+ builder.append(line);
+ builder.append('\n');
+ }
+ } finally {
+ input.close();
+ }
+
+ loadFromString(builder.toString());
+ }
+
+ /**
+ * Loads this {@link FileConfiguration} from the specified location.
+ *
+ * All the values contained within this configuration will be removed, leaving
+ * only settings and defaults, and the new values will be loaded from the given file.
+ *
+ * If the file cannot be loaded for any reason, an exception will be thrown.
+ *
+ * @param file File to load from.
+ * @throws FileNotFoundException Thrown when the given file cannot be opened.
+ * @throws IOException Thrown when the given file cannot be read.
+ * @throws InvalidConfigurationException Thrown when the given file is not a valid Configuration.
+ * @throws IllegalArgumentException Thrown when file is null.
+ */
+ public void load(String file) throws FileNotFoundException, IOException, InvalidConfigurationException {
+ if (file == null) {
+ throw new IllegalArgumentException("File cannot be null");
+ }
+
+ load(new File(file));
+ }
+
+ /**
+ * Loads this {@link FileConfiguration} from the specified string, as opposed to from file.
+ *
+ * All the values contained within this configuration will be removed, leaving
+ * only settings and defaults, and the new values will be loaded from the given string.
+ *
+ * If the string is invalid in any way, an exception will be thrown.
+ *
+ * @param contents Contents of a Configuration to load.
+ * @throws InvalidConfigurationException Thrown if the specified string is invalid.
+ * @throws IllegalArgumentException Thrown if contents is null.
+ */
+ public abstract void loadFromString(String contents) throws InvalidConfigurationException;
+
+ @Override
+ public FileConfigurationOptions options() {
+ if (options == null) {
+ options = new FileConfigurationOptions(this);
+ }
+
+ return (FileConfigurationOptions)options;
+ }
+}
\ No newline at end of file
diff --git a/paper-api/src/main/java/org/bukkit/configuration/file/FileConfigurationOptions.java b/paper-api/src/main/java/org/bukkit/configuration/file/FileConfigurationOptions.java
new file mode 100644
index 0000000000..804272045b
--- /dev/null
+++ b/paper-api/src/main/java/org/bukkit/configuration/file/FileConfigurationOptions.java
@@ -0,0 +1,67 @@
+package org.bukkit.configuration.file;
+
+import org.bukkit.configuration.*;
+
+/**
+ * Various settings for controlling the input and output of a {@link FileConfiguration}
+ */
+public class FileConfigurationOptions extends MemoryConfigurationOptions {
+ private String header = null;
+
+ protected FileConfigurationOptions(MemoryConfiguration configuration) {
+ super(configuration);
+ }
+
+ @Override
+ public FileConfiguration configuration() {
+ return (FileConfiguration)super.configuration();
+ }
+
+ @Override
+ public FileConfigurationOptions copyDefaults(boolean value) {
+ super.copyDefaults(value);
+ return this;
+ }
+
+ @Override
+ public FileConfigurationOptions pathSeparator(char value) {
+ super.pathSeparator(value);
+ return this;
+ }
+
+ /**
+ * Gets the header that will be applied to the top of the saved output.
+ *
+ * This header will be commented out and applied directly at the top of the
+ * generated output of the {@link FileConfiguration}. It is not required to
+ * include a newline at the end of the header as it will automatically be applied,
+ * but you may include one if you wish for extra spacing.
+ *
+ * Null is a valid value which will indicate that no header is to be applied.
+ * The default value is null.
+ *
+ * @return Header
+ */
+ public String header() {
+ return header;
+ }
+
+ /**
+ * Sets the header that will be applied to the top of the saved output.
+ *
+ * This header will be commented out and applied directly at the top of the
+ * generated output of the {@link FileConfiguration}. It is not required to
+ * include a newline at the end of the header as it will automatically be applied,
+ * but you may include one if you wish for extra spacing.
+ *
+ * Null is a valid value which will indicate that no header is to be applied.
+ * The default value is null.
+ *
+ * @param value New header
+ * @return This object, for chaining
+ */
+ public FileConfigurationOptions header(String value) {
+ this.header = value;
+ return this;
+ }
+}
diff --git a/paper-api/src/main/java/org/bukkit/configuration/file/YamlConfiguration.java b/paper-api/src/main/java/org/bukkit/configuration/file/YamlConfiguration.java
new file mode 100644
index 0000000000..2ce6fcba28
--- /dev/null
+++ b/paper-api/src/main/java/org/bukkit/configuration/file/YamlConfiguration.java
@@ -0,0 +1,228 @@
+package org.bukkit.configuration.file;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import org.bukkit.Bukkit;
+import org.bukkit.configuration.InvalidConfigurationException;
+import org.bukkit.configuration.Configuration;
+import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.configuration.serialization.ConfigurationSerializable;
+import org.bukkit.configuration.serialization.ConfigurationSerialization;
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.SafeConstructor;
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.representer.Representer;
+
+/**
+ * An implementation of {@link Configuration} which saves all files in Yaml.
+ */
+public class YamlConfiguration extends FileConfiguration {
+ protected static final String COMMENT_PREFIX = "# ";
+ protected static final String BLANK_CONFIG = "{}\n";
+ private final DumperOptions yamlOptions = new DumperOptions();
+ private final Representer yamlRepresenter = new Representer();
+ private final Yaml yaml = new Yaml(new SafeConstructor(), yamlRepresenter, yamlOptions);
+
+ @Override
+ public String saveToString() {
+ Map
+ * Any errors loading the Configuration will be logged and then ignored.
+ * If the specified input is not a valid config, a blank config will be returned.
+ *
+ * @param file Input file
+ * @return Resulting configuration
+ * @throws IllegalArgumentException Thrown is file is null
+ */
+ public static YamlConfiguration loadConfiguration(File file) {
+ if (file == null) {
+ throw new IllegalArgumentException("File cannot be null");
+ }
+
+ YamlConfiguration config = new YamlConfiguration();
+
+ try {
+ config.load(file);
+ } catch (FileNotFoundException ex) {
+ } catch (IOException ex) {
+ Bukkit.getLogger().log(Level.SEVERE, "Cannot load " + file, ex);
+ } catch (InvalidConfigurationException ex) {
+ if (ex.getCause() instanceof YAMLException) {
+ Bukkit.getLogger().severe("Config file " + file + " isn't valid! " + ex.getCause());
+ } else if ((ex.getCause() == null) || (ex.getCause() instanceof ClassCastException)) {
+ Bukkit.getLogger().severe("Config file " + file + " isn't valid!");
+ } else {
+ Bukkit.getLogger().log(Level.SEVERE, "Cannot load " + file + ": " + ex.getCause().getClass(), ex);
+ }
+ }
+
+ return config;
+ }
+
+ /**
+ * Creates a new {@link YamlConfiguration}, loading from the given stream.
+ *
+ * Any errors loading the Configuration will be logged and then ignored.
+ * If the specified input is not a valid config, a blank config will be returned.
+ *
+ * @param stream Input stream
+ * @return Resulting configuration
+ * @throws IllegalArgumentException Thrown is stream is null
+ */
+ public static YamlConfiguration loadConfiguration(InputStream stream) {
+ if (stream == null) {
+ throw new IllegalArgumentException("Stream cannot be null");
+ }
+
+ YamlConfiguration config = new YamlConfiguration();
+
+ try {
+ config.load(stream);
+ } catch (IOException ex) {
+ Bukkit.getLogger().log(Level.SEVERE, "Cannot load configuration", ex);
+ } catch (InvalidConfigurationException ex) {
+ if (ex.getCause() instanceof YAMLException) {
+ Bukkit.getLogger().severe("Config file isn't valid! " + ex.getCause());
+ } else if ((ex.getCause() == null) || (ex.getCause() instanceof ClassCastException)) {
+ Bukkit.getLogger().severe("Config file isn't valid!");
+ } else {
+ Bukkit.getLogger().log(Level.SEVERE, "Cannot load configuration: " + ex.getCause().getClass(), ex);
+ }
+ }
+
+ return config;
+ }
+}
diff --git a/paper-api/src/main/java/org/bukkit/configuration/file/YamlConfigurationOptions.java b/paper-api/src/main/java/org/bukkit/configuration/file/YamlConfigurationOptions.java
new file mode 100644
index 0000000000..eee75fb183
--- /dev/null
+++ b/paper-api/src/main/java/org/bukkit/configuration/file/YamlConfigurationOptions.java
@@ -0,0 +1,63 @@
+package org.bukkit.configuration.file;
+
+/**
+ * Various settings for controlling the input and output of a {@link YamlConfiguration}
+ */
+public class YamlConfigurationOptions extends FileConfigurationOptions {
+ private int indent = 2;
+
+ protected YamlConfigurationOptions(YamlConfiguration configuration) {
+ super(configuration);
+ }
+
+ @Override
+ public YamlConfiguration configuration() {
+ return (YamlConfiguration)super.configuration();
+ }
+
+ @Override
+ public YamlConfigurationOptions copyDefaults(boolean value) {
+ super.copyDefaults(value);
+ return this;
+ }
+
+ @Override
+ public YamlConfigurationOptions pathSeparator(char value) {
+ super.pathSeparator(value);
+ return this;
+ }
+
+ @Override
+ public YamlConfigurationOptions header(String value) {
+ super.header(value);
+ return this;
+ }
+
+ /**
+ * Gets how much spaces should be used to indent each line.
+ *
+ * The minimum value this may be is 2, and the maximum is 9.
+ *
+ * @return How much to indent by
+ */
+ public int indent() {
+ return indent;
+ }
+
+ /**
+ * Sets how much spaces should be used to indent each line.
+ *
+ * The minimum value this may be is 2, and the maximum is 9.
+ *
+ * @param value New indent
+ * @return This object, for chaining
+ */
+ public YamlConfigurationOptions indent(int value) {
+ if ((indent < 2) || (value > 9)) {
+ throw new IllegalArgumentException("Indent must be between 1 and 10 characters");
+ }
+
+ this.indent = value;
+ return this;
+ }
+}
diff --git a/paper-api/src/main/java/org/bukkit/configuration/serialization/ConfigurationSerializable.java b/paper-api/src/main/java/org/bukkit/configuration/serialization/ConfigurationSerializable.java
new file mode 100644
index 0000000000..6e2af87301
--- /dev/null
+++ b/paper-api/src/main/java/org/bukkit/configuration/serialization/ConfigurationSerializable.java
@@ -0,0 +1,24 @@
+package org.bukkit.configuration.serialization;
+
+import java.util.Map;
+
+/**
+ * Represents an object that may be serialized.
+ *
+ * These objects MUST implement one of the following, in addition to the methods
+ * as defined by this interface:
+ * - A static method "deserialize" that accepts a single {@link Map
+ * This class must provide a method to restore this class, as defined in the
+ * {@link ConfigurationSerializable} interface javadocs.
+ *
+ * @return Map containing the current state of this class
+ */
+ public Map
+ * The class must implement {@link ConfigurationSerializable}, including the extra methods
+ * as specified in the javadoc of ConfigurationSerializable.
+ *
+ * If a new instance could not be made, an example being the class not fully implementing
+ * the interface, null will be returned.
+ *
+ * @param args Arguments for deserialization
+ * @param clazz Class to deserialize into
+ * @return New instance of the specified class
+ */
+ public static ConfigurationSerializable deserializeObject(Map
+ * The class must implement {@link ConfigurationSerializable}, including the extra methods
+ * as specified in the javadoc of ConfigurationSerializable.
+ *
+ * If a new instance could not be made, an example being the class not fully implementing
+ * the interface, null will be returned.
+ *
+ * @param args Arguments for deserialization
+ * @return New instance of the specified class
+ */
+ public static ConfigurationSerializable deserializeObject(Map
+ * Using this annotation on any other class than a {@link ConfigurationSerializable} will
+ * have no effect.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface SerializableAs {
+ /**
+ * This is the name your class will be stored and retrieved as.
+ *
+ * This name MUST be unique. We recommend using names such as "MyPluginThing" instead of
+ * "Thing".
+ *
+ * @return Name to serialize the class as.
+ */
+ public String value();
+}
diff --git a/paper-api/src/main/java/org/bukkit/inventory/ItemStack.java b/paper-api/src/main/java/org/bukkit/inventory/ItemStack.java
index 61d43c18e6..b948ccdb1e 100644
--- a/paper-api/src/main/java/org/bukkit/inventory/ItemStack.java
+++ b/paper-api/src/main/java/org/bukkit/inventory/ItemStack.java
@@ -1,12 +1,16 @@
package org.bukkit.inventory;
+import java.io.Serializable;
+import java.util.LinkedHashMap;
+import java.util.Map;
import org.bukkit.Material;
+import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.material.MaterialData;
/**
* Represents a stack of items
*/
-public class ItemStack {
+public class ItemStack implements Serializable, ConfigurationSerializable {
private int type;
private int amount = 0;
private MaterialData data = null;
@@ -208,4 +212,36 @@ public class ItemStack {
hash = hash * 7 + 23 * getAmount(); // too bad these are mutable values... Q_Q
return hash;
}
+
+ public Map
+ * If there is a default config.yml embedded in this plugin, it will be provided
+ * as a default for this Configuration.
+ *
+ * @return Plugin configuration
+ */
+ public FileConfiguration getConfig();
+
+ /**
+ * Gets an embedded resource in this plugin
+ *
+ * @param filename Filename of the resource
+ * @return File if found, otherwise null
+ */
+ public InputStream getResource(String filename);
+
+ /**
+ * Saves the {@link FileConfiguration} retrievable by {@link #getConfig()}.
+ */
+ public void saveConfig();
/**
* Gets the associated PluginLoader responsible for this plugin
diff --git a/paper-api/src/main/java/org/bukkit/plugin/java/JavaPlugin.java b/paper-api/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
index 93dc08cc24..2645b83bf0 100644
--- a/paper-api/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
+++ b/paper-api/src/main/java/org/bukkit/plugin/java/JavaPlugin.java
@@ -7,17 +7,27 @@ import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.server.ddl.DdlGenerator;
import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginCommand;
+import org.bukkit.configuration.InvalidConfigurationException;
+import org.bukkit.configuration.file.FileConfiguration;
+import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginLoader;
import org.bukkit.util.config.Configuration;
+import org.yaml.snakeyaml.error.YAMLException;
/**
* Represents a Java plugin
@@ -34,6 +44,8 @@ public abstract class JavaPlugin implements Plugin {
private Configuration config = null;
private boolean naggable = true;
private EbeanServer ebean = null;
+ private FileConfiguration newConfig = null;
+ private File configFile = null;
public JavaPlugin() {}
@@ -99,10 +111,32 @@ public abstract class JavaPlugin implements Plugin {
* the configuration file will have no values.
*
* @return The configuration.
+ * @deprecated See the new
*/
+ @Deprecated
public Configuration getConfiguration() {
return config;
}
+
+ public FileConfiguration getConfig() {
+ return newConfig;
+ }
+
+ public void saveConfig() {
+ try {
+ newConfig.save(configFile);
+ } catch (IOException ex) {
+ Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Could not save config to " + configFile, ex);
+ }
+ }
+
+ public InputStream getResource(String filename) {
+ if (filename == null) {
+ throw new IllegalArgumentException("Filename cannot be null");
+ }
+
+ return getClassLoader().getResourceAsStream(filename);
+ }
/**
* Returns the ClassLoader which holds this plugin
@@ -153,8 +187,17 @@ public abstract class JavaPlugin implements Plugin {
this.description = description;
this.dataFolder = dataFolder;
this.classLoader = classLoader;
- this.config = new Configuration(new File(dataFolder, "config.yml"));
+ this.configFile = new File(dataFolder, "config.yml");
+ this.config = new Configuration(configFile);
this.config.load();
+ this.newConfig = YamlConfiguration.loadConfiguration(configFile);
+
+ InputStream defConfigStream = getResource("config.yml");
+ if (defConfigStream != null) {
+ YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream);
+
+ newConfig.setDefaults(defConfig);
+ }
if (description.isDatabaseEnabled()) {
ServerConfig db = new ServerConfig();
diff --git a/paper-api/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/paper-api/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
index ad69872e06..bf9379bd7e 100644
--- a/paper-api/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+++ b/paper-api/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
@@ -15,6 +15,9 @@ import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.regex.Pattern;
import org.bukkit.Server;
+import org.bukkit.configuration.serialization.ConfigurationSerializable;
+import org.bukkit.configuration.serialization.ConfigurationSerialization;
+import org.bukkit.configuration.serialization.SerializableAs;
import org.bukkit.event.CustomEventListener;
import org.bukkit.event.Event;
import org.bukkit.event.Listener;
@@ -229,6 +232,20 @@ public class JavaPluginLoader implements PluginLoader {
public void setClass(final String name, final Class> clazz) {
if (!classes.containsKey(name)) {
classes.put(name, clazz);
+
+ if (ConfigurationSerializable.class.isAssignableFrom(clazz)) {
+ Class extends ConfigurationSerializable> serializable = (Class extends ConfigurationSerializable>)clazz;
+ ConfigurationSerialization.registerClass(serializable);
+ }
+ }
+ }
+
+ public void removeClass(String name) {
+ Class> clazz = classes.remove(name);
+
+ if (ConfigurationSerializable.class.isAssignableFrom(clazz)) {
+ Class extends ConfigurationSerializable> serializable = (Class extends ConfigurationSerializable>)clazz;
+ ConfigurationSerialization.unregisterClass(serializable);
}
}
@@ -973,7 +990,7 @@ public class JavaPluginLoader implements PluginLoader {
Set This class is currently incomplete. It is not yet possible to get a node.
* clone()
in order to get a copy.
- *
- * @author sk89q
*/
-public class Vector implements Cloneable {
+@SerializableAs("Vector")
+public class Vector implements Cloneable, ConfigurationSerializable {
private static final long serialVersionUID = -2657651106777219169L;
private static Random random = new Random();
@@ -635,4 +638,32 @@ public class Vector implements Cloneable {
public static Vector getRandom() {
return new Vector(random.nextDouble(), random.nextDouble(), random.nextDouble());
}
+
+ public Map