Converted ConfigData and started others
- Bumped Spigot version to 1.18.1, so we get config comment support natively - Made getters perform a basic casting by default, this was done by the ConfigData code before
This commit is contained in:
parent
89b7246f4f
commit
be5f9ded60
6 changed files with 137 additions and 383 deletions
|
@ -169,7 +169,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.spigotmc</groupId>
|
<groupId>org.spigotmc</groupId>
|
||||||
<artifactId>spigot-api</artifactId>
|
<artifactId>spigot-api</artifactId>
|
||||||
<version>1.12.2-R0.1-SNAPSHOT</version>
|
<version>1.18.1-R0.1-SNAPSHOT</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -18,7 +18,7 @@ import java.util.function.Consumer
|
||||||
@HasConfig(global = true)
|
@HasConfig(global = true)
|
||||||
abstract class ButtonPlugin : JavaPlugin() {
|
abstract class ButtonPlugin : JavaPlugin() {
|
||||||
protected val iConfig = IHaveConfig { saveConfig() }
|
protected val iConfig = IHaveConfig { saveConfig() }
|
||||||
private var yaml: CommentedConfiguration? = null
|
private var yaml: YamlConfiguration? = null
|
||||||
|
|
||||||
protected val data //TODO
|
protected val data //TODO
|
||||||
: IHaveConfig? = null
|
: IHaveConfig? = null
|
||||||
|
@ -39,7 +39,7 @@ abstract class ButtonPlugin : JavaPlugin() {
|
||||||
*/
|
*/
|
||||||
protected fun pluginPreDisable() {}
|
protected fun pluginPreDisable() {}
|
||||||
override fun onEnable() {
|
override fun onEnable() {
|
||||||
if (!loadConfig()) {
|
if (!reloadIConfig()) {
|
||||||
logger.warning("Please fix the issues and restart the server to load the plugin.")
|
logger.warning("Please fix the issues and restart the server to load the plugin.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -52,12 +52,12 @@ abstract class ButtonPlugin : JavaPlugin() {
|
||||||
IHaveConfig.pregenConfig(this, null)
|
IHaveConfig.pregenConfig(this, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadConfig(): Boolean {
|
private fun reloadIConfig(): Boolean {
|
||||||
val config = config ?: return false
|
val config = config
|
||||||
var section = config.getConfigurationSection("global")
|
var section = config.getConfigurationSection("global")
|
||||||
if (section == null) section = config.createSection("global")
|
if (section == null) section = config.createSection("global")
|
||||||
iConfig.reset(section)
|
iConfig.reset(section)
|
||||||
return true
|
return configLoaded // If loading fails, getConfig() returns a temporary instance
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDisable() {
|
override fun onDisable() {
|
||||||
|
@ -78,7 +78,7 @@ abstract class ButtonPlugin : JavaPlugin() {
|
||||||
|
|
||||||
fun tryReloadConfig(): Boolean {
|
fun tryReloadConfig(): Boolean {
|
||||||
if (!justReload()) return false
|
if (!justReload()) return false
|
||||||
loadConfig()
|
reloadIConfig()
|
||||||
componentStack.forEach(Consumer { c: Component<*>? -> updateConfig(this, c!!) })
|
componentStack.forEach(Consumer { c: Component<*>? -> updateConfig(this, c!!) })
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ abstract class ButtonPlugin : JavaPlugin() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
val file = File(dataFolder, "config.yml")
|
val file = File(dataFolder, "config.yml")
|
||||||
val yaml = CommentedConfiguration(file)
|
val yaml = YamlConfiguration()
|
||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
try {
|
try {
|
||||||
yaml.load(file)
|
yaml.load(file)
|
||||||
|
@ -102,16 +102,17 @@ abstract class ButtonPlugin : JavaPlugin() {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
this.yaml = yaml
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
this.yaml = yaml
|
|
||||||
val res = getTextResource("configHelp.yml") ?: return true
|
val res = getTextResource("configHelp.yml") ?: return true
|
||||||
val yc = YamlConfiguration.loadConfiguration(res)
|
val yc = YamlConfiguration.loadConfiguration(res)
|
||||||
for ((key, value) in yc.getValues(true)) if (value is String) yaml.addComment(key.replace(
|
for ((key, value) in yc.getValues(true))
|
||||||
".generalDescriptionInsteadOfAConfig",
|
if (value is String) yaml.setComments(
|
||||||
""
|
key.replace(".generalDescriptionInsteadOfAConfig", ""),
|
||||||
),
|
value.split("\n").map { str -> "# " + str.trim { it <= ' ' } }
|
||||||
*value.split("\n").map { str -> "# " + str.trim { it <= ' ' } }.toTypedArray()
|
)
|
||||||
)
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,13 +122,11 @@ abstract class ButtonPlugin : JavaPlugin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun saveConfig() {
|
override fun saveConfig() {
|
||||||
try {
|
if (configLoaded) super.saveConfig()
|
||||||
if (yaml != null) yaml!!.save()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
TBMCCoreAPI.SendException("Failed to save config", e, this)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val configLoaded get() = yaml != null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers command and sets its plugin.
|
* Registers command and sets its plugin.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,229 +0,0 @@
|
||||||
package buttondevteam.lib.architecture;
|
|
||||||
|
|
||||||
import org.bukkit.configuration.InvalidConfigurationException;
|
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
|
||||||
import org.bukkit.configuration.file.YamlConstructor;
|
|
||||||
import org.bukkit.configuration.file.YamlRepresenter;
|
|
||||||
import org.yaml.snakeyaml.DumperOptions;
|
|
||||||
import org.yaml.snakeyaml.Yaml;
|
|
||||||
import org.yaml.snakeyaml.representer.Representer;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A copy of Towny's CommentedConfiguration: https://github.com/TownyAdvanced/Towny/blob/master/src/com/palmergames/bukkit/config/CommentedConfiguration.java
|
|
||||||
* Modified to remove dependency on the FileMgmt class
|
|
||||||
*
|
|
||||||
* @author dumptruckman & Articdive
|
|
||||||
*/
|
|
||||||
public class CommentedConfiguration extends YamlConfiguration {
|
|
||||||
private HashMap<String, String> comments;
|
|
||||||
private File file;
|
|
||||||
|
|
||||||
private final DumperOptions yamlOptions = new DumperOptions();
|
|
||||||
private final Representer yamlRepresenter = new YamlRepresenter();
|
|
||||||
private final Yaml yaml = new Yaml(new YamlConstructor(), yamlRepresenter, yamlOptions);
|
|
||||||
|
|
||||||
public CommentedConfiguration(File file) {
|
|
||||||
|
|
||||||
super();
|
|
||||||
comments = new HashMap<>();
|
|
||||||
this.file = file;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean load() {
|
|
||||||
|
|
||||||
boolean loaded = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.load(file);
|
|
||||||
} catch (InvalidConfigurationException | IOException e) {
|
|
||||||
loaded = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return loaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void save() throws IOException {
|
|
||||||
|
|
||||||
boolean saved = true;
|
|
||||||
|
|
||||||
// Save the config just like normal
|
|
||||||
try {
|
|
||||||
this.save(file);
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
saved = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there's comments to add and it saved fine, we need to add comments
|
|
||||||
if (!comments.isEmpty() && saved) {
|
|
||||||
// String array of each line in the config file
|
|
||||||
String[] yamlContents = Files.readAllLines(file.toPath()).toArray(new String[0]);
|
|
||||||
|
|
||||||
// This will hold the newly formatted line
|
|
||||||
StringBuilder newContents = new StringBuilder();
|
|
||||||
// This holds the current path the lines are at in the config
|
|
||||||
String currentPath = "";
|
|
||||||
// This flags if the line is a node or unknown text.
|
|
||||||
boolean node;
|
|
||||||
// The depth of the path. (number of words separated by periods - 1)
|
|
||||||
int depth = 0;
|
|
||||||
|
|
||||||
// Loop through the config lines
|
|
||||||
for (String line : yamlContents) {
|
|
||||||
// If the line is a node (and not something like a list value)
|
|
||||||
if (line.contains(": ") || (line.length() > 1 && line.charAt(line.length() - 1) == ':')) {
|
|
||||||
|
|
||||||
// This is a node so flag it as one
|
|
||||||
node = true;
|
|
||||||
|
|
||||||
// Grab the index of the end of the node name
|
|
||||||
int index;
|
|
||||||
index = line.indexOf(": ");
|
|
||||||
if (index < 0) {
|
|
||||||
index = line.length() - 1;
|
|
||||||
}
|
|
||||||
// If currentPath is empty, store the node name as the currentPath. (this is only on the first iteration, i think)
|
|
||||||
if (currentPath.isEmpty()) {
|
|
||||||
currentPath = line.substring(0, index);
|
|
||||||
} else {
|
|
||||||
// Calculate the whitespace preceding the node name
|
|
||||||
int whiteSpace = 0;
|
|
||||||
for (int n = 0; n < line.length(); n++) {
|
|
||||||
if (line.charAt(n) == ' ') {
|
|
||||||
whiteSpace++;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Find out if the current depth (whitespace * 2) is greater/lesser/equal to the previous depth
|
|
||||||
if (whiteSpace / 2 > depth) {
|
|
||||||
// Path is deeper. Add a . and the node name
|
|
||||||
currentPath += "." + line.substring(whiteSpace, index);
|
|
||||||
depth++;
|
|
||||||
} else if (whiteSpace / 2 < depth) {
|
|
||||||
// Path is shallower, calculate current depth from whitespace (whitespace / 2) and subtract that many levels from the currentPath
|
|
||||||
int newDepth = whiteSpace / 2;
|
|
||||||
for (int i = 0; i < depth - newDepth; i++) {
|
|
||||||
currentPath = currentPath.replace(currentPath.substring(currentPath.lastIndexOf(".")), "");
|
|
||||||
}
|
|
||||||
// Grab the index of the final period
|
|
||||||
int lastIndex = currentPath.lastIndexOf(".");
|
|
||||||
if (lastIndex < 0) {
|
|
||||||
// if there isn't a final period, set the current path to nothing because we're at root
|
|
||||||
currentPath = "";
|
|
||||||
} else {
|
|
||||||
// If there is a final period, replace everything after it with nothing
|
|
||||||
currentPath = currentPath.replace(currentPath.substring(currentPath.lastIndexOf(".")), "");
|
|
||||||
currentPath += ".";
|
|
||||||
}
|
|
||||||
// Add the new node name to the path
|
|
||||||
currentPath += line.substring(whiteSpace, index);
|
|
||||||
// Reset the depth
|
|
||||||
depth = newDepth;
|
|
||||||
} else {
|
|
||||||
// Path is same depth, replace the last path node name to the current node name
|
|
||||||
int lastIndex = currentPath.lastIndexOf(".");
|
|
||||||
if (lastIndex < 0) {
|
|
||||||
// if there isn't a final period, set the current path to nothing because we're at root
|
|
||||||
currentPath = "";
|
|
||||||
} else {
|
|
||||||
// If there is a final period, replace everything after it with nothing
|
|
||||||
currentPath = currentPath.replace(currentPath.substring(currentPath.lastIndexOf(".")), "");
|
|
||||||
currentPath += ".";
|
|
||||||
}
|
|
||||||
//currentPath = currentPath.replace(currentPath.substring(currentPath.lastIndexOf(".")), "");
|
|
||||||
currentPath += line.substring(whiteSpace, index);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
node = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node) {
|
|
||||||
// If there's a comment for the current path, retrieve it and flag that path as already commented
|
|
||||||
String comment = comments.get(currentPath);
|
|
||||||
|
|
||||||
if (comment != null) {
|
|
||||||
// Add the comment to the beginning of the current line
|
|
||||||
line = comment + System.getProperty("line.separator") + line + System.getProperty("line.separator");
|
|
||||||
} else {
|
|
||||||
// Add a new line as it is a node, but has no comment
|
|
||||||
line += System.getProperty("line.separator");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Add the (modified) line to the total config String
|
|
||||||
if (!node) {
|
|
||||||
newContents.append(line).append(System.getProperty("line.separator"));
|
|
||||||
} else {
|
|
||||||
newContents.append(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Due to a Bukkit Bug with the Configuration
|
|
||||||
* we just need to remove any extra comments at the start of a file.
|
|
||||||
*/
|
|
||||||
while (newContents.toString().startsWith(" " + System.getProperty("line.separator"))) {
|
|
||||||
newContents = new StringBuilder(newContents.toString().replaceFirst(" " + System.getProperty("line.separator"), ""));
|
|
||||||
}
|
|
||||||
Files.write(file.toPath(), newContents.toString().getBytes(StandardCharsets.UTF_8));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a comment just before the specified path. The comment can be
|
|
||||||
* multiple lines. An empty string will indicate a blank line.
|
|
||||||
*
|
|
||||||
* @param path Configuration path to add comment.
|
|
||||||
* @param commentLines Comments to add. One String per line.
|
|
||||||
*/
|
|
||||||
public void addComment(String path, String... commentLines) {
|
|
||||||
|
|
||||||
StringBuilder commentstring = new StringBuilder();
|
|
||||||
StringBuilder leadingSpaces = new StringBuilder();
|
|
||||||
for (int n = 0; n < path.length(); n++) {
|
|
||||||
if (path.charAt(n) == '.') {
|
|
||||||
leadingSpaces.append(" ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (String line : commentLines) {
|
|
||||||
if (!line.isEmpty()) {
|
|
||||||
line = leadingSpaces + line;
|
|
||||||
} else {
|
|
||||||
line = " ";
|
|
||||||
}
|
|
||||||
if (commentstring.length() > 0) {
|
|
||||||
commentstring.append(System.getProperty("line.separator"));
|
|
||||||
}
|
|
||||||
commentstring.append(line);
|
|
||||||
}
|
|
||||||
comments.put(path, commentstring.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String saveToString() {
|
|
||||||
yamlOptions.setIndent(options().indent());
|
|
||||||
yamlOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
|
||||||
yamlOptions.setWidth(10000);
|
|
||||||
yamlRepresenter.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
|
||||||
|
|
||||||
|
|
||||||
String dump = yaml.dump(getValues(false));
|
|
||||||
|
|
||||||
|
|
||||||
if (dump.equals(BLANK_CONFIG)) {
|
|
||||||
dump = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return dump;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,7 +6,6 @@ import lombok.*
|
||||||
import org.bukkit.Bukkit
|
import org.bukkit.Bukkit
|
||||||
import org.bukkit.configuration.Configuration
|
import org.bukkit.configuration.Configuration
|
||||||
import org.bukkit.scheduler.BukkitTask
|
import org.bukkit.scheduler.BukkitTask
|
||||||
import java.util.function.BiFunction
|
|
||||||
import java.util.function.Function
|
import java.util.function.Function
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,10 +20,10 @@ open class ConfigData<T> internal constructor(
|
||||||
val path: String,
|
val path: String,
|
||||||
def: T?,
|
def: T?,
|
||||||
primitiveDef: Any?,
|
primitiveDef: Any?,
|
||||||
private val getter: Function<Any?, T>?,
|
private val getter: Function<Any?, T>,
|
||||||
private val setter: Function<T, Any?>?
|
private val setter: Function<T, Any?>
|
||||||
) {
|
) {
|
||||||
private val def: Any?
|
private val pdef: Any?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The config value should not change outside this instance
|
* The config value should not change outside this instance
|
||||||
|
@ -32,9 +31,8 @@ open class ConfigData<T> internal constructor(
|
||||||
private var value: T? = null
|
private var value: T? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
this.def = primitiveDef ?: def?.let { setter?.apply(it) }
|
this.pdef = primitiveDef ?: def?.let { setter.apply(it) }
|
||||||
?: throw IllegalArgumentException("Either def or primitiveDef must be set. A getter and setter must be present when using primitiveDef.")
|
?: throw IllegalArgumentException("Either def or primitiveDef must be set. A getter and setter must be present when using primitiveDef.")
|
||||||
require(getter == null == (setter == null)) { "Both setters and getters must be present (or none if def is primitive)." }
|
|
||||||
get() //Generate config automatically
|
get() //Generate config automatically
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,67 +48,46 @@ open class ConfigData<T> internal constructor(
|
||||||
if (value != null) return value //Speed things up
|
if (value != null) return value //Speed things up
|
||||||
val config = config?.config
|
val config = config?.config
|
||||||
var `val`: Any?
|
var `val`: Any?
|
||||||
if (config == null || !config.isSet(path)) { //Call set() if config == null
|
if (config == null || !config.isSet(path)) {
|
||||||
`val` = primitiveDef // TODO: primitiveDef --> def, def --> getter(primitiveDef)
|
`val` = pdef
|
||||||
if ((def == null || this is ReadOnlyConfigData<*>) && config != null) //In Discord's case def may be null
|
setInternal(pdef) // Save default value even if read-only
|
||||||
setInternal(primitiveDef) //If read-only then we still need to save the default value so it can be set
|
|
||||||
else set(def) //Save default value - def is always set
|
|
||||||
} else `val` = config.get(path) //config==null: testing
|
} else `val` = config.get(path) //config==null: testing
|
||||||
if (`val` == null) //If it's set to null explicitly
|
if (`val` == null) //If it's set to null explicitly
|
||||||
`val` = primitiveDef
|
`val` = pdef
|
||||||
val convert = BiFunction { _val: Any?, _def: Any? ->
|
fun convert(_val: Any?, _pdef: Any?): Any? {
|
||||||
if (_def is Number) //If we expect a number
|
return if (_pdef is Number) //If we expect a number
|
||||||
_val = if (_val is Number) ChromaUtils.convertNumber(
|
if (_val is Number)
|
||||||
_val as Number?,
|
ChromaUtils.convertNumber(_val as Number?, _pdef.javaClass as Class<out Number?>)
|
||||||
_def.javaClass as Class<out Number?>
|
else _pdef //If we didn't get a number, return default (which is a number)
|
||||||
) else _def //If we didn't get a number, return default (which is a number)
|
else if (_val is List<*> && _pdef != null && _pdef.javaClass.isArray)
|
||||||
else if (_val is List<*> && _def != null && _def.javaClass.isArray) _val = (_val as List<T>).toArray<T>(
|
_val.toTypedArray()
|
||||||
java.lang.reflect.Array.newInstance(
|
else _val
|
||||||
_def.javaClass.componentType,
|
|
||||||
0
|
|
||||||
) as Array<T>
|
|
||||||
)
|
|
||||||
_val
|
|
||||||
}
|
|
||||||
if (getter != null) {
|
|
||||||
`val` = convert.apply(`val`, primitiveDef)
|
|
||||||
var hmm: T? = getter.apply(`val`)
|
|
||||||
if (hmm == null) hmm = def //Set if the getter returned null
|
|
||||||
return hmm
|
|
||||||
}
|
|
||||||
`val` = convert.apply(`val`, def)
|
|
||||||
return `val` as T?. also {
|
|
||||||
value = it //Always cache, if not cached yet
|
|
||||||
}
|
}
|
||||||
|
return getter.apply(convert(`val`, pdef)).also { value = it }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun set(value: T?) {
|
fun set(value: T?) {
|
||||||
if (this is ReadOnlyConfigData<*>) return //Safety for Discord channel/role data
|
if (this is ReadOnlyConfigData<*>) return //Safety for Discord channel/role data
|
||||||
val `val`: Any?
|
val `val` = value?.let { setter.apply(value) }
|
||||||
`val` = if (setter != null && value != null) setter.apply(value) else value
|
setInternal(`val`)
|
||||||
if (config!!.getConfig<Any>() != null) setInternal(`val`)
|
|
||||||
this.value = value
|
this.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setInternal(`val`: Any?) {
|
private fun setInternal(`val`: Any?) {
|
||||||
config!!.getConfig<Any>().set(path, `val`)
|
if (config == null) return
|
||||||
|
config.config.set(path, `val`)
|
||||||
signalChange(config)
|
signalChange(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
@AllArgsConstructor
|
private class SaveTask(val task: BukkitTask, val saveAction: Runnable)
|
||||||
private class SaveTask {
|
|
||||||
var task: BukkitTask? = null
|
|
||||||
var saveAction: Runnable? = null
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
class ConfigDataBuilder<T> internal constructor(private val config: IHaveConfig, private val path: String) {
|
||||||
class ConfigDataBuilder<T> {
|
|
||||||
private val config: IHaveConfig? = null
|
|
||||||
private val path: String? = null
|
|
||||||
private var def: T? = null
|
private var def: T? = null
|
||||||
private var primitiveDef: Any? = null
|
private var primitiveDef: Any? = null
|
||||||
private var getter: Function<Any?, T?>? = null
|
|
||||||
private var setter: Function<T?, Any?>? = null
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
private var getter: Function<Any?, T> = Function { it as T }
|
||||||
|
private var setter: Function<T, Any?> = Function { it }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default value to use, as used in code. If not a primitive type, use the [.getter] and [.setter] methods.
|
* The default value to use, as used in code. If not a primitive type, use the [.getter] and [.setter] methods.
|
||||||
|
@ -145,7 +122,7 @@ open class ConfigData<T> internal constructor(
|
||||||
* @param getter A function that receives the primitive type and returns the runtime type
|
* @param getter A function that receives the primitive type and returns the runtime type
|
||||||
* @return This builder
|
* @return This builder
|
||||||
*/
|
*/
|
||||||
fun getter(getter: Function<Any?, T>?): ConfigDataBuilder<T> {
|
fun getter(getter: Function<Any?, T>): ConfigDataBuilder<T> {
|
||||||
this.getter = getter
|
this.getter = getter
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
@ -157,7 +134,7 @@ open class ConfigData<T> internal constructor(
|
||||||
* @param setter A function that receives the runtime type and returns the primitive type
|
* @param setter A function that receives the runtime type and returns the primitive type
|
||||||
* @return This builder
|
* @return This builder
|
||||||
*/
|
*/
|
||||||
fun setter(setter: Function<T, Any?>?): ConfigDataBuilder<T> {
|
fun setter(setter: Function<T, Any?>): ConfigDataBuilder<T> {
|
||||||
this.setter = setter
|
this.setter = setter
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
@ -167,9 +144,9 @@ open class ConfigData<T> internal constructor(
|
||||||
*
|
*
|
||||||
* @return A ConfigData instance.
|
* @return A ConfigData instance.
|
||||||
*/
|
*/
|
||||||
fun build(): ConfigData<T?> {
|
fun build(): ConfigData<T> {
|
||||||
val config = ConfigData(config, path, def, primitiveDef, getter, setter)
|
val config = ConfigData(config, path, def, primitiveDef, getter, setter)
|
||||||
this.config!!.onConfigBuild(config)
|
this.config.onConfigBuild(config)
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,26 +155,26 @@ open class ConfigData<T> internal constructor(
|
||||||
*
|
*
|
||||||
* @return A ReadOnlyConfigData instance.
|
* @return A ReadOnlyConfigData instance.
|
||||||
*/
|
*/
|
||||||
fun buildReadOnly(): ReadOnlyConfigData<T?> {
|
fun buildReadOnly(): ReadOnlyConfigData<T> {
|
||||||
val config = ReadOnlyConfigData(config, path, def, primitiveDef, getter, setter)
|
val config = ReadOnlyConfigData(config, path, def, primitiveDef, getter, setter)
|
||||||
this.config!!.onConfigBuild(config)
|
this.config.onConfigBuild(config)
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "ConfigData.ConfigDataBuilder(config=" + config + ", path=" + path + ", def=" + def + ", primitiveDef=" + primitiveDef + ", getter=" + getter + ", setter=" + setter + ")"
|
return "ConfigData.ConfigDataBuilder(config=$config, path=$path, def=$def, primitiveDef=$primitiveDef, getter=$getter, setter=$setter)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val saveTasks = HashMap<Configuration, SaveTask>()
|
private val saveTasks = HashMap<Configuration, SaveTask>()
|
||||||
fun signalChange(config: IHaveConfig?) {
|
fun signalChange(config: IHaveConfig) {
|
||||||
val cc = config!!.getConfig<Any>()
|
val cc = config.config
|
||||||
val sa = config.saveAction
|
val sa = config.saveAction
|
||||||
if (!saveTasks.containsKey(cc.getRoot())) {
|
if (!saveTasks.containsKey(cc.root)) {
|
||||||
synchronized(saveTasks) {
|
synchronized(saveTasks) {
|
||||||
saveTasks.put(
|
saveTasks.put(
|
||||||
cc.getRoot(),
|
cc.root,
|
||||||
SaveTask(Bukkit.getScheduler().runTaskLaterAsynchronously(MainPlugin.Instance, {
|
SaveTask(Bukkit.getScheduler().runTaskLaterAsynchronously(MainPlugin.Instance, {
|
||||||
synchronized(
|
synchronized(
|
||||||
saveTasks
|
saveTasks
|
||||||
|
@ -216,16 +193,16 @@ open class ConfigData<T> internal constructor(
|
||||||
synchronized(saveTasks) {
|
synchronized(saveTasks) {
|
||||||
val st = saveTasks[config]
|
val st = saveTasks[config]
|
||||||
if (st != null) {
|
if (st != null) {
|
||||||
st.task!!.cancel()
|
st.task.cancel()
|
||||||
saveTasks.remove(config)
|
saveTasks.remove(config)
|
||||||
st.saveAction!!.run()
|
st.saveAction.run()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> builder(config: IHaveConfig?, path: String?): ConfigDataBuilder<T> {
|
fun <T> builder(config: IHaveConfig, path: String): ConfigDataBuilder<T> {
|
||||||
return ConfigDataBuilder(config, path)
|
return ConfigDataBuilder(config, path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
package buttondevteam.lib.architecture;
|
package buttondevteam.lib.architecture
|
||||||
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function
|
||||||
|
|
||||||
public class ReadOnlyConfigData<T> extends ConfigData<T> {
|
class ReadOnlyConfigData<T> internal constructor(
|
||||||
ReadOnlyConfigData(IHaveConfig config, String path, T def, Object primitiveDef, Function<Object, T> getter, Function<T, Object> setter) {
|
config: IHaveConfig?,
|
||||||
super(config, path, def, primitiveDef, getter, setter);
|
path: String,
|
||||||
}
|
def: T?,
|
||||||
|
primitiveDef: Any?,
|
||||||
ReadOnlyConfigData(IHaveConfig config, String path, T def, Object primitiveDef) {
|
getter: Function<Any?, T>,
|
||||||
super(config, path, def, primitiveDef, null, null);
|
setter: Function<T, Any?>
|
||||||
}
|
) : ConfigData<T>(config, path, def, primitiveDef, getter, setter)
|
||||||
}
|
|
|
@ -1,67 +1,75 @@
|
||||||
package buttondevteam.lib.chat.commands;
|
package buttondevteam.lib.chat.commands
|
||||||
|
|
||||||
import buttondevteam.core.MainPlugin;
|
import buttondevteam.core.MainPlugin
|
||||||
import buttondevteam.lib.TBMCCoreAPI;
|
import buttondevteam.lib.TBMCCoreAPI
|
||||||
import buttondevteam.lib.chat.Command2Sender;
|
import buttondevteam.lib.chat.Command2Sender
|
||||||
import buttondevteam.lib.chat.ICommand2;
|
import buttondevteam.lib.chat.ICommand2
|
||||||
import lombok.val;
|
import org.bukkit.configuration.ConfigurationSection
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
import org.bukkit.configuration.file.YamlConfiguration
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import java.io.IOException
|
||||||
|
import java.io.InputStreamReader
|
||||||
import java.io.IOException;
|
import java.lang.reflect.Method
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deals with reading the commands.yml file from the plugin. The file is generated by ButtonProcessor at compile-time.
|
* Deals with reading the commands.yml file from the plugin. The file is generated by ButtonProcessor at compile-time.
|
||||||
* Only used when registering commands.
|
* Only used when registering commands.
|
||||||
*/
|
*/
|
||||||
public class CommandArgumentHelpManager<TC extends ICommand2<TP>, TP extends Command2Sender> {
|
class CommandArgumentHelpManager<TC : ICommand2<TP>, TP : Command2Sender>(command: TC) {
|
||||||
private ConfigurationSection commandConfig;
|
private var commandConfig: ConfigurationSection? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the yaml file for the given command class.
|
* Read the yaml file for the given command class.
|
||||||
*
|
*
|
||||||
* @param command The command object to use
|
* @param command The command object to use
|
||||||
*/
|
*/
|
||||||
public CommandArgumentHelpManager(TC command) {
|
init {
|
||||||
val commandClass = command.getClass();
|
val commandClass = command.javaClass
|
||||||
// It will load it for each class, but it would be complicated to solve that
|
// It will load it for each class, but it would be complicated to solve that
|
||||||
// Most plugins don't have a lot of command classes anyway
|
// Most plugins don't have a lot of command classes anyway
|
||||||
try (val str = commandClass.getResourceAsStream("/commands.yml")) {
|
try {
|
||||||
if (str == null) {
|
commandClass.getResourceAsStream("/commands.yml").use { str ->
|
||||||
TBMCCoreAPI.SendException("Error while getting command data!", new Exception("Resource not found!"), MainPlugin.Instance);
|
if (str == null) {
|
||||||
return;
|
TBMCCoreAPI.SendException(
|
||||||
}
|
"Error while getting command data!",
|
||||||
val config = YamlConfiguration.loadConfiguration(new InputStreamReader(str));
|
Exception("Resource not found!"),
|
||||||
commandConfig = config.getConfigurationSection(commandClass.getCanonicalName().replace('$', '.'));
|
MainPlugin.Instance
|
||||||
if (commandConfig == null) {
|
)
|
||||||
MainPlugin.Instance.getLogger().warning("Failed to get command data for " + commandClass + "! Make sure to use 'clean install' when building the project.");
|
return@use
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
val config = YamlConfiguration.loadConfiguration(InputStreamReader(str))
|
||||||
TBMCCoreAPI.SendException("Error while getting command data!", e, MainPlugin.Instance);
|
commandConfig = config.getConfigurationSection(commandClass.canonicalName.replace('$', '.'))
|
||||||
}
|
if (commandConfig == null) {
|
||||||
}
|
MainPlugin.Instance.logger.warning("Failed to get command data for $commandClass! Make sure to use 'clean install' when building the project.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
TBMCCoreAPI.SendException("Error while getting command data!", e, MainPlugin.Instance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a parameter help string for the given subcommand method by reading it from the plugin.
|
* Returns a parameter help string for the given subcommand method by reading it from the plugin.
|
||||||
*
|
*
|
||||||
* @param method The subcommand method
|
* @param method The subcommand method
|
||||||
* @return The parameter part of the usage string for the command
|
* @return The parameter part of the usage string for the command
|
||||||
*/
|
*/
|
||||||
public String getParameterHelpForMethod(Method method) {
|
fun getParameterHelpForMethod(method: Method): String? {
|
||||||
val cs = commandConfig.getConfigurationSection(method.getName());
|
val cs = commandConfig?.getConfigurationSection(method.name)
|
||||||
if (cs == null) {
|
if (cs == null) {
|
||||||
MainPlugin.Instance.getLogger().warning("Failed to get command data for " + method + "! Make sure to use 'clean install' when building the project.");
|
MainPlugin.Instance.logger.warning("Failed to get command data for $method! Make sure to use 'clean install' when building the project.")
|
||||||
return null;
|
return null
|
||||||
}
|
}
|
||||||
val mname = cs.getString("method");
|
val mname = cs.getString("method")
|
||||||
val params = cs.getString("params");
|
val params = cs.getString("params")
|
||||||
int i = mname.indexOf('('); //Check only the name - the whole method is still stored for backwards compatibility and in case it may be useful
|
val i =
|
||||||
if (i != -1 && method.getName().equals(mname.substring(0, i)) && params != null) {
|
mname.indexOf('(') //Check only the name - the whole method is still stored for backwards compatibility and in case it may be useful
|
||||||
return params;
|
if (i != -1 && method.name == mname.substring(0, i) && params != null) {
|
||||||
} else
|
return params
|
||||||
TBMCCoreAPI.SendException("Error while getting command data for " + method + "!", new Exception("Method '" + method + "' != " + mname + " or params is " + params), MainPlugin.Instance);
|
} else TBMCCoreAPI.SendException(
|
||||||
return null;
|
"Error while getting command data for $method!",
|
||||||
}
|
Exception("Method '$method' != $mname or params is $params"),
|
||||||
|
MainPlugin.Instance
|
||||||
|
)
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue