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>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.12.2-R0.1-SNAPSHOT</version>
|
||||
<version>1.18.1-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
|
@ -18,7 +18,7 @@ import java.util.function.Consumer
|
|||
@HasConfig(global = true)
|
||||
abstract class ButtonPlugin : JavaPlugin() {
|
||||
protected val iConfig = IHaveConfig { saveConfig() }
|
||||
private var yaml: CommentedConfiguration? = null
|
||||
private var yaml: YamlConfiguration? = null
|
||||
|
||||
protected val data //TODO
|
||||
: IHaveConfig? = null
|
||||
|
@ -39,7 +39,7 @@ abstract class ButtonPlugin : JavaPlugin() {
|
|||
*/
|
||||
protected fun pluginPreDisable() {}
|
||||
override fun onEnable() {
|
||||
if (!loadConfig()) {
|
||||
if (!reloadIConfig()) {
|
||||
logger.warning("Please fix the issues and restart the server to load the plugin.")
|
||||
return
|
||||
}
|
||||
|
@ -52,12 +52,12 @@ abstract class ButtonPlugin : JavaPlugin() {
|
|||
IHaveConfig.pregenConfig(this, null)
|
||||
}
|
||||
|
||||
private fun loadConfig(): Boolean {
|
||||
val config = config ?: return false
|
||||
private fun reloadIConfig(): Boolean {
|
||||
val config = config
|
||||
var section = config.getConfigurationSection("global")
|
||||
if (section == null) section = config.createSection("global")
|
||||
iConfig.reset(section)
|
||||
return true
|
||||
return configLoaded // If loading fails, getConfig() returns a temporary instance
|
||||
}
|
||||
|
||||
override fun onDisable() {
|
||||
|
@ -78,7 +78,7 @@ abstract class ButtonPlugin : JavaPlugin() {
|
|||
|
||||
fun tryReloadConfig(): Boolean {
|
||||
if (!justReload()) return false
|
||||
loadConfig()
|
||||
reloadIConfig()
|
||||
componentStack.forEach(Consumer { c: Component<*>? -> updateConfig(this, c!!) })
|
||||
return true
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ abstract class ButtonPlugin : JavaPlugin() {
|
|||
return false
|
||||
}
|
||||
val file = File(dataFolder, "config.yml")
|
||||
val yaml = CommentedConfiguration(file)
|
||||
val yaml = YamlConfiguration()
|
||||
if (file.exists()) {
|
||||
try {
|
||||
yaml.load(file)
|
||||
|
@ -102,16 +102,17 @@ abstract class ButtonPlugin : JavaPlugin() {
|
|||
e.printStackTrace()
|
||||
return false
|
||||
}
|
||||
this.yaml = yaml
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
this.yaml = yaml
|
||||
val res = getTextResource("configHelp.yml") ?: return true
|
||||
val yc = YamlConfiguration.loadConfiguration(res)
|
||||
for ((key, value) in yc.getValues(true)) if (value is String) yaml.addComment(key.replace(
|
||||
".generalDescriptionInsteadOfAConfig",
|
||||
""
|
||||
),
|
||||
*value.split("\n").map { str -> "# " + str.trim { it <= ' ' } }.toTypedArray()
|
||||
)
|
||||
for ((key, value) in yc.getValues(true))
|
||||
if (value is String) yaml.setComments(
|
||||
key.replace(".generalDescriptionInsteadOfAConfig", ""),
|
||||
value.split("\n").map { str -> "# " + str.trim { it <= ' ' } }
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -121,13 +122,11 @@ abstract class ButtonPlugin : JavaPlugin() {
|
|||
}
|
||||
|
||||
override fun saveConfig() {
|
||||
try {
|
||||
if (yaml != null) yaml!!.save()
|
||||
} catch (e: Exception) {
|
||||
TBMCCoreAPI.SendException("Failed to save config", e, this)
|
||||
}
|
||||
if (configLoaded) super.saveConfig()
|
||||
}
|
||||
|
||||
val configLoaded get() = yaml != null
|
||||
|
||||
/**
|
||||
* 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.configuration.Configuration
|
||||
import org.bukkit.scheduler.BukkitTask
|
||||
import java.util.function.BiFunction
|
||||
import java.util.function.Function
|
||||
|
||||
/**
|
||||
|
@ -21,10 +20,10 @@ open class ConfigData<T> internal constructor(
|
|||
val path: String,
|
||||
def: T?,
|
||||
primitiveDef: Any?,
|
||||
private val getter: Function<Any?, T>?,
|
||||
private val setter: Function<T, Any?>?
|
||||
private val getter: Function<Any?, T>,
|
||||
private val setter: Function<T, Any?>
|
||||
) {
|
||||
private val def: Any?
|
||||
private val pdef: Any?
|
||||
|
||||
/**
|
||||
* The config value should not change outside this instance
|
||||
|
@ -32,9 +31,8 @@ open class ConfigData<T> internal constructor(
|
|||
private var value: T? = null
|
||||
|
||||
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.")
|
||||
require(getter == null == (setter == null)) { "Both setters and getters must be present (or none if def is primitive)." }
|
||||
get() //Generate config automatically
|
||||
}
|
||||
|
||||
|
@ -50,67 +48,46 @@ open class ConfigData<T> internal constructor(
|
|||
if (value != null) return value //Speed things up
|
||||
val config = config?.config
|
||||
var `val`: Any?
|
||||
if (config == null || !config.isSet(path)) { //Call set() if config == null
|
||||
`val` = primitiveDef // TODO: primitiveDef --> def, def --> getter(primitiveDef)
|
||||
if ((def == null || this is ReadOnlyConfigData<*>) && config != null) //In Discord's case def may be null
|
||||
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
|
||||
if (config == null || !config.isSet(path)) {
|
||||
`val` = pdef
|
||||
setInternal(pdef) // Save default value even if read-only
|
||||
} else `val` = config.get(path) //config==null: testing
|
||||
if (`val` == null) //If it's set to null explicitly
|
||||
`val` = primitiveDef
|
||||
val convert = BiFunction { _val: Any?, _def: Any? ->
|
||||
if (_def is Number) //If we expect a number
|
||||
_val = if (_val is Number) ChromaUtils.convertNumber(
|
||||
_val as Number?,
|
||||
_def.javaClass as Class<out Number?>
|
||||
) else _def //If we didn't get a number, return default (which is a number)
|
||||
else if (_val is List<*> && _def != null && _def.javaClass.isArray) _val = (_val as List<T>).toArray<T>(
|
||||
java.lang.reflect.Array.newInstance(
|
||||
_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
|
||||
`val` = pdef
|
||||
fun convert(_val: Any?, _pdef: Any?): Any? {
|
||||
return if (_pdef is Number) //If we expect a number
|
||||
if (_val is Number)
|
||||
ChromaUtils.convertNumber(_val as Number?, _pdef.javaClass as Class<out Number?>)
|
||||
else _pdef //If we didn't get a number, return default (which is a number)
|
||||
else if (_val is List<*> && _pdef != null && _pdef.javaClass.isArray)
|
||||
_val.toTypedArray()
|
||||
else _val
|
||||
}
|
||||
return getter.apply(convert(`val`, pdef)).also { value = it }
|
||||
}
|
||||
|
||||
fun set(value: T?) {
|
||||
if (this is ReadOnlyConfigData<*>) return //Safety for Discord channel/role data
|
||||
val `val`: Any?
|
||||
`val` = if (setter != null && value != null) setter.apply(value) else value
|
||||
if (config!!.getConfig<Any>() != null) setInternal(`val`)
|
||||
val `val` = value?.let { setter.apply(value) }
|
||||
setInternal(`val`)
|
||||
this.value = value
|
||||
}
|
||||
|
||||
private fun setInternal(`val`: Any?) {
|
||||
config!!.getConfig<Any>().set(path, `val`)
|
||||
if (config == null) return
|
||||
config.config.set(path, `val`)
|
||||
signalChange(config)
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
private class SaveTask {
|
||||
var task: BukkitTask? = null
|
||||
var saveAction: Runnable? = null
|
||||
}
|
||||
private class SaveTask(val task: BukkitTask, val saveAction: Runnable)
|
||||
|
||||
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
|
||||
class ConfigDataBuilder<T> {
|
||||
private val config: IHaveConfig? = null
|
||||
private val path: String? = null
|
||||
class ConfigDataBuilder<T> internal constructor(private val config: IHaveConfig, private val path: String) {
|
||||
private var def: T? = 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.
|
||||
|
@ -145,7 +122,7 @@ open class ConfigData<T> internal constructor(
|
|||
* @param getter A function that receives the primitive type and returns the runtime type
|
||||
* @return This builder
|
||||
*/
|
||||
fun getter(getter: Function<Any?, T>?): ConfigDataBuilder<T> {
|
||||
fun getter(getter: Function<Any?, T>): ConfigDataBuilder<T> {
|
||||
this.getter = getter
|
||||
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
|
||||
* @return This builder
|
||||
*/
|
||||
fun setter(setter: Function<T, Any?>?): ConfigDataBuilder<T> {
|
||||
fun setter(setter: Function<T, Any?>): ConfigDataBuilder<T> {
|
||||
this.setter = setter
|
||||
return this
|
||||
}
|
||||
|
@ -167,9 +144,9 @@ open class ConfigData<T> internal constructor(
|
|||
*
|
||||
* @return A ConfigData instance.
|
||||
*/
|
||||
fun build(): ConfigData<T?> {
|
||||
fun build(): ConfigData<T> {
|
||||
val config = ConfigData(config, path, def, primitiveDef, getter, setter)
|
||||
this.config!!.onConfigBuild(config)
|
||||
this.config.onConfigBuild(config)
|
||||
return config
|
||||
}
|
||||
|
||||
|
@ -178,26 +155,26 @@ open class ConfigData<T> internal constructor(
|
|||
*
|
||||
* @return A ReadOnlyConfigData instance.
|
||||
*/
|
||||
fun buildReadOnly(): ReadOnlyConfigData<T?> {
|
||||
fun buildReadOnly(): ReadOnlyConfigData<T> {
|
||||
val config = ReadOnlyConfigData(config, path, def, primitiveDef, getter, setter)
|
||||
this.config!!.onConfigBuild(config)
|
||||
this.config.onConfigBuild(config)
|
||||
return config
|
||||
}
|
||||
|
||||
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 {
|
||||
private val saveTasks = HashMap<Configuration, SaveTask>()
|
||||
fun signalChange(config: IHaveConfig?) {
|
||||
val cc = config!!.getConfig<Any>()
|
||||
fun signalChange(config: IHaveConfig) {
|
||||
val cc = config.config
|
||||
val sa = config.saveAction
|
||||
if (!saveTasks.containsKey(cc.getRoot())) {
|
||||
if (!saveTasks.containsKey(cc.root)) {
|
||||
synchronized(saveTasks) {
|
||||
saveTasks.put(
|
||||
cc.getRoot(),
|
||||
cc.root,
|
||||
SaveTask(Bukkit.getScheduler().runTaskLaterAsynchronously(MainPlugin.Instance, {
|
||||
synchronized(
|
||||
saveTasks
|
||||
|
@ -216,16 +193,16 @@ open class ConfigData<T> internal constructor(
|
|||
synchronized(saveTasks) {
|
||||
val st = saveTasks[config]
|
||||
if (st != null) {
|
||||
st.task!!.cancel()
|
||||
st.task.cancel()
|
||||
saveTasks.remove(config)
|
||||
st.saveAction!!.run()
|
||||
st.saveAction.run()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun <T> builder(config: IHaveConfig?, path: String?): ConfigDataBuilder<T> {
|
||||
fun <T> builder(config: IHaveConfig, path: String): ConfigDataBuilder<T> {
|
||||
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> {
|
||||
ReadOnlyConfigData(IHaveConfig config, String path, T def, Object primitiveDef, Function<Object, T> getter, Function<T, Object> setter) {
|
||||
super(config, path, def, primitiveDef, getter, setter);
|
||||
}
|
||||
|
||||
ReadOnlyConfigData(IHaveConfig config, String path, T def, Object primitiveDef) {
|
||||
super(config, path, def, primitiveDef, null, null);
|
||||
}
|
||||
}
|
||||
class ReadOnlyConfigData<T> internal constructor(
|
||||
config: IHaveConfig?,
|
||||
path: String,
|
||||
def: T?,
|
||||
primitiveDef: Any?,
|
||||
getter: Function<Any?, T>,
|
||||
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.lib.TBMCCoreAPI;
|
||||
import buttondevteam.lib.chat.Command2Sender;
|
||||
import buttondevteam.lib.chat.ICommand2;
|
||||
import lombok.val;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Method;
|
||||
import buttondevteam.core.MainPlugin
|
||||
import buttondevteam.lib.TBMCCoreAPI
|
||||
import buttondevteam.lib.chat.Command2Sender
|
||||
import buttondevteam.lib.chat.ICommand2
|
||||
import org.bukkit.configuration.ConfigurationSection
|
||||
import org.bukkit.configuration.file.YamlConfiguration
|
||||
import java.io.IOException
|
||||
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.
|
||||
* Only used when registering commands.
|
||||
*/
|
||||
public class CommandArgumentHelpManager<TC extends ICommand2<TP>, TP extends Command2Sender> {
|
||||
private ConfigurationSection commandConfig;
|
||||
class CommandArgumentHelpManager<TC : ICommand2<TP>, TP : Command2Sender>(command: TC) {
|
||||
private var commandConfig: ConfigurationSection? = null
|
||||
|
||||
/**
|
||||
* Read the yaml file for the given command class.
|
||||
*
|
||||
* @param command The command object to use
|
||||
*/
|
||||
public CommandArgumentHelpManager(TC command) {
|
||||
val commandClass = command.getClass();
|
||||
// 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
|
||||
try (val str = commandClass.getResourceAsStream("/commands.yml")) {
|
||||
if (str == null) {
|
||||
TBMCCoreAPI.SendException("Error while getting command data!", new Exception("Resource not found!"), MainPlugin.Instance);
|
||||
return;
|
||||
}
|
||||
val config = YamlConfiguration.loadConfiguration(new InputStreamReader(str));
|
||||
commandConfig = config.getConfigurationSection(commandClass.getCanonicalName().replace('$', '.'));
|
||||
if (commandConfig == null) {
|
||||
MainPlugin.Instance.getLogger().warning("Failed to get command data for " + commandClass + "! Make sure to use 'clean install' when building the project.");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
TBMCCoreAPI.SendException("Error while getting command data!", e, MainPlugin.Instance);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Read the yaml file for the given command class.
|
||||
*
|
||||
* @param command The command object to use
|
||||
*/
|
||||
init {
|
||||
val commandClass = command.javaClass
|
||||
// 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
|
||||
try {
|
||||
commandClass.getResourceAsStream("/commands.yml").use { str ->
|
||||
if (str == null) {
|
||||
TBMCCoreAPI.SendException(
|
||||
"Error while getting command data!",
|
||||
Exception("Resource not found!"),
|
||||
MainPlugin.Instance
|
||||
)
|
||||
return@use
|
||||
}
|
||||
val config = YamlConfiguration.loadConfiguration(InputStreamReader(str))
|
||||
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.
|
||||
*
|
||||
* @param method The subcommand method
|
||||
* @return The parameter part of the usage string for the command
|
||||
*/
|
||||
public String getParameterHelpForMethod(Method method) {
|
||||
val cs = commandConfig.getConfigurationSection(method.getName());
|
||||
if (cs == null) {
|
||||
MainPlugin.Instance.getLogger().warning("Failed to get command data for " + method + "! Make sure to use 'clean install' when building the project.");
|
||||
return null;
|
||||
}
|
||||
val mname = cs.getString("method");
|
||||
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
|
||||
if (i != -1 && method.getName().equals(mname.substring(0, i)) && params != null) {
|
||||
return params;
|
||||
} else
|
||||
TBMCCoreAPI.SendException("Error while getting command data for " + method + "!", new Exception("Method '" + method + "' != " + mname + " or params is " + params), MainPlugin.Instance);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns a parameter help string for the given subcommand method by reading it from the plugin.
|
||||
*
|
||||
* @param method The subcommand method
|
||||
* @return The parameter part of the usage string for the command
|
||||
*/
|
||||
fun getParameterHelpForMethod(method: Method): String? {
|
||||
val cs = commandConfig?.getConfigurationSection(method.name)
|
||||
if (cs == null) {
|
||||
MainPlugin.Instance.logger.warning("Failed to get command data for $method! Make sure to use 'clean install' when building the project.")
|
||||
return null
|
||||
}
|
||||
val mname = cs.getString("method")
|
||||
val params = cs.getString("params")
|
||||
val i =
|
||||
mname.indexOf('(') //Check only the name - the whole method is still stored for backwards compatibility and in case it may be useful
|
||||
if (i != -1 && method.name == mname.substring(0, i) && params != null) {
|
||||
return params
|
||||
} else TBMCCoreAPI.SendException(
|
||||
"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