Convert some code to Kotlin

Why not
Lombok didn't work and I had other frustations with Java streams
Removed some unnecessary tab complete code
This commit is contained in:
Norbi Peti 2023-02-20 22:31:14 +01:00
parent 9a859de583
commit 0bf1f9789b
9 changed files with 1143 additions and 1250 deletions

View file

@ -35,11 +35,11 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version> <version>3.8.1</version>
<configuration> <configuration>
<compilerArgument>-proc:none</compilerArgument> <compilerArgument>-proc:none</compilerArgument>
<source>8</source> <source>17</source>
<target>8</target> <target>17</target>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>

View file

@ -27,6 +27,37 @@
</resources> </resources>
<finalName>Chroma-Core</finalName> <finalName>Chroma-Core</finalName>
<plugins> <plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/main/kotlin</sourceDir>
<sourceDir>${project.basedir}/src/main/java</sourceDir>
</sourceDirs>
</configuration>
</execution>
<execution>
<id>test-compile</id>
<goals>
<goal>test-compile</goal>
</goals>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/test/kotlin</sourceDir>
<sourceDir>${project.basedir}/src/test/java</sourceDir>
</sourceDirs>
</configuration>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
@ -200,6 +231,11 @@
<artifactId>javatuples</artifactId> <artifactId>javatuples</artifactId>
<version>1.2</version> <version>1.2</version>
</dependency> </dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies> </dependencies>
<organization> <organization>
<name>TBMCPlugins</name> <name>TBMCPlugins</name>
@ -217,6 +253,7 @@
<github.global.server>github</github.global.server> <github.global.server>github</github.global.server>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<noprefix.version>1.0.1</noprefix.version> <noprefix.version>1.0.1</noprefix.version>
<kotlin.version>1.8.10</kotlin.version>
</properties> </properties>
<scm> <scm>
<url>https://github.com/TBMCPlugins/mvn-repo</url> <url>https://github.com/TBMCPlugins/mvn-repo</url>

View file

