diff --git a/build.gradle b/build.gradle index 64f4736..ed6e6d6 100644 --- a/build.gradle +++ b/build.gradle @@ -39,7 +39,7 @@ java { PluginManifest pluginManifest = [ name : 'CommandPrompter', - version : new Version(major: 2, minor: 7, patch: 0, fix: 0, classifier: 'SNAPSHOT'), + version : new Version(major: 2, minor: 8, patch: 0, fix: 0, classifier: 'SNAPSHOT'), author : 'CyR1en', description: 'Perfect companion plugin for inventory UI menu.', entry : 'com.cyr1en.commandprompter.CommandPrompter' @@ -57,6 +57,7 @@ repositories { maven { url 'https://repo.codemc.io/repository/maven-snapshots/' } maven { url 'https://repo.extendedclip.com/content/repositories/placeholderapi/' } maven { url 'https://jitpack.io' } + maven { url 'https://repo.glaremasters.me/repository/towny/'} flatDir { dirs 'libs' } } @@ -74,12 +75,15 @@ dependencies { implementation 'com.github.stefvanschie.inventoryframework:IF:0.10.11' compileOnly 'me.clip:placeholderapi:2.11.2' - compileOnly "net.kyori:adventure-text-serializer-legacy:4.13.1" - compileOnly "net.kyori:adventure-text-serializer-plain:4.13.1" - compileOnly "org.spigotmc:spigot-api:1.20-R0.1-SNAPSHOT" + compileOnly "net.kyori:adventure-text-serializer-legacy:4.15.0" + compileOnly "net.kyori:adventure-text-serializer-plain:4.15.0" + compileOnly "net.kyori:adventure-text-minimessage:4.15.0" + compileOnly 'com.palmergames.bukkit.towny:towny:0.100.0.0' + compileOnly "org.spigotmc:spigot-api:1.20.4-R0.1-SNAPSHOT" compileOnly 'com.github.LeonMangler:SuperVanish:6.2.17' compileOnly 'com.github.mbax:VanishNoPacket:3.22' compileOnly 'org.jetbrains:annotations:23.0.0' + compileOnly 'net.luckperms:api:5.4' // Local implementation fileTree(dir: 'libs', include: '*.jar') diff --git a/src/main/java/com/cyr1en/commandprompter/CommandPrompter.java b/src/main/java/com/cyr1en/commandprompter/CommandPrompter.java index 72c4719..547969a 100644 --- a/src/main/java/com/cyr1en/commandprompter/CommandPrompter.java +++ b/src/main/java/com/cyr1en/commandprompter/CommandPrompter.java @@ -102,6 +102,8 @@ public void onEnable() { Bukkit.getScheduler().runTaskLater(this, () -> { hookContainer = new HookContainer(this); hookContainer.initHooks(); + headCache.registerFilters(); + promptManager.registerPrompts(); ChatPrompt.resolveListener(this); }, 1L); } diff --git a/src/main/java/com/cyr1en/commandprompter/api/prompt/InputValidator.java b/src/main/java/com/cyr1en/commandprompter/api/prompt/InputValidator.java new file mode 100644 index 0000000..c6381a8 --- /dev/null +++ b/src/main/java/com/cyr1en/commandprompter/api/prompt/InputValidator.java @@ -0,0 +1,36 @@ +package com.cyr1en.commandprompter.api.prompt; + + +/** + * A functional interface for validating input. + * + *
Implement this interface to create a custom validator for your prompt.
+ */ +public interface InputValidator { + + /** + * Validate the input. + * + *Return true if the input is valid, false otherwise.
+ * + * @param input the input to validate + * @return true if the input is valid, false otherwise + */ + boolean validate(String input); + + /** + * Get the alias for this validator. + * + *The alias is used to identify the validator.
+ * + * @return the alias for this validator + */ + String alias(); + + /** + * Get the message to send when validation fails. + * + * @return the message to send when validation fails + */ + String messageOnFail(); +} diff --git a/src/main/java/com/cyr1en/commandprompter/api/prompt/Prompt.java b/src/main/java/com/cyr1en/commandprompter/api/prompt/Prompt.java index bdb01b7..962c6ca 100644 --- a/src/main/java/com/cyr1en/commandprompter/api/prompt/Prompt.java +++ b/src/main/java/com/cyr1en/commandprompter/api/prompt/Prompt.java @@ -30,7 +30,6 @@ import com.cyr1en.commandprompter.prompt.PromptParser; import java.util.List; -import java.util.regex.Pattern; public interface Prompt { @@ -70,35 +69,20 @@ public interface Prompt { PromptManager getPromptManager(); List
+ * Main component of this hook are the cache filters for LuckPerms groups.
+ */
+@TargetPlugin(pluginName = "LuckPerms")
+public class LuckPermsHook extends BaseHook implements FilterHook {
+
+ private LuckPerms api;
+
+ /**
+ * Construct a new LuckPermsHook.
+ *
+ * @param plugin the plugin
+ */
+ public LuckPermsHook(CommandPrompter plugin) {
+ super(plugin);
+ RegisteredServiceProvider
+ * This is a special case of {@link CacheFilter} where the regex key is 'og'.
+ * This would filter all players that are in the same group as the relative player.
+ */
+ private static class OwnGroupFilter extends CacheFilter {
+
+ private final LuckPermsHook hook;
+
+ /**
+ * Construct a new own group filter.
+ *
+ * @param hook the LuckPerms hook
+ */
+ public OwnGroupFilter(LuckPermsHook hook) {
+ super(Pattern.compile("lpo"), "PlayerUI.Filter-Format.LuckPermsOwnGroup");
+ this.hook = hook;
+ }
+
+ @Override
+ public CacheFilter reConstruct(String promptKey) {
+ // No need to re-construct
+ return this;
+ }
+
+ /**
+ * Filter all players that are in the same group as the relative player.
+ *
+ * @param relativePlayer the players to filter
+ * @return a list of players with the same group
+ */
+ @Override
+ public List
+ * The regex for this filter is 'g(\S+)'. In regex capturing group 1 is the group
+ * name that would be used to filter.
+ */
+ private static class GroupFilter extends CacheFilter {
+
+ private final String groupName;
+ private final LuckPermsHook hook;
+
+ /**
+ * Default construct a new group filter.
+ *
+ * @param hook the LuckPerms hook
+ */
+ public GroupFilter(LuckPermsHook hook) {
+ this("", hook);
+ }
+
+ /**
+ * Constructor to construct a new group filter with a group name.
+ *
+ *
+ * This constructor will be used by the {@link #reConstruct(String)} function.
+ *
+ * @param groupName the group name that would be used to filter
+ * @param hook the LuckPerms hook
+ */
+ public GroupFilter(String groupName, LuckPermsHook hook) {
+ super(Pattern.compile("lpg(\\S+);"), "PlayerUI.Filter-Format.LuckPermsGroup", 1);
+ this.groupName = groupName;
+ this.hook = hook;
+ }
+
+ @Override
+ public CacheFilter reConstruct(String promptKey) {
+ var matcher = this.getRegexKey().matcher(promptKey);
+ var found = matcher.find();
+ var groupName = found ? matcher.group(1) : "";
+ return new GroupFilter(groupName, hook);
+ }
+
+ @Override
+ public List
+ * This method will parse the contents of the {@link PromptContext} and
+ * create a {@link PromptQueue} for the sender. It will also parse the
+ * {@link Prompt} and add it to the {@link PromptQueue}.
+ *
+ *
+ * This function returns the hashCode of the {@link PromptQueue} that was
+ * created. This is used to retroactively cancel the prompt within a certain time.
+ *
* @param promptContext Context to parse
* @return hashCode of the {@link PromptQueue} that was created.
*/
public int parsePrompts(PromptContext promptContext) {
var prompts = getPrompts(promptContext);
+ prompts = MMUtil.filterOutMiniMessageTags(prompts);
+
plugin.getPluginLogger().debug("Prompts: " + prompts);
var command = promptContext.getContent().trim();
@@ -120,7 +136,7 @@ public int parsePrompts(PromptContext promptContext) {
var sender = promptContext.getSender();
var promptArgs = ArgumentUtil.findPattern(PromptArgument.class, cleanPrompt);
plugin.getPluginLogger().debug("Prompt args: " + promptArgs);
- var inputValidation = extractInputValidation(cleanPrompt);
+ var inputValidator = extractInputValidation(cleanPrompt);
// Set papi placeholders if exists
var promptTxt = ArgumentUtil.stripArgs(cleanPrompt);
@@ -131,7 +147,7 @@ public int parsePrompts(PromptContext promptContext) {
String.class, List.class)
.newInstance(plugin, promptContext, promptTxt, promptArgs);
- p.setRegexCheck(plugin.getPromptConfig().findIVRegexCheckInConfig(inputValidation));
+ p.setInputValidator(inputValidator);
if (promptArgs.contains(PromptArgument.DISABLE_SANITATION))
p.setInputSanitization(false);
@@ -143,16 +159,16 @@ public int parsePrompts(PromptContext promptContext) {
}
}
return manager.getPromptRegistry().get(promptContext.getSender()).hashCode();
-
}
- private String extractInputValidation(String prompt) {
+ private InputValidator extractInputValidation(String prompt) {
// iv is with pattern -iv:
+ * In cases where the prompt key contains additional information, this method
+ * could be used to reconstruct a certain subclass of {@link CacheFilter}.
+ *
+ *
+ * Additional filter information can be parsed from the {@link PromptParser}
+ * but for better readability, it is recommended to use this method instead.
*
* @param promptKey the prompt key
* @return the cloned cache filter
@@ -135,7 +162,7 @@ public static class RadialFilter extends CacheFilter {
* @param radius the radius
*/
public RadialFilter(int radius) {
- super(Pattern.compile("r(\\d+)"), "PlayerUI.Filter-Format.Radial");
+ super(Pattern.compile("r(\\d+)"), "PlayerUI.Filter-Format.Radial", 1);
this.radius = radius;
}
diff --git a/src/main/java/com/cyr1en/commandprompter/prompt/ui/HeadCache.java b/src/main/java/com/cyr1en/commandprompter/prompt/ui/HeadCache.java
index 38e8826..204ac9d 100644
--- a/src/main/java/com/cyr1en/commandprompter/prompt/ui/HeadCache.java
+++ b/src/main/java/com/cyr1en/commandprompter/prompt/ui/HeadCache.java
@@ -2,7 +2,10 @@
import com.cyr1en.commandprompter.CommandPrompter;
import com.cyr1en.commandprompter.PluginLogger;
+import com.cyr1en.commandprompter.hook.hooks.FilterHook;
+import com.cyr1en.commandprompter.hook.hooks.LuckPermsHook;
import com.cyr1en.commandprompter.hook.hooks.PapiHook;
+import com.cyr1en.commandprompter.hook.hooks.TownyHook;
import com.cyr1en.commandprompter.util.Util;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
@@ -36,7 +39,6 @@ public HeadCache(CommandPrompter plugin) {
this.plugin = plugin;
this.logger = plugin.getPluginLogger();
this.filters = new ArrayList<>();
- registerFilters();
HEAD_CACHE = CacheBuilder.newBuilder().maximumSize(plugin.getPromptConfig().cacheSize())
.build(new CacheLoader<>() {
@Override
@@ -55,15 +57,28 @@ public HeadCache(CommandPrompter plugin) {
});
}
- private void registerFilters() {
+ private static final Class extends FilterHook>[] fHooks = new Class[]{
+ TownyHook.class,
+ LuckPermsHook.class
+ };
+
+ public void registerFilters() {
registerFilter(new CacheFilter.WorldFilter());
registerFilter(new CacheFilter.RadialFilter());
+ for (var fHook : fHooks) {
+ plugin.getHookContainer()
+ .getHook(fHook)
+ .ifHooked(hook -> hook.registerFilters(this))
+ .complete();
+ }
}
public void registerFilter(CacheFilter filter) {
if (Objects.isNull(filter)) return;
- if (!filters.contains(filter))
+ if (!filters.contains(filter)) {
filters.add(filter);
+ logger.debug("Registered filter: " + filter.getClass().getSimpleName());
+ }
}
public List
+ * This will always return true.
+ */
+public class NoopValidator implements InputValidator {
+ @Override
+ public boolean validate(String input) {
+ return true;
+ }
+
+ @Override
+ public String alias() {
+ return "noop";
+ }
+
+ @Override
+ public String messageOnFail() {
+ return "";
+ }
+}
diff --git a/src/main/java/com/cyr1en/commandprompter/prompt/validators/OnlinePlayerValidator.java b/src/main/java/com/cyr1en/commandprompter/prompt/validators/OnlinePlayerValidator.java
new file mode 100644
index 0000000..360cf8f
--- /dev/null
+++ b/src/main/java/com/cyr1en/commandprompter/prompt/validators/OnlinePlayerValidator.java
@@ -0,0 +1,22 @@
+package com.cyr1en.commandprompter.prompt.validators;
+
+import com.cyr1en.commandprompter.api.prompt.InputValidator;
+import org.bukkit.Bukkit;
+
+/**
+ * A validator that checks if the input is an online player.
+ *
+ * @param alias The alias for this validator.
+ * @param messageOnFail The message to send when validation fails.
+ */
+public record OnlinePlayerValidator(String alias, String messageOnFail) implements InputValidator {
+
+ @Override
+ public boolean validate(String input) {
+ if (input == null || input.isBlank())
+ return false;
+ var player = Bukkit.getPlayer(input);
+ return player != null && player.isOnline();
+ }
+
+}
diff --git a/src/main/java/com/cyr1en/commandprompter/prompt/validators/RegexValidator.java b/src/main/java/com/cyr1en/commandprompter/prompt/validators/RegexValidator.java
new file mode 100644
index 0000000..f17179b
--- /dev/null
+++ b/src/main/java/com/cyr1en/commandprompter/prompt/validators/RegexValidator.java
@@ -0,0 +1,34 @@
+package com.cyr1en.commandprompter.prompt.validators;
+
+import com.cyr1en.commandprompter.api.prompt.InputValidator;
+
+import java.util.regex.Pattern;
+
+/**
+ * A validator that uses regex to validate input.
+ *
+ * This validator is used to validate input based on a regex pattern.
+ *
+ * @param alias The alias for this validator.
+ * @param regex The regex pattern to use for validation.
+ * @param messageOnFail The message to send when validation fails.
+ */
+public record RegexValidator(String alias, Pattern regex, String messageOnFail) implements InputValidator {
+
+ @Override
+ public boolean validate(String input) {
+ if (input == null || regex == null)
+ return false;
+ return regex.matcher(input).matches();
+ }
+
+ /**
+ * Get the regex pattern.
+ *
+ * @return the regex pattern.
+ */
+ @Override
+ public Pattern regex() {
+ return regex;
+ }
+}
diff --git a/src/main/java/com/cyr1en/commandprompter/util/MMUtil.java b/src/main/java/com/cyr1en/commandprompter/util/MMUtil.java
new file mode 100644
index 0000000..1174286
--- /dev/null
+++ b/src/main/java/com/cyr1en/commandprompter/util/MMUtil.java
@@ -0,0 +1,28 @@
+package com.cyr1en.commandprompter.util;
+
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.minimessage.MiniMessage;
+import net.kyori.adventure.text.minimessage.tag.standard.StandardTags;
+
+import java.util.List;
+
+public class MMUtil {
+
+ public static List