diff --git a/core/src/main/java/com/sekwah/advancedportals/core/AdvancedPortalsCore.java b/core/src/main/java/com/sekwah/advancedportals/core/AdvancedPortalsCore.java index e7267977..ebf13cde 100644 --- a/core/src/main/java/com/sekwah/advancedportals/core/AdvancedPortalsCore.java +++ b/core/src/main/java/com/sekwah/advancedportals/core/AdvancedPortalsCore.java @@ -6,9 +6,11 @@ import com.sekwah.advancedportals.core.commands.subcommands.desti.CreateDestiSubCommand; import com.sekwah.advancedportals.core.commands.subcommands.portal.*; import com.sekwah.advancedportals.core.connector.commands.CommandRegister; +import com.sekwah.advancedportals.core.registry.TagRegistry; import com.sekwah.advancedportals.core.serializeddata.DataStorage; import com.sekwah.advancedportals.core.module.AdvancedPortalsModule; import com.sekwah.advancedportals.core.repository.ConfigRepository; +import com.sekwah.advancedportals.core.tags.activation.DestiTag; import com.sekwah.advancedportals.core.util.InfoLogger; import com.sekwah.advancedportals.core.util.Lang; @@ -35,6 +37,9 @@ public class AdvancedPortalsCore { @Inject private ConfigRepository configRepository; + @Inject + private TagRegistry tagRegistry; + public AdvancedPortalsCore(File dataStorageLoc, InfoLogger infoLogger) { this.dataStorage = new DataStorage(dataStorageLoc); this.infoLogger = infoLogger; @@ -61,24 +66,26 @@ public void onEnable() { Lang.loadLanguage(configRepository.getTranslation()); this.registerCommands(); + this.registerTags(); this.infoLogger.log(Lang.translate("logger.pluginenable")); } + private void registerTags() { + this.tagRegistry.registerTag(new DestiTag()); + } + /** * */ public void registerCommands() { this.registerPortalCommand(commandRegister); this.registerDestinationCommand(commandRegister); - - // TODO run annotation grabbing shit } private void registerPortalCommand(CommandRegister commandRegister) { this.portalCommand = new CommandWithSubCommands(this); - // TODO remove once annotations are done this.portalCommand.registerSubCommand("version", new VersionSubCommand()); this.portalCommand.registerSubCommand("langupdate", new LangUpdateSubCommand()); this.portalCommand.registerSubCommand("reload", new ReloadSubCommand()); @@ -95,8 +102,6 @@ private void registerPortalCommand(CommandRegister commandRegister) { private void registerDestinationCommand(CommandRegister commandRegister) { this.destiCommand = new CommandWithSubCommands(this); - - // TODO remove once annotations are done this.destiCommand.registerSubCommand("create", new CreateDestiSubCommand()); commandRegister.registerCommand("destination", this.destiCommand); @@ -126,4 +131,8 @@ public DataStorage getDataStorage() { public AdvancedPortalsModule getModule() { return this.module; } + + public TagRegistry getTagRegistry() { + return this.tagRegistry; + } } diff --git a/core/src/main/java/com/sekwah/advancedportals/core/commands/subcommands/portal/CreatePortalSubCommand.java b/core/src/main/java/com/sekwah/advancedportals/core/commands/subcommands/portal/CreatePortalSubCommand.java index bcfb3259..446bb604 100644 --- a/core/src/main/java/com/sekwah/advancedportals/core/commands/subcommands/portal/CreatePortalSubCommand.java +++ b/core/src/main/java/com/sekwah/advancedportals/core/commands/subcommands/portal/CreatePortalSubCommand.java @@ -4,6 +4,7 @@ import com.sekwah.advancedportals.core.commands.SubCommand; import com.sekwah.advancedportals.core.connector.containers.CommandSenderContainer; import com.sekwah.advancedportals.core.connector.containers.PlayerContainer; +import com.sekwah.advancedportals.core.registry.TagRegistry; import com.sekwah.advancedportals.core.serializeddata.DataTag; import com.sekwah.advancedportals.core.permissions.PortalPermissions; import com.sekwah.advancedportals.core.portal.AdvancedPortal; @@ -11,6 +12,7 @@ import com.sekwah.advancedportals.core.util.InfoLogger; import com.sekwah.advancedportals.core.util.Lang; import com.sekwah.advancedportals.core.util.TagReader; +import com.sekwah.advancedportals.core.warphandler.Tag; import java.util.ArrayList; import java.util.Arrays; @@ -24,6 +26,9 @@ public class CreatePortalSubCommand implements SubCommand { @Inject InfoLogger infoLogger; + @Inject + TagRegistry tagRegistry; + @Override public void onCommand(CommandSenderContainer sender, String[] args) { if(args.length > 1) { @@ -67,8 +72,63 @@ public boolean hasPermission(CommandSenderContainer sender) { @Override public List onTabComplete(CommandSenderContainer sender, String[] args) { - // TODO add tab complete for tags - return null; + + if(TagReader.isClosedString(args)) { + return List.of(); + } + + + + List allTags = tagRegistry.getTags(); + List suggestions = new ArrayList<>(); + if(args.length > 0) { + var lastArg = args[args.length - 1]; + // Check if the split results in exactly 2 or if its 1 and ends with : + var split = lastArg.split(":"); + if(split.length == 2 || (split.length == 1 && lastArg.endsWith(":"))) { + // Loop over tags in allTags and check if the first half of split is equal to the tag name or alias + for(Tag tag : allTags) { + if(tag instanceof Tag.AutoComplete autoComplete) { + var tagSuggestions = autoComplete.autoComplete(split.length == 2 ? split[1] : ""); + if(tagSuggestions != null) { + // Loop over suggestions and add split[0] + ":" to the start + for (String tagSuggestion : tagSuggestions) { + suggestions.add(split[0] + ":" + tagSuggestion); + } + } + } + } + // This is returning right but something is going wrong with "desti:A" whenever anything is typed after : + return suggestions; + } + } + + ArrayList portalTags = TagReader.getTagsFromArgs(args); + + allTags.stream().filter(tag -> { + for (DataTag portalTag : portalTags) { + if(portalTag.NAME.equals(tag.getName())) { + return false; + } + // check the tag aliases + for (String alias : tag.getAliases()) { + if(portalTag.NAME.equals(alias)) { + return false; + } + } + } + return true; + }).forEach(tag -> { + suggestions.add(tag.getName()); + suggestions.addAll(Arrays.stream(tag.getAliases()).toList()); + }); + + // Loop over all suggestions and add : to the end + for (int i = 0; i < suggestions.size(); i++) { + suggestions.set(i, suggestions.get(i) + ":"); + } + + return suggestions; } @Override diff --git a/core/src/main/java/com/sekwah/advancedportals/core/destination/Destination.java b/core/src/main/java/com/sekwah/advancedportals/core/destination/Destination.java index 8b56fe6e..46a5529c 100644 --- a/core/src/main/java/com/sekwah/advancedportals/core/destination/Destination.java +++ b/core/src/main/java/com/sekwah/advancedportals/core/destination/Destination.java @@ -27,7 +27,7 @@ public class Destination implements TagTarget { @Inject - TagRegistry tagRegistry; + TagRegistry tagRegistry; @SerializedName("l") private PlayerLocation loc; diff --git a/core/src/main/java/com/sekwah/advancedportals/core/module/AdvancedPortalsModule.java b/core/src/main/java/com/sekwah/advancedportals/core/module/AdvancedPortalsModule.java index 43d78c10..8b44ca4b 100644 --- a/core/src/main/java/com/sekwah/advancedportals/core/module/AdvancedPortalsModule.java +++ b/core/src/main/java/com/sekwah/advancedportals/core/module/AdvancedPortalsModule.java @@ -2,6 +2,7 @@ import com.google.inject.*; import com.sekwah.advancedportals.core.AdvancedPortalsCore; +import com.sekwah.advancedportals.core.registry.TagRegistry; import com.sekwah.advancedportals.core.serializeddata.config.Config; import com.sekwah.advancedportals.core.serializeddata.config.ConfigProvider; import com.sekwah.advancedportals.core.serializeddata.DataStorage; @@ -45,6 +46,7 @@ protected void configure() { // Providers bind(Config.class).toProvider(ConfigProvider.class); + bind(TagRegistry.class).asEagerSingleton(); // Delayed Bindings for(DelayedBinding delayedBinding : delayedBindings) { diff --git a/core/src/main/java/com/sekwah/advancedportals/core/portal/AdvancedPortal.java b/core/src/main/java/com/sekwah/advancedportals/core/portal/AdvancedPortal.java index 359ac163..d11fafdb 100644 --- a/core/src/main/java/com/sekwah/advancedportals/core/portal/AdvancedPortal.java +++ b/core/src/main/java/com/sekwah/advancedportals/core/portal/AdvancedPortal.java @@ -20,7 +20,7 @@ public class AdvancedPortal implements TagTarget { @Inject - transient TagRegistry tagRegistry; + transient TagRegistry tagRegistry; @SerializedName("max") private WorldLocation maxLoc; diff --git a/core/src/main/java/com/sekwah/advancedportals/core/registry/TagRegistry.java b/core/src/main/java/com/sekwah/advancedportals/core/registry/TagRegistry.java index f6612644..4406ae28 100644 --- a/core/src/main/java/com/sekwah/advancedportals/core/registry/TagRegistry.java +++ b/core/src/main/java/com/sekwah/advancedportals/core/registry/TagRegistry.java @@ -2,13 +2,9 @@ import com.google.inject.Inject; import com.sekwah.advancedportals.core.AdvancedPortalsCore; -import com.sekwah.advancedportals.core.portal.AdvancedPortal; import com.sekwah.advancedportals.core.warphandler.Tag; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.*; /** * Allows a portal to register a tag and add a handler. If a plugin wants to add functionality @@ -16,23 +12,18 @@ * * @author sekwah41 */ -public class TagRegistry { +public class TagRegistry { @Inject - private AdvancedPortalsCore portalsCore; + AdvancedPortalsCore portalsCore; - /** - * List of tag names which should be in order alphabetically - */ - private ArrayList tags = new ArrayList(); - /** - * Description of tags for help commands (will try to use translation strings before rendering - * Possibly add a way to allow addons to supply extra info to the Lang class in the future. - */ - private Map tagDesc = new HashMap(); - private Map activationHandlers = new HashMap(); - private Map creationHandlers = new HashMap(); - private Map statusHandlers = new HashMap(); + private final ArrayList literalTags = new ArrayList<>(); + + private final ArrayList tags = new ArrayList<>(); + + private final Map activationTags = new HashMap<>(); + private final Map creationTags = new HashMap<>(); + private final Map statusTags = new HashMap<>(); /** * Portals to trigger when a portal is activated @@ -41,7 +32,7 @@ public class TagRegistry { * @return */ public Tag.Activation getActivationHandler(String arg) { - return this.activationHandlers.get(arg); + return this.activationTags.get(arg); } /** @@ -50,7 +41,7 @@ public Tag.Activation getActivationHandler(String arg) { * @return */ public Tag.Creation getCreationHandler(String arg) { - return this.creationHandlers.get(arg); + return this.creationTags.get(arg); } /** @@ -59,81 +50,57 @@ public Tag.Creation getCreationHandler(String arg) { * @return */ public Tag.TagStatus getTagStatusHandler(String arg) { - return this.statusHandlers.get(arg); + return this.statusTags.get(arg); } - /** - * It is reccomended that you use the taghandlers to add tag functionality. However - * if needed such as extra data for a tag then this is here. - * - * @param tag - * @return if the tag was registered + * File must extend + * @return if the tag has been registered or if it already exists. */ - private boolean registerTag(String tag) { - if (tag.contains(" ")) { - this.portalsCore.getInfoLogger().logWarning("The tag '" - + tag + "' is invalid as it contains spaces."); - return false; - } - if (this.tags.contains(tag)) { - this.portalsCore.getInfoLogger().logWarning("The tag " - + tag + " has already been registered."); - return false; - } + public boolean registerTag(Tag tag) { + + String tagName = tag.getName(); + this.tags.add(tag); - Collections.sort(this.tags); - return true; - } - private boolean registerTag(String tag, String desc) { - if (registerTag(tag)) { - this.tagDesc.put(tag, desc); - return true; + // Check literal tags for clashes + if(this.literalTags.contains(tagName)) { + this.portalsCore.getInfoLogger().logWarning("A tag with the name " + tagName + " already exists."); + return false; } - return false; - } - /** - * Returns a non referenced copy of the array list. - * @return - */ - public ArrayList getTags() { - ArrayList newArrayList = new ArrayList<>(); - newArrayList.addAll(this.tags); - return newArrayList; - } - - public boolean isTagRegistered(String tag){ - return this.tagDesc.containsKey(tag); - } + for (String alias : tag.getAliases()) { + if(this.literalTags.contains(alias)) { + this.portalsCore.getInfoLogger().logWarning("A tag with the alias " + alias + " already exists."); + return false; + } + } - /** - * File must extend - * @return if the tag has been registered or if it already exists. - */ - public boolean registerTag(String tag, Tag tagHandler) { + // Add name and aliases to literalTags to check for clashes + this.literalTags.add(tagName); + Collections.addAll(this.literalTags, tag.getAliases()); - if (tag == null) { + if (tagName == null) { this.portalsCore.getInfoLogger().logWarning("A tag cannot be null."); return false; } - if (!this.registerTag(tag)) { - return false; + if (tag instanceof Tag.Activation tagActivation) { + this.activationTags.put(tagName, tagActivation); } - - if (tagHandler instanceof Tag.Activation tagActivation) { - this.activationHandlers.put(tag, tagActivation); - } - if (tagHandler instanceof Tag.TagStatus tagStatus) { - this.statusHandlers.put(tag, tagStatus); + if (tag instanceof Tag.TagStatus tagStatus) { + this.statusTags.put(tagName, tagStatus); } - if (tagHandler instanceof Tag.Creation tagCreation) { - this.creationHandlers.put(tag, tagCreation); + if (tag instanceof Tag.Creation tagCreation) { + this.creationTags.put(tagName, tagCreation); } return true; } + public List getTags() { + // Make a copy of the list to prevent issues with modification + + return this.tags; + } } diff --git a/core/src/main/java/com/sekwah/advancedportals/core/tags/activation/DestiTag.java b/core/src/main/java/com/sekwah/advancedportals/core/tags/activation/DestiTag.java index f9d7265b..82cabead 100644 --- a/core/src/main/java/com/sekwah/advancedportals/core/tags/activation/DestiTag.java +++ b/core/src/main/java/com/sekwah/advancedportals/core/tags/activation/DestiTag.java @@ -2,10 +2,14 @@ import com.sekwah.advancedportals.core.connector.containers.PlayerContainer; import com.sekwah.advancedportals.core.registry.TagTarget; +import com.sekwah.advancedportals.core.util.Lang; import com.sekwah.advancedportals.core.warphandler.ActivationData; import com.sekwah.advancedportals.core.warphandler.Tag; -public class DestiTag implements Tag.Activation { +import java.util.ArrayList; +import java.util.List; + +public class DestiTag implements Tag.Activation, Tag.AutoComplete { private final TagType[] tagTypes = new TagType[]{ TagType.PORTAL }; @@ -14,6 +18,21 @@ public TagType[] getTagTypes() { return tagTypes; } + @Override + public String getName() { + return "destination"; + } + + @Override + public String[] getAliases() { + return new String[]{"desti"}; + } + + @Override + public String description() { + return Lang.translate("tag.desti.description"); + } + @Override public boolean preActivated(TagTarget target, PlayerContainer player, ActivationData activeData, String[] argData) { return false; @@ -28,4 +47,15 @@ public void postActivated(TagTarget target, PlayerContainer player, ActivationDa public boolean activated(TagTarget target, PlayerContainer player, ActivationData activeData, String[] argData) { return false; } + + @Override + public List autoComplete(String argData) { + List autoCompletes = new ArrayList<>(); + // Get all and filter by the argData + + autoCompletes.add("AIR"); + autoCompletes.add("WATER"); + + return autoCompletes; + } } diff --git a/core/src/main/java/com/sekwah/advancedportals/core/util/TagReader.java b/core/src/main/java/com/sekwah/advancedportals/core/util/TagReader.java index 627e2331..f80075ea 100644 --- a/core/src/main/java/com/sekwah/advancedportals/core/util/TagReader.java +++ b/core/src/main/java/com/sekwah/advancedportals/core/util/TagReader.java @@ -7,6 +7,30 @@ public class TagReader { + public static boolean isClosedString(String[] args) { + StringBuilder currentValue = new StringBuilder(); + boolean inQuotes = false; + + for (String arg : args) { + if (arg.contains(":") && !inQuotes) { + int colonIndex = arg.indexOf(':'); + currentValue = new StringBuilder(arg.substring(colonIndex + 1)); + inQuotes = currentValue.toString().startsWith("\""); + } else { + if (!currentValue.isEmpty()) { + currentValue.append(" "); + } + currentValue.append(arg); + } + + if (inQuotes && arg.endsWith("\"")) { + inQuotes = false; + } + } + + return inQuotes; + } + public static ArrayList getTagsFromArgs(String[] args) { HashMap> tagMap = new HashMap<>(); StringBuilder currentValue = new StringBuilder(); diff --git a/core/src/main/java/com/sekwah/advancedportals/core/warphandler/Tag.java b/core/src/main/java/com/sekwah/advancedportals/core/warphandler/Tag.java index 8b4dd19d..bada7c22 100644 --- a/core/src/main/java/com/sekwah/advancedportals/core/warphandler/Tag.java +++ b/core/src/main/java/com/sekwah/advancedportals/core/warphandler/Tag.java @@ -3,6 +3,8 @@ import com.sekwah.advancedportals.core.connector.containers.PlayerContainer; import com.sekwah.advancedportals.core.registry.TagTarget; +import java.util.List; + /** * If a tag can be used for any of them then either make it cast the target or if it doesn't need a target * then the exact same tag can be registered into both and ignore the portal info. @@ -28,6 +30,24 @@ enum TagType { */ TagType[] getTagTypes(); + String getName(); + + String[] getAliases(); + + String description(); + + interface AutoComplete extends Tag { + + /** + * This is used to get the auto complete for the tag. This is called when the player is typing the tag. + * + * @param argData + * @return + */ + List autoComplete(String argData); + + } + /** * The events for portal creation and destroying */ diff --git a/lang/src/main/resources/lang/en_GB.lang b/lang/src/main/resources/lang/en_GB.lang index e3049863..f24672e3 100644 --- a/lang/src/main/resources/lang/en_GB.lang +++ b/lang/src/main/resources/lang/en_GB.lang @@ -116,3 +116,5 @@ items.selector.name=Portal Region Selector items.selector.pos=Select pos %1$s items.interact.left=Left Click items.interact.right=Right Click + +tag.desti.description=Sets the destination of the portal