@ -1,150 +1,135 @@
package buttondevteam.lib.architecture; package buttondevteam.lib.architecture
import buttondevteam.buttonproc.HasConfig; import buttondevteam.buttonproc.HasConfig
import buttondevteam.core.ComponentManager; import buttondevteam.core.ComponentManager
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI
import buttondevteam.lib.chat.Command2MC; import buttondevteam.lib.architecture.Component.Companion.updateConfig
import buttondevteam.lib.chat.ICommand2MC; import buttondevteam.lib.chat.Command2MC
import lombok.AccessLevel; import buttondevteam.lib.chat.Command2MC.registerCommand
import lombok.Getter; import buttondevteam.lib.chat.Command2MC.unregisterCommands
import org.bukkit.configuration.InvalidConfigurationException; import buttondevteam.lib.chat.ICommand2MC
import org.bukkit.configuration.file.FileConfiguration; import lombok.AccessLevel
import org.bukkit.configuration.file.YamlConfiguration; import lombok.Getter
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.configuration.InvalidConfigurationException
import org.bukkit.configuration.file.FileConfiguration
import java.io.File; import org.bukkit.configuration.file.YamlConfiguration
import java.io.IOException; import org.bukkit.plugin.java.JavaPlugin
import java.lang.annotation.ElementType; import java.io.File
import java.lang.annotation.Retention; import java.io.IOException
import java.lang.annotation.RetentionPolicy; import java.util.*
import java.lang.annotation.Target; import java.util.function.Consumer
import java.util.Arrays; import java.util.function.Function
import java.util.Optional;
import java.util.Stack;
@HasConfig(global = true) @HasConfig(global = true)
public abstract class ButtonPlugin extends JavaPlugin { abstract class ButtonPlugin : JavaPlugin() {
@Getter //Needs to be static as we don't know the plugin when a command is handled
private static final Command2MC command2MC = new Command2MC();
@Getter(AccessLevel.PROTECTED) @Getter(AccessLevel.PROTECTED)
private final IHaveConfig iConfig = new IHaveConfig(this::saveConfig); private val iConfig = IHaveConfig { saveConfig() }
private CommentedConfiguration yaml; private var yaml: CommentedConfiguration? = null
@Getter(AccessLevel.PROTECTED) @Getter(AccessLevel.PROTECTED)
private IHaveConfig data; //TODO private val data //TODO
: IHaveConfig? = null
/** /**
* Used to unregister components in the right order - and to reload configs * Used to unregister components in the right order - and to reload configs
*/ */
@Getter @Getter
private final Stack<Component<?>> componentStack = new Stack<>(); private val componentStack = Stack<Component<*>>()
protected abstract fun pluginEnable()
protected abstract void pluginEnable();
/** /**
* Called after the components are unregistered * Called after the components are unregistered
*/ */
protected abstract void pluginDisable(); protected abstract fun pluginDisable()
/** /**
* Called before the components are unregistered * Called before the components are unregistered
*/ */
protected void pluginPreDisable() { protected fun pluginPreDisable() {}
} override fun onEnable() {
@Override
public final void onEnable() {
if (!loadConfig()) { if (!loadConfig()) {
getLogger().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
} }
try { try {
pluginEnable(); pluginEnable()
} catch (Exception e) { } catch (e: Exception) {
TBMCCoreAPI.SendException("Error while enabling plugin " + getName() + "!", e, this); TBMCCoreAPI.SendException("Error while enabling plugin $name!", e, this)
} }
if (configGenAllowed(this)) //If it's not disabled (by default it's not) if (configGenAllowed(this)) //If it's not disabled (by default it's not)
IHaveConfig.pregenConfig(this, null); IHaveConfig.pregenConfig(this, null)
} }
private boolean loadConfig() { private fun loadConfig(): Boolean {
var config = getConfig(); val config = config ?: return false
if (config == null) var section = config.getConfigurationSection("global")
return false; if (section == null) section = config.createSection("global")
var section = config.getConfigurationSection("global"); iConfig.reset(section)
if (section == null) section = config.createSection("global"); return true
iConfig.reset(section);
return true;
} }
@Override override fun onDisable() {
public final void onDisable() {
try { try {
pluginPreDisable(); pluginPreDisable()
ComponentManager.unregComponents(this); ComponentManager.unregComponents(this)
pluginDisable(); pluginDisable()
if (ConfigData.saveNow(getConfig())) if (ConfigData.saveNow(config)) logger.info("Saved configuration changes.")
getLogger().info("Saved configuration changes."); ButtonPlugin.getCommand2MC().unregisterCommands(this)
getCommand2MC().unregisterCommands(this); } catch (e: Exception) {
} catch (Exception e) { TBMCCoreAPI.SendException("Error while disabling plugin $name!", e, this)
TBMCCoreAPI.SendException("Error while disabling plugin " + getName() + "!", e, this);
} }
} }
@Override override fun reloadConfig() {
public void reloadConfig() { tryReloadConfig()
tryReloadConfig();
} }
public boolean tryReloadConfig() { fun tryReloadConfig(): Boolean {
if (!justReload()) return false; if (!justReload()) return false
loadConfig(); loadConfig()
componentStack.forEach(c -> Component.updateConfig(this, c)); componentStack.forEach(Consumer { c: Component<*>? -> updateConfig(this, c!!) })
return true; return true
} }
public boolean justReload() { fun justReload(): Boolean {
if (yaml != null && ConfigData.saveNow(getConfig())) { if (yaml != null && ConfigData.saveNow(config)) {
getLogger().warning("Saved pending configuration changes to the file, didn't reload. Apply your changes again."); logger.warning("Saved pending configuration changes to the file, didn't reload. Apply your changes again.")
return false; return false
} }
var file = new File(getDataFolder(), "config.yml"); val file = File(dataFolder, "config.yml")
var yaml = new CommentedConfiguration(file); val yaml = CommentedConfiguration(file)
if (file.exists()) { if (file.exists()) {
try { try {
yaml.load(file); yaml.load(file)
} catch (IOException | InvalidConfigurationException e) { } catch (e: IOException) {
getLogger().warning("Failed to load config! Check for syntax errors."); logger.warning("Failed to load config! Check for syntax errors.")
e.printStackTrace(); e.printStackTrace()
return false; return false
} catch (e: InvalidConfigurationException) {
logger.warning("Failed to load config! Check for syntax errors.")
e.printStackTrace()
return false
} }
} }
this.yaml = yaml; this.yaml = yaml
var res = getTextResource("configHelp.yml"); val res = getTextResource("configHelp.yml") ?: return true
if (res == null) val yc = YamlConfiguration.loadConfiguration(res)
return true; for ((key, value) in yc.getValues(true)) if (value is String) yaml.addComment(key.replace(".generalDescriptionInsteadOfAConfig", ""),
var yc = YamlConfiguration.loadConfiguration(res); *Arrays.stream<String>(value.split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray())
for (var kv : yc.getValues(true).entrySet()) .map<String> { str: String -> "# " + str.trim { it <= ' ' } }.toArray<String> { _Dummy_.__Array__() })
if (kv.getValue() instanceof String) return true
yaml.addComment(kv.getKey().replace(".generalDescriptionInsteadOfAConfig", ""),
Arrays.stream(((String) kv.getValue()).split("\n"))
.map(str -> "# " + str.trim()).toArray(String[]::new));
return true;
} }
@Override override fun getConfig(): FileConfiguration {
public FileConfiguration getConfig() { if (yaml == null) justReload()
if (yaml == null) return if (yaml == null) YamlConfiguration() else yaml //Return a temporary instance
justReload();
if (yaml == null) return new YamlConfiguration(); //Return a temporary instance
return yaml;
} }
@Override override fun saveConfig() {
public void saveConfig() {
try { try {
if (yaml != null) if (yaml != null) yaml!!.save()
yaml.save(); } catch (e: Exception) {
} catch (Exception e) { TBMCCoreAPI.SendException("Failed to save config", e, this)
TBMCCoreAPI.SendException("Failed to save config", e, this);
} }
} }
@ -153,19 +138,21 @@ public abstract class ButtonPlugin extends JavaPlugin {
* *
* @param command The command to register * @param command The command to register
*/ */
protected void registerCommand(ICommand2MC command) { fun registerCommand(command: ICommand2MC) {
command.registerToPlugin(this); command.registerToPlugin(this)
getCommand2MC().registerCommand(command); ButtonPlugin.getCommand2MC().registerCommand(command)
} }
@Retention(RetentionPolicy.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
@Target(ElementType.TYPE) @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS)
public @interface ConfigOpts { annotation class ConfigOpts(val disableConfigGen: Boolean = false)
boolean disableConfigGen() default false; companion object {
} @Getter //Needs to be static as we don't know the plugin when a command is handled
public static boolean configGenAllowed(Object obj) { private val command2MC = Command2MC()
return !Optional.ofNullable(obj.getClass().getAnnotation(ConfigOpts.class)) fun configGenAllowed(obj: Any): Boolean {
.map(ConfigOpts::disableConfigGen).orElse(false); return !Optional.ofNullable(obj.javaClass.getAnnotation(ConfigOpts::class.java))
.map(Function<ConfigOpts, Boolean> { obj: ConfigOpts -> obj.disableConfigGen() }).orElse(false)
}
} }
} }

View file

@ -1,195 +1,57 @@
package buttondevteam.lib.architecture; package buttondevteam.lib.architecture
import buttondevteam.buttonproc.HasConfig; import buttondevteam.buttonproc.HasConfig
import buttondevteam.core.ComponentManager; import buttondevteam.core.ComponentManager
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI
import buttondevteam.lib.architecture.exceptions.UnregisteredComponentException; import buttondevteam.lib.architecture.exceptions.UnregisteredComponentException
import buttondevteam.lib.chat.ICommand2MC; import buttondevteam.lib.chat.ICommand2MC
import lombok.Getter; import lombok.Getter
import lombok.NonNull; import org.bukkit.configuration.ConfigurationSection
import lombok.val; import org.bukkit.event.Listener
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.plugin.java.JavaPlugin
import org.bukkit.event.Listener; import java.util.*
import org.bukkit.plugin.java.JavaPlugin; import java.util.function.Consumer
import java.util.function.Function
import java.util.Collections; import java.util.stream.Collectors
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.stream.Collectors;
/** /**
* Configuration is based on class name * Configuration is based on class name
*/ */
@HasConfig(global = false) //Used for obtaining javadoc @HasConfig(global = false) //Used for obtaining javadoc
public abstract class Component<TP extends JavaPlugin> {
@SuppressWarnings("rawtypes") private static HashMap<Class<? extends Component>, Component<? extends JavaPlugin>> components = new HashMap<>(); abstract class Component<TP : JavaPlugin?> {
@Getter
private var enabled = false
@Getter @Getter
private boolean enabled = false; private var plugin: TP = null
@Getter @Getter
@NonNull private val config = IHaveConfig(null)
private TP plugin;
private @Getter final IHaveConfig config = new IHaveConfig(null);
private @Getter IHaveConfig data; //TODO
public final ConfigData<Boolean> shouldBeEnabled = config.getData("enabled", @Getter
Optional.ofNullable(getClass().getAnnotation(ComponentMetadata.class)).map(ComponentMetadata::enabledByDefault).orElse(true)); private val data //TODO
: IHaveConfig? = null
/** @JvmField
* Registers a component checking it's dependencies and calling {@link #register(JavaPlugin)}.<br> val shouldBeEnabled = config.getData("enabled",
* Make sure to register the dependencies first.<br> Optional.ofNullable(javaClass.getAnnotation(ComponentMetadata::class.java)).map(Function<ComponentMetadata, Boolean> { obj: ComponentMetadata -> obj.enabledByDefault() }).orElse(true))
* The component will be enabled automatically, regardless of when it was registered.<br>
* <b>If not using {@link ButtonPlugin}, call {@link ComponentManager#unregComponents(ButtonPlugin)} on plugin disable.</b> fun log(message: String) {
* plugin!!.logger.info("[" + className + "] " + message)
* @param component The component to register
* @return Whether the component is registered successfully (it may have failed to enable)
*/
public static <T extends JavaPlugin> boolean registerComponent(T plugin, Component<T> component) {
return registerUnregisterComponent(plugin, component, true);
} }
/** fun logWarn(message: String) {
* Unregisters a component by calling {@link #unregister(JavaPlugin)}.<br> plugin!!.logger.warning("[" + className + "] " + message)
* Make sure to unregister the dependencies last.<br>
* <b>Components will be unregistered in opposite order of registering by default by {@link ButtonPlugin} or {@link ComponentManager#unregComponents(ButtonPlugin)}.</b>
*
* @param component The component to unregister
* @return Whether the component is unregistered successfully (it also got disabled)
*/
public static <T extends JavaPlugin> boolean unregisterComponent(T plugin, Component<T> component) {
return registerUnregisterComponent(plugin, component, false);
}
public static <T extends JavaPlugin> boolean registerUnregisterComponent(T plugin, Component<T> component, boolean register) {
try {
val metaAnn = component.getClass().getAnnotation(ComponentMetadata.class);
if (metaAnn != null) {
@SuppressWarnings("rawtypes") Class<? extends Component>[] dependencies = metaAnn.depends();
for (val dep : dependencies) { //TODO: Support dependencies at enable/disable as well
if (!components.containsKey(dep)) {
plugin.getLogger().warning("Failed to " + (register ? "" : "un") + "register component " + component.getClassName() + " as a required dependency is missing/disabled: " + dep.getSimpleName());
return false;
}
}
}
if (register) {
if (components.containsKey(component.getClass())) {
TBMCCoreAPI.SendException("Failed to register component " + component.getClassName(), new IllegalArgumentException("The component is already registered!"), plugin);
return false;
}
component.plugin = plugin;
component.config.setSaveAction(plugin::saveConfig);
updateConfig(plugin, component);
component.register(plugin);
components.put(component.getClass(), component);
if (plugin instanceof ButtonPlugin)
((ButtonPlugin) plugin).getComponentStack().push(component);
if (ComponentManager.areComponentsEnabled() && component.shouldBeEnabled.get()) {
try { //Enable components registered after the previous ones getting enabled
setComponentEnabled(component, true);
return true;
} catch (Exception | NoClassDefFoundError e) {
TBMCCoreAPI.SendException("Failed to enable component " + component.getClassName() + "!", e, component);
return true;
}
}
} else {
if (!components.containsKey(component.getClass()))
return true; //Already unregistered
if (component.enabled) {
try {
setComponentEnabled(component, false);
} catch (Exception | NoClassDefFoundError e) {
TBMCCoreAPI.SendException("Failed to disable component " + component.getClassName() + "!", e, component);
return false; //If failed to disable, won't unregister either
}
}
component.unregister(plugin);
components.remove(component.getClass());
}
return true;
} catch (Exception e) {
TBMCCoreAPI.SendException("Failed to " + (register ? "" : "un") + "register component " + component.getClassName() + "!", e, plugin);
return false;
}
}
/**
* Enables or disables the given component. If the component fails to enable, it will be disabled.
*
* @param component The component to register
* @param enabled Whether it's enabled or not
*/
public static void setComponentEnabled(Component<?> component, boolean enabled) throws UnregisteredComponentException {
if (!components.containsKey(component.getClass()))
throw new UnregisteredComponentException(component);
if (component.enabled == enabled) return; //Don't do anything
if (component.enabled = enabled) {
try {
updateConfig(component.getPlugin(), component);
component.enable();
if (ButtonPlugin.configGenAllowed(component)) {
IHaveConfig.pregenConfig(component, null);
}
} catch (Exception e) {
try { //Automatically disable components that fail to enable properly
setComponentEnabled(component, false);
throw e;
} catch (Exception ex) {
Throwable t = ex;
for (var th = t; th != null; th = th.getCause())
t = th; //Set if not null
if (t != e)
t.initCause(e);
throw ex;
}
}
} else {
component.disable();
ButtonPlugin.getCommand2MC().unregisterCommands(component);
}
}
public static void updateConfig(JavaPlugin plugin, Component<?> component) {
if (plugin.getConfig() != null) { //Production
var compconf = plugin.getConfig().getConfigurationSection("components");
if (compconf == null) compconf = plugin.getConfig().createSection("components");
var configSect = compconf.getConfigurationSection(component.getClassName());
if (configSect == null)
configSect = compconf.createSection(component.getClassName());
component.config.reset(configSect);
} //Testing: it's already set
}
/**
* Returns the currently registered components<br>
*
* @return The currently registered components
*/
@SuppressWarnings("rawtypes")
public static Map<Class<? extends Component>, Component<? extends JavaPlugin>> getComponents() {
return Collections.unmodifiableMap(components);
}
public void log(String message) {
plugin.getLogger().info("[" + getClassName() + "] " + message);
}
public void logWarn(String message) {
plugin.getLogger().warning("[" + getClassName() + "] " + message);
} }
/** /**
* Registers the module, when called by the JavaPlugin class. * Registers the module, when called by the JavaPlugin class.
* This gets fired when the plugin is enabled. Use {@link #enable()} to register commands and such. * This gets fired when the plugin is enabled. Use [.enable] to register commands and such.
* *
* @param plugin Plugin object * @param plugin Plugin object
*/ */
@SuppressWarnings({"unused"}) protected open fun register(plugin: JavaPlugin?) {}
protected void register(JavaPlugin plugin) {
}
/** /**
* Unregisters the module, when called by the JavaPlugin class. * Unregisters the module, when called by the JavaPlugin class.
@ -198,35 +60,32 @@ public abstract class Component<TP extends JavaPlugin> {
* *
* @param plugin Plugin object * @param plugin Plugin object
*/ */
@SuppressWarnings({"unused"}) protected open fun unregister(plugin: JavaPlugin?) {}
protected void unregister(JavaPlugin plugin) {
}
/** /**
* Enables the module, when called by the JavaPlugin class. Call * Enables the module, when called by the JavaPlugin class. Call
* registerCommand() and registerListener() within this method.<br> * registerCommand() and registerListener() within this method.<br></br>
* To access the plugin, use {@link #getPlugin()}. * To access the plugin, use [.getPlugin].
*/ */
protected abstract void enable(); protected abstract fun enable()
/** /**
* Disables the module, when called by the JavaPlugin class. Do * Disables the module, when called by the JavaPlugin class. Do
* any cleanups needed within this method. * any cleanups needed within this method.
* To access the plugin, use {@link #getPlugin()}. * To access the plugin, use [.getPlugin].
*/ */
protected abstract void disable(); protected abstract fun disable()
/** /**
* Registers a command to the component. Make sure to use {@link buttondevteam.lib.chat.CommandClass} and {@link buttondevteam.lib.chat.Command2.Subcommand}. * Registers a command to the component. Make sure to use [buttondevteam.lib.chat.CommandClass] and [buttondevteam.lib.chat.Command2.Subcommand].
* You don't need to register the command in plugin.yml. * You don't need to register the command in plugin.yml.
* *
* @param command Custom coded command class * @param command Custom coded command class
*/ */
protected final void registerCommand(ICommand2MC command) { fun registerCommand(command: ICommand2MC) {
if (plugin instanceof ButtonPlugin) if (plugin is ButtonPlugin) command.registerToPlugin(plugin as ButtonPlugin)
command.registerToPlugin((ButtonPlugin) plugin); command.registerToComponent(this)
command.registerToComponent(this); ButtonPlugin.getCommand2MC().registerCommand(command)
ButtonPlugin.getCommand2MC().registerCommand(command);
} }
/** /**
@ -235,9 +94,9 @@ public abstract class Component<TP extends JavaPlugin> {
* @param listener The event listener to register * @param listener The event listener to register
* @return The provided listener * @return The provided listener
*/ */
protected final Listener registerListener(Listener listener) { protected fun registerListener(listener: Listener): Listener {
TBMCCoreAPI.RegisterEventsForExceptions(listener, plugin); TBMCCoreAPI.RegisterEventsForExceptions(listener, plugin)
return listener; return listener
} }
/** /**
@ -247,28 +106,176 @@ public abstract class Component<TP extends JavaPlugin> {
* @param defaultProvider A mapping between config paths and config generators * @param defaultProvider A mapping between config paths and config generators
* @return A map containing configs * @return A map containing configs
*/ */
protected Map<String, IHaveConfig> getConfigMap(String key, Map<String, Consumer<IHaveConfig>> defaultProvider) { fun getConfigMap(key: String?, defaultProvider: Map<String, Consumer<IHaveConfig?>>): Map<String, IHaveConfig> {
val c = getConfig().getConfig(); val c: ConfigurationSection = getConfig().getConfig()
var cs = c.getConfigurationSection(key); var cs = c.getConfigurationSection(key)
if (cs == null) cs = c.createSection(key); if (cs == null) cs = c.createSection(key)
val res = cs.getValues(false).entrySet().stream().filter(e -> e.getValue() instanceof ConfigurationSection) val res = cs!!.getValues(false).entries.stream().filter { (_, value): Map.Entry<String?, Any?> -> value is ConfigurationSection }
.collect(Collectors.toMap(Map.Entry::getKey, kv -> { .collect(Collectors.toMap<Map.Entry<String?, Any?>, String, IHaveConfig>(Function<Map.Entry<String?, Any?>, String> { (key1, value) -> java.util.Map.Entry.key }, Function<Map.Entry<String?, Any?>, IHaveConfig> { (_, value): Map.Entry<String?, Any?> ->
var conf = new IHaveConfig(getPlugin()::saveConfig); val conf = IHaveConfig { getPlugin().saveConfig() }
conf.reset((ConfigurationSection) kv.getValue()); conf.reset(value as ConfigurationSection?)
return conf; conf
})); }))
if (res.size() == 0) { if (res.size == 0) {
for (val entry : defaultProvider.entrySet()) { for ((key1, value) in defaultProvider) {
val conf = new IHaveConfig(getPlugin()::saveConfig); val conf = IHaveConfig { getPlugin().saveConfig() }
conf.reset(cs.createSection(entry.getKey())); conf.reset(cs.createSection(key1))
entry.getValue().accept(conf); value.accept(conf)
res.put(entry.getKey(), conf); res[key1] = conf
} }
} }
return res; return res
} }
private String getClassName() { private val className: String
return getClass().getSimpleName(); private get() = javaClass.simpleName
companion object {
private val components = HashMap<Class<out Component<*>>, Component<out JavaPlugin>>()
/**
* Registers a component checking it's dependencies and calling [.register].<br></br>
* Make sure to register the dependencies first.<br></br>
* The component will be enabled automatically, regardless of when it was registered.<br></br>
* **If not using [ButtonPlugin], call [ComponentManager.unregComponents] on plugin disable.**
*
* @param component The component to register
* @return Whether the component is registered successfully (it may have failed to enable)
*/
@JvmStatic
fun <T : JavaPlugin?> registerComponent(plugin: T, component: Component<T>): Boolean {
return registerUnregisterComponent(plugin, component, true)
}
/**
* Unregisters a component by calling [.unregister].<br></br>
* Make sure to unregister the dependencies last.<br></br>
* **Components will be unregistered in opposite order of registering by default by [ButtonPlugin] or [ComponentManager.unregComponents].**
*
* @param component The component to unregister
* @return Whether the component is unregistered successfully (it also got disabled)
*/
@JvmStatic
fun <T : JavaPlugin?> unregisterComponent(plugin: T, component: Component<T>): Boolean {
return registerUnregisterComponent(plugin, component, false)
}
fun <T : JavaPlugin?> registerUnregisterComponent(plugin: T, component: Component<T>, register: Boolean): Boolean {
return try {
val metaAnn = component.javaClass.getAnnotation(ComponentMetadata::class.java)
if (metaAnn != null) {
val dependencies: Array<Class<out Component<*>>> = metaAnn.depends()
for (dep in dependencies) { //TODO: Support dependencies at enable/disable as well
if (!components.containsKey(dep)) {
plugin!!.logger.warning("Failed to " + (if (register) "" else "un") + "register component " + component.className + " as a required dependency is missing/disabled: " + dep.simpleName)
return false
}
}
}
if (register) {
if (components.containsKey(component.javaClass)) {
TBMCCoreAPI.SendException("Failed to register component " + component.className, IllegalArgumentException("The component is already registered!"), plugin)
return false
}
component.plugin = plugin
component.config.saveAction = Runnable { plugin!!.saveConfig() }
updateConfig(plugin, component)
component.register(plugin)
components[component.javaClass] = component
if (plugin is ButtonPlugin) (plugin as ButtonPlugin).componentStack.push(component)
if (ComponentManager.areComponentsEnabled() && component.shouldBeEnabled.get()) {
return try { //Enable components registered after the previous ones getting enabled
setComponentEnabled(component, true)
true
} catch (e: Exception) {
TBMCCoreAPI.SendException("Failed to enable component " + component.className + "!", e, component)
true
} catch (e: NoClassDefFoundError) {
TBMCCoreAPI.SendException("Failed to enable component " + component.className + "!", e, component)
true
}
}
} else {
if (!components.containsKey(component.javaClass)) return true //Already unregistered
if (component.enabled) {
try {
setComponentEnabled(component, false)
} catch (e: Exception) {
TBMCCoreAPI.SendException("Failed to disable component " + component.className + "!", e, component)
return false //If failed to disable, won't unregister either
} catch (e: NoClassDefFoundError) {
TBMCCoreAPI.SendException("Failed to disable component " + component.className + "!", e, component)
return false
}
}
component.unregister(plugin)
components.remove(component.javaClass)
}
true
} catch (e: Exception) {
TBMCCoreAPI.SendException("Failed to " + (if (register) "" else "un") + "register component " + component.className + "!", e, plugin)
false
}
}
/**
* Enables or disables the given component. If the component fails to enable, it will be disabled.
*
* @param component The component to register
* @param enabled Whether it's enabled or not
*/
@JvmStatic
@Throws(UnregisteredComponentException::class)
fun setComponentEnabled(component: Component<*>, enabled: Boolean) {
if (!components.containsKey(component.javaClass)) throw UnregisteredComponentException(component)
if (component.enabled == enabled) return //Don't do anything
if (enabled.also { component.enabled = it }) {
try {
updateConfig(component.getPlugin(), component)
component.enable()
if (ButtonPlugin.configGenAllowed(component)) {
IHaveConfig.pregenConfig(component, null)
}
} catch (e: Exception) {
try { //Automatically disable components that fail to enable properly
setComponentEnabled(component, false)
throw e
} catch (ex: Exception) {
var t: Throwable = ex
var th: Throwable? = t
while (th != null) {
t = th //Set if not null
th = th.cause
}
if (t !== e) t.initCause(e)
throw ex
}
}
} else {
component.disable()
ButtonPlugin.getCommand2MC().unregisterCommands(component)
}
}
@JvmStatic
fun updateConfig(plugin: JavaPlugin, component: Component<*>) {
if (plugin.config != null) { //Production
var compconf = plugin.config.getConfigurationSection("components")
if (compconf == null) compconf = plugin.config.createSection("components")
var configSect = compconf!!.getConfigurationSection(component.className)
if (configSect == null) configSect = compconf.createSection(component.className)
component.config.reset(configSect)
} //Testing: it's already set
}
/**
* Returns the currently registered components<br></br>
*
* @return The currently registered components
*/
@JvmStatic
fun getComponents(): Map<Class<out Component<*>>, Component<out JavaPlugin>> {
return Collections.unmodifiableMap(components)
}
} }
} }

