diff --git a/Datura/src/main/java/me/totalfreedom/datura/cmd/KickCommand.java b/Datura/src/main/java/me/totalfreedom/datura/cmd/KickCommand.java new file mode 100644 index 0000000..0746fb2 --- /dev/null +++ b/Datura/src/main/java/me/totalfreedom/datura/cmd/KickCommand.java @@ -0,0 +1,26 @@ +package me.totalfreedom.datura.cmd; + +import me.totalfreedom.command.CommandBase; +import me.totalfreedom.command.annotation.Completion; +import me.totalfreedom.command.annotation.Info; +import me.totalfreedom.command.annotation.Permissive; +import me.totalfreedom.command.annotation.Subcommand; +import me.totalfreedom.datura.Datura; +import org.bukkit.entity.Player; + +@Completion(args = {"%player%"}, index = 0) +@Info(name = "kick", description = "Kick a player from the server.", usage = "/kick ") +@Permissive(perm = "datura.kick") +public class KickCommand extends CommandBase +{ + protected KickCommand(final Datura plugin) + { + super(plugin); + } + + @Subcommand(permission = "datura.kick", args = {Player.class}) + public void kick(final Player player) + { + player.kickPlayer("You have been kicked from the server."); + } +} diff --git a/Fossil/src/main/java/me/totalfreedom/fossil/command/KickCommand.java b/Fossil/src/main/java/me/totalfreedom/fossil/command/KickCommand.java deleted file mode 100644 index 898f1aa..0000000 --- a/Fossil/src/main/java/me/totalfreedom/fossil/command/KickCommand.java +++ /dev/null @@ -1,30 +0,0 @@ -package me.totalfreedom.fossil.command; - -import me.totalfreedom.command.CommandBase; -import me.totalfreedom.command.annotation.Base; -import me.totalfreedom.command.annotation.Info; -import me.totalfreedom.command.annotation.Permissive; -import me.totalfreedom.command.annotation.Subcommand; -import me.totalfreedom.fossil.Fossil; -import net.kyori.adventure.text.Component; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -@Info(name = "kick", description = "Kick a player", usage = "/ ") -@Permissive(perm = "fossil.kick") -public class KickCommand extends CommandBase -{ - public KickCommand(final Fossil plugin) { - super(plugin); - } - - @Base - public void run(final CommandSender sender) { - sender.sendMessage(Component.text("You must specify a player to kick.")); - } - - @Subcommand(permission = "fossil.kick", args = {Player.class, String.class}) - public void kickPlayer(final Player player, final String string) { - player.kick(Component.text(string)); - } -} diff --git a/Fossil/src/main/resources/plugin.yml b/Fossil/src/main/resources/plugin.yml new file mode 100644 index 0000000..99f537c --- /dev/null +++ b/Fossil/src/main/resources/plugin.yml @@ -0,0 +1,8 @@ +name: Fossil +version: 1.0 +main: me.totalfreedom.fossil.Fossil +author: TotalFreedom +description: The Fun Module for the Freedom Network. +depend: + - Datura + - Patchwork \ No newline at end of file diff --git a/Patchwork/src/main/java/me/totalfreedom/api/Context.java b/Patchwork/src/main/java/me/totalfreedom/api/Context.java index e574434..2a837d7 100644 --- a/Patchwork/src/main/java/me/totalfreedom/api/Context.java +++ b/Patchwork/src/main/java/me/totalfreedom/api/Context.java @@ -11,11 +11,18 @@ import org.bukkit.event.block.Action; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.function.Function; + @FunctionalInterface public interface Context { T get(); + default Context map(@NotNull final Function mapper) + { + return () -> mapper.apply(get()); + } + default @Nullable String asString() { if (get() instanceof String string) diff --git a/Patchwork/src/main/java/me/totalfreedom/command/BukkitDelegator.java b/Patchwork/src/main/java/me/totalfreedom/command/BukkitDelegator.java index eb249e8..93ca292 100644 --- a/Patchwork/src/main/java/me/totalfreedom/command/BukkitDelegator.java +++ b/Patchwork/src/main/java/me/totalfreedom/command/BukkitDelegator.java @@ -1,19 +1,25 @@ package me.totalfreedom.command; import me.totalfreedom.api.Context; +import me.totalfreedom.command.annotation.Completion; import me.totalfreedom.command.annotation.Subcommand; import me.totalfreedom.provider.ContextProvider; import me.totalfreedom.utils.FreedomLogger; import net.kyori.adventure.text.Component; +import org.bukkit.Bukkit; +import org.bukkit.World; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.command.PluginIdentifiableCommand; +import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Set; public class BukkitDelegator extends Command implements PluginIdentifiableCommand @@ -25,8 +31,8 @@ public class BukkitDelegator extends Command implements PluginIdentifiableComman BukkitDelegator(final JavaPlugin plugin, final CommandBase command) { super(command.getInfo().name()); - this.plugin = plugin; this.command = command; + this.plugin = command.getPlugin(); this.setDescription(command.getInfo().description()); this.setUsage(command.getInfo().usage()); this.setPermission(command.getPerms().perm()); @@ -80,7 +86,7 @@ public class BukkitDelegator extends Command implements PluginIdentifiableComman final Context context = () -> provider.fromString(arg); if (!argType.isInstance(context.get())) { - throw new IllegalStateException(); + throw new IllegalStateException(); // TODO: Change this. } objects = Arrays.copyOf(objects, objects.length + 1); objects[objects.length - 1] = context.get(); @@ -113,6 +119,50 @@ public class BukkitDelegator extends Command implements PluginIdentifiableComman return true; } + @Override + public List tabComplete(final CommandSender sender, final String alias, final String[] args) + { + final Set completions = command.getCompletions(); + final List results = new ArrayList<>(); + for (final Completion completion : completions) + { + if (completion.index() != args.length) + { + continue; + } + + for (final String p : completion.args()) + { + switch (p) + { + case "%player%" -> results.addAll(Bukkit.getOnlinePlayers() + .stream() + .map(Player::getName) + .toList()); + case "%world%" -> results.addAll(Bukkit.getWorlds() + .stream() + .map(World::getName) + .toList()); + case "%number%" -> results.addAll(List.of( + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9")); + case "%location%" -> results.add("world,x,y,z"); + default -> results.add(p); + } + } + } + + return results.stream().filter(s -> s.startsWith(args[args.length - 1])).toList(); + } + @Override public @NotNull Plugin getPlugin() { diff --git a/Patchwork/src/main/java/me/totalfreedom/command/CommandBase.java b/Patchwork/src/main/java/me/totalfreedom/command/CommandBase.java index 9b90197..3e0accc 100644 --- a/Patchwork/src/main/java/me/totalfreedom/command/CommandBase.java +++ b/Patchwork/src/main/java/me/totalfreedom/command/CommandBase.java @@ -1,15 +1,14 @@ package me.totalfreedom.command; -import me.totalfreedom.command.annotation.Base; -import me.totalfreedom.command.annotation.Info; -import me.totalfreedom.command.annotation.Permissive; -import me.totalfreedom.command.annotation.Subcommand; +import me.totalfreedom.command.annotation.*; import me.totalfreedom.utils.Pair; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.command.CommandSender; import org.bukkit.plugin.java.JavaPlugin; import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import java.util.stream.Stream; public abstract class CommandBase @@ -18,6 +17,7 @@ public abstract class CommandBase private final Info info; private final Permissive perms; private final Map subcommands; + private final Set completions; private final Pair baseMethodPair; protected CommandBase(final JavaPlugin plugin) @@ -26,6 +26,7 @@ public abstract class CommandBase this.perms = this.getClass().getDeclaredAnnotation(Permissive.class); this.plugin = plugin; this.subcommands = new HashMap<>(); + this.completions = new HashSet<>(); if (this.getClass().isAnnotationPresent(Base.class)) { @@ -40,9 +41,20 @@ public abstract class CommandBase this.baseMethodPair = null; } + registerAnnotations(); + } + + private void registerAnnotations() + { Stream.of(this.getClass().getDeclaredMethods()) .filter(method -> method.isAnnotationPresent(Subcommand.class)) - .forEach(method -> this.subcommands.put(method.getDeclaredAnnotation(Subcommand.class), method)); + .forEach(method -> this.subcommands.put( + method.getDeclaredAnnotation(Subcommand.class), + method)); + + List.of(this.getClass().getDeclaredAnnotationsByType(Completion.class)) + .stream() + .forEach(completions::add); } public Pair getBaseMethodPair() @@ -69,4 +81,9 @@ public abstract class CommandBase { return this.subcommands; } + + Set getCompletions() + { + return this.completions; + } } diff --git a/Patchwork/src/main/java/me/totalfreedom/command/annotation/Completion.java b/Patchwork/src/main/java/me/totalfreedom/command/annotation/Completion.java new file mode 100644 index 0000000..82002af --- /dev/null +++ b/Patchwork/src/main/java/me/totalfreedom/command/annotation/Completion.java @@ -0,0 +1,15 @@ +package me.totalfreedom.command.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface Completion +{ + String[] args(); + + int index(); +} diff --git a/Patchwork/src/main/java/me/totalfreedom/command/annotation/Subcommand.java b/Patchwork/src/main/java/me/totalfreedom/command/annotation/Subcommand.java index de4945e..76fb1d3 100644 --- a/Patchwork/src/main/java/me/totalfreedom/command/annotation/Subcommand.java +++ b/Patchwork/src/main/java/me/totalfreedom/command/annotation/Subcommand.java @@ -6,8 +6,6 @@ import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface Subcommand { - String name() default ""; - String permission(); Class[] args() default {}; diff --git a/Patchwork/src/main/java/me/totalfreedom/particle/Trail.java b/Patchwork/src/main/java/me/totalfreedom/particle/Trail.java new file mode 100644 index 0000000..68707fb --- /dev/null +++ b/Patchwork/src/main/java/me/totalfreedom/particle/Trail.java @@ -0,0 +1,11 @@ +package me.totalfreedom.particle; + +import org.bukkit.Color; +import org.jetbrains.annotations.Nullable; + +public interface Trail +{ + TrailType getTrailType(); + + @Nullable Color getColor(); +} diff --git a/Patchwork/src/main/java/me/totalfreedom/particle/TrailType.java b/Patchwork/src/main/java/me/totalfreedom/particle/TrailType.java new file mode 100644 index 0000000..e37fae4 --- /dev/null +++ b/Patchwork/src/main/java/me/totalfreedom/particle/TrailType.java @@ -0,0 +1,32 @@ +package me.totalfreedom.particle; + +import org.bukkit.Particle; + +public enum TrailType +{ + DEFAULT(Particle.REDSTONE), + HEART(Particle.HEART), + FLAME(Particle.FLAME), + RAINBOW(Particle.REDSTONE), + MUSIC(Particle.NOTE), + SNOW(Particle.SNOWBALL), + SPELL(Particle.SPELL_MOB), + SPELL_AMBIENT(Particle.SPELL_MOB_AMBIENT), + PORTAL(Particle.PORTAL), + ENCHANTMENT(Particle.ENCHANTMENT_TABLE), + STROBE(Particle.DUST_COLOR_TRANSITION), + VIBRATION(Particle.VIBRATION), + SPARK(Particle.ELECTRIC_SPARK); + + final Particle type; + + TrailType(final Particle type) + { + this.type = type; + } + + public Particle getType() + { + return type; + } +}