View file

@ -1,103 +1,83 @@
package buttondevteam.lib.chat; package buttondevteam.lib.chat
import buttondevteam.core.MainPlugin; import buttondevteam.core.MainPlugin
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI
import buttondevteam.lib.chat.commands.CommandArgument; import buttondevteam.lib.chat.Command2.Subcommand
import buttondevteam.lib.chat.commands.CommandArgumentHelpManager; import buttondevteam.lib.chat.commands.*
import buttondevteam.lib.chat.commands.NumberArg; import buttondevteam.lib.player.ChromaGamerBase
import buttondevteam.lib.chat.commands.SubcommandData; import com.mojang.brigadier.CommandDispatcher
import buttondevteam.lib.player.ChromaGamerBase; import com.mojang.brigadier.arguments.*
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.builder.ArgumentBuilder
import com.mojang.brigadier.arguments.*; import com.mojang.brigadier.context.CommandContext
import com.mojang.brigadier.builder.ArgumentBuilder; import com.mojang.brigadier.context.ParsedCommandNode
import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.tree.CommandNode
import com.mojang.brigadier.tree.CommandNode; import com.mojang.brigadier.tree.LiteralCommandNode
import com.mojang.brigadier.tree.LiteralCommandNode; import lombok.RequiredArgsConstructor
import lombok.RequiredArgsConstructor; import org.bukkit.Bukkit
import lombok.val; import org.javatuples.Pair
import org.bukkit.Bukkit; import org.javatuples.Triplet
import org.javatuples.Pair; import java.lang.reflect.Method
import org.javatuples.Triplet; import java.util.function.Function
import org.jetbrains.annotations.NotNull; import java.util.function.Predicate
import java.util.function.Supplier
import java.lang.annotation.ElementType; import java.util.stream.Collectors
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/** /**
* The method name is the subcommand, use underlines (_) to add further subcommands. * The method name is the subcommand, use underlines (_) to add further subcommands.
* The args may be null if the conversion failed and it's optional. * The args may be null if the conversion failed and it's optional.
*/ */
@RequiredArgsConstructor @RequiredArgsConstructor
public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Sender> { abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender> {
/** /**
* Parameters annotated with this receive all the remaining arguments * Parameters annotated with this receive all the remaining arguments
*/ */
@Target(ElementType.PARAMETER) @Target(AnnotationTarget.VALUE_PARAMETER)
@Retention(RetentionPolicy.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
public @interface TextArg { annotation class TextArg
}
/** /**
* Methods annotated with this will be recognised as subcommands * Methods annotated with this will be recognised as subcommands
*/ */
@Target(ElementType.METHOD) @Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(RetentionPolicy.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
public @interface Subcommand { annotation class Subcommand(
/**
* Allowed for OPs only by default
*/
String MOD_GROUP = "mod";
/** /**
* Help text to show players. A usage message will be also shown below it. * Help text to show players. A usage message will be also shown below it.
*/ */
String[] helpText() default {}; val helpText: Array<String> = [],
/** /**
* The main permission which allows using this command (individual access can be still revoked with "chroma.command.X"). * The main permission which allows using this command (individual access can be still revoked with "chroma.command.X").
* Used to be "tbmc.admin". The {@link #MOD_GROUP} is provided to use with this. * Used to be "tbmc.admin". The [.MOD_GROUP] is provided to use with this.
*/ */
String permGroup() default ""; val permGroup: String = "", val aliases: Array<String> = []) {
companion object {
String[] aliases() default {}; /**
* Allowed for OPs only by default
*/
const val MOD_GROUP = "mod"
}
} }
@Target(ElementType.PARAMETER) @Target(AnnotationTarget.VALUE_PARAMETER)
@Retention(RetentionPolicy.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
public @interface OptionalArg { annotation class OptionalArg
}
@RequiredArgsConstructor protected class ParamConverter<T>(val converter: Function<String, T>, val errormsg: String, val allSupplier: Supplier<Iterable<String>>)
protected static class ParamConverter<T> {
public final Function<String, T> converter;
public final String errormsg;
public final Supplier<Iterable<String>> allSupplier;
}
protected final HashMap<Class<?>, ParamConverter<?>> paramConverters = new HashMap<>(); protected val paramConverters = HashMap<Class<*>, ParamConverter<*>>()
private final ArrayList<String> commandHelp = new ArrayList<>(); //Mainly needed by Discord private val commandHelp = ArrayList<String>() //Mainly needed by Discord
private final CommandDispatcher<TP> dispatcher = new CommandDispatcher<>(); private val dispatcher = CommandDispatcher<TP>()
/** /**
* The first character in the command line that shows that it's a command. * The first character in the command line that shows that it's a command.
*/ */
private final char commandChar; private val commandChar = 0.toChar()
/** /**
* Whether the command's actual code has to be run on the primary thread. * Whether the command's actual code has to be run on the primary thread.
*/ */
private final boolean runOnPrimaryThread; private val runOnPrimaryThread = false
/** /**
* Adds a param converter that obtains a specific object from a string parameter. * Adds a param converter that obtains a specific object from a string parameter.
@ -107,64 +87,56 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
* @param cl The class of the result object * @param cl The class of the result object
* @param converter The converter to use * @param converter The converter to use
* @param allSupplier The supplier of all possible values (ideally) * @param allSupplier The supplier of all possible values (ideally)
*/ </T> */
public <T> void addParamConverter(Class<T> cl, Function<String, T> converter, String errormsg, open fun <T> addParamConverter(cl: Class<T>, converter: Function<String, T>, errormsg: String,
Supplier<Iterable<String>> allSupplier) { allSupplier: Supplier<Iterable<String>>) {
paramConverters.put(cl, new ParamConverter<>(converter, errormsg, allSupplier)); paramConverters[cl] = ParamConverter<T>(converter, errormsg, allSupplier)
} }
public boolean handleCommand(TP sender, String commandline) { open fun handleCommand(sender: TP, commandline: String): Boolean {
var results = dispatcher.parse(commandline, sender); val results = dispatcher.parse(commandline, sender)
if (results.getReader().canRead()) { if (results.reader.canRead()) {
return false; // Unknown command return false // Unknown command
} }
Bukkit.getScheduler().runTaskAsynchronously(MainPlugin.Instance, () -> {
try {
dispatcher.execute(results);
} catch (CommandSyntaxException e) {
sender.sendMessage(e.getMessage());
} catch (Exception e) {
TBMCCoreAPI.SendException("Command execution failed for sender " + sender.getName() + "(" + sender.getClass().getCanonicalName() + ") and message " + commandline, e, MainPlugin.Instance);
}
});
return true; //We found a method
}
//Needed because permission checking may load the (perhaps offline) sender's file which is disallowed on the main thread //Needed because permission checking may load the (perhaps offline) sender's file which is disallowed on the main thread
Bukkit.getScheduler().runTaskAsynchronously(MainPlugin.Instance) {
try {
dispatcher.execute(results)
} catch (e: CommandSyntaxException) {
sender.sendMessage(e.message)
} catch (e: Exception) {
TBMCCoreAPI.SendException("Command execution failed for sender " + sender.name + "(" + sender.javaClass.canonicalName + ") and message " + commandline, e, MainPlugin.Instance)
}
}
return true //We found a method
}
//TODO: Add to the help //TODO: Add to the help
private fun processSenderType(sender: TP, sd: SubcommandData<TC, TP>, params: ArrayList<Any>): Boolean {
private boolean processSenderType(TP sender, SubcommandData<TC, TP> sd, ArrayList<Object> params) { val sendertype = sd.senderType
val sendertype = sd.senderType; val cg: ChromaGamerBase
final ChromaGamerBase cg; if (sendertype.isAssignableFrom(sender.javaClass)) params.add(sender) //The command either expects a CommandSender or it is a Player, or some other expected type
if (sendertype.isAssignableFrom(sender.getClass())) else if (sender is Command2MCSender // TODO: This is Minecraft only
params.add(sender); //The command either expects a CommandSender or it is a Player, or some other expected type && sendertype.isAssignableFrom((sender as Command2MCSender).sender.javaClass))
else if (sender instanceof Command2MCSender // TODO: This is Minecraft only params.add((sender as Command2MCSender).sender)
&& sendertype.isAssignableFrom(((Command2MCSender) sender).getSender().getClass())) else if ((ChromaGamerBase::class.java.isAssignableFrom(sendertype) && sender is Command2MCSender)
params.add(((Command2MCSender) sender).getSender()); && ChromaGamerBase.getFromSender((sender as Command2MCSender).sender).also { cg = it } != null && cg.javaClass == sendertype) //The command expects a user of our system
else if (ChromaGamerBase.class.isAssignableFrom(sendertype) params.add(cg) else {
&& sender instanceof Command2MCSender val type = sendertype.simpleName.fold("") { s, ch -> s + if (ch.isUpperCase()) " " + ch.lowercase() else ch }
&& (cg = ChromaGamerBase.getFromSender(((Command2MCSender) sender).getSender())) != null sender.sendMessage("§cYou need to be a $type to use this command.")
&& cg.getClass() == sendertype) //The command expects a user of our system sender.sendMessage(sd.getHelpText(sender)) //Send what the command is about, could be useful for commands like /member where some subcommands aren't player-only
params.add(cg); return true
else {
String type = sendertype.getSimpleName().chars().mapToObj(ch -> Character.isUpperCase(ch)
? " " + Character.toLowerCase(ch)
: ch + "").collect(Collectors.joining());
sender.sendMessage("§cYou need to be a " + type + " to use this command.");
sender.sendMessage(sd.getHelpText(sender)); //Send what the command is about, could be useful for commands like /member where some subcommands aren't player-only
return true;
} }
return false; return false
} }
/** /**
* Register a command in the command system. The way this command gets registered may change depending on the implementation. * Register a command in the command system. The way this command gets registered may change depending on the implementation.
* Always invoke {@link #registerCommandSuper(ICommand2)} when implementing this method. * Always invoke [.registerCommandSuper] when implementing this method.
* *
* @param command The command to register * @param command The command to register
*/ */
public abstract void registerCommand(TC command); abstract fun registerCommand(command: TC)
/** /**
* Registers a command in the Command2 system, so it can be looked up and executed. * Registers a command in the Command2 system, so it can be looked up and executed.
@ -172,23 +144,21 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
* @param command The command to register * @param command The command to register
* @return The Brigadier command node if you need it for something (like tab completion) * @return The Brigadier command node if you need it for something (like tab completion)
*/ */
protected LiteralCommandNode<TP> registerCommandSuper(TC command) { protected fun registerCommandSuper(command: TC): LiteralCommandNode<TP> {
LiteralCommandNode<TP> mainCommandNode = null; var mainCommandNode: LiteralCommandNode<TP>? = null
for (val meth : command.getClass().getMethods()) { for (meth in command.javaClass.getMethods()) {
val ann = meth.getAnnotation(Subcommand.class); val ann = meth.getAnnotation<Subcommand>(Subcommand::class.java) ?: continue
if (ann == null) continue; val methodPath = CommandUtils.getCommandPath(meth.name, ' ')
String methodPath = getCommandPath(meth.getName(), ' '); val result = registerNodeFromPath(command!!.commandPath + methodPath)
val result = registerNodeFromPath(command.getCommandPath() + methodPath); result.value0.addChild(getExecutableNode(meth, command, ann, result.value2, CommandArgumentHelpManager(command)))
result.getValue0().addChild(getExecutableNode(meth, command, ann, result.getValue2(), new CommandArgumentHelpManager<>(command))); if (mainCommandNode == null) mainCommandNode = result.value1 else if (result.value1!!.name != mainCommandNode.name) {
if (mainCommandNode == null) mainCommandNode = result.getValue1(); MainPlugin.Instance.logger.warning("Multiple commands are defined in the same class! This is not supported. Class: " + command.javaClass.simpleName)
else if (!result.getValue1().getName().equals(mainCommandNode.getName())) {
MainPlugin.Instance.getLogger().warning("Multiple commands are defined in the same class! This is not supported. Class: " + command.getClass().getSimpleName());
} }
} }
if (mainCommandNode == null) { if (mainCommandNode == null) {
throw new RuntimeException("There are no subcommands defined in the command class " + command.getClass().getSimpleName() + "!"); throw RuntimeException("There are no subcommands defined in the command class " + command.javaClass.getSimpleName() + "!")
} }
return mainCommandNode; return mainCommandNode
} }
/** /**
@ -199,21 +169,21 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
* @param path The command path * @param path The command path
* @return The executable node * @return The executable node
*/ */
private LiteralCommandNode<TP> getExecutableNode(Method method, TC command, Subcommand ann, String path, CommandArgumentHelpManager<TC, TP> argHelpManager) { private fun getExecutableNode(method: Method, command: TC, ann: Subcommand, path: String, argHelpManager: CommandArgumentHelpManager<TC, TP>): LiteralCommandNode<TP> {
val paramsAndSenderType = getCommandParametersAndSender(method, argHelpManager); // Param order is important val paramsAndSenderType = getCommandParametersAndSender(method, argHelpManager) // Param order is important
val params = paramsAndSenderType.getValue0(); val params = paramsAndSenderType.value0
val paramMap = new HashMap<String, CommandArgument>(); val paramMap = HashMap<String, CommandArgument?>()
for (val param : params) { for (param in params) {
paramMap.put(param.name, param); paramMap[param!!.name] = param
} }
val node = CoreCommandBuilder.<TP, TC>literal(path, params[0].type, paramMap, params, command) val node = CoreCommandBuilder.literal<TP, TC>(path, params[0]!!.type, paramMap, params, command)
.helps(command.getHelpText(method, ann)).permits(sender -> hasPermission(sender, command, method)) .helps(command!!.getHelpText(method, ann)).permits { sender: TP -> hasPermission(sender, command, method) }
.executes(this::executeCommand); .executes { context: CommandContext<TP> -> executeCommand(context) }
ArgumentBuilder<TP, ?> parent = node; var parent: ArgumentBuilder<TP, *> = node
for (val param : params) { // Register parameters in the right order for (param in params) { // Register parameters in the right order
parent.then(parent = CoreArgumentBuilder.argument(param.name, getArgumentType(param), param.optional)); parent.then(CoreArgumentBuilder.argument(param!!.name, getArgumentType(param), param.optional).also { parent = it })
} }
return node.build(); return node.build()
} }
/** /**
@ -223,19 +193,17 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
* @return The last no-op node that can be used to register the executable node, * @return The last no-op node that can be used to register the executable node,
* the main command node and the last part of the command path (that isn't registered yet) * the main command node and the last part of the command path (that isn't registered yet)
*/ */
private Triplet<CommandNode<TP>, LiteralCommandNode<TP>, String> registerNodeFromPath(String path) { private fun registerNodeFromPath(path: String): Triplet<CommandNode<TP>, LiteralCommandNode<TP>?, String> {
String[] split = path.split(" "); val split = path.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
CommandNode<TP> parent = dispatcher.getRoot(); var parent: CommandNode<TP> = dispatcher.root
LiteralCommandNode<TP> mainCommand = null; var mainCommand: LiteralCommandNode<TP>? = null
for (int i = 0; i < split.length - 1; i++) { for (i in 0 until split.size - 1) {
String part = split[i]; val part = split[i]
var child = parent.getChild(part); val child = parent.getChild(part)
if (child == null) if (child == null) parent.addChild(CoreCommandBuilder.literalNoOp<TP, TC>(part).executes { context: CommandContext<TP> -> executeHelpText(context) }.build().also { parent = it }) else parent = child
parent.addChild(parent = CoreCommandBuilder.<TP, TC>literalNoOp(part).executes(this::executeHelpText).build()); if (i == 0) mainCommand = parent as LiteralCommandNode<TP> // Has to be a literal, if not, well, error
else parent = child;
if (i == 0) mainCommand = (LiteralCommandNode<TP>) parent; // Has to be a literal, if not, well, error
} }
return new Triplet<>(parent, mainCommand, split[split.length - 1]); return Triplet(parent, mainCommand, split[split.size - 1])
} }
/** /**
@ -246,22 +214,21 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
* @return Parameter data objects and the sender type * @return Parameter data objects and the sender type
* @throws RuntimeException If there is no sender parameter declared in the method * @throws RuntimeException If there is no sender parameter declared in the method
*/ */
private Pair<CommandArgument[], Class<?>> getCommandParametersAndSender(Method method, CommandArgumentHelpManager<TC, TP> argHelpManager) { private fun getCommandParametersAndSender(method: Method, argHelpManager: CommandArgumentHelpManager<TC, TP>): Pair<Array<CommandArgument?>, Class<*>> {
val parameters = method.getParameters(); val parameters = method.parameters
if (parameters.length == 0) if (parameters.size == 0) throw RuntimeException("No sender parameter for method '$method'")
throw new RuntimeException("No sender parameter for method '" + method + "'"); val ret = arrayOfNulls<CommandArgument>(parameters.size)
val ret = new CommandArgument[parameters.length]; val usage = argHelpManager.getParameterHelpForMethod(method)
val usage = argHelpManager.getParameterHelpForMethod(method); val paramNames = usage?.split(" ".toRegex())?.dropLastWhile { it.isEmpty() }?.toTypedArray()
val paramNames = usage != null ? usage.split(" ") : null; for (i in 1 until parameters.size) {
for (int i = 1; i < parameters.length; i++) { val numAnn = parameters[i].getAnnotation(NumberArg::class.java)
val numAnn = parameters[i].getAnnotation(NumberArg.class); ret[i - 1] = CommandArgument(paramNames?.get(i) ?: "param$i", parameters[i].type,
ret[i - 1] = new CommandArgument(paramNames == null ? "param" + i : paramNames[i], parameters[i].getType(), parameters[i].isVarArgs || parameters[i].isAnnotationPresent(TextArg::class.java),
parameters[i].isVarArgs() || parameters[i].isAnnotationPresent(TextArg.class), if (numAnn == null) null else Pair(numAnn.lowerLimit(), numAnn.upperLimit()),
numAnn == null ? null : new Pair<>(numAnn.lowerLimit(), numAnn.upperLimit()), parameters[i].isAnnotationPresent(OptionalArg::class.java),
parameters[i].isAnnotationPresent(OptionalArg.class), paramNames?.get(i) ?: "param$i") // TODO: Description (JavaDoc?)
paramNames == null ? "param" + i : paramNames[i]); // TODO: Description (JavaDoc?)
} }
return new Pair<>(ret, parameters[0].getType()); return Pair(ret, parameters[0].type)
} }
/** /**
@ -271,29 +238,12 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
* @param arg Our representation of the command argument * @param arg Our representation of the command argument
* @return The Brigadier representation of the command argument * @return The Brigadier representation of the command argument
*/ */
private ArgumentType<?> getArgumentType(CommandArgument arg) { private fun getArgumentType(arg: CommandArgument?): ArgumentType<*> {
final Class<?> ptype = arg.type; val ptype = arg!!.type
Number lowerLimit = arg.limits.getValue0(), upperLimit = arg.limits.getValue1(); val lowerLimit: Number = arg.limits.value0
if (arg.greedy) val upperLimit: Number = arg.limits.value1
return StringArgumentType.greedyString(); return if (arg.greedy) StringArgumentType.greedyString() else if (ptype == String::class.java) StringArgumentType.word() else if (ptype == Int::class.javaPrimitiveType || ptype == Int::class.java || ptype == Byte::class.javaPrimitiveType || ptype == Byte::class.java || ptype == Short::class.javaPrimitiveType || ptype == Short::class.java) IntegerArgumentType.integer(lowerLimit.toInt(), upperLimit.toInt()) else if (ptype == Long::class.javaPrimitiveType || ptype == Long::class.java) LongArgumentType.longArg(lowerLimit.toLong(), upperLimit.toLong()) else if (ptype == Float::class.javaPrimitiveType || ptype == Float::class.java) FloatArgumentType.floatArg(lowerLimit.toFloat(), upperLimit.toFloat()) else if (ptype == Double::class.javaPrimitiveType || ptype == Double::class.java) DoubleArgumentType.doubleArg(lowerLimit.toDouble(), upperLimit.toDouble()) else if (ptype == Char::class.javaPrimitiveType || ptype == Char::class.java) StringArgumentType.word() else if (ptype == Boolean::class.javaPrimitiveType || ptype == Boolean::class.java) BoolArgumentType.bool() else {
else if (ptype == String.class) StringArgumentType.word()
return StringArgumentType.word();
else if (ptype == int.class || ptype == Integer.class
|| ptype == byte.class || ptype == Byte.class
|| ptype == short.class || ptype == Short.class)
return IntegerArgumentType.integer(lowerLimit.intValue(), upperLimit.intValue());
else if (ptype == long.class || ptype == Long.class)
return LongArgumentType.longArg(lowerLimit.longValue(), upperLimit.longValue());
else if (ptype == float.class || ptype == Float.class)
return FloatArgumentType.floatArg(lowerLimit.floatValue(), upperLimit.floatValue());
else if (ptype == double.class || ptype == Double.class)
return DoubleArgumentType.doubleArg(lowerLimit.doubleValue(), upperLimit.doubleValue());
else if (ptype == char.class || ptype == Character.class)
return StringArgumentType.word();
else if (ptype == boolean.class || ptype == Boolean.class)
return BoolArgumentType.bool();
else {
return StringArgumentType.word();
} }
} }
@ -304,9 +254,12 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
* @param context The command context * @param context The command context
* @return Vanilla command success level (0) * @return Vanilla command success level (0)
*/ */
private int executeHelpText(CommandContext<TP> context) { private fun executeHelpText(context: CommandContext<TP>): Int {
System.out.println("Nodes:\n" + context.getNodes().stream().map(node -> node.getNode().getName() + "@" + node.getRange()).collect(Collectors.joining("\n"))); println("""
return 0; Nodes:
${context.nodes.stream().map { node: ParsedCommandNode<TP> -> node.node.name + "@" + node.range }.collect(Collectors.joining("\n"))}
""".trimIndent())
return 0
} }
/** /**
@ -315,9 +268,9 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
* @param context The command context * @param context The command context
* @return Vanilla command success level (0) * @return Vanilla command success level (0)
*/ */
private int executeCommand(CommandContext<TP> context) { private fun executeCommand(context: CommandContext<TP>): Int {
System.out.println("Execute command"); println("Execute command")
System.out.println("Should be running sync: " + runOnPrimaryThread); println("Should be running sync: $runOnPrimaryThread")
/*if (!hasPermission(sender, sd.command, sd.method)) { /*if (!hasPermission(sender, sd.command, sd.method)) {
sender.sendMessage("§cYou don't have permission to use this command"); sender.sendMessage("§cYou don't have permission to use this command");
@ -368,36 +321,20 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
if (sync) if (sync)
Bukkit.getScheduler().runTask(MainPlugin.Instance, invokeCommand); Bukkit.getScheduler().runTask(MainPlugin.Instance, invokeCommand);
else else
invokeCommand.run();*/ invokeCommand.run();*/return 0
return 0;
} }
public abstract boolean hasPermission(TP sender, TC command, Method subcommand); abstract fun hasPermission(sender: TP, command: TC, subcommand: Method?): Boolean
val commandsText: Array<String>
public String[] getCommandsText() { get() = commandHelp.toTypedArray()
return commandHelp.toArray(new String[0]);
}
/**
* Returns the path of the given subcommand excluding the class' path. It will start with the given replace char.
*
* @param methodName The method's name, method.getName()
* @param replaceChar The character to use between subcommands
* @return The command path starting with the replace char.
*/
@NotNull
public String getCommandPath(String methodName, char replaceChar) {
return methodName.equals("def") ? "" : replaceChar + methodName.replace('_', replaceChar).toLowerCase();
}
/** /**
* Get all registered command nodes. This returns all registered Chroma commands with all the information about them. * Get all registered command nodes. This returns all registered Chroma commands with all the information about them.
* *
* @return A set of command node objects containing the commands * @return A set of command node objects containing the commands
*/ */
public Set<CoreCommandNode<TP, TC>> getCommandNodes() { val commandNodes: Set<CoreCommandNode<TP, TC?>?>
return dispatcher.getRoot().getChildren().stream().map(node -> (CoreCommandNode<TP, TC>) node).collect(Collectors.toUnmodifiableSet()); get() = dispatcher.root.children.stream().map { node: CommandNode<TP>? -> node as CoreCommandNode<TP, TC?>? }.collect(Collectors.toUnmodifiableSet())
}
/** /**
* Get a node that belongs to the given command. * Get a node that belongs to the given command.
@ -405,8 +342,8 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
* @param command The exact name of the command * @param command The exact name of the command
* @return A command node * @return A command node
*/ */
public CoreCommandNode<TP, TC> getCommandNode(String command) { fun getCommandNode(command: String?): CoreCommandNode<TP, TC> {
return (CoreCommandNode<TP, TC>) dispatcher.getRoot().getChild(command); return dispatcher.root.getChild(command) as CoreCommandNode<TP, TC>
} }
/** /**
@ -414,8 +351,8 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
* *
* @param command The command class (object) to unregister * @param command The command class (object) to unregister
*/ */
public void unregisterCommand(ICommand2<TP> command) { fun unregisterCommand(command: ICommand2<TP>) {
dispatcher.getRoot().getChildren().removeIf(node -> ((CoreCommandNode<TP, TC>) node).getData().command == command); dispatcher.root.children.removeIf { node: CommandNode<TP> -> (node as CoreCommandNode<TP, TC>).data.command === command }
} }
/** /**
@ -423,17 +360,14 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
* *
* @param condition The condition for removing a given command * @param condition The condition for removing a given command
*/ */
public void unregisterCommandIf(Predicate<CoreCommandNode<TP, TC>> condition, boolean nested) { fun unregisterCommandIf(condition: Predicate<CoreCommandNode<TP, TC>?>, nested: Boolean) {
dispatcher.getRoot().getChildren().removeIf(node -> condition.test((CoreCommandNode<TP, TC>) node)); dispatcher.root.children.removeIf { node: CommandNode<TP>? -> condition.test(node as CoreCommandNode<TP, TC>?) }
if (nested) if (nested) for (child in dispatcher.root.children) unregisterCommandIf(condition, child as CoreCommandNode<TP, TC>)
for (var child : dispatcher.getRoot().getChildren())
unregisterCommandIf(condition, (CoreCommandNode<TP, TC>) child);
} }
private void unregisterCommandIf(Predicate<CoreCommandNode<TP, TC>> condition, CoreCommandNode<TP, TC> root) { private fun unregisterCommandIf(condition: Predicate<CoreCommandNode<TP, TC>?>, root: CoreCommandNode<TP, TC>) {
// Can't use getCoreChildren() here because the collection needs to be modifiable // Can't use getCoreChildren() here because the collection needs to be modifiable
root.getChildren().removeIf(node -> condition.test((CoreCommandNode<TP, TC>) node)); root.children.removeIf { node: CommandNode<TP>? -> condition.test(node as CoreCommandNode<TP, TC>?) }
for (var child : root.getCoreChildren()) for (child in root.coreChildren) unregisterCommandIf(condition, child)
unregisterCommandIf(condition, child);
} }
} }

View file

@ -1,54 +1,48 @@
package buttondevteam.lib.chat; package buttondevteam.lib.chat
import buttondevteam.core.MainPlugin; import buttondevteam.core.MainPlugin
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI
import buttondevteam.lib.architecture.ButtonPlugin; import buttondevteam.lib.architecture.ButtonPlugin
import buttondevteam.lib.architecture.Component; import buttondevteam.lib.architecture.Component
import buttondevteam.lib.chat.commands.SubcommandData; import buttondevteam.lib.chat.commands.CommandUtils
import buttondevteam.lib.player.ChromaGamerBase; import buttondevteam.lib.chat.commands.SubcommandData
import com.mojang.brigadier.arguments.StringArgumentType; import buttondevteam.lib.player.ChromaGamerBase
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.arguments.StringArgumentType
import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder
import com.mojang.brigadier.tree.CommandNode; import com.mojang.brigadier.builder.RequiredArgumentBuilder
import com.mojang.brigadier.tree.LiteralCommandNode; import com.mojang.brigadier.context.CommandContext
import lombok.val; import com.mojang.brigadier.suggestion.Suggestion
import me.lucko.commodore.Commodore; import com.mojang.brigadier.suggestion.SuggestionProvider
import me.lucko.commodore.CommodoreProvider; import com.mojang.brigadier.suggestion.Suggestions
import org.bukkit.Bukkit; import com.mojang.brigadier.suggestion.SuggestionsBuilder
import org.bukkit.Location; import com.mojang.brigadier.tree.ArgumentCommandNode
import org.bukkit.OfflinePlayer; import com.mojang.brigadier.tree.CommandNode
import org.bukkit.command.*; import com.mojang.brigadier.tree.LiteralCommandNode
import org.bukkit.entity.Player; import me.lucko.commodore.Commodore
import org.bukkit.event.Listener; import me.lucko.commodore.CommodoreProvider
import org.bukkit.permissions.Permission; import org.bukkit.Bukkit
import org.bukkit.permissions.PermissionDefault; import org.bukkit.Location
import org.javatuples.Triplet; import org.bukkit.OfflinePlayer
import org.bukkit.command.*
import java.lang.annotation.Annotation; import org.bukkit.entity.Player
import java.lang.reflect.Method; import org.bukkit.event.Listener
import java.lang.reflect.Parameter; import org.bukkit.permissions.Permission
import java.util.Arrays; import org.bukkit.permissions.PermissionDefault
import java.util.Collections; import org.javatuples.Triplet
import java.util.List; import java.lang.reflect.Method
import java.util.Optional; import java.lang.reflect.Parameter
import java.util.concurrent.atomic.AtomicBoolean; import java.util.*
import java.util.function.Function; import java.util.function.BiConsumer
import java.util.function.Supplier; import java.util.function.Function
import java.util.stream.Collectors; import java.util.function.Supplier
import java.util.stream.Stream;
public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implements Listener {
public Command2MC() {
super('/', true);
}
class Command2MC : Command2<ICommand2MC?, Command2MCSender?>('/', true), Listener {
/** /**
* Don't use directly, use the method in Component and ButtonPlugin to automatically unregister the command when needed. * Don't use directly, use the method in Component and ButtonPlugin to automatically unregister the command when needed.
* *
* @param command The command to register * @param command The command to register
*/ */
@Override override fun registerCommand(command: ICommand2MC) {
public void registerCommand(ICommand2MC command) {
/*String mainpath; /*String mainpath;
var plugin = command.getPlugin(); var plugin = command.getPlugin();
{ {
@ -56,84 +50,77 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
int i = cpath.indexOf(' '); int i = cpath.indexOf(' ');
mainpath = cpath.substring(0, i == -1 ? cpath.length() : i); mainpath = cpath.substring(0, i == -1 ? cpath.length() : i);
}*/ }*/
var commandNode = super.registerCommandSuper(command); val commandNode = super.registerCommandSuper(command)
var bcmd = registerOfficially(command, commandNode); val bcmd = registerOfficially(command, commandNode)
if (bcmd != null) // TODO: Support aliases if (bcmd != null) // TODO: Support aliases
super.registerCommandSuper(command); super.registerCommandSuper(command)
val perm = "chroma.command." + command.commandPath.replace(' ', '.')
var perm = "chroma.command." + command.getCommandPath().replace(' ', '.');
if (Bukkit.getPluginManager().getPermission(perm) == null) //Check needed for plugin reset if (Bukkit.getPluginManager().getPermission(perm) == null) //Check needed for plugin reset
Bukkit.getPluginManager().addPermission(new Permission(perm, Bukkit.getPluginManager().addPermission(Permission(perm,
PermissionDefault.TRUE)); //Allow commands by default, it will check mod-only PermissionDefault.TRUE)) //Allow commands by default, it will check mod-only
for (val method : command.getClass().getMethods()) { for (method in command.javaClass.methods) {
if (!method.isAnnotationPresent(Subcommand.class)) continue; if (!method.isAnnotationPresent(Subcommand::class.java)) continue
var path = getCommandPath(method.getName(), '.'); val path = CommandUtils.getCommandPath(method.name, '.')
if (path.length() > 0) { if (path.length > 0) {
var subperm = perm + path; val subperm = perm + path
if (Bukkit.getPluginManager().getPermission(subperm) == null) //Check needed for plugin reset if (Bukkit.getPluginManager().getPermission(subperm) == null) //Check needed for plugin reset
Bukkit.getPluginManager().addPermission(new Permission(subperm, Bukkit.getPluginManager().addPermission(Permission(subperm,
PermissionDefault.TRUE)); //Allow commands by default, it will check mod-only PermissionDefault.TRUE)) //Allow commands by default, it will check mod-only
} }
String pg = permGroup(command, method); val pg = permGroup(command, method)
if (pg.length() == 0) continue; if (pg.length == 0) continue
String permGroup = "chroma." + pg; val permGroup = "chroma.$pg"
if (Bukkit.getPluginManager().getPermission(permGroup) == null) //It may occur multiple times if (Bukkit.getPluginManager().getPermission(permGroup) == null) //It may occur multiple times
Bukkit.getPluginManager().addPermission(new Permission(permGroup, Bukkit.getPluginManager().addPermission(Permission(permGroup,
PermissionDefault.OP)); //Do not allow any commands that belong to a group PermissionDefault.OP)) //Do not allow any commands that belong to a group
} }
} }
@Override override fun hasPermission(sender: Command2MCSender, command: ICommand2MC, method: Method): Boolean {
public boolean hasPermission(Command2MCSender sender, ICommand2MC command, Method method) { return hasPermission(sender.sender, command, method)
return hasPermission(sender.getSender(), command, method);
} }
public boolean hasPermission(CommandSender sender, ICommand2MC command, Method method) { fun hasPermission(sender: CommandSender, command: ICommand2MC?, method: Method): Boolean {
if (sender instanceof ConsoleCommandSender) return true; //Always allow the console if (sender is ConsoleCommandSender) return true //Always allow the console
if (command == null) return true; //Allow viewing the command - it doesn't do anything anyway if (command == null) return true //Allow viewing the command - it doesn't do anything anyway
String pg; var pg: String
boolean p = true; var p = true
var cmdperm = "chroma.command." + command.getCommandPath().replace(' ', '.'); val cmdperm = "chroma.command." + command.commandPath.replace(' ', '.')
var path = getCommandPath(method.getName(), '.'); val path = CommandUtils.getCommandPath(method.name, '.')
String[] perms = { val perms = arrayOf(
path.length() > 0 ? cmdperm + path : null, if (path.length > 0) cmdperm + path else null,
cmdperm, cmdperm,
(pg = permGroup(command, method)).length() > 0 ? "chroma." + pg : null if (permGroup(command, method).also { pg = it }.length > 0) "chroma.$pg" else null
}; )
for (String perm : perms) { for (perm in perms) {
if (perm != null) { if (perm != null) {
if (p) { //Use OfflinePlayer to avoid fetching player data if (p) { //Use OfflinePlayer to avoid fetching player data
if (sender instanceof OfflinePlayer) p = if (sender is OfflinePlayer) MainPlugin.permission.playerHas(if (sender is Player) sender.location.world.name else null, sender as OfflinePlayer, perm) else false //Use sender's method
p = MainPlugin.permission.playerHas(sender instanceof Player ? ((Player) sender).getLocation().getWorld().getName() : null, (OfflinePlayer) sender, perm); if (!p) p = sender.hasPermission(perm)
else } else break //If any of the permissions aren't granted then don't allow
p = false; //Use sender's method
if (!p) p = sender.hasPermission(perm);
} else break; //If any of the permissions aren't granted then don't allow
} }
} }
return p; return p
} }
/** /**
* Returns the first group found in the hierarchy starting from the command method <b>or</b> the mod group if <i>any</i></i> of the superclasses are mod only. * Returns the first group found in the hierarchy starting from the command method **or** the mod group if *any* of the superclasses are mod only.
* *
* @param method The subcommand to check * @param method The subcommand to check
* @return The permission group for the subcommand or empty string * @return The permission group for the subcommand or empty string
*/ */
private String permGroup(ICommand2MC command, Method method) { private fun permGroup(command: ICommand2MC, method: Method?): String {
if (method != null) { if (method != null) {
val sc = method.getAnnotation(Subcommand.class); val sc = method.getAnnotation(Subcommand::class.java)
if (sc != null && sc.permGroup().length() > 0) { if (sc != null && sc.permGroup().length > 0) {
return sc.permGroup(); return sc.permGroup()
} }
} }
if (getAnnForValue(command.getClass(), CommandClass.class, CommandClass::modOnly, false)) return if (getAnnForValue(command.javaClass, CommandClass::class.java, Function { obj: CommandClass -> obj.modOnly() }, false)) Subcommand.MOD_GROUP else getAnnForValue(command.javaClass, CommandClass::class.java, Function<CommandClass, String> { obj: CommandClass -> obj.permGroup() }, "")
return Subcommand.MOD_GROUP;
return getAnnForValue(command.getClass(), CommandClass.class, CommandClass::permGroup, "");
} }
/** /**
* Loops until it finds a value that is <b>not</b> the same as def * Loops until it finds a value that is **not** the same as def
* *
* @param sourceCl The class which has the annotation * @param sourceCl The class which has the annotation
* @param annCl The annotation to get * @param annCl The annotation to get
@ -142,297 +129,262 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
* @param <T> The annotation type * @param <T> The annotation type
* @param <V> The type of the value * @param <V> The type of the value
* @return The value returned by the first superclass or def * @return The value returned by the first superclass or def
*/ </V></T> */
private <T extends Annotation, V> V getAnnForValue(Class<?> sourceCl, Class<T> annCl, Function<T, V> annMethod, V def) { private fun <T : Annotation?, V> getAnnForValue(sourceCl: Class<*>, annCl: Class<T>, annMethod: Function<T, V>, def: V): V {
for (Class<?> cl = sourceCl; cl != null; cl = cl.getSuperclass()) { var cl: Class<*>? = sourceCl
val cc = cl.getAnnotation(annCl); while (cl != null) {
V r; val cc = cl.getAnnotation(annCl)
if (cc != null && (r = annMethod.apply(cc)) != def) return r; var r: V
if (cc != null && annMethod.apply(cc).also { r = it } !== def) return r
cl = cl.superclass
} }
return def; return def
} }
/** /**
* Automatically colors the message red. * Automatically colors the message red.
* {@see super#addParamConverter} * {@see super#addParamConverter}
*/ */
@Override override fun <T> addParamConverter(cl: Class<T>, converter: Function<String, T>, errormsg: String, allSupplier: Supplier<Iterable<String>>) {
public <T> void addParamConverter(Class<T> cl, Function<String, T> converter, String errormsg, Supplier<Iterable<String>> allSupplier) { super.addParamConverter(cl, converter, "§c$errormsg", allSupplier)
super.addParamConverter(cl, converter, "§c" + errormsg, allSupplier);
} }
public void unregisterCommands(ButtonPlugin plugin) { fun unregisterCommands(plugin: ButtonPlugin) {
unregisterCommandIf(node -> Optional.ofNullable(node.getData().command).map(ICommand2MC::getPlugin).map(plugin::equals).orElse(false), true); unregisterCommandIf({ node: CoreCommandNode<Command2MCSender?, ICommand2MC?> -> Optional.ofNullable(node.data.command).map { obj: ICommand2MC -> obj.plugin }.map { obj: ButtonPlugin? -> plugin.equals(obj) }.orElse(false) }, true)
} }
public void unregisterCommands(Component<?> component) { fun unregisterCommands(component: Component<*>) {
unregisterCommandIf(node -> Optional.ofNullable(node.getData().command).map(ICommand2MC::getPlugin) unregisterCommandIf({ node: CoreCommandNode<Command2MCSender?, ICommand2MC?> ->
.map(comp -> component.getClass().getSimpleName().equals(comp.getClass().getSimpleName())).orElse(false), true); Optional.ofNullable(node.data.command).map { obj: ICommand2MC -> obj.plugin }
.map { comp: ButtonPlugin -> component.javaClass.simpleName == comp.javaClass.simpleName }.orElse(false)
}, true)
} }
@Override override fun handleCommand(sender: Command2MCSender, commandline: String): Boolean {
public boolean handleCommand(Command2MCSender sender, String commandline) { return handleCommand(sender, commandline, true)
return handleCommand(sender, commandline, true);
} }
private boolean handleCommand(Command2MCSender sender, String commandline, boolean checkPlugin) { private fun handleCommand(sender: Command2MCSender, commandline: String, checkPlugin: Boolean): Boolean {
int i = commandline.indexOf(' '); val i = commandline.indexOf(' ')
String mainpath = commandline.substring(1, i == -1 ? commandline.length() : i); //Without the slash val mainpath = commandline.substring(1, if (i == -1) commandline.length else i) //Without the slash
PluginCommand pcmd; var pcmd: PluginCommand
if (!checkPlugin return if ((!checkPlugin
|| MainPlugin.Instance.prioritizeCustomCommands.get() || MainPlugin.Instance.prioritizeCustomCommands.get()) || Bukkit.getPluginCommand(mainpath).also { pcmd = it } == null //Our commands aren't PluginCommands
|| (pcmd = Bukkit.getPluginCommand(mainpath)) == null //Our commands aren't PluginCommands || pcmd.plugin is ButtonPlugin) //Unless it's specified in the plugin.yml
|| pcmd.getPlugin() instanceof ButtonPlugin) //Unless it's specified in the plugin.yml super.handleCommand(sender, commandline) else false
return super.handleCommand(sender, commandline);
else
return false;
} }
private boolean shouldRegisterOfficially = true; private var shouldRegisterOfficially = true
private fun registerOfficially(command: ICommand2MC, node: LiteralCommandNode<Command2MCSender>): Command? {
private Command registerOfficially(ICommand2MC command, LiteralCommandNode<Command2MCSender> node) { return if (!shouldRegisterOfficially || command.plugin == null) null else try {
if (!shouldRegisterOfficially || command.getPlugin() == null) return null; val cmdmap = Bukkit.getServer().javaClass.getMethod("getCommandMap").invoke(Bukkit.getServer()) as SimpleCommandMap
try { val path = command.commandPath
var cmdmap = (SimpleCommandMap) Bukkit.getServer().getClass().getMethod("getCommandMap").invoke(Bukkit.getServer()); val x = path.indexOf(' ')
var path = command.getCommandPath(); val mainPath = path.substring(0, if (x == -1) path.length else x)
int x = path.indexOf(' '); var bukkitCommand: Command
var mainPath = path.substring(0, x == -1 ? path.length() : x); run {
Command bukkitCommand; //Commands conflicting with Essentials have to be registered in plugin.yml
{ //Commands conflicting with Essentials have to be registered in plugin.yml val oldcmd = cmdmap.getCommand(command.plugin.name + ":" + mainPath) //The label with the fallback prefix is always registered
var oldcmd = cmdmap.getCommand(command.getPlugin().getName() + ":" + mainPath); //The label with the fallback prefix is always registered
if (oldcmd == null) { if (oldcmd == null) {
bukkitCommand = new BukkitCommand(mainPath); bukkitCommand = BukkitCommand(mainPath)
cmdmap.register(command.getPlugin().getName(), bukkitCommand); cmdmap.register(command.plugin.name, bukkitCommand)
} else { } else {
bukkitCommand = oldcmd; bukkitCommand = oldcmd
if (bukkitCommand instanceof PluginCommand) if (bukkitCommand is PluginCommand) (bukkitCommand as PluginCommand).executor = CommandExecutor { sender: CommandSender, command: Command, label: String, args: Array<String> -> this.executeCommand(sender, command, label, args) }
((PluginCommand) bukkitCommand).setExecutor(this::executeCommand);
} }
bukkitCommand = oldcmd == null ? new BukkitCommand(mainPath) : oldcmd; bukkitCommand = oldcmd ?: BukkitCommand(mainPath)
} }
if (CommodoreProvider.isSupported()) if (CommodoreProvider.isSupported()) TabcompleteHelper.registerTabcomplete(command, node, bukkitCommand)
TabcompleteHelper.registerTabcomplete(command, node, bukkitCommand); bukkitCommand
return bukkitCommand; } catch (e: Exception) {
} catch (Exception e) { if (command.component == null) TBMCCoreAPI.SendException("Failed to register command in command map!", e, command.plugin) else TBMCCoreAPI.SendException("Failed to register command in command map!", e, command.component)
if (command.getComponent() == null) shouldRegisterOfficially = false
TBMCCoreAPI.SendException("Failed to register command in command map!", e, command.getPlugin()); null
else
TBMCCoreAPI.SendException("Failed to register command in command map!", e, command.getComponent());
shouldRegisterOfficially = false;
return null;
} }
} }
private boolean executeCommand(CommandSender sender, Command command, String label, String[] args) { private fun executeCommand(sender: CommandSender, command: Command, label: String, args: Array<String>): Boolean {
var user = ChromaGamerBase.getFromSender(sender); val user = ChromaGamerBase.getFromSender(sender)
if (user == null) { if (user == null) {
TBMCCoreAPI.SendException("Failed to run Bukkit command for user!", new Throwable("No Chroma user found"), MainPlugin.Instance); TBMCCoreAPI.SendException("Failed to run Bukkit command for user!", Throwable("No Chroma user found"), MainPlugin.Instance)
sender.sendMessage("§cAn internal error occurred."); sender.sendMessage("§cAn internal error occurred.")
return true; return true
} }
handleCommand(new Command2MCSender(sender, user.channel.get(), sender), handleCommand(Command2MCSender(sender, user.channel.get(), sender),
("/" + command.getName() + " " + String.join(" ", args)).trim(), false); ///trim(): remove space if there are no args ("/" + command.name + " " + java.lang.String.join(" ", *args)).trim { it <= ' ' }, false) ///trim(): remove space if there are no args
return true; return true
} }
private static class BukkitCommand extends Command { private class BukkitCommand(name: String?) : Command(name) {
protected BukkitCommand(String name) { override fun execute(sender: CommandSender, commandLabel: String, args: Array<String>): Boolean {
super(name); return ButtonPlugin.getCommand2MC().executeCommand(sender, this, commandLabel, args)
} }
@Override @Throws(IllegalArgumentException::class)
public boolean execute(CommandSender sender, String commandLabel, String[] args) { override fun tabComplete(sender: CommandSender, alias: String, args: Array<String>): List<String> {
return ButtonPlugin.getCommand2MC().executeCommand(sender, this, commandLabel, args); return emptyList()
} }
@Override @Throws(IllegalArgumentException::class)
public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { override fun tabComplete(sender: CommandSender, alias: String, args: Array<String>, location: Location): List<String> {
return Collections.emptyList(); return emptyList()
}
@Override
public List<String> tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException {
return Collections.emptyList();
} }
} }
private static class TabcompleteHelper { private object TabcompleteHelper {
private static Commodore commodore; private var commodore: Commodore? = null
private fun appendSubcommand(path: String, parent: CommandNode<Any>,
private static LiteralCommandNode<Object> appendSubcommand(String path, CommandNode<Object> parent, subcommand: SubcommandData<ICommand2MC>?): LiteralCommandNode<Any> {
SubcommandData<ICommand2MC> subcommand) { var scmd: LiteralCommandNode<Any>
LiteralCommandNode<Object> scmd; if (parent.getChild(path) as LiteralCommandNode<kotlin.Any?>?. also { scmd = it } != null) return scmd
if ((scmd = (LiteralCommandNode<Object>) parent.getChild(path)) != null) val scmdBuilder = LiteralArgumentBuilder.literal<Any>(path)
return scmd; if (subcommand != null) scmdBuilder.requires { o: Any? ->
var scmdBuilder = LiteralArgumentBuilder.literal(path); val sender = commodore!!.getBukkitSender(o)
if (subcommand != null) subcommand.hasPermission(sender)
scmdBuilder.requires(o -> { }
var sender = commodore.getBukkitSender(o); scmd = scmdBuilder.build()
return subcommand.hasPermission(sender); parent.addChild(scmd)
}); return scmd
scmd = scmdBuilder.build();
parent.addChild(scmd);
return scmd;
} }
private static void registerTabcomplete(ICommand2MC command2MC, LiteralCommandNode<Command2MCSender> commandNode, Command bukkitCommand) { private fun registerTabcomplete(command2MC: ICommand2MC, commandNode: LiteralCommandNode<Command2MCSender>, bukkitCommand: Command) {
if (commodore == null) { if (commodore == null) {
commodore = CommodoreProvider.getCommodore(MainPlugin.Instance); //Register all to the Core, it's easier commodore = CommodoreProvider.getCommodore(MainPlugin.Instance) //Register all to the Core, it's easier
commodore.register(LiteralArgumentBuilder.literal("un").redirect(RequiredArgumentBuilder.argument("unsomething", commodore.register(LiteralArgumentBuilder.literal<Any?>("un").redirect(RequiredArgumentBuilder.argument<Any?, String>("unsomething",
StringArgumentType.word()).suggests((context, builder) -> builder.suggest("untest").buildFuture()).build())); StringArgumentType.word()).suggests { context: CommandContext<Any?>?, builder: SuggestionsBuilder -> builder.suggest("untest").buildFuture() }.build()))
} }
String[] path = command2MC.getCommandPath().split(" "); commodore!!.dispatcher.root.getChild(commandNode.name) // TODO: Probably unnecessary
var shouldRegister = new AtomicBoolean(true); val customTCmethods = Arrays.stream(command2MC.javaClass.declaredMethods) //val doesn't recognize the type arguments
@SuppressWarnings("unchecked") var maincmd = commodore.getRegisteredNodes().stream() .flatMap { method: Method ->
.filter(node -> node.getLiteral().equalsIgnoreCase(path[0])) Optional.ofNullable(method.getAnnotation(CustomTabCompleteMethod::class.java)).stream()
.filter(node -> {shouldRegister.set(false); return true;}) .flatMap { ctcmAnn: CustomTabCompleteMethod ->
.map(node -> (LiteralCommandNode<Object>) node).findAny() val paths = Optional.of<Array<String?>>(ctcmAnn.subcommand()).filter { s: Array<String?> -> s.size > 0 }
.orElseGet(() -> LiteralArgumentBuilder.literal(path[0]).build()); //Commodore 1.8 removes previous nodes .orElseGet {
var cmd = maincmd; arrayOf(
for (int i = 1; i < path.length; i++) { CommandUtils.getCommandPath(method.name, ' ').trim { it <= ' ' }
var scmd = subcmds.stream().filter(sd -> sd.method.getName().equals("def")).findAny().orElse(null); )
cmd = appendSubcommand(path[i], cmd, scmd); //Add each part of the path as a child of the previous one
} }
final var customTCmethods = Arrays.stream(command2MC.getClass().getDeclaredMethods()) //val doesn't recognize the type arguments Arrays.stream(paths).map { name: String? -> Triplet(name, ctcmAnn, method) }
.flatMap(method -> Stream.of(Optional.ofNullable(method.getAnnotation(CustomTabCompleteMethod.class))) }
.filter(Optional::isPresent).map(Optional::get) // Java 9 has .stream() }.toList()
.flatMap(ctcm -> { for (subcmd in subcmds) {
var paths = Optional.of(ctcm.subcommand()).filter(s -> s.length > 0) val subpathAsOne = CommandUtils.getCommandPath(subcmd.method.getName(), ' ').trim { it <= ' ' }
.orElseGet(() -> new String[]{ val subpath = subpathAsOne.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
ButtonPlugin.getCommand2MC().getCommandPath(method.getName(), ' ').trim() var scmd: CommandNode<Any> = cmd
}); if (subpath[0].length > 0) { //If the method is def, it will contain one empty string
return Arrays.stream(paths).map(name -> new Triplet<>(name, ctcm, method)); for (s in subpath) {
})).collect(Collectors.toList()); scmd = appendSubcommand(s, scmd, subcmd) //Add method name part of the path (could_be_multiple())
for (SubcommandData<ICommand2MC> subcmd : subcmds) {
String subpathAsOne = ButtonPlugin.getCommand2MC().getCommandPath(subcmd.method.getName(), ' ').trim();
String[] subpath = subpathAsOne.split(" ");
CommandNode<Object> scmd = cmd;
if (subpath[0].length() > 0) { //If the method is def, it will contain one empty string
for (String s : subpath) {
scmd = appendSubcommand(s, scmd, subcmd); //Add method name part of the path (could_be_multiple())
} }
} }
Parameter[] parameters = subcmd.method.getParameters(); val parameters: Array<Parameter> = subcmd.method.getParameters()
for (int i = 1; i < parameters.length; i++) { //Skip sender for (i in 1 until parameters.size) { //Skip sender
Parameter parameter = parameters[i]; val parameter = parameters[i]
final boolean customParamType; val customParamType: Boolean
// TODO: Arg type // TODO: Arg type
val param = subcmd.parameters[i - 1]; val param: Any = subcmd.parameters.get(i - 1)
val customTC = Optional.ofNullable(parameter.getAnnotation(CustomTabComplete.class)) val customTC = Optional.ofNullable(parameter.getAnnotation(CustomTabComplete::class.java))
.map(CustomTabComplete::value); .map(Function<CustomTabComplete, Array<String>> { obj: CustomTabComplete -> obj.value() })
var customTCmethod = customTCmethods.stream().filter(t -> subpathAsOne.equalsIgnoreCase(t.getValue0())) val customTCmethod = customTCmethods.stream().filter { t: Triplet<String?, CustomTabCompleteMethod, Method> -> subpathAsOne.equals(t.value0, ignoreCase = true) }
.filter(t -> param.replaceAll("[\\[\\]<>]", "").equalsIgnoreCase(t.getValue1().param())) .filter { t: Triplet<String?, CustomTabCompleteMethod, Method> -> param.replaceAll("[\\[\\]<>]", "").equalsIgnoreCase(t.value1.param()) }
.findAny(); .findAny()
var argb = RequiredArgumentBuilder.argument(param, type) val argb: RequiredArgumentBuilder<S, T> = RequiredArgumentBuilder.argument(param, type)
.suggests((context, builder) -> { .suggests(SuggestionProvider<S?> { context: CommandContext<S?>, builder: SuggestionsBuilder ->
if (parameter.isVarArgs()) { //Do it before the builder is used if (parameter.isVarArgs) { //Do it before the builder is used
int nextTokenStart = context.getInput().lastIndexOf(' ') + 1; val nextTokenStart = context.getInput().lastIndexOf(' ') + 1
builder = builder.createOffset(nextTokenStart); builder = builder.createOffset(nextTokenStart)
} }
if (customTC.isPresent()) if (customTC.isPresent) for (ctc in customTC.get()) builder.suggest(ctc)
for (val ctc : customTC.get()) var ignoreCustomParamType = false
builder.suggest(ctc); if (customTCmethod.isPresent) {
boolean ignoreCustomParamType = false; val tr = customTCmethod.get()
if (customTCmethod.isPresent()) { if (tr.value1.ignoreTypeCompletion()) ignoreCustomParamType = true
var tr = customTCmethod.get(); val method = tr.value2
if (tr.getValue1().ignoreTypeCompletion()) val params = method.parameters
ignoreCustomParamType = true; val args = arrayOfNulls<Any>(params.size)
final var method = tr.getValue2(); var j = 0
val params = method.getParameters(); var k = 0
val args = new Object[params.length]; while (j < args.size && k < subcmd.parameters.length) {
for (int j = 0, k = 0; j < args.length && k < subcmd.parameters.length; j++) { val paramObj = params[j]
val paramObj = params[j]; if (CommandSender::class.java.isAssignableFrom(paramObj.type)) {
if (CommandSender.class.isAssignableFrom(paramObj.getType())) { args[j] = commodore!!.getBukkitSender(context.getSource())
args[j] = commodore.getBukkitSender(context.getSource()); j++
continue; continue
} }
val paramValueString = context.getArgument(subcmd.parameters[k], String.class); val paramValueString = context.getArgument(subcmd.parameters.get(k), String::class.java)
if (paramObj.getType() == String.class) { if (paramObj.type == String::class.java) {
args[j] = paramValueString; args[j] = paramValueString
continue; j++
continue
} }
val converter = getParamConverter(params[j].getType(), command2MC); val converter = getParamConverter(params[j].type, command2MC) ?: break
if (converter == null) val paramValue = converter.converter.apply(paramValueString)
break; ?: //For example, the player provided an invalid plugin name
val paramValue = converter.converter.apply(paramValueString); break
if (paramValue == null) //For example, the player provided an invalid plugin name args[j] = paramValue
break; k++ //Only increment if not CommandSender
args[j] = paramValue; j++
k++; //Only increment if not CommandSender
} }
if (args.length == 0 || args[args.length - 1] != null) { //Arguments filled entirely if (args.size == 0 || args[args.size - 1] != null) { //Arguments filled entirely
try { try {
val suggestions = method.invoke(command2MC, args); val suggestions = method.invoke(command2MC, *args)
if (suggestions instanceof Iterable) { if (suggestions is Iterable<*>) {
//noinspection unchecked for (suggestion in suggestions) if (suggestion is String) builder.suggest(suggestion as String?) else throw ClassCastException("Bad return type! It should return an Iterable<String> or a String[].")
for (Object suggestion : (Iterable<Object>) suggestions) } else if (suggestions is Array<String>) for (suggestion in suggestions as Array<String?>) builder.suggest(suggestion) else throw ClassCastException("Bad return type! It should return a String[] or an Iterable<String>.")
if (suggestion instanceof String) } catch (e: Exception) {
builder.suggest((String) suggestion); val msg = "Failed to run tabcomplete method " + method.name + " for command " + command2MC.javaClass.simpleName
else if (command2MC.component == null) TBMCCoreAPI.SendException(msg, e, command2MC.plugin) else TBMCCoreAPI.SendException(msg, e, command2MC.component)
throw new ClassCastException("Bad return type! It should return an Iterable<String> or a String[].");
} else if (suggestions instanceof String[])
for (String suggestion : (String[]) suggestions)
builder.suggest(suggestion);
else
throw new ClassCastException("Bad return type! It should return a String[] or an Iterable<String>.");
} catch (Exception e) {
String msg = "Failed to run tabcomplete method " + method.getName() + " for command " + command2MC.getClass().getSimpleName();
if (command2MC.getComponent() == null)
TBMCCoreAPI.SendException(msg, e, command2MC.getPlugin());
else
TBMCCoreAPI.SendException(msg, e, command2MC.getComponent());
} }
} }
} }
if (!ignoreCustomParamType && customParamType) { if (!ignoreCustomParamType && customParamType) {
val converter = getParamConverter(ptype, command2MC); val converter = getParamConverter(ptype, command2MC)
if (converter != null) { if (converter != null) {
var suggestions = converter.allSupplier.get(); val suggestions = converter.allSupplier.get()
for (String suggestion : suggestions) for (suggestion in suggestions) builder.suggest(suggestion)
builder.suggest(suggestion);
} }
} }
if (ptype == boolean.class || ptype == Boolean.class) if (ptype === Boolean::class.javaPrimitiveType || ptype === Boolean::class.java) builder.suggest("true").suggest("false")
builder.suggest("true").suggest("false"); val loweredInput = builder.remaining.lowercase(Locale.getDefault())
final String loweredInput = builder.getRemaining().toLowerCase(); builder.suggest(param).buildFuture().whenComplete(BiConsumer<Suggestions, Throwable> { s: Suggestions, e: Throwable? -> //The list is automatically ordered
return builder.suggest(param).buildFuture().whenComplete((s, e) -> //The list is automatically ordered s.list.add(s.list.removeAt(0))
s.getList().add(s.getList().remove(0))) //So we need to put the <param> at the end after that }) //So we need to put the <param> at the end after that
.whenComplete((ss, e) -> ss.getList().removeIf(s -> { .whenComplete(BiConsumer<Suggestions, Throwable> { ss: Suggestions, e: Throwable? ->
String text = s.getText(); ss.list.removeIf { s: Suggestion ->
return !text.startsWith("<") && !text.startsWith("[") && !text.toLowerCase().startsWith(loweredInput); val text = s.text
})); !text.startsWith("<") && !text.startsWith("[") && !text.lowercase(Locale.getDefault()).startsWith(loweredInput)
}); }
var arg = argb.build(); })
scmd.addChild(arg); })
scmd = arg; val arg: ArgumentCommandNode<S, T> = argb.build()
scmd.addChild(arg)
scmd = arg
} }
} }
if (shouldRegister.get()) { if (shouldRegister.get()) {
commodore.register(maincmd); commodore.register(maincmd)
//MinecraftArgumentTypes.getByKey(NamespacedKey.minecraft("")) //MinecraftArgumentTypes.getByKey(NamespacedKey.minecraft(""))
String pluginName = command2MC.getPlugin().getName().toLowerCase(); val pluginName = command2MC.plugin.name.lowercase(Locale.getDefault())
var prefixedcmd = LiteralArgumentBuilder.literal(pluginName + ":" + path[0]) val prefixedcmd = LiteralArgumentBuilder.literal<Any>(pluginName + ":" + path.get(0))
.redirect(maincmd).build(); .redirect(maincmd).build()
commodore.register(prefixedcmd); commodore!!.register(prefixedcmd)
for (String alias : bukkitCommand.getAliases()) { for (alias in bukkitCommand.aliases) {
commodore.register(LiteralArgumentBuilder.literal(alias).redirect(maincmd).build()); commodore!!.register(LiteralArgumentBuilder.literal<Any>(alias).redirect(maincmd).build())
commodore.register(LiteralArgumentBuilder.literal(pluginName + ":" + alias).redirect(maincmd).build()); commodore!!.register(LiteralArgumentBuilder.literal<Any>("$pluginName:$alias").redirect(maincmd).build())
} }
} }
} }
} }
private static ParamConverter<?> getParamConverter(Class<?> cl, ICommand2MC command2MC) { companion object {
val converter = ButtonPlugin.getCommand2MC().paramConverters.get(cl); private fun getParamConverter(cl: Class<*>, command2MC: ICommand2MC): ParamConverter<*>? {
val converter = ButtonPlugin.getCommand2MC().paramConverters[cl]
if (converter == null) { if (converter == null) {
String msg = "Could not find a suitable converter for type " + cl.getSimpleName(); val msg = "Could not find a suitable converter for type " + cl.simpleName
Exception exception = new NullPointerException("converter is null"); val exception: Exception = NullPointerException("converter is null")
if (command2MC.getComponent() == null) if (command2MC.component == null) TBMCCoreAPI.SendException(msg, exception, command2MC.plugin) else TBMCCoreAPI.SendException(msg, exception, command2MC.component)
TBMCCoreAPI.SendException(msg, exception, command2MC.getPlugin()); return null
else }
TBMCCoreAPI.SendException(msg, exception, command2MC.getComponent()); return converter
return null;
} }
return converter;
} }
} }

View file

@ -0,0 +1,19 @@
package buttondevteam.lib.chat.commands;
import lombok.experimental.UtilityClass;
import org.jetbrains.annotations.NotNull;
@UtilityClass
public class CommandUtils {
/**
* Returns the path of the given subcommand excluding the class' path. It will start with the given replace char.
*
* @param methodName The method's name, method.getName()
* @param replaceChar The character to use between subcommands
* @return The command path starting with the replacement char.
*/
@NotNull
public static String getCommandPath(String methodName, char replaceChar) {
return methodName.equals("def") ? "" : replaceChar + methodName.replace('_', replaceChar).toLowerCase();
}
}

View file

@ -9,7 +9,7 @@
<packaging>pom</packaging> <packaging>pom</packaging>
<version>master-SNAPSHOT</version> <version>master-SNAPSHOT</version>
<properties> <properties>
<lombok.version>1.18.10</lombok.version> <lombok.version>1.18.26</lombok.version>
</properties> </properties>
<name>Core POM for Chroma</name> <name>Core POM for Chroma</name>
@ -21,30 +21,14 @@
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version> <version>3.8.1</version>
<configuration> <configuration>
<release>8</release> <release>17</release>
<annotationProcessorPaths> <annotationProcessorPaths>
<annotationProcessorPath> <path>
<groupId>com.github.bsideup.jabel</groupId>
<artifactId>jabel-javac-plugin</artifactId>
<version>0.2.0</version>
</annotationProcessorPath>
<annotationProcessorPath>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
<version>${lombok.version}</version> <version>1.18.26</version>
</annotationProcessorPath> </path>
<annotationProcessorPath>
<groupId>com.github.TBMCPlugins.ChromaCore</groupId>
<artifactId>ButtonProcessor</artifactId>
<version>master-SNAPSHOT</version>
</annotationProcessorPath>
</annotationProcessorPaths> </annotationProcessorPaths>
<annotationProcessors> <!-- Order is important, so these lines are needed -->
<annotationProcessor>com.github.bsideup.jabel.JabelJavacProcessor</annotationProcessor>
<annotationProcessor>lombok.launch.AnnotationProcessorHider$AnnotationProcessor
</annotationProcessor>
<annotationProcessor>buttondevteam.buttonproc.ButtonProcessor</annotationProcessor>
</annotationProcessors>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
@ -97,31 +81,4 @@
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<profiles>
<profile>
<id>intellij-idea-only</id>
<activation>
<property>
<name>idea.maven.embedder.version</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<release>11</release>
<!--
<compilerArgs>
<arg>HYPHENHYPHENenable-preview</arg>
</compilerArgs>
-->
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project> </project>

View file

@ -9,7 +9,7 @@
<packaging>pom</packaging> <packaging>pom</packaging>
<version>master-SNAPSHOT</version> <version>master-SNAPSHOT</version>
<properties> <properties>
<lombok.version>1.18.12</lombok.version> <lombok.version>1.18.26</lombok.version>
</properties> </properties>
<name>Chroma Parent</name> <name>Chroma Parent</name>
@ -27,7 +27,7 @@
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version> <version>3.8.1</version>
<configuration> <configuration>
<release>8</release> <release>17</release>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>