From 5278d9282e372058ce0b97f07510edc100628474 Mon Sep 17 00:00:00 2001 From: JvstvsHD <79066214+JvstvsHD@users.noreply.github.com> Date: Sun, 21 Jul 2024 14:43:24 +0200 Subject: [PATCH 1/7] Initialize work for #11 Add a base structure for all commands --- gradle/libs.versions.toml | 1 + .../necrify/common/AbstractNecrifyPlugin.java | 26 ++++++- .../common/commands/NecrifyCommand.java | 18 +++++ plugin/build.gradle.kts | 1 + .../velocity/NecrifyVelocityPlugin.java | 76 ++++++++++++++++--- .../necrify/velocity/config/ConfigData.java | 17 ++++- 6 files changed, 124 insertions(+), 15 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ee6332d..468e572 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -43,6 +43,7 @@ adventure-api = { group = "net.kyori", name = "adventure-api", version.ref = "ad adventure-text-minimessage = { group = "net.kyori", name = "adventure-text-minimessage", version.ref = "adventure" } cloud-core = { group = "org.incendo", name = "cloud-core", version.ref = "cloud" } cloud-annotations = { group = "org.incendo", name = "cloud-annotations", version.ref = "cloud" } +cloud-velocity = { group = "org.incendo", name = "cloud-velocity", version.ref = "cloud" } jetbrains-annotations = { group = "org.jetbrains", name = "annotations", version.ref = "jetbrains-annotations" } eventbus = { group = "org.greenrobot", name = "eventbus-java", version.ref = "eventbus" } slf4j-api = { group = "org.slf4j", name = "slf4j-api", version.ref = "slf4j" } diff --git a/plugin-common/src/main/java/de/jvstvshd/necrify/common/AbstractNecrifyPlugin.java b/plugin-common/src/main/java/de/jvstvshd/necrify/common/AbstractNecrifyPlugin.java index 53b9172..1321bee 100644 --- a/plugin-common/src/main/java/de/jvstvshd/necrify/common/AbstractNecrifyPlugin.java +++ b/plugin-common/src/main/java/de/jvstvshd/necrify/common/AbstractNecrifyPlugin.java @@ -26,10 +26,14 @@ import de.jvstvshd.necrify.api.Necrify; import de.jvstvshd.necrify.api.user.NecrifyUser; +import de.jvstvshd.necrify.common.commands.NecrifyCommand; import de.jvstvshd.necrify.common.punishment.NecrifyKick; import net.kyori.adventure.text.Component; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.annotations.AnnotationParser; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; import java.util.UUID; import java.util.concurrent.ExecutorService; @@ -41,9 +45,6 @@ public AbstractNecrifyPlugin(ExecutorService executorService) { this.executorService = executorService; } - public AbstractNecrifyPlugin() { - } - @Override public @NotNull ExecutorService getExecutor() { return executorService; @@ -55,4 +56,23 @@ public AbstractNecrifyPlugin() { } public abstract NecrifyKick createKick(Component reason, NecrifyUser user, UUID punishmentUuid); + + /** + * Registers commands for the plugin via the {@link AnnotationParser} from the cloud framework. It is possible to only + * register the commands of the /necrify root, but also the top-level ones (e.g. /ban, /kick, etc.). + * + * @param manager the command manager to register the commands to. + * @param topLevelCommands whether to register top-level commands (/ban, /kick, etc.) or not (i.e. only /necrify commands). + */ + public final void registerCommands(CommandManager manager, boolean topLevelCommands) { + AnnotationParser parser = new AnnotationParser<>(manager, NecrifyUser.class); + final var oldExtractor = parser.commandExtractor(); + if (!topLevelCommands) { + parser.commandExtractor(instance -> { + var commands = new ArrayList<>(oldExtractor.extractCommands(instance)); + return commands.stream().filter(commandDescriptor -> commandDescriptor.commandToken().startsWith("necrify")).toList(); + }); + } + parser.parse(new NecrifyCommand(this)); + } } diff --git a/plugin-common/src/main/java/de/jvstvshd/necrify/common/commands/NecrifyCommand.java b/plugin-common/src/main/java/de/jvstvshd/necrify/common/commands/NecrifyCommand.java index d40225a..84fd0e3 100644 --- a/plugin-common/src/main/java/de/jvstvshd/necrify/common/commands/NecrifyCommand.java +++ b/plugin-common/src/main/java/de/jvstvshd/necrify/common/commands/NecrifyCommand.java @@ -24,7 +24,25 @@ package de.jvstvshd.necrify.common.commands; +import de.jvstvshd.necrify.common.AbstractNecrifyPlugin; +import org.incendo.cloud.annotations.Command; + public class NecrifyCommand { + private final AbstractNecrifyPlugin plugin; + + public NecrifyCommand(AbstractNecrifyPlugin plugin) { + this.plugin = plugin; + } + + @Command("necrify ban") + @Command("ban") + public void banCommand() { + + } + + @Command("necrify kick") + public void kickCommand() { + } } diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 6db4e67..4b1267a 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -14,6 +14,7 @@ repositories { dependencies { api(projects.pluginCommon) + api(libs.cloud.velocity) annotationProcessor(libs.velocity.api) compileOnly(libs.velocity.api) compileOnly(libs.luckperms.api) diff --git a/plugin/src/main/java/de/jvstvshd/necrify/velocity/NecrifyVelocityPlugin.java b/plugin/src/main/java/de/jvstvshd/necrify/velocity/NecrifyVelocityPlugin.java index 4901db7..fb76312 100644 --- a/plugin/src/main/java/de/jvstvshd/necrify/velocity/NecrifyVelocityPlugin.java +++ b/plugin/src/main/java/de/jvstvshd/necrify/velocity/NecrifyVelocityPlugin.java @@ -28,12 +28,18 @@ import com.google.common.base.Joiner; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.inject.Inject; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.TypeLiteral; import com.velocitypowered.api.command.CommandManager; +import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.event.EventManager; import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; import com.velocitypowered.api.plugin.Plugin; import com.velocitypowered.api.plugin.annotation.DataDirectory; +import com.velocitypowered.api.proxy.ConsoleCommandSource; +import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier; @@ -73,6 +79,10 @@ import de.jvstvshd.necrify.velocity.user.VelocityUser; import de.jvstvshd.necrify.velocity.user.VelocityUserManager; import net.kyori.adventure.text.Component; +import org.incendo.cloud.SenderMapper; +import org.incendo.cloud.execution.ExecutionCoordinator; +import org.incendo.cloud.velocity.CloudInjectionModule; +import org.incendo.cloud.velocity.VelocityCommandManager; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; @@ -105,6 +115,9 @@ public class NecrifyVelocityPlugin extends AbstractNecrifyPlugin { private final MessagingChannelCommunicator communicator; private EventDispatcher eventDispatcher; + @Inject + private Injector injector; + /** * Since 1.19.1, cancelling chat messages on proxy is not possible anymore. Therefore, we have to listen to the chat event on the actual game server. This means * that there has to be a spigot/paper extension to this plugin which is not yet available unless there's a possibility. Therefore all mute related features are disabled for now. @@ -178,6 +191,14 @@ private void setup(CommandManager commandManager, EventManager eventManager) { commandManager.register(TempmuteCommand.tempmuteCommand(this)); commandManager.register(KickCommand.kickCommand(this)); commandManager.register(WhitelistCommand.whitelistCommand(this)); + + final Injector childInjector = injector.createChildInjector( + new CloudInjectionModule<>( + NecrifyUser.class, + ExecutionCoordinator.simpleCoordinator(), + SenderMapper.create(this::createUser, this::getCommandSource))); + registerCommands(childInjector.getInstance(Key.get(new TypeLiteral>() { + })), getConfig().getConfiguration().isAllowTopLevelCommands()); } @SuppressWarnings({"unchecked", "UnstableApiUsage"}) @@ -278,19 +299,54 @@ public void setUserManager(@NotNull UserManager userManager) { public CompletableFuture> getPunishment(@NotNull UUID punishmentId) { return Util.executeAsync(() -> (Optional) Query.query("SELECT u.* FROM punishment.necrify_user u " + "INNER JOIN punishment.necrify_punishment p ON u.uuid = p.uuid " + "WHERE p.punishment_id = ?;").single(Call.of().bind(punishmentId, Adapters.UUID_ADAPTER)).map(row -> { var userId = row.getObject(1, UUID.class); - var cachedUser = getUserManager().getUser(userId); - if (cachedUser.isPresent()) { - return cachedUser.get().getPunishment(punishmentId).orElse(null); - } - var user = new VelocityUser(row.getObject(1, UUID.class), row.getString(2), row.getBoolean(3), this); - getExecutor().execute(() -> { - Query.query("SELECT type, expiration, reason, punishment_id FROM punishment.necrify_punishment WHERE uuid = ?;").single(Call.of().bind(userId, Adapters.UUID_ADAPTER)).map(user::addPunishment).all(); - getEventDispatcher().dispatch(new UserLoadedEvent(user).setOrigin(EventOrigin.ofClass(getClass()))); - }); - return user.getPunishment(punishmentId).orElse(null); + return createUser(userId).getPunishment(punishmentId).orElse(null); }).first(), getExecutor()); } + public NecrifyUser createUser(CommandSource source) { + if (source instanceof Player) { + return createUser(((Player) source).getUniqueId()); + } else if (source instanceof ConsoleCommandSource) { + return new VelocityUser(new UUID(0, 0), "CONSOLE", true, this); + } else { + return new VelocityUser(UUID.randomUUID(), "unknown_source", false, this); + } + } + + /** + * Creates a user with the given UUID. If the user is already cached, the cached user is returned. + *

Note: this user does not hold any valid data besides his uuid and maybe player instance (if online). After returning + * the value, the missing user data will be loaded, whereafter the {@link UserLoadedEvent} will be fired.

+ * + * @param userId the UUID of the user to create. + * @return the created user. + */ + public NecrifyUser createUser(UUID userId) { + var cachedUser = getUserManager().getUser(userId); + if (cachedUser.isPresent()) { + return cachedUser.get(); + } + var user = new VelocityUser(userId, "unknown", false, this); + getExecutor().execute(() -> { + Query.query("SELECT type, expiration, reason, punishment_id FROM punishment.necrify_punishment WHERE uuid = ?;").single(Call.of().bind(userId, Adapters.UUID_ADAPTER)).map(user::addPunishment).all(); + getEventDispatcher().dispatch(new UserLoadedEvent(user).setOrigin(EventOrigin.ofClass(getClass()))); + }); + return user; + } + + public CommandSource getCommandSource(NecrifyUser user) { + if (user instanceof VelocityUser velocityUser) { + var player = velocityUser.getPlayer(); + if (player != null) { + return player; + } + } + if ("console".equalsIgnoreCase(user.getUsername()) && user.getUuid().equals(new UUID(0, 0))) { + return server.getConsoleCommandSource(); + } + return server.getPlayer(user.getUuid()).orElse(null); + } + @Override public @NotNull EventDispatcher getEventDispatcher() { return eventDispatcher; diff --git a/plugin/src/main/java/de/jvstvshd/necrify/velocity/config/ConfigData.java b/plugin/src/main/java/de/jvstvshd/necrify/velocity/config/ConfigData.java index 72ae492..d4e0419 100644 --- a/plugin/src/main/java/de/jvstvshd/necrify/velocity/config/ConfigData.java +++ b/plugin/src/main/java/de/jvstvshd/necrify/velocity/config/ConfigData.java @@ -42,14 +42,19 @@ public class ConfigData { @JsonAlias("whitelistActivated") private boolean whitelistActivated; - public ConfigData(DataBaseData dataBaseData, Locale defaultLanguage, boolean whitelistActivated) { + @JsonProperty("allow-top-level-commands") + @JsonAlias("allowTopLevelCommands") + private boolean allowTopLevelCommands; + + public ConfigData(DataBaseData dataBaseData, Locale defaultLanguage, boolean whitelistActivated, boolean allowTopLevelCommands) { this.dataBaseData = dataBaseData; this.defaultLanguage = defaultLanguage; this.whitelistActivated = whitelistActivated; + this.allowTopLevelCommands = allowTopLevelCommands; } public ConfigData() { - this(new DataBaseData(), Locale.ENGLISH, false); + this(new DataBaseData(), Locale.ENGLISH, false, true); } public final DataBaseData getDataBaseData() { @@ -67,4 +72,12 @@ public boolean isWhitelistActivated() { public void setWhitelistActivated(boolean whitelistActivated) { this.whitelistActivated = whitelistActivated; } + + public boolean isAllowTopLevelCommands() { + return allowTopLevelCommands; + } + + public void setAllowTopLevelCommands(boolean allowTopLevelCommands) { + this.allowTopLevelCommands = allowTopLevelCommands; + } } From a7c5688189634175d0105b307b7888f051adcdc4 Mon Sep 17 00:00:00 2001 From: JvstvsHD <79066214+JvstvsHD@users.noreply.github.com> Date: Wed, 24 Jul 2024 13:58:19 +0200 Subject: [PATCH 2/7] Add .gitignore --- .gitignore | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ffcd4bc --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.gradle +.idea +build +necrify-api/build +necrify-common/build +necrify-paper/build +necrify-velocity/build +necrify-velocity/run \ No newline at end of file From 5aadb5944a3ed72209c37f0ca6978b63309fff9c Mon Sep 17 00:00:00 2001 From: JvstvsHD <79066214+JvstvsHD@users.noreply.github.com> Date: Fri, 26 Jul 2024 18:30:31 +0200 Subject: [PATCH 3/7] Enhance CommandSender - Added dedicated console user - Added more methods to send messages easier - Added documentation in CommandSender.java - Fixed some smaller bugs --- .../necrify/api/user/CommandSender.java | 38 ++++++ .../necrify/api/user/NecrifyUser.java | 3 + .../necrify/common/AbstractNecrifyPlugin.java | 53 +++++++- .../common/user/AbstractConsoleUser.java | 114 ++++++++++++++++++ .../main/resources/translations/de.properties | 2 +- necrify-velocity/build.gradle.kts | 2 +- .../velocity/impl/DefaultPlayerResolver.java | 6 +- .../velocity/internal/PunishmentHelper.java | 2 +- .../necrify/velocity/internal/Util.java | 10 +- .../ResourceBundleMessageProvider.java | 5 +- .../velocity/user/VelocityConsoleUser.java | 39 ++++++ .../necrify/velocity/user/VelocityUser.java | 45 ++++--- .../velocity/user/VelocityUserManager.java | 10 +- 13 files changed, 297 insertions(+), 32 deletions(-) create mode 100644 necrify-common/src/main/java/de/jvstvshd/necrify/common/user/AbstractConsoleUser.java create mode 100644 necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/user/VelocityConsoleUser.java diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/user/CommandSender.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/user/CommandSender.java index b49df39..89bf910 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/user/CommandSender.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/user/CommandSender.java @@ -24,12 +24,50 @@ package de.jvstvshd.necrify.api.user; +import de.jvstvshd.necrify.api.message.MessageProvider; import net.kyori.adventure.text.Component; import org.jetbrains.annotations.NotNull; +import java.util.Locale; + +/** + * Represents an entity that is able to interact with the server via messages and may send commands to it. It is also + * able to hold permissions. + */ public interface CommandSender { + /** + * Sends a message to the command sender. The message may be given in form of a {@link net.kyori.adventure.text.TranslatableComponent}, + * which will be translated to the correct language when being displayed. + * @param message a non-null component that represents the message to be sent. + */ void sendMessage(@NotNull Component message); + /** + * Sends a message to the command sender. The message must contain a valid translation key that is present in the + * language files of the server. The message will be translated to the correct language when being displayed, if a + * translation is available. If this key does not map to a translation, the key itself will be displayed. + *

+ * Per default, this will use {@link de.jvstvshd.necrify.api.message.MessageProvider#provide(String, Component...)} + * @param key a non-null string that represents the translation key of the message to be sent. + */ + void sendMessage(@NotNull String key, Component... args); + + /** + * Sends an error message to the command sender. This should be used to inform the command sender about an error + * that happened within the command execution, e.g. while updating data in the database. Ideally, this yields the same + * result as {@code sendMessage("error.internal")} content-wise, but may differ style-wise. + * @see #sendMessage(String, Component...) + * @see MessageProvider#internalError() + * @see MessageProvider#internalError(Locale) + */ + void sendErrorMessage(); + + /** + * Checks whether the command sender has a certain permission. This requires a permission system to be set up + * and the entity in question to exist. + * @param permission a non-null string that represents the permission to be checked. + * @return true if the command sender has the permission, false otherwise. + */ boolean hasPermission(@NotNull String permission); } diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/user/NecrifyUser.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/user/NecrifyUser.java index ba2b913..024f4bf 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/user/NecrifyUser.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/user/NecrifyUser.java @@ -32,6 +32,7 @@ import org.jetbrains.annotations.Nullable; import java.util.List; +import java.util.Locale; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -188,6 +189,8 @@ default Optional getPunishment(@NotNull UUID punishmentUuid) { return getPunishments().stream().filter(punishment -> punishment.getPunishmentUuid().equals(punishmentUuid)).findFirst(); } + Locale getLocale(); + /* *//** * Method to add punishments to users. This method is only meant to be used until events are implemented and all * other components can be used so this method will not be used. diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/AbstractNecrifyPlugin.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/AbstractNecrifyPlugin.java index 1321bee..e4d7dfb 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/AbstractNecrifyPlugin.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/AbstractNecrifyPlugin.java @@ -25,15 +25,30 @@ package de.jvstvshd.necrify.common; import de.jvstvshd.necrify.api.Necrify; +import de.jvstvshd.necrify.api.punishment.StandardPunishmentType; import de.jvstvshd.necrify.api.user.NecrifyUser; import de.jvstvshd.necrify.common.commands.NecrifyCommand; +import de.jvstvshd.necrify.common.commands.NecrifyUserParser; import de.jvstvshd.necrify.common.punishment.NecrifyKick; +import de.jvstvshd.necrify.common.commands.UserNotFoundParseException; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import org.incendo.cloud.CommandManager; import org.incendo.cloud.annotations.AnnotationParser; +import org.incendo.cloud.exception.ArgumentParseException; +import org.incendo.cloud.exception.handling.ExceptionHandler; +import org.incendo.cloud.minecraft.extras.parser.ComponentParser; +import org.incendo.cloud.parser.ParserDescriptor; +import org.incendo.cloud.parser.standard.StringParser; +import org.incendo.cloud.type.tuple.Pair; import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; import java.util.ArrayList; +import java.util.List; +import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutorService; @@ -55,8 +70,6 @@ public AbstractNecrifyPlugin(ExecutorService executorService) { return getExecutor(); } - public abstract NecrifyKick createKick(Component reason, NecrifyUser user, UUID punishmentUuid); - /** * Registers commands for the plugin via the {@link AnnotationParser} from the cloud framework. It is possible to only * register the commands of the /necrify root, but also the top-level ones (e.g. /ban, /kick, etc.). @@ -73,6 +86,40 @@ public final void registerCommands(CommandManager manager, boolean return commands.stream().filter(commandDescriptor -> commandDescriptor.commandToken().startsWith("necrify")).toList(); }); } - parser.parse(new NecrifyCommand(this)); + + manager.exceptionController() + .registerHandler(ArgumentParseException.class, ExceptionHandler.unwrappingHandler(UserNotFoundParseException.class)) + .registerHandler(UserNotFoundParseException.class, context -> { + context.context().sender().sendMessage("commands.general.not-found", Component.text(context.exception().playerName()).color(NamedTextColor.YELLOW)); + }); + /*manager.exceptionController()//.registerHandler(ArgumentParseException.class, ExceptionHandler.unwrappingHandler(ArgumentParseException.class)) + .registerHandler(ArgumentParseException.class, context -> { + context.context().sender().sendMessage("commands.general.invalid-argument"); + System.out.println(context.exception().getCause()); + });*/ + manager.captionRegistry().registerProvider((caption, user) -> { + var component = getMessageProvider().provide(caption.key(), user.getLocale()); + return PlainTextComponentSerializer.plainText().serialize(component); + }); + var parserRegistry = manager.parserRegistry(); + parserRegistry.registerParser(ParserDescriptor.of(new NecrifyUserParser(this.getUserManager()), NecrifyUser.class)); + parserRegistry.registerParser(ComponentParser.componentParser(MiniMessage.miniMessage(), StringParser.StringMode.GREEDY_FLAG_YIELDING)); + var commands = new NecrifyCommand(this); + parser.parse(commands); + } + + //TODO: Move config to necrify-common + public String getDefaultReason(StandardPunishmentType type) { + return "You were " + switch (type) { + case KICK -> "kicked from the server."; + case BAN, PERMANENT_BAN -> "banned from the server."; + case MUTE, PERMANENT_MUTE -> "muted."; + } + ""; } + + public abstract NecrifyKick createKick(Component reason, NecrifyUser user, UUID punishmentUuid); + + public abstract Logger getLogger(); + + public abstract Set> getOnlinePlayers(); } diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/user/AbstractConsoleUser.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/user/AbstractConsoleUser.java new file mode 100644 index 0000000..5c5d999 --- /dev/null +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/user/AbstractConsoleUser.java @@ -0,0 +1,114 @@ +package de.jvstvshd.necrify.common.user; + +import de.jvstvshd.necrify.api.duration.PunishmentDuration; +import de.jvstvshd.necrify.api.message.MessageProvider; +import de.jvstvshd.necrify.api.punishment.*; +import de.jvstvshd.necrify.api.user.NecrifyUser; +import de.jvstvshd.necrify.api.user.UserDeletionReason; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Locale; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +public abstract class AbstractConsoleUser implements NecrifyUser { + + private final Locale locale; + private final MessageProvider provider; + + public AbstractConsoleUser(Locale locale, MessageProvider provider) { + this.locale = locale; + this.provider = provider; + } + + public AbstractConsoleUser(MessageProvider provider) { + this.provider = provider; + this.locale = Locale.getDefault(); + } + + private void throwUnsupported() { + throw new UnsupportedOperationException("This method is not supported for console users."); + } + + @Override + public @NotNull UUID getUuid() { + return new UUID(0, 0); + } + + @Override + public @Nullable String getUsername() { + return "CONSOLE"; + } + + @Override + public @NotNull Ban ban(@Nullable Component reason, @NotNull PunishmentDuration duration) { + throwUnsupported(); + return null; + } + + @Override + public @NotNull Ban banPermanent(@Nullable Component reason) { + throwUnsupported(); + return null; + } + + @Override + public @NotNull Mute mute(@Nullable Component reason, @NotNull PunishmentDuration duration) { + throwUnsupported(); + return null; + } + + @Override + public @NotNull Mute mutePermanent(@Nullable Component reason) { + throwUnsupported(); + return null; + } + + @Override + public @NotNull Kick kick(@Nullable Component reason) { + throwUnsupported(); + return null; + } + + @Override + public @NotNull List getPunishments(PunishmentType... types) { + return List.of(); + } + + @Override + public @NotNull CompletableFuture queryUsername(boolean update) { + return CompletableFuture.completedFuture("CONSOLE"); + } + + @Override + public boolean isWhitelisted() { + return false; + } + + @Override + public void setWhitelisted(boolean whitelisted) { + throwUnsupported(); + } + + @Override + public void delete(@NotNull UserDeletionReason reason) { + throwUnsupported(); + } + + @Override + public boolean hasPermission(@NotNull String permission) { + return true; + } + + @Override + public Locale getLocale() { + return locale; + } + + public MessageProvider provider() { + return provider; + } +} diff --git a/necrify-common/src/main/resources/translations/de.properties b/necrify-common/src/main/resources/translations/de.properties index b99630a..f0747c8 100644 --- a/necrify-common/src/main/resources/translations/de.properties +++ b/necrify-common/src/main/resources/translations/de.properties @@ -31,7 +31,7 @@ command.whitelist.status=Der Whitelist-Status des Spielers {0} ist folgender: {1 command.whitelist.success=Der Eintrag wurde erfolgreich aktualisiert. command.whitelist.enabled=Die Whitelist wurde aktiviert. command.whitelist.disabled=Die Whitelist wurde deaktiviert. -error.internal=§4Ein interner Fehler ist aufgetreten. Bitte kontaktiere die Netzwerk-Administration. +error.internal=Ein interner Fehler ist aufgetreten. Bitte kontaktiere die Netzwerk-Administration. helper.type=Typ: helper.reason=Grund: helper.temporal.duration=Dauer: diff --git a/necrify-velocity/build.gradle.kts b/necrify-velocity/build.gradle.kts index 00b0fc4..4f50f6c 100644 --- a/necrify-velocity/build.gradle.kts +++ b/necrify-velocity/build.gradle.kts @@ -41,6 +41,7 @@ tasks { archiveFileName.set("${rootProject.name}-Velocity-${project.version}.jar") archiveBaseName.set("necrify") dependencies { + //Do not relocate sqlite since it loads some native libraries val prefix: (String) -> String = { "de.jvstvshd.necrify.lib.$it" } relocate("com.fasterxml.jackson", prefix("jackson")) relocate("com.github.benmanes.caffeine", prefix("caffeine")) @@ -59,7 +60,6 @@ tasks { relocate("org.jetbrains.annotations", prefix("jetbrains.annotations")) relocate("org.mariadb", prefix("mariadb")) relocate("org.postgresql", prefix("postgresql")) - relocate("org.sqlite", prefix("sqlite")) relocate("org.yaml.snakeyaml", prefix("snakeyaml")) relocate("sun.jna", prefix("sun.jna")) relocate("waffle", prefix("waffle")) diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/impl/DefaultPlayerResolver.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/impl/DefaultPlayerResolver.java index 2cead91..0947128 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/impl/DefaultPlayerResolver.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/impl/DefaultPlayerResolver.java @@ -130,10 +130,6 @@ public CompletableFuture getOrQueryPlayerUuid(@NotNull String name, @NotNu if (getPlayerUuid(name).isPresent()) { return CompletableFuture.completedFuture(getPlayerUuid(name).get()); } - try { - return CompletableFuture.completedFuture(Util.parseUuid(name)); - } catch (IllegalArgumentException e) { - return queryPlayerUuid(name, executor); - } + return queryPlayerUuid(name, executor); } } diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/internal/PunishmentHelper.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/internal/PunishmentHelper.java index 9110972..351ae7f 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/internal/PunishmentHelper.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/internal/PunishmentHelper.java @@ -94,7 +94,7 @@ public static Optional parseDuration(CommandContext getPlayerUuid(CommandContext context, NecrifyVelocityPlugin plugin) { var argument = context.getArgument("player", String.class); if (argument.length() <= 16) { diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/internal/Util.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/internal/Util.java index 90e6098..609c246 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/internal/Util.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/internal/Util.java @@ -93,9 +93,13 @@ public static UUID parseUuid(String uuidString) { try { return UUID.fromString(uuidString); } catch (IllegalArgumentException e) { - return UUID.fromString(uuidString.replaceAll( - "(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", - "$1-$2-$3-$4-$5")); + try { + return UUID.fromString(uuidString.replaceAll( + "(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", + "$1-$2-$3-$4-$5")); + } catch (Exception ex) { + return null; + } } } diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/message/ResourceBundleMessageProvider.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/message/ResourceBundleMessageProvider.java index 353d420..1d113f5 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/message/ResourceBundleMessageProvider.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/message/ResourceBundleMessageProvider.java @@ -29,6 +29,7 @@ import de.jvstvshd.necrify.velocity.config.ConfigData; import net.kyori.adventure.key.Key; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.translation.GlobalTranslator; import net.kyori.adventure.translation.TranslationRegistry; @@ -115,12 +116,12 @@ private static Locale locale(String fileName) { @Override public @NotNull Component internalError(@Nullable Locale locale) { - return provide("error.internal", locale); + return provide("error.internal", locale).color(NamedTextColor.DARK_RED); } @Override public @NotNull Component internalError() { - return provide("error.internal"); + return provide("error.internal").color(NamedTextColor.DARK_RED); } @Override diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/user/VelocityConsoleUser.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/user/VelocityConsoleUser.java new file mode 100644 index 0000000..0b66333 --- /dev/null +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/user/VelocityConsoleUser.java @@ -0,0 +1,39 @@ +package de.jvstvshd.necrify.velocity.user; + +import com.velocitypowered.api.proxy.ConsoleCommandSource; +import de.jvstvshd.necrify.api.message.MessageProvider; +import de.jvstvshd.necrify.common.user.AbstractConsoleUser; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.NotNull; + +import java.util.Locale; + +public class VelocityConsoleUser extends AbstractConsoleUser { + + private final ConsoleCommandSource console; + + public VelocityConsoleUser(Locale locale, MessageProvider provider, ConsoleCommandSource console) { + super(locale, provider); + this.console = console; + } + + public VelocityConsoleUser(MessageProvider provider, ConsoleCommandSource console) { + super(provider); + this.console = console; + } + + @Override + public void sendMessage(@NotNull Component message) { + console.sendMessage(message); + } + + @Override + public void sendMessage(@NotNull String key, Component... args) { + sendMessage(provider().provide(key, getLocale(), args)); + } + + @Override + public void sendErrorMessage() { + sendMessage(provider().internalError()); + } +} diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/user/VelocityUser.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/user/VelocityUser.java index 4e38009..d3dae4c 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/user/VelocityUser.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/user/VelocityUser.java @@ -53,10 +53,7 @@ import java.net.http.HttpResponse; import java.sql.SQLException; import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.UUID; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; @@ -175,17 +172,17 @@ private synchronized void validatePunishments() { @Override public @NotNull CompletableFuture queryUsername(boolean update) { - HttpClient httpClient = HttpClient.newHttpClient(); - HttpRequest request = HttpRequest.newBuilder(URI.create("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid)).GET().build(); - return httpClient - .sendAsync(request, HttpResponse.BodyHandlers.ofString()) - .thenApply(response -> JsonParser.parseString(response.body()).getAsJsonObject().get("name").getAsString()) - .thenApplyAsync(s -> { - if (update) - name = s; - return s; - }); - + try (HttpClient httpClient = HttpClient.newHttpClient()) { + HttpRequest request = HttpRequest.newBuilder(URI.create("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid)).GET().build(); + return httpClient + .sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenApply(response -> JsonParser.parseString(response.body()).getAsJsonObject().get("name").getAsString()) + .thenApplyAsync(s -> { + if (update) + name = s; + return s; + }); + } } public Optional queryPlayer() { @@ -198,6 +195,16 @@ public void sendMessage(@NotNull Component message) { queryPlayer().ifPresent(player -> player.sendMessage(message)); } + @Override + public void sendMessage(@NotNull String key, Component... args) { + sendMessage(messageProvider.provide(key, args)); + } + + @Override + public void sendErrorMessage() { + sendMessage(messageProvider.internalError()); + } + @Override public boolean hasPermission(@NotNull String permission) { return queryPlayer().map(player -> player.hasPermission(permission)).orElse(false); @@ -296,4 +303,12 @@ public void delete(@NotNull UserDeletionReason reason) { plugin.getEventDispatcher().dispatch(new UserDeletedEvent(this, reason)); throw new UnsupportedOperationException("not implemented yet"); } + + @Override + public Locale getLocale() { + if (player != null) { + return player.getEffectiveLocale(); + } + return plugin.getConfig().getConfiguration().getDefaultLanguage(); + } } diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/user/VelocityUserManager.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/user/VelocityUserManager.java index eb87420..bd95a3f 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/user/VelocityUserManager.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/user/VelocityUserManager.java @@ -131,10 +131,14 @@ public VelocityUserManager(ExecutorService executor, ProxyServer server, Cache { var user = Query.query(SELECT_USER_BY_NAME_QUERY) .single(Call.of().bind(player)) - .map(row -> new VelocityUser(row.getObject(1, UUID.class), player, row.getBoolean(3), plugin)) + .map(row -> new VelocityUser(row.getObject(1, UUID.class), player, row.getBoolean(2), plugin)) .first(); user.ifPresent(velocityUser -> { Query.query(SELECT_USER_PUNISHMENTS_QUERY) @@ -160,6 +164,10 @@ public VelocityUserManager(ExecutorService executor, ProxyServer server, Cache> createUser(@NotNull String player) { + var parsedUuid = Util.parseUuid(player); + if (parsedUuid != null) { + return loadUser(parsedUuid); + } return executeAsync(() -> { var uuid = MojangAPI.getUuid(player); return uuid.map(id -> createUser(id, player)); From 0c3e803e61f4ca7ca60818b9ef01cbd5bcd7d1fd Mon Sep 17 00:00:00 2001 From: JvstvsHD <79066214+JvstvsHD@users.noreply.github.com> Date: Fri, 26 Jul 2024 21:45:17 +0200 Subject: [PATCH 4/7] Implemented /mute, /ban, /unmute und /unban commands API: Change return type of punishing methods to CompletableFuture<...>, thus exceptions are no longer swallowed and be easily checked by a handler. MiniMessage is now being used to serialize reasons when sending over messaging channels Fixed some smaller issues --- gradle/libs.versions.toml | 7 +- .../necrify/api/event/NecrifyEvent.java | 2 +- .../necrify/api/event/origin/EventOrigin.java | 12 ++ .../api/punishment/TemporalPunishment.java | 4 +- .../necrify/api/user/NecrifyUser.java | 10 +- necrify-common/build.gradle.kts | 3 + .../necrify/common/AbstractNecrifyPlugin.java | 2 +- .../common/commands/NecrifyCommand.java | 195 +++++++++++++++++- .../common/commands/NecrifyUserParser.java | 35 ++++ .../commands/UserNotFoundParseException.java | 20 ++ .../jvstvshd/necrify/common/plugin/Util.java | 13 ++ .../common/punishment/AbstractPunishment.java | 7 +- .../AbstractTemporalPunishment.java | 3 +- .../necrify/common/punishment/NecrifyBan.java | 2 +- .../common/punishment/PunishmentBuilder.java | 16 +- .../common/user/AbstractConsoleUser.java | 10 +- .../necrify/common/util/PunishmentHelper.java | 63 ++++++ .../listeners/MessagingChannelListener.java | 6 +- .../paper/listeners/MuteInformation.java | 3 +- .../MessagingChannelCommunicator.java | 3 +- .../velocity/NecrifyVelocityPlugin.java | 97 +++++++-- .../necrify/velocity/user/VelocityUser.java | 24 ++- 22 files changed, 482 insertions(+), 55 deletions(-) create mode 100644 necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/NecrifyUserParser.java create mode 100644 necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/UserNotFoundParseException.java create mode 100644 necrify-common/src/main/java/de/jvstvshd/necrify/common/util/PunishmentHelper.java diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 468e572..bbc1598 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -41,9 +41,12 @@ sqlite = { group = "org.xerial", name = "sqlite-jdbc", version.ref = "sqlite" } mysql = { group = "com.mysql", name = "mysql-connector-j", version.ref = "mysql" } adventure-api = { group = "net.kyori", name = "adventure-api", version.ref = "adventure" } adventure-text-minimessage = { group = "net.kyori", name = "adventure-text-minimessage", version.ref = "adventure" } +adventure-text-serializer-plain = { group = "net.kyori", name = "adventure-text-serializer-plain", version.ref = "adventure" } cloud-core = { group = "org.incendo", name = "cloud-core", version.ref = "cloud" } cloud-annotations = { group = "org.incendo", name = "cloud-annotations", version.ref = "cloud" } cloud-velocity = { group = "org.incendo", name = "cloud-velocity", version.ref = "cloud" } +cloud-minecraft-extras = { group = "org.incendo", name = "cloud-minecraft-extras", version.ref = "cloud" } +cloud-brigadier = { group = "org.incendo", name = "cloud-brigadier", version.ref = "cloud" } jetbrains-annotations = { group = "org.jetbrains", name = "annotations", version.ref = "jetbrains-annotations" } eventbus = { group = "org.greenrobot", name = "eventbus-java", version.ref = "eventbus" } slf4j-api = { group = "org.slf4j", name = "slf4j-api", version.ref = "slf4j" } @@ -52,5 +55,5 @@ slf4j-api = { group = "org.slf4j", name = "slf4j-api", version.ref = "slf4j" } database = ["postgresql", "hikari", "sadu", "mariadb", "sqlite", "mysql", "sadu-queries"] jackson = ["jackson-databind", "jackson-yaml", "jackson-datatype-jsr310"] -adventure = ["adventure-api", "adventure-text-minimessage"] -cloud = ["cloud-core", "cloud-annotations"] \ No newline at end of file +adventure = ["adventure-api", "adventure-text-minimessage", "adventure-text-serializer-plain"] +cloud = ["cloud-core", "cloud-annotations", "cloud-minecraft-extras"] \ No newline at end of file diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/NecrifyEvent.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/NecrifyEvent.java index 4dc74c4..f4fc952 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/NecrifyEvent.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/NecrifyEvent.java @@ -35,7 +35,7 @@ public abstract class NecrifyEvent { private final String name; - private EventOrigin origin = null; + private EventOrigin origin = EventOrigin.nullOrigin(); private EventDispatcher executingDispatcher = null; public NecrifyEvent(String name) { diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/origin/EventOrigin.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/origin/EventOrigin.java index bdcf8c2..248677d 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/origin/EventOrigin.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/origin/EventOrigin.java @@ -35,4 +35,16 @@ static EventOrigin ofClass(Class clazz) { } boolean originatesFrom(Object object); + + static EventOrigin nullOrigin() { + return new NullEventOrigin(); + } + + class NullEventOrigin implements EventOrigin { + + @Override + public boolean originatesFrom(Object object) { + return false; + } + } } diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/TemporalPunishment.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/TemporalPunishment.java index d6a0cd1..e9bd5de 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/TemporalPunishment.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/TemporalPunishment.java @@ -63,7 +63,7 @@ public interface TemporalPunishment extends Punishment { CompletableFuture change(@NotNull PunishmentDuration newDuration, @Nullable Component newReason) throws PunishmentException; @Override - default CompletableFuture change(Component newReason) throws PunishmentException { - return change(getDuration(), newReason); + default CompletableFuture change(@Nullable Component newReason) { + return null; } } diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/user/NecrifyUser.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/user/NecrifyUser.java index 024f4bf..30c9a4e 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/user/NecrifyUser.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/user/NecrifyUser.java @@ -81,7 +81,7 @@ public interface NecrifyUser extends CommandSender { * @return the ban object representing the ban. The ban may not be active yet, as the execution takes some time to complete. */ @NotNull - Ban ban(@Nullable Component reason, @NotNull PunishmentDuration duration); + CompletableFuture ban(@Nullable Component reason, @NotNull PunishmentDuration duration); /** * Bans the user permanently with the given reason. A permanently banned user is not able to join the server this system @@ -94,7 +94,7 @@ public interface NecrifyUser extends CommandSender { * @return the ban object representing the ban. The ban may not be active yet, as the execution takes some time to complete. */ @NotNull - Ban banPermanent(@Nullable Component reason); + CompletableFuture banPermanent(@Nullable Component reason); /** * Mutes the user with the given reason and duration. A muted user is not able to send messages in the chat of the server @@ -108,7 +108,7 @@ public interface NecrifyUser extends CommandSender { * @return the mute object representing the mute. The mute may not be active yet, as the execution takes some time to complete. */ @NotNull - Mute mute(@Nullable Component reason, @NotNull PunishmentDuration duration); + CompletableFuture mute(@Nullable Component reason, @NotNull PunishmentDuration duration); /** * Mutes the user permanently with the given reason. A permanently muted user is not able to send messages in the chat of @@ -120,7 +120,7 @@ public interface NecrifyUser extends CommandSender { * @return the mute object representing the mute. The mute may not be active yet, as the execution takes some time to complete. */ @NotNull - Mute mutePermanent(@Nullable Component reason); + CompletableFuture mutePermanent(@Nullable Component reason); /** * Kicks the user with the given reason. A kicked user is removed from the server this system belongs to. They are able to @@ -131,7 +131,7 @@ public interface NecrifyUser extends CommandSender { * @return the kick object representing the kick. The kick may not be active yet, as the execution takes some time to complete. */ @NotNull - Kick kick(@Nullable Component reason); + CompletableFuture kick(@Nullable Component reason); /** * This method queries all punishments with the given {@link UUID} of a player and returns them in a list. diff --git a/necrify-common/build.gradle.kts b/necrify-common/build.gradle.kts index a5c2b55..d8c6b13 100644 --- a/necrify-common/build.gradle.kts +++ b/necrify-common/build.gradle.kts @@ -13,7 +13,10 @@ dependencies { exclude(group = "org.slf4j", module = "slf4j-api") } api(libs.bundles.cloud) + compileOnly(libs.cloud.brigadier) + compileOnly(libs.brigadier) annotationProcessor(libs.cloud.annotations) + compileOnly(libs.slf4j.api) compileOnly("com.google.code.gson:gson:2.10.1") compileOnly(libs.bundles.adventure) testImplementation(libs.junit.jupiter.api) diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/AbstractNecrifyPlugin.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/AbstractNecrifyPlugin.java index e4d7dfb..ac1d80a 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/AbstractNecrifyPlugin.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/AbstractNecrifyPlugin.java @@ -103,7 +103,7 @@ public final void registerCommands(CommandManager manager, boolean }); var parserRegistry = manager.parserRegistry(); parserRegistry.registerParser(ParserDescriptor.of(new NecrifyUserParser(this.getUserManager()), NecrifyUser.class)); - parserRegistry.registerParser(ComponentParser.componentParser(MiniMessage.miniMessage(), StringParser.StringMode.GREEDY_FLAG_YIELDING)); + parserRegistry.registerParser(ComponentParser.componentParser(MiniMessage.miniMessage(), StringParser.StringMode.GREEDY)); var commands = new NecrifyCommand(this); parser.parse(commands); } diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/NecrifyCommand.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/NecrifyCommand.java index 84fd0e3..4cef4c1 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/NecrifyCommand.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/NecrifyCommand.java @@ -24,25 +24,210 @@ package de.jvstvshd.necrify.common.commands; +import com.mojang.brigadier.LiteralMessage; +import com.mojang.brigadier.Message; +import de.jvstvshd.necrify.api.PunishmentException; +import de.jvstvshd.necrify.api.event.punishment.PunishmentCancelledEvent; +import de.jvstvshd.necrify.api.message.MessageProvider; +import de.jvstvshd.necrify.api.punishment.Punishment; +import de.jvstvshd.necrify.api.punishment.StandardPunishmentType; +import de.jvstvshd.necrify.api.user.NecrifyUser; +import de.jvstvshd.necrify.api.user.UserManager; import de.jvstvshd.necrify.common.AbstractNecrifyPlugin; +import de.jvstvshd.necrify.common.plugin.Util; +import de.jvstvshd.necrify.common.util.PunishmentHelper; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.event.HoverEventSource; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.incendo.cloud.annotation.specifier.Greedy; +import org.incendo.cloud.annotations.Argument; import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Default; +import org.incendo.cloud.annotations.Permission; +import org.incendo.cloud.annotations.parser.Parser; +import org.incendo.cloud.annotations.processing.CommandContainer; +import org.incendo.cloud.annotations.suggestion.Suggestions; +import org.incendo.cloud.brigadier.suggestion.TooltipSuggestion; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.injection.ParameterInjector; +import org.incendo.cloud.minecraft.extras.suggestion.ComponentTooltipSuggestion; +import org.incendo.cloud.parser.ArgumentParseResult; +import org.incendo.cloud.suggestion.Suggestion; +import org.incendo.cloud.type.Either; +import org.incendo.cloud.util.annotation.AnnotationAccessor; +import org.slf4j.Logger; + +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; public class NecrifyCommand { private final AbstractNecrifyPlugin plugin; + private final MiniMessage miniMessage; + private final UserManager userManager; + private final Logger logger; + private final ExecutorService executor; public NecrifyCommand(AbstractNecrifyPlugin plugin) { this.plugin = plugin; + this.userManager = plugin.getUserManager(); + this.miniMessage = MiniMessage.miniMessage(); + this.logger = plugin.getLogger(); + this.executor = plugin.getExecutor(); + } + + @Command("necrify ban [reason]") + @Command("ban [reason]") + @Permission(value = {"necrify.command.ban", "necrify.admin"}, mode = Permission.Mode.ANY_OF) + public void banCommand( + NecrifyUser sender, + @Argument(value = "target", description = "Player to ban", suggestions = "suggestOnlinePlayers") NecrifyUser target, + @Argument(value = "reason", description = "Reason the user should be banned for") @Greedy String reason + ) { + var finalReason = reasonOrDefaultTo(reason, StandardPunishmentType.PERMANENT_BAN); + target.banPermanent(finalReason).whenComplete((ban, throwable) -> { + if (throwable != null) { + logException(sender, throwable); + return; + } + sender.sendMessage("command.ban.success", + miniMessage(target.getUsername()).color(NamedTextColor.YELLOW), + copyComponent(target.getUuid().toString()).color(NamedTextColor.YELLOW), + finalReason); + }); } - @Command("necrify ban") - @Command("ban") - public void banCommand() { + @Command("necrify mute [reason]") + @Command("mute [reason]") + @Permission(value = {"necrify.command.mute", "necrify.admin"}, mode = Permission.Mode.ANY_OF) + public void muteCommand( + NecrifyUser sender, + @Argument(value = "target", description = "Player to mute", suggestions = "suggestOnlinePlayers") NecrifyUser target, + @Argument(value = "reason", description = "Reason the user should be muted for") @Greedy String reason + ) { + var finalReason = reasonOrDefaultTo(reason, StandardPunishmentType.PERMANENT_MUTE); + target.mutePermanent(finalReason).whenComplete((mute, throwable) -> { + if (throwable != null) { + logException(sender, throwable); + return; + } + sender.sendMessage("command.mute.success", + miniMessage(target.getUsername()).color(NamedTextColor.YELLOW), + copyComponent(target.getUuid().toString()).color(NamedTextColor.YELLOW), + finalReason); + }); + } + + @Command("necrify unban ") + @Command("unban ") + @Permission(value = {"necrify.command.unban", "necrify.admin"}, mode = Permission.Mode.ANY_OF) + public void unbanCommand( + NecrifyUser sender, + @Argument(value = "target", description = "Player to unban") NecrifyUser target + ) { + var punishments = target.getPunishments(StandardPunishmentType.BAN, StandardPunishmentType.PERMANENT_BAN); + try { + removePunishments(sender, "unban", punishments); + } catch (Exception e) { + logException(e); + } + } + @Command("necrify unmute ") + @Command("unmute ") + @Permission(value = {"necrify.command.unmute", "necrify.admin"}, mode = Permission.Mode.ANY_OF) + public void unmuteCommand( + NecrifyUser sender, + @Argument(value = "target", description = "Player to unmute", suggestions = "suggestOnlinePlayers") NecrifyUser target + ) { + var punishments = target.getPunishments(StandardPunishmentType.MUTE, StandardPunishmentType.PERMANENT_MUTE); + removePunishments(sender, "unmute", punishments); } - @Command("necrify kick") - public void kickCommand() { + private void removePunishments(NecrifyUser source, String commandName, List punishments) { + if (punishments.isEmpty()) { + source.sendMessage(plugin.getMessageProvider().provide("command.punishment.not-banned").color(NamedTextColor.RED)); + return; + } + if (punishments.size() > 1) { + source.sendMessage(plugin.getMessageProvider().provide("command." + commandName + ".multiple-bans").color(NamedTextColor.YELLOW)); + for (Punishment punishment : punishments) { + source.sendMessage(buildComponent(PunishmentHelper.buildPunishmentData(punishment, plugin.getMessageProvider()), punishment)); + } + } else { + Punishment punishment = punishments.getFirst(); + try { + punishment.cancel().whenCompleteAsync((unused, th) -> { + if (th != null) { + logException(source, th); + plugin.getLogger().error("An error occurred while removing punishment {} for player {}", punishment.getPunishmentUuid(), punishment.getUser().getUsername(), th); + source.sendMessage(plugin.getMessageProvider().internalError()); + return; + } + source.sendMessage(plugin.getMessageProvider().provide("command." + commandName + ".success").color(NamedTextColor.GREEN)); + }, plugin.getService()); + } catch (Exception e) { + logException(source, e); + } + } + } + + @Suggestions("suggestOnlinePlayers") + public List suggestNames(CommandContext context, CommandInput input) { + return plugin + .getOnlinePlayers() + .stream() + .filter(pair -> pair.first().toLowerCase(Locale.ROOT).startsWith(input.peekString().toLowerCase(Locale.ROOT))) + .map(pair -> ComponentTooltipSuggestion.suggestion(pair.first(), + miniMessage("The player (/) you want to select.", + Placeholder.parsed("name", pair.first()), + Placeholder.parsed("uuid", pair.second().toString())) + )).toList(); + } + + private Component buildComponent(Component dataComponent, Punishment punishment) { + return dataComponent.clickEvent(ClickEvent.runCommand("/punishment " + punishment.getPunishmentUuid() + .toString().toLowerCase(Locale.ROOT) + " remove")) + .hoverEvent((HoverEventSource) op -> HoverEvent.showText(Component + .text("Click to remove punishment").color(NamedTextColor.GREEN))); + } + + private Component reasonOrDefaultTo(String reason, StandardPunishmentType type) { + return miniMessage(reason == null ? plugin.getDefaultReason(type) : reason); + } + + private Component miniMessage(String message, TagResolver... resolvers) { + return miniMessage.deserialize(message, resolvers); + } + + public TextComponent copyComponent(String text) { + return Component.text(text).clickEvent(ClickEvent.suggestCommand(text)) + .hoverEvent((HoverEventSource) op -> HoverEvent.showText(plugin + .getMessageProvider() + .provide("commands.general.copy") + .color(NamedTextColor.GREEN))); + } + + + + private void logException(Throwable throwable) { + logger.error("An error occurred while executing a command", throwable); + } + private void logException(NecrifyUser sender, Throwable throwable) { + logger.error("An error occurred while executing a command for player {} ({})", sender.getUsername(), sender.getUuid(), throwable); + sender.sendErrorMessage(); } } diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/NecrifyUserParser.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/NecrifyUserParser.java new file mode 100644 index 0000000..2aac8c1 --- /dev/null +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/NecrifyUserParser.java @@ -0,0 +1,35 @@ +package de.jvstvshd.necrify.common.commands; + +import de.jvstvshd.necrify.api.user.NecrifyUser; +import de.jvstvshd.necrify.api.user.UserManager; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.parser.ArgumentParseResult; +import org.incendo.cloud.parser.ArgumentParser; + +import java.util.concurrent.CompletableFuture; + +public class NecrifyUserParser implements ArgumentParser.FutureArgumentParser { + + private final UserManager userManager; + + public NecrifyUserParser(UserManager userManager) { + this.userManager = userManager; + } + + @Override + public @NonNull CompletableFuture<@NonNull ArgumentParseResult> parseFuture(@NonNull CommandContext commandContext, @NonNull CommandInput commandInput) { + var target = commandInput.peekString(); + return userManager.loadOrCreateUser(target).handle((necrifyUser, throwable) -> { + if (throwable != null) { + return ArgumentParseResult.failure(throwable); + } + if (necrifyUser.isPresent()) { + commandInput.readString(); + return ArgumentParseResult.success(necrifyUser.get()); + } + return ArgumentParseResult.failure(new UserNotFoundParseException(NecrifyUser.class, commandContext, target)); + }); + } +} diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/UserNotFoundParseException.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/UserNotFoundParseException.java new file mode 100644 index 0000000..2f92e3a --- /dev/null +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/UserNotFoundParseException.java @@ -0,0 +1,20 @@ +package de.jvstvshd.necrify.common.commands; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.caption.Caption; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.exception.parsing.ParserException; + +public class UserNotFoundParseException extends ParserException { + + private final String playerName; + + public UserNotFoundParseException(@NonNull Class argumentParser, @NonNull CommandContext context, String playerName) { + super(argumentParser, context, Caption.of("")); + this.playerName = playerName; + } + + public String playerName() { + return playerName; + } +} diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/plugin/Util.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/plugin/Util.java index df7720c..8b9184f 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/plugin/Util.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/plugin/Util.java @@ -24,6 +24,14 @@ package de.jvstvshd.necrify.common.plugin; +import de.jvstvshd.necrify.api.message.MessageProvider; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.event.HoverEventSource; +import net.kyori.adventure.text.format.NamedTextColor; + import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; @@ -49,4 +57,9 @@ public static CompletableFuture executeAsync(Callable task, Executor s public static String trimUuid(UUID origin) { return origin.toString().toLowerCase().replace("-", ""); } + + public static TextComponent copyComponent(String text, MessageProvider provider) { + return Component.text(text).clickEvent(ClickEvent.suggestCommand(text)) + .hoverEvent((HoverEventSource) op -> HoverEvent.showText(provider.provide("commands.general.copy").color(NamedTextColor.GREEN))); + } } \ No newline at end of file diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/AbstractPunishment.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/AbstractPunishment.java index 9d345d4..d77046f 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/AbstractPunishment.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/AbstractPunishment.java @@ -39,6 +39,7 @@ import org.jetbrains.annotations.Nullable; import java.util.NoSuchElementException; +import java.util.Objects; import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; @@ -67,10 +68,10 @@ public abstract class AbstractPunishment implements Punishment { public AbstractPunishment(@NotNull NecrifyUser user, @NotNull Component reason, @NotNull UUID punishmentUuid, @NotNull AbstractNecrifyPlugin plugin, @Nullable Punishment successor) { - this.reason = reason; + this.reason = Objects.requireNonNull(reason, "punishment must be reasoned"); this.service = plugin.getService(); - this.user = user; - this.punishmentUuid = punishmentUuid; + this.user = Objects.requireNonNull(user, "punishment must be bound to a user"); + this.punishmentUuid = Objects.requireNonNull(punishmentUuid, "punishment must have a uuid"); this.successor = successor; this.validity = true; this.messageProvider = plugin.getMessageProvider(); diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/AbstractTemporalPunishment.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/AbstractTemporalPunishment.java index 6402d8c..183f6d0 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/AbstractTemporalPunishment.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/AbstractTemporalPunishment.java @@ -38,6 +38,7 @@ import net.kyori.adventure.text.Component; import org.jetbrains.annotations.NotNull; +import java.util.Objects; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -47,7 +48,7 @@ public abstract class AbstractTemporalPunishment extends AbstractPunishment impl public AbstractTemporalPunishment(NecrifyUser user, Component reason, UUID punishmentUuid, PunishmentDuration duration, AbstractNecrifyPlugin plugin, Punishment successor) { super(user, reason, punishmentUuid, plugin, successor); - this.duration = duration; + this.duration = Objects.requireNonNull(duration, "temporal punishment must have a duration"); } public PunishmentDuration getDuration() { diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/NecrifyBan.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/NecrifyBan.java index 089c31a..c72f173 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/NecrifyBan.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/NecrifyBan.java @@ -56,7 +56,7 @@ public boolean isOngoing() { @Override public CompletableFuture applyPunishment() throws PunishmentException { - return super.punish().whenComplete((p, throwable) -> { + return super.applyPunishment().whenComplete((p, throwable) -> { tryKick(); }); } diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/PunishmentBuilder.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/PunishmentBuilder.java index 1575fcf..81c1837 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/PunishmentBuilder.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/PunishmentBuilder.java @@ -95,8 +95,7 @@ public PunishmentBuilder withSuccessor(Punishment successor) { } public NecrifyBan buildBan() { - if (punishmentUuid == null) - punishmentUuid = UUID.randomUUID(); + validateValues(); return new NecrifyBan(user, reason, punishmentUuid, duration, plugin, successor); } @@ -106,10 +105,23 @@ public NecrifyBan buildBan() { * @return The kick punishment. */ public NecrifyKick buildKick() { + validateValues(); return plugin.createKick(reason, user, punishmentUuid); } public NecrifyMute buildMute() { + validateValues(); return new NecrifyMute(user, reason, punishmentUuid, duration, plugin, successor); } + + private void validateValues() { + if (punishmentUuid == null) + punishmentUuid = UUID.randomUUID(); + if (user == null) + throw new NullPointerException("user is null"); + if (reason == null) + throw new NullPointerException("reason is null"); + if (duration == null) + throw new NullPointerException("duration is null"); + } } diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/user/AbstractConsoleUser.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/user/AbstractConsoleUser.java index 5c5d999..282eb0c 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/user/AbstractConsoleUser.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/user/AbstractConsoleUser.java @@ -44,31 +44,31 @@ private void throwUnsupported() { } @Override - public @NotNull Ban ban(@Nullable Component reason, @NotNull PunishmentDuration duration) { + public @NotNull CompletableFuture ban(@Nullable Component reason, @NotNull PunishmentDuration duration) { throwUnsupported(); return null; } @Override - public @NotNull Ban banPermanent(@Nullable Component reason) { + public @NotNull CompletableFuture banPermanent(@Nullable Component reason) { throwUnsupported(); return null; } @Override - public @NotNull Mute mute(@Nullable Component reason, @NotNull PunishmentDuration duration) { + public @NotNull CompletableFuture mute(@Nullable Component reason, @NotNull PunishmentDuration duration) { throwUnsupported(); return null; } @Override - public @NotNull Mute mutePermanent(@Nullable Component reason) { + public @NotNull CompletableFuture mutePermanent(@Nullable Component reason) { throwUnsupported(); return null; } @Override - public @NotNull Kick kick(@Nullable Component reason) { + public @NotNull CompletableFuture kick(@Nullable Component reason) { throwUnsupported(); return null; } diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/util/PunishmentHelper.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/util/PunishmentHelper.java new file mode 100644 index 0000000..db8949b --- /dev/null +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/util/PunishmentHelper.java @@ -0,0 +1,63 @@ +/* + * This file is part of Necrify (formerly Velocity Punishment), which is licensed under the MIT license. + * + * Copyright (c) 2022-2024 JvstvsHD + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package de.jvstvshd.necrify.common.util; + +import de.jvstvshd.necrify.api.message.MessageProvider; +import de.jvstvshd.necrify.api.punishment.Punishment; +import de.jvstvshd.necrify.api.punishment.TemporalPunishment; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; + +public class PunishmentHelper { + + private PunishmentHelper() { + throw new IllegalStateException("Utility class"); + } + + public static Component buildPunishmentData(Punishment punishment, MessageProvider provider) { + return Component.text() + .append(provider.provide("helper.type").color(NamedTextColor.AQUA), + Component.text(punishment.getType().getName()).color(NamedTextColor.YELLOW), + Component.newline(), + provider.provide("helper.reason").color(NamedTextColor.AQUA), + punishment.getReason(), + Component.newline(), + punishment instanceof TemporalPunishment temporalPunishment ? + buildPunishmentDataTemporal(temporalPunishment, provider) : Component.text(""), + Component.newline() + ) + .build(); + } + + public static Component buildPunishmentDataTemporal(TemporalPunishment punishment, MessageProvider provider) { + return punishment.isPermanent() ? Component.text("permanent").color(NamedTextColor.RED) : Component.text() + .append(provider.provide("helper.temporal.duration").color(NamedTextColor.AQUA), + Component.text(punishment.getDuration().remainingDuration()).color(NamedTextColor.YELLOW), + Component.newline(), + provider.provide("helper.temporal.end").color(NamedTextColor.AQUA), + Component.text(punishment.getDuration().expirationAsString()).color(NamedTextColor.YELLOW)) + .build(); + } +} diff --git a/necrify-paper/src/main/java/de/jvstvshd/necrify/paper/listeners/MessagingChannelListener.java b/necrify-paper/src/main/java/de/jvstvshd/necrify/paper/listeners/MessagingChannelListener.java index f534073..574e334 100644 --- a/necrify-paper/src/main/java/de/jvstvshd/necrify/paper/listeners/MessagingChannelListener.java +++ b/necrify-paper/src/main/java/de/jvstvshd/necrify/paper/listeners/MessagingChannelListener.java @@ -53,6 +53,10 @@ public void onPluginMessageReceived(@NotNull String channel, @NotNull Player pla plugin.getSLF4JLogger().error("Could not parse MuteData", e); return; } + if (data.getType() == MuteData.RESET) { + plugin.cachedMutes().stream().filter(muteInformation -> muteInformation.getPlayer().getUniqueId().equals(data.getUuid())).forEach(plugin.cachedMutes()::remove); + return; + } var mute = MuteInformation.from(data); switch (data.getType()) { case MuteData.ADD -> plugin.cachedMutes().add(mute); @@ -60,8 +64,6 @@ public void onPluginMessageReceived(@NotNull String channel, @NotNull Player pla plugin.cachedMutes().removeIf(muteInformation -> muteInformation.getPunishmentUUID().equals(mute.getPunishmentUUID())); case MuteData.UPDATE -> plugin.cachedMutes().stream().filter(muteInformation -> muteInformation.getPunishmentUUID().equals(mute.getPunishmentUUID())).findFirst().ifPresent(muteInformation -> muteInformation.updateTo(mute)); - case MuteData.RESET -> - plugin.cachedMutes().stream().filter(muteInformation -> muteInformation.getPlayer().getUniqueId().equals(mute.getPlayer().getUniqueId())).forEach(plugin.cachedMutes()::remove); } } } diff --git a/necrify-paper/src/main/java/de/jvstvshd/necrify/paper/listeners/MuteInformation.java b/necrify-paper/src/main/java/de/jvstvshd/necrify/paper/listeners/MuteInformation.java index 37a1ee0..ff0cc6b 100644 --- a/necrify-paper/src/main/java/de/jvstvshd/necrify/paper/listeners/MuteInformation.java +++ b/necrify-paper/src/main/java/de/jvstvshd/necrify/paper/listeners/MuteInformation.java @@ -27,6 +27,7 @@ import de.jvstvshd.necrify.api.duration.PunishmentDuration; import de.jvstvshd.necrify.common.plugin.MuteData; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -48,7 +49,7 @@ public MuteInformation(Component reason, PunishmentDuration duration, Player pla } public static MuteInformation from(MuteData muteData) { - return new MuteInformation(LegacyComponentSerializer.legacySection().deserialize(muteData.getReason()), + return new MuteInformation(MiniMessage.miniMessage().deserialize(muteData.getReason()), PunishmentDuration.from(muteData.getExpiration()), Bukkit.getPlayer(muteData.getUuid()), muteData.getPunishmentId()); } diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/MessagingChannelCommunicator.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/MessagingChannelCommunicator.java index e372edf..b5e5c79 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/MessagingChannelCommunicator.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/MessagingChannelCommunicator.java @@ -39,6 +39,7 @@ import de.jvstvshd.necrify.api.punishment.util.ReasonHolder; import de.jvstvshd.necrify.common.plugin.MuteData; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.slf4j.Logger; @@ -86,7 +87,7 @@ private void queueMute(MuteData muteData) throws JsonProcessingException { } private MuteData from(Mute mute, int type, Function reason) { - return new MuteData(mute.getUser().getUuid(), LegacyComponentSerializer.legacySection().serialize(reason.apply(mute)), mute.getDuration().expiration(), type, mute.getPunishmentUuid()); + return new MuteData(mute.getUser().getUuid(), MiniMessage.miniMessage().serialize(reason.apply(mute)), mute.getDuration().expiration(), type, mute.getPunishmentUuid()); } @SuppressWarnings("UnstableApiUsage") diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/NecrifyVelocityPlugin.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/NecrifyVelocityPlugin.java index fb76312..a859bd9 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/NecrifyVelocityPlugin.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/NecrifyVelocityPlugin.java @@ -31,8 +31,10 @@ import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.TypeLiteral; +import com.mojang.brigadier.arguments.StringArgumentType; import com.velocitypowered.api.command.CommandManager; import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.command.VelocityBrigadierMessage; import com.velocitypowered.api.event.EventManager; import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; @@ -76,11 +78,18 @@ import de.jvstvshd.necrify.velocity.internal.Util; import de.jvstvshd.necrify.velocity.listener.ConnectListener; import de.jvstvshd.necrify.velocity.message.ResourceBundleMessageProvider; +import de.jvstvshd.necrify.velocity.user.VelocityConsoleUser; import de.jvstvshd.necrify.velocity.user.VelocityUser; import de.jvstvshd.necrify.velocity.user.VelocityUserManager; +import io.leangen.geantyref.TypeToken; import net.kyori.adventure.text.Component; import org.incendo.cloud.SenderMapper; +import org.incendo.cloud.brigadier.suggestion.TooltipSuggestion; import org.incendo.cloud.execution.ExecutionCoordinator; +import org.incendo.cloud.minecraft.extras.parser.ComponentParser; +import org.incendo.cloud.minecraft.extras.suggestion.ComponentTooltipSuggestion; +import org.incendo.cloud.parser.standard.StringParser; +import org.incendo.cloud.type.tuple.Pair; import org.incendo.cloud.velocity.CloudInjectionModule; import org.incendo.cloud.velocity.VelocityCommandManager; import org.jetbrains.annotations.NotNull; @@ -91,9 +100,11 @@ import java.sql.SQLException; import java.time.Duration; import java.util.Optional; +import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executors; +import java.util.stream.Collectors; @Plugin(id = "necrify", name = "Necrify", version = "1.2.0-SNAPSHOT", description = "A simple punishment plugin for Velocity", authors = {"JvstvsHD"}) public class NecrifyVelocityPlugin extends AbstractNecrifyPlugin { @@ -191,31 +202,83 @@ private void setup(CommandManager commandManager, EventManager eventManager) { commandManager.register(TempmuteCommand.tempmuteCommand(this)); commandManager.register(KickCommand.kickCommand(this)); commandManager.register(WhitelistCommand.whitelistCommand(this)); - final Injector childInjector = injector.createChildInjector( new CloudInjectionModule<>( NecrifyUser.class, - ExecutionCoordinator.simpleCoordinator(), + ExecutionCoordinator.coordinatorFor(ExecutionCoordinator.nonSchedulingExecutor()), SenderMapper.create(this::createUser, this::getCommandSource))); - registerCommands(childInjector.getInstance(Key.get(new TypeLiteral>() { - })), getConfig().getConfiguration().isAllowTopLevelCommands()); + var cManager = childInjector.getInstance(Key.get(new TypeLiteral>() { + })); + cManager.appendSuggestionMapper(suggestion -> { + if (!(suggestion instanceof ComponentTooltipSuggestion componentTooltipSuggestion)) + return suggestion; + + return TooltipSuggestion.suggestion(suggestion.suggestion(), VelocityBrigadierMessage.tooltip(componentTooltipSuggestion.tooltip())); + }); + var brigadierManager = cManager.brigadierManager(); + brigadierManager.setNativeSuggestions(new TypeToken>() { + }, true); + brigadierManager.setNativeNumberSuggestions(true); + registerCommands(cManager, getConfig().getConfiguration().isAllowTopLevelCommands()); + brigadierManager.registerMapping(new TypeToken>() { + }, builder -> { + builder.to(necrifyUserParser -> { + return StringArgumentType.greedyString(); + }).nativeSuggestions(); + }); + /*brigadierManager.setNativeSuggestions(new TypeToken>() { + }, true);*/ } @SuppressWarnings({"unchecked", "UnstableApiUsage"}) private HikariDataSource createDataSource() { var dbData = configurationManager.getConfiguration().getDataBaseData(); + var driverClass = getDriverClass(dbData.sqlType().name().toLowerCase()); ConfigurationStage stage = switch (dbData.sqlType().name().toLowerCase()) { - case "sqlite" -> - DataSourceCreator.create(SqLite.get()).configure(sqLiteJdbc -> sqLiteJdbc.path(dataDirectory.resolve("punishment.db"))).create(); - case "postgresql" -> - DataSourceCreator.create(PostgreSql.get()).configure(jdbcConfig -> jdbcConfig.host(dbData.getHost()) - - .port(dbData.getPort()).database(dbData.getDatabase()).user(dbData.getUsername()).password(dbData.getPassword())).create(); + case "sqlite" -> DataSourceCreator + .create(SqLite.get()) + .configure(sqLiteJdbc -> sqLiteJdbc + .driverClass(driverClass) + .path(dataDirectory.resolve("punishment.db"))) + .create(); + case "postgresql" -> DataSourceCreator + .create(PostgreSql.get()) + .configure(jdbcConfig -> jdbcConfig + .driverClass(driverClass) + .host(dbData.getHost()) + .port(dbData.getPort()) + .database(dbData.getDatabase()) + .user(dbData.getUsername()) + .password(dbData.getPassword())) + .create(); + + default -> DataSourceCreator + .create((Database, ?>) dbData.sqlType()) + .configure(jdbcConfig -> jdbcConfig + .driverClass(driverClass) + .host(dbData.getHost()) + .port(dbData.getPort()) + .database(dbData.getDatabase()) + .user(dbData.getUsername()) + .password(dbData.getPassword())) + .create(); + }; + return stage + .withMaximumPoolSize(dbData.getMaxPoolSize()) + .withMinimumIdle(dbData.getMinIdle()) + .withPoolName("necrify-hikari") + .forSchema(dbData.getPostgresSchema()) + .build(); + } - default -> - DataSourceCreator.create((Database, ?>) dbData.sqlType()).configure(jdbcConfig -> jdbcConfig.host(dbData.getHost()).port(dbData.getPort()).database(dbData.getDatabase()).user(dbData.getUsername()).password(dbData.getPassword())).create(); + private Class getDriverClass(String type) { + return switch (type) { + case "sqlite" -> org.sqlite.JDBC.class; + case "postgresql", "postgres" -> org.postgresql.Driver.class; + case "mariadb" -> org.mariadb.jdbc.Driver.class; + case "mysql" -> com.mysql.cj.jdbc.Driver.class; + default -> throw new IllegalArgumentException("Unknown database type: " + type); }; - return stage.withMaximumPoolSize(dbData.getMaxPoolSize()).withMinimumIdle(dbData.getMinIdle()).withPoolName("necrify-hikari").forSchema(dbData.getPostgresSchema()).build(); } @SuppressWarnings("UnstableApiUsage") @@ -272,6 +335,7 @@ public void setMessageProvider(@NotNull MessageProvider messageProvider) { this.messageProvider = messageProvider; } + @Override public Logger getLogger() { return logger; } @@ -307,7 +371,7 @@ public NecrifyUser createUser(CommandSource source) { if (source instanceof Player) { return createUser(((Player) source).getUniqueId()); } else if (source instanceof ConsoleCommandSource) { - return new VelocityUser(new UUID(0, 0), "CONSOLE", true, this); + return new VelocityConsoleUser(messageProvider, server.getConsoleCommandSource()); } else { return new VelocityUser(UUID.randomUUID(), "unknown_source", false, this); } @@ -361,4 +425,9 @@ public void setEventDispatcher(@NotNull EventDispatcher eventDispatcher) { public NecrifyKick createKick(Component reason, NecrifyUser user, UUID punishmentUuid) { return new VelocityKick(user, reason, punishmentUuid, this); } + + @Override + public Set> getOnlinePlayers() { + return server.getAllPlayers().stream().map(player -> Pair.of(player.getUsername(), player.getUniqueId())).collect(Collectors.toSet()); + } } diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/user/VelocityUser.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/user/VelocityUser.java index d3dae4c..428810a 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/user/VelocityUser.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/user/VelocityUser.java @@ -108,7 +108,7 @@ public VelocityUser(@NotNull UUID uuid, @Nullable String name, boolean whitelist } @Override - public @NotNull Ban ban(@Nullable Component reason, @NotNull PunishmentDuration duration) { + public @NotNull CompletableFuture ban(@Nullable Component reason, @NotNull PunishmentDuration duration) { return punish(PunishmentBuilder.newBuilder(plugin) .withDuration(duration) .withReason(reason) @@ -117,12 +117,12 @@ public VelocityUser(@NotNull UUID uuid, @Nullable String name, boolean whitelist } @Override - public @NotNull Ban banPermanent(@Nullable Component reason) { + public @NotNull CompletableFuture banPermanent(@Nullable Component reason) { return ban(reason, PunishmentDuration.permanent()); } @Override - public @NotNull Mute mute(@Nullable Component reason, @NotNull PunishmentDuration duration) { + public @NotNull CompletableFuture mute(@Nullable Component reason, @NotNull PunishmentDuration duration) { return punish(PunishmentBuilder.newBuilder(plugin) .withDuration(duration) .withReason(reason) @@ -131,24 +131,30 @@ public VelocityUser(@NotNull UUID uuid, @Nullable String name, boolean whitelist } @Override - public @NotNull Mute mutePermanent(@Nullable Component reason) { + public @NotNull CompletableFuture mutePermanent(@Nullable Component reason) { return mute(reason, PunishmentDuration.permanent()); } @Override - public @NotNull Kick kick(@Nullable Component reason) { + public @NotNull CompletableFuture kick(@Nullable Component reason) { var kick = PunishmentBuilder.newBuilder(plugin) .withReason(reason) .withUser(this) .buildKick(); kick.punish(); - return kick; + return CompletableFuture.completedFuture(kick); } - private T punish(T punishment) { + //We're just returning the same instance that was passed in via 'punishment', so we can safely cast it to T. + @SuppressWarnings("unchecked") + private CompletableFuture punish(T punishment) { punishments.add(punishment); - punishment.punish(); - return punishment; + return (CompletableFuture) punishment.punish().whenComplete((ignored, throwable) -> { + if (throwable != null) { + plugin.getLogger().error("An error occurred while punishing user {}", punishment.getUser().getUuid(), throwable); + punishment.getUser().sendErrorMessage(); + } + }); } @SuppressWarnings("unchecked") From dbf71fc7030be89d83521732074218627da80f7c Mon Sep 17 00:00:00 2001 From: JvstvsHD <79066214+JvstvsHD@users.noreply.github.com> Date: Sat, 27 Jul 2024 16:35:39 +0200 Subject: [PATCH 5/7] License unlicensed files --- .../common/commands/NecrifyUserParser.java | 24 +++++++++++++++++++ .../commands/UserNotFoundParseException.java | 24 +++++++++++++++++++ .../common/user/AbstractConsoleUser.java | 24 +++++++++++++++++++ .../velocity/user/VelocityConsoleUser.java | 24 +++++++++++++++++++ 4 files changed, 96 insertions(+) diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/NecrifyUserParser.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/NecrifyUserParser.java index 2aac8c1..298c5f5 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/NecrifyUserParser.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/NecrifyUserParser.java @@ -1,3 +1,27 @@ +/* + * This file is part of Necrify (formerly Velocity Punishment), which is licensed under the MIT license. + * + * Copyright (c) 2022-2024 JvstvsHD + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package de.jvstvshd.necrify.common.commands; import de.jvstvshd.necrify.api.user.NecrifyUser; diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/UserNotFoundParseException.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/UserNotFoundParseException.java index 2f92e3a..c74bdeb 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/UserNotFoundParseException.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/UserNotFoundParseException.java @@ -1,3 +1,27 @@ +/* + * This file is part of Necrify (formerly Velocity Punishment), which is licensed under the MIT license. + * + * Copyright (c) 2022-2024 JvstvsHD + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package de.jvstvshd.necrify.common.commands; import org.checkerframework.checker.nullness.qual.NonNull; diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/user/AbstractConsoleUser.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/user/AbstractConsoleUser.java index 282eb0c..51e3ac0 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/user/AbstractConsoleUser.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/user/AbstractConsoleUser.java @@ -1,3 +1,27 @@ +/* + * This file is part of Necrify (formerly Velocity Punishment), which is licensed under the MIT license. + * + * Copyright (c) 2022-2024 JvstvsHD + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package de.jvstvshd.necrify.common.user; import de.jvstvshd.necrify.api.duration.PunishmentDuration; diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/user/VelocityConsoleUser.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/user/VelocityConsoleUser.java index 0b66333..d67bb33 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/user/VelocityConsoleUser.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/user/VelocityConsoleUser.java @@ -1,3 +1,27 @@ +/* + * This file is part of Necrify (formerly Velocity Punishment), which is licensed under the MIT license. + * + * Copyright (c) 2022-2024 JvstvsHD + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package de.jvstvshd.necrify.velocity.user; import com.velocitypowered.api.proxy.ConsoleCommandSource; From a845208c1bbd1166f7188c82f3c65f972612fd11 Mon Sep 17 00:00:00 2001 From: JvstvsHD <79066214+JvstvsHD@users.noreply.github.com> Date: Sat, 27 Jul 2024 19:20:07 +0200 Subject: [PATCH 6/7] Add /tempban, /tempmute and /kick command Also added some nice preview completions that will show you the expiration date and the formatted MiniMessage when hovering over the completion --- .../api/duration/PunishmentDuration.java | 32 ++++- .../api/punishment/PunishmentType.java | 4 +- .../punishment/StandardPunishmentType.java | 4 +- .../necrify/common/AbstractNecrifyPlugin.java | 8 +- .../common/commands/NecrifyCommand.java | 133 +++++++++++++----- .../commands/PunishmentDurationParser.java | 74 ++++++++++ .../necrify/common/punishment/NecrifyBan.java | 2 +- .../common/punishment/NecrifyMute.java | 2 +- .../common/punishment/PunishmentBuilder.java | 6 +- .../velocity/NecrifyVelocityPlugin.java | 4 +- .../commands/PunishmentRemovalCommand.java | 4 +- .../impl/DefaultPunishmentManager.java | 2 +- .../necrify/velocity/user/VelocityUser.java | 4 +- 13 files changed, 222 insertions(+), 57 deletions(-) create mode 100644 necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/PunishmentDurationParser.java diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/duration/PunishmentDuration.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/duration/PunishmentDuration.java index de9f29c..c0d5401 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/duration/PunishmentDuration.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/duration/PunishmentDuration.java @@ -27,6 +27,7 @@ import java.sql.Timestamp; import java.time.Duration; import java.time.LocalDateTime; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -43,6 +44,13 @@ public interface PunishmentDuration extends Comparable { * * @param source the source string. * @return the parsed duration + * @throws Parser.ParseException if...

    + *
  • the source string is empty
  • + *
  • the source string does not contain parsable tokens
  • + *
  • the source string does not contain a unit character after a number
  • + *
  • the source string contains an unknown unit character
  • + *
  • the numeric value is negative
  • + *
* @see Parser#parse() */ static PunishmentDuration parse(String source) { @@ -94,6 +102,7 @@ static PunishmentDuration fromMillis(long millis) { /** * Converts the given {@link Duration} into a {@link PunishmentDuration}. The duration is relative the given length * into the future from a given point in time. It is absolute as soon as a punishment is enforced. + * * @param duration how long the punishment should last * @return the converted duration */ @@ -205,18 +214,23 @@ public void convert() { int index = 0; for (String number : numbers) { index += number.length(); - final long numericValue = Long.parseLong(number); + final long numericValue; + try { + numericValue = Long.parseLong(number); + } catch (NumberFormatException e) { + throw new ParseException("Not a number: " + e.getMessage()); + } if (numericValue < 0) - throw new IllegalArgumentException("Illegal numeric value: " + numericValue); + throw new ParseException("Illegal numeric value: " + numericValue); final char unit; try { unit = Character.toLowerCase(source.charAt(index)); } catch (IndexOutOfBoundsException e) { - throw new IllegalArgumentException("Number is not followed by unit marking character."); + throw new ParseException("Number is not followed by unit marking character."); } TimeUnit timeUnit = characterMapping.get(unit); if (timeUnit == null) - throw new IllegalArgumentException("Unknown time unit for character '" + unit + "'"); + throw new ParseException("Unknown time unit for character '" + unit + "'"); converted.put(timeUnit, numericValue); index++; } @@ -231,9 +245,11 @@ public void convert() { * @return the parsed duration */ public PunishmentDuration parse() { + if (source.isEmpty()) + throw new ParseException("Source string is empty."); convert(); if (rawDuration.isEmpty()) { - throw new IllegalArgumentException("Converted map is empty."); + throw new ParseException("Converted map is empty."); } return fromMillis(durationToMillis()); } @@ -249,5 +265,11 @@ private long durationToMillis() { } return total; } + + public static class ParseException extends RuntimeException { + public ParseException(String message) { + super(message); + } + } } } diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/PunishmentType.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/PunishmentType.java index 8fe5443..0a0b19c 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/PunishmentType.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/PunishmentType.java @@ -35,7 +35,7 @@ public interface PunishmentType { * @since 1.0.1 */ default boolean isMute() { - return this == StandardPunishmentType.MUTE || this == StandardPunishmentType.PERMANENT_MUTE; + return this == StandardPunishmentType.TEMPORARY_MUTE || this == StandardPunishmentType.PERMANENT_MUTE; } /** @@ -45,6 +45,6 @@ default boolean isMute() { * @since 1.0.1 */ default boolean isBan() { - return this == StandardPunishmentType.BAN || this == StandardPunishmentType.PERMANENT_BAN; + return this == StandardPunishmentType.TEMPORARY_BAN || this == StandardPunishmentType.PERMANENT_BAN; } } diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/StandardPunishmentType.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/StandardPunishmentType.java index 4b0d06b..ff18534 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/StandardPunishmentType.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/StandardPunishmentType.java @@ -29,9 +29,9 @@ public enum StandardPunishmentType implements PunishmentType { - BAN(false, "BAN", 1), + TEMPORARY_BAN(false, "BAN", 1), PERMANENT_BAN(true, "PERMANENT_BAN", 2), - MUTE(false, "MUTE", 3), + TEMPORARY_MUTE(false, "MUTE", 3), PERMANENT_MUTE(true, "PERMANENT_MUTE", 4), KICK(false, "KICK", 5); diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/AbstractNecrifyPlugin.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/AbstractNecrifyPlugin.java index ac1d80a..d78e333 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/AbstractNecrifyPlugin.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/AbstractNecrifyPlugin.java @@ -25,10 +25,12 @@ package de.jvstvshd.necrify.common; import de.jvstvshd.necrify.api.Necrify; +import de.jvstvshd.necrify.api.duration.PunishmentDuration; import de.jvstvshd.necrify.api.punishment.StandardPunishmentType; import de.jvstvshd.necrify.api.user.NecrifyUser; import de.jvstvshd.necrify.common.commands.NecrifyCommand; import de.jvstvshd.necrify.common.commands.NecrifyUserParser; +import de.jvstvshd.necrify.common.commands.PunishmentDurationParser; import de.jvstvshd.necrify.common.punishment.NecrifyKick; import de.jvstvshd.necrify.common.commands.UserNotFoundParseException; import net.kyori.adventure.text.Component; @@ -47,7 +49,6 @@ import org.slf4j.Logger; import java.util.ArrayList; -import java.util.List; import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutorService; @@ -104,6 +105,7 @@ public final void registerCommands(CommandManager manager, boolean var parserRegistry = manager.parserRegistry(); parserRegistry.registerParser(ParserDescriptor.of(new NecrifyUserParser(this.getUserManager()), NecrifyUser.class)); parserRegistry.registerParser(ComponentParser.componentParser(MiniMessage.miniMessage(), StringParser.StringMode.GREEDY)); + parserRegistry.registerParser(ParserDescriptor.of(new PunishmentDurationParser(), PunishmentDuration.class)); var commands = new NecrifyCommand(this); parser.parse(commands); } @@ -112,8 +114,8 @@ public final void registerCommands(CommandManager manager, boolean public String getDefaultReason(StandardPunishmentType type) { return "You were " + switch (type) { case KICK -> "kicked from the server."; - case BAN, PERMANENT_BAN -> "banned from the server."; - case MUTE, PERMANENT_MUTE -> "muted."; + case TEMPORARY_BAN, PERMANENT_BAN -> "banned from the server."; + case TEMPORARY_MUTE, PERMANENT_MUTE -> "muted."; } + ""; } diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/NecrifyCommand.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/NecrifyCommand.java index 4cef4c1..3ee56f6 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/NecrifyCommand.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/NecrifyCommand.java @@ -24,17 +24,12 @@ package de.jvstvshd.necrify.common.commands; -import com.mojang.brigadier.LiteralMessage; -import com.mojang.brigadier.Message; -import de.jvstvshd.necrify.api.PunishmentException; -import de.jvstvshd.necrify.api.event.punishment.PunishmentCancelledEvent; -import de.jvstvshd.necrify.api.message.MessageProvider; +import de.jvstvshd.necrify.api.duration.PunishmentDuration; import de.jvstvshd.necrify.api.punishment.Punishment; import de.jvstvshd.necrify.api.punishment.StandardPunishmentType; import de.jvstvshd.necrify.api.user.NecrifyUser; import de.jvstvshd.necrify.api.user.UserManager; import de.jvstvshd.necrify.common.AbstractNecrifyPlugin; -import de.jvstvshd.necrify.common.plugin.Util; import de.jvstvshd.necrify.common.util.PunishmentHelper; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; @@ -45,25 +40,15 @@ import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.incendo.cloud.annotation.specifier.Greedy; import org.incendo.cloud.annotations.Argument; import org.incendo.cloud.annotations.Command; -import org.incendo.cloud.annotations.Default; import org.incendo.cloud.annotations.Permission; -import org.incendo.cloud.annotations.parser.Parser; -import org.incendo.cloud.annotations.processing.CommandContainer; import org.incendo.cloud.annotations.suggestion.Suggestions; -import org.incendo.cloud.brigadier.suggestion.TooltipSuggestion; import org.incendo.cloud.context.CommandContext; import org.incendo.cloud.context.CommandInput; -import org.incendo.cloud.injection.ParameterInjector; import org.incendo.cloud.minecraft.extras.suggestion.ComponentTooltipSuggestion; -import org.incendo.cloud.parser.ArgumentParseResult; import org.incendo.cloud.suggestion.Suggestion; -import org.incendo.cloud.type.Either; -import org.incendo.cloud.util.annotation.AnnotationAccessor; import org.slf4j.Logger; import java.util.Collections; @@ -88,13 +73,16 @@ public NecrifyCommand(AbstractNecrifyPlugin plugin) { this.executor = plugin.getExecutor(); } + //COMMANDS + //Punishing + @Command("necrify ban [reason]") @Command("ban [reason]") @Permission(value = {"necrify.command.ban", "necrify.admin"}, mode = Permission.Mode.ANY_OF) public void banCommand( NecrifyUser sender, @Argument(value = "target", description = "Player to ban", suggestions = "suggestOnlinePlayers") NecrifyUser target, - @Argument(value = "reason", description = "Reason the user should be banned for") @Greedy String reason + @Argument(value = "reason", description = "Reason the user should be banned for", suggestions = "suggestMiniMessage") @Greedy String reason ) { var finalReason = reasonOrDefaultTo(reason, StandardPunishmentType.PERMANENT_BAN); target.banPermanent(finalReason).whenComplete((ban, throwable) -> { @@ -115,7 +103,7 @@ public void banCommand( public void muteCommand( NecrifyUser sender, @Argument(value = "target", description = "Player to mute", suggestions = "suggestOnlinePlayers") NecrifyUser target, - @Argument(value = "reason", description = "Reason the user should be muted for") @Greedy String reason + @Argument(value = "reason", description = "Reason the user should be muted for", suggestions = "suggestMiniMessage") @Greedy String reason ) { var finalReason = reasonOrDefaultTo(reason, StandardPunishmentType.PERMANENT_MUTE); target.mutePermanent(finalReason).whenComplete((mute, throwable) -> { @@ -130,6 +118,75 @@ public void muteCommand( }); } + @Command("necrify kick [reason]") + @Command("kick [reason]") + @Permission(value = {"necrify.command.kick", "necrify.admin"}, mode = Permission.Mode.ANY_OF) + public void kickCommand( + NecrifyUser sender, + @Argument(value = "target", description = "Player to kick", suggestions = "suggestOnlinePlayers") NecrifyUser target, + @Argument(value = "reason", description = "Reason the user should be kicked for", suggestions = "suggestMiniMessage") @Greedy String reason + ) { + var finalReason = reasonOrDefaultTo(reason, StandardPunishmentType.KICK); + target.kick(finalReason).whenComplete((unused, throwable) -> { + if (throwable != null) { + logException(sender, throwable); + return; + } + sender.sendMessage("command.kick.success", + miniMessage(target.getUsername()).color(NamedTextColor.YELLOW), + copyComponent(target.getUuid().toString()).color(NamedTextColor.YELLOW), + finalReason); + }); + } + + @Command("necrify tempban [reason]") + @Command("tempban [reason]") + @Permission(value = {"necrify.command.tempban", "necrify.admin"}, mode = Permission.Mode.ANY_OF) + public void tempbanCommand( + NecrifyUser sender, + @Argument(value = "target", description = "Player to tempban", suggestions = "suggestOnlinePlayers") NecrifyUser target, + @Argument(value = "duration", description = "Duration the user should be banned for") PunishmentDuration duration, + @Argument(value = "reason", description = "Reason the user should be banned for", suggestions = "suggestMiniMessage") @Greedy String reason + ) { + var finalReason = reasonOrDefaultTo(reason, StandardPunishmentType.TEMPORARY_BAN); + target.ban(finalReason, duration).whenComplete((ban, throwable) -> { + if (throwable != null) { + logException(sender, throwable); + return; + } + sender.sendMessage("command.tempban.success", + miniMessage(target.getUsername()).color(NamedTextColor.YELLOW), + copyComponent(target.getUuid().toString()).color(NamedTextColor.YELLOW), + finalReason, + miniMessage(duration.expirationAsString()).color(NamedTextColor.YELLOW)); + }); + } + + @Command("necrify tempmute [reason]") + @Command("tempmute [reason]") + @Permission(value = {"necrify.command.tempmute", "necrify.admin"}, mode = Permission.Mode.ANY_OF) + public void tempmuteCommand( + NecrifyUser sender, + @Argument(value = "target", description = "Player to tempmute", suggestions = "suggestOnlinePlayers") NecrifyUser target, + @Argument(value = "duration", description = "Duration the user should be muted for") PunishmentDuration duration, + @Argument(value = "reason", description = "Reason the user should be muted for", suggestions = "suggestMiniMessage") @Greedy String reason + ) { + var finalReason = reasonOrDefaultTo(reason, StandardPunishmentType.TEMPORARY_MUTE); + target.mute(finalReason, duration).whenComplete((mute, throwable) -> { + if (throwable != null) { + logException(sender, throwable); + return; + } + sender.sendMessage("command.tempmute.success", + miniMessage(target.getUsername()).color(NamedTextColor.YELLOW), + copyComponent(target.getUuid().toString()).color(NamedTextColor.YELLOW), + finalReason, + miniMessage(duration.expirationAsString()).color(NamedTextColor.YELLOW)); + }); + } + + //Removal of punishments + @Command("necrify unban ") @Command("unban ") @Permission(value = {"necrify.command.unban", "necrify.admin"}, mode = Permission.Mode.ANY_OF) @@ -137,7 +194,7 @@ public void unbanCommand( NecrifyUser sender, @Argument(value = "target", description = "Player to unban") NecrifyUser target ) { - var punishments = target.getPunishments(StandardPunishmentType.BAN, StandardPunishmentType.PERMANENT_BAN); + var punishments = target.getPunishments(StandardPunishmentType.TEMPORARY_BAN, StandardPunishmentType.PERMANENT_BAN); try { removePunishments(sender, "unban", punishments); } catch (Exception e) { @@ -152,10 +209,33 @@ public void unmuteCommand( NecrifyUser sender, @Argument(value = "target", description = "Player to unmute", suggestions = "suggestOnlinePlayers") NecrifyUser target ) { - var punishments = target.getPunishments(StandardPunishmentType.MUTE, StandardPunishmentType.PERMANENT_MUTE); + var punishments = target.getPunishments(StandardPunishmentType.TEMPORARY_MUTE, StandardPunishmentType.PERMANENT_MUTE); removePunishments(sender, "unmute", punishments); } + //SUGGESTIONS + + @Suggestions("suggestOnlinePlayers") + public List suggestNames(CommandContext context, CommandInput input) { + return plugin + .getOnlinePlayers() + .stream() + .filter(pair -> pair.first().toLowerCase(Locale.ROOT).startsWith(input.peekString().toLowerCase(Locale.ROOT))) + .map(pair -> ComponentTooltipSuggestion.suggestion(pair.first(), + miniMessage("The player (/) you want to select.", + Placeholder.parsed("name", pair.first()), + Placeholder.parsed("uuid", pair.second().toString())) + )).toList(); + } + + @Suggestions("suggestMiniMessage") + public List suggestMiniMessage(CommandContext context, CommandInput input) { + return Collections.singletonList(ComponentTooltipSuggestion.suggestion(input.remainingInput() + " (hover for preview)", + miniMessage(input.remainingInput()))); + } + + //HELPER METHODS + private void removePunishments(NecrifyUser source, String commandName, List punishments) { if (punishments.isEmpty()) { source.sendMessage(plugin.getMessageProvider().provide("command.punishment.not-banned").color(NamedTextColor.RED)); @@ -184,18 +264,7 @@ private void removePunishments(NecrifyUser source, String commandName, List suggestNames(CommandContext context, CommandInput input) { - return plugin - .getOnlinePlayers() - .stream() - .filter(pair -> pair.first().toLowerCase(Locale.ROOT).startsWith(input.peekString().toLowerCase(Locale.ROOT))) - .map(pair -> ComponentTooltipSuggestion.suggestion(pair.first(), - miniMessage("The player (/) you want to select.", - Placeholder.parsed("name", pair.first()), - Placeholder.parsed("uuid", pair.second().toString())) - )).toList(); - } + //Communication, messaging, logging private Component buildComponent(Component dataComponent, Punishment punishment) { return dataComponent.clickEvent(ClickEvent.runCommand("/punishment " + punishment.getPunishmentUuid() diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/PunishmentDurationParser.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/PunishmentDurationParser.java new file mode 100644 index 0000000..1d75b64 --- /dev/null +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/PunishmentDurationParser.java @@ -0,0 +1,74 @@ +/* + * This file is part of Necrify (formerly Velocity Punishment), which is licensed under the MIT license. + * + * Copyright (c) 2022-2024 JvstvsHD + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package de.jvstvshd.necrify.common.commands; + +import com.mojang.brigadier.LiteralMessage; +import de.jvstvshd.necrify.api.duration.PunishmentDuration; +import de.jvstvshd.necrify.api.user.NecrifyUser; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.brigadier.suggestion.TooltipSuggestion; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.minecraft.extras.suggestion.ComponentTooltipSuggestion; +import org.incendo.cloud.parser.ArgumentParseResult; +import org.incendo.cloud.parser.ArgumentParser; +import org.incendo.cloud.suggestion.SuggestionProvider; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +public class PunishmentDurationParser implements ArgumentParser { + + @Override + public @NonNull ArgumentParseResult<@NonNull PunishmentDuration> parse(@NonNull CommandContext<@NonNull NecrifyUser> commandContext, @NonNull CommandInput commandInput) { + var input = commandInput.peekString(); + if (input.endsWith("(hover over me)")) + input = input.substring(0, input.length() - " (hover over me)".length()); + try { + var duration = PunishmentDuration.parse(input); + commandInput.readString(); + return ArgumentParseResult.success(duration); + } catch (PunishmentDuration.Parser.ParseException e) { + return ArgumentParseResult.failure(e); + } + } + @Override + public @NonNull SuggestionProvider suggestionProvider() { + return (context, input) -> { + var string = input.peekString(); + ComponentTooltipSuggestion suggestion; + try { + var duration = PunishmentDuration.parse(string); + var expiration = duration.expirationAsString(); + suggestion = ComponentTooltipSuggestion.suggestion(string + " | correct (hover over me)", MiniMessage.miniMessage().deserialize("until " + expiration)); + } catch (PunishmentDuration.Parser.ParseException e) { + suggestion = ComponentTooltipSuggestion.suggestion(string + " | incorrect (hover over me)", MiniMessage.miniMessage().deserialize("Invalid duration format: " + e.getMessage())); + } + return CompletableFuture.completedFuture(List.of(suggestion)); + }; + } +} diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/NecrifyBan.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/NecrifyBan.java index c72f173..25bbf7e 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/NecrifyBan.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/NecrifyBan.java @@ -88,6 +88,6 @@ public boolean isPermanent() { @Override public @NotNull StandardPunishmentType getType() { - return getDuration().isPermanent() ? StandardPunishmentType.PERMANENT_BAN : StandardPunishmentType.BAN; + return getDuration().isPermanent() ? StandardPunishmentType.PERMANENT_BAN : StandardPunishmentType.TEMPORARY_BAN; } } diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/NecrifyMute.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/NecrifyMute.java index b6bfdef..7f67b8d 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/NecrifyMute.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/NecrifyMute.java @@ -54,7 +54,7 @@ public boolean isOngoing() { @Override public @NotNull StandardPunishmentType getType() { - return isPermanent() ? StandardPunishmentType.PERMANENT_MUTE : StandardPunishmentType.MUTE; + return isPermanent() ? StandardPunishmentType.PERMANENT_MUTE : StandardPunishmentType.TEMPORARY_MUTE; } @Override diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/PunishmentBuilder.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/PunishmentBuilder.java index 81c1837..dd6c57b 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/PunishmentBuilder.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/PunishmentBuilder.java @@ -96,7 +96,7 @@ public PunishmentBuilder withSuccessor(Punishment successor) { public NecrifyBan buildBan() { validateValues(); - return new NecrifyBan(user, reason, punishmentUuid, duration, plugin, successor); + return new NecrifyBan(user, reason, punishmentUuid, duration.absolute(), plugin, successor); } /** @@ -111,7 +111,7 @@ public NecrifyKick buildKick() { public NecrifyMute buildMute() { validateValues(); - return new NecrifyMute(user, reason, punishmentUuid, duration, plugin, successor); + return new NecrifyMute(user, reason, punishmentUuid, duration.absolute(), plugin, successor); } private void validateValues() { @@ -121,7 +121,5 @@ private void validateValues() { throw new NullPointerException("user is null"); if (reason == null) throw new NullPointerException("reason is null"); - if (duration == null) - throw new NullPointerException("duration is null"); } } diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/NecrifyVelocityPlugin.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/NecrifyVelocityPlugin.java index a859bd9..f2d4a5a 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/NecrifyVelocityPlugin.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/NecrifyVelocityPlugin.java @@ -216,8 +216,8 @@ private void setup(CommandManager commandManager, EventManager eventManager) { return TooltipSuggestion.suggestion(suggestion.suggestion(), VelocityBrigadierMessage.tooltip(componentTooltipSuggestion.tooltip())); }); var brigadierManager = cManager.brigadierManager(); - brigadierManager.setNativeSuggestions(new TypeToken>() { - }, true); + /*brigadierManager.setNativeSuggestions(new TypeToken>() { + }, true);*/ brigadierManager.setNativeNumberSuggestions(true); registerCommands(cManager, getConfig().getConfiguration().isAllowTopLevelCommands()); brigadierManager.registerMapping(new TypeToken>() { diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/PunishmentRemovalCommand.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/PunishmentRemovalCommand.java index 1c5d6bc..44df0c0 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/PunishmentRemovalCommand.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/PunishmentRemovalCommand.java @@ -48,13 +48,13 @@ public class PunishmentRemovalCommand { public static BrigadierCommand unmuteCommand(NecrifyVelocityPlugin plugin) { var node = Util.permissibleCommand("unmute", "necrify.command.unmute") - .then(Util.punishmentRemoveArgument(plugin).executes(context -> execute(context, plugin, "unmute", StandardPunishmentType.MUTE, StandardPunishmentType.PERMANENT_MUTE))); + .then(Util.punishmentRemoveArgument(plugin).executes(context -> execute(context, plugin, "unmute", StandardPunishmentType.TEMPORARY_MUTE, StandardPunishmentType.PERMANENT_MUTE))); return new BrigadierCommand(node); } public static BrigadierCommand unbanCommand(NecrifyVelocityPlugin plugin) { var node = Util.permissibleCommand("unban", "necrify.command.unban") - .then(Util.punishmentRemoveArgument(plugin).executes(context -> execute(context, plugin, "unban", StandardPunishmentType.BAN, StandardPunishmentType.PERMANENT_BAN))); + .then(Util.punishmentRemoveArgument(plugin).executes(context -> execute(context, plugin, "unban", StandardPunishmentType.TEMPORARY_BAN, StandardPunishmentType.PERMANENT_BAN))); return new BrigadierCommand(node); } diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/impl/DefaultPunishmentManager.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/impl/DefaultPunishmentManager.java index d9eaa1e..69d993f 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/impl/DefaultPunishmentManager.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/impl/DefaultPunishmentManager.java @@ -108,6 +108,6 @@ public NecrifyVelocityPlugin plugin() { @Override public CompletableFuture isBanned(UUID playerUuid, Executor executor) { - return Util.executeAsync(() -> !getPunishments(playerUuid, executor, StandardPunishmentType.BAN, StandardPunishmentType.PERMANENT_BAN).get().isEmpty(), executor); + return Util.executeAsync(() -> !getPunishments(playerUuid, executor, StandardPunishmentType.TEMPORARY_BAN, StandardPunishmentType.PERMANENT_BAN).get().isEmpty(), executor); } } \ No newline at end of file diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/user/VelocityUser.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/user/VelocityUser.java index 428810a..d8fc723 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/user/VelocityUser.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/user/VelocityUser.java @@ -233,8 +233,8 @@ public Punishment addPunishment(Row row) throws SQLException { .withPunishmentUuid(punishmentUuid); Punishment punishment; switch (type) { - case BAN, PERMANENT_BAN -> punishment = builder.buildBan(); - case MUTE, PERMANENT_MUTE -> punishment = builder.buildMute(); + case TEMPORARY_BAN, PERMANENT_BAN -> punishment = builder.buildBan(); + case TEMPORARY_MUTE, PERMANENT_MUTE -> punishment = builder.buildMute(); case KICK -> punishment = builder.buildKick(); default -> throw new UnsupportedOperationException("unhandled punishment type: " + type.getName()); } From 7eecfb511f67ef0be53fa188b7d42c15508d7bd4 Mon Sep 17 00:00:00 2001 From: JvstvsHD <79066214+JvstvsHD@users.noreply.github.com> Date: Sun, 28 Jul 2024 18:30:46 +0200 Subject: [PATCH 7/7] Add /necrify punishment and user commands Split original /punishment command into: - /necrify punishment info|cancel|remove|change - /necrify user info|delete /necrify punishment and /necrify user delete are currently not usable since further features are missing --- .../necrify/common/AbstractNecrifyPlugin.java | 12 ++- .../common/commands/NecrifyCommand.java | 82 ++++++++++++++++++- .../common/commands/PunishmentParser.java | 76 +++++++++++++++++ .../necrify/common/{plugin => util}/Util.java | 19 ++++- .../main/resources/translations/de.properties | 9 +- .../main/resources/translations/en.properties | 6 +- .../velocity/NecrifyVelocityPlugin.java | 13 ++- 7 files changed, 200 insertions(+), 17 deletions(-) create mode 100644 necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/PunishmentParser.java rename necrify-common/src/main/java/de/jvstvshd/necrify/common/{plugin => util}/Util.java (81%) diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/AbstractNecrifyPlugin.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/AbstractNecrifyPlugin.java index d78e333..749a6f2 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/AbstractNecrifyPlugin.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/AbstractNecrifyPlugin.java @@ -26,13 +26,11 @@ import de.jvstvshd.necrify.api.Necrify; import de.jvstvshd.necrify.api.duration.PunishmentDuration; +import de.jvstvshd.necrify.api.punishment.Punishment; import de.jvstvshd.necrify.api.punishment.StandardPunishmentType; import de.jvstvshd.necrify.api.user.NecrifyUser; -import de.jvstvshd.necrify.common.commands.NecrifyCommand; -import de.jvstvshd.necrify.common.commands.NecrifyUserParser; -import de.jvstvshd.necrify.common.commands.PunishmentDurationParser; +import de.jvstvshd.necrify.common.commands.*; import de.jvstvshd.necrify.common.punishment.NecrifyKick; -import de.jvstvshd.necrify.common.commands.UserNotFoundParseException; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.minimessage.MiniMessage; @@ -93,6 +91,11 @@ public final void registerCommands(CommandManager manager, boolean .registerHandler(UserNotFoundParseException.class, context -> { context.context().sender().sendMessage("commands.general.not-found", Component.text(context.exception().playerName()).color(NamedTextColor.YELLOW)); }); + manager.exceptionController() + .registerHandler(ArgumentParseException.class, ExceptionHandler.unwrappingHandler(PunishmentParser.PunishmentParseException.class)) + .registerHandler(PunishmentParser.PunishmentParseException.class, context -> { + context.context().sender().sendMessage(context.exception().getMessage()); + }); /*manager.exceptionController()//.registerHandler(ArgumentParseException.class, ExceptionHandler.unwrappingHandler(ArgumentParseException.class)) .registerHandler(ArgumentParseException.class, context -> { context.context().sender().sendMessage("commands.general.invalid-argument"); @@ -106,6 +109,7 @@ public final void registerCommands(CommandManager manager, boolean parserRegistry.registerParser(ParserDescriptor.of(new NecrifyUserParser(this.getUserManager()), NecrifyUser.class)); parserRegistry.registerParser(ComponentParser.componentParser(MiniMessage.miniMessage(), StringParser.StringMode.GREEDY)); parserRegistry.registerParser(ParserDescriptor.of(new PunishmentDurationParser(), PunishmentDuration.class)); + parserRegistry.registerParser(ParserDescriptor.of(new PunishmentParser(this), Punishment.class)); var commands = new NecrifyCommand(this); parser.parse(commands); } diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/NecrifyCommand.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/NecrifyCommand.java index 3ee56f6..b6b0beb 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/NecrifyCommand.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/NecrifyCommand.java @@ -28,6 +28,7 @@ import de.jvstvshd.necrify.api.punishment.Punishment; import de.jvstvshd.necrify.api.punishment.StandardPunishmentType; import de.jvstvshd.necrify.api.user.NecrifyUser; +import de.jvstvshd.necrify.api.user.UserDeletionReason; import de.jvstvshd.necrify.api.user.UserManager; import de.jvstvshd.necrify.common.AbstractNecrifyPlugin; import de.jvstvshd.necrify.common.util.PunishmentHelper; @@ -43,6 +44,7 @@ import org.incendo.cloud.annotation.specifier.Greedy; import org.incendo.cloud.annotations.Argument; import org.incendo.cloud.annotations.Command; +import org.incendo.cloud.annotations.Default; import org.incendo.cloud.annotations.Permission; import org.incendo.cloud.annotations.suggestion.Suggestions; import org.incendo.cloud.context.CommandContext; @@ -65,6 +67,9 @@ public class NecrifyCommand { private final Logger logger; private final ExecutorService executor; + private static final List PUNISHMENT_COMMAND_OPTIONS = List.of("cancel", "remove", "info", "change"); + private static final List USER_COMMAND_OPTIONS = List.of("info", "delete"); + public NecrifyCommand(AbstractNecrifyPlugin plugin) { this.plugin = plugin; this.userManager = plugin.getUserManager(); @@ -213,6 +218,59 @@ public void unmuteCommand( removePunishments(sender, "unmute", punishments); } + @Command("necrify punishment [option]") + @Permission(value = {"necrify.command.punishment", "necrify.admin"}, mode = Permission.Mode.ANY_OF) + public void punishmentCommand( + NecrifyUser sender, + @Argument(value = "punishment", description = "Punishment to manage") Punishment punishment, + @Argument(value = "option", description = "Option to manage the punishment", suggestions = "suggestPunishmentCommandOptions") @Default("info") String option + ) { + switch (option) { + case "info" -> + sender.sendMessage(buildComponent(PunishmentHelper.buildPunishmentData(punishment, plugin.getMessageProvider()), punishment)); + case "cancel", "remove" -> { + punishment.cancel().whenCompleteAsync((unused, th) -> { + if (th != null) { + logException(sender, th); + return; + } + sender.sendMessage("command.punishment.cancel.success"); + }, plugin.getService()); + } + case "change" -> { + sender.sendMessage(miniMessage("Soon (TM)").color(NamedTextColor.LIGHT_PURPLE)); + } + } + } + + @Command("necrify user [option]") + @Permission(value = {"necrify.command.user", "necrify.admin"}, mode = Permission.Mode.ANY_OF) + public void userCommand( + NecrifyUser sender, + @Argument(value = "target", description = "Player to manage", suggestions = "suggestOnlinePlayers") NecrifyUser target, + @Argument(value = "option", description = "Option to manage the player", suggestions = "suggestUserCommandOptions") @Default("info") String option + ) { + switch (option) { + case "info" -> { + var punishments = target.getPunishments(); + for (Punishment punishment : punishments) { + Component component = PunishmentHelper.buildPunishmentData(punishment, plugin.getMessageProvider()) + .clickEvent(ClickEvent.suggestCommand(punishment.getPunishmentUuid().toString().toLowerCase(Locale.ROOT))) + .hoverEvent((HoverEventSource) op -> HoverEvent.showText(plugin.getMessageProvider().provide("commands.general.copy") + .color(NamedTextColor.GREEN))); + sender.sendMessage(component); + } + } + case "delete" -> { + //TODO add confirmation + target.delete(UserDeletionReason.USER_DELETED); + sender.sendMessage("command.user.delete.success", + miniMessage(target.getUsername()).color(NamedTextColor.YELLOW), + copyComponent(target.getUuid().toString()).color(NamedTextColor.YELLOW)); + } + } + } + //SUGGESTIONS @Suggestions("suggestOnlinePlayers") @@ -234,15 +292,34 @@ public List suggestMiniMessage(CommandContext miniMessage(input.remainingInput()))); } + @Suggestions("suggestPunishmentCommandOptions") + public List suggestPunishmentCommandOptions(CommandContext context, CommandInput input) { + return PUNISHMENT_COMMAND_OPTIONS + .stream() + .filter(option -> option.toLowerCase().startsWith(input.peekString().toLowerCase())) + .map(option -> ComponentTooltipSuggestion.suggestion(option, miniMessage(option))) + .toList(); + } + + @Suggestions("suggestUserCommandOptions") + public List suggestUserCommandOptions(CommandContext context, CommandInput input) { + return USER_COMMAND_OPTIONS + .stream() + .filter(option -> option.toLowerCase().startsWith(input.peekString().toLowerCase())) + .map(option -> ComponentTooltipSuggestion.suggestion(option, miniMessage(option))) + .toList(); + } + //HELPER METHODS private void removePunishments(NecrifyUser source, String commandName, List punishments) { + var type = commandName.substring(2); if (punishments.isEmpty()) { - source.sendMessage(plugin.getMessageProvider().provide("command.punishment.not-banned").color(NamedTextColor.RED)); + source.sendMessage(plugin.getMessageProvider().provide("command.punishment.not-" + type).color(NamedTextColor.RED)); return; } if (punishments.size() > 1) { - source.sendMessage(plugin.getMessageProvider().provide("command." + commandName + ".multiple-bans").color(NamedTextColor.YELLOW)); + source.sendMessage(plugin.getMessageProvider().provide("command." + commandName + ".multiple-" + type + "s").color(NamedTextColor.YELLOW)); for (Punishment punishment : punishments) { source.sendMessage(buildComponent(PunishmentHelper.buildPunishmentData(punishment, plugin.getMessageProvider()), punishment)); } @@ -290,7 +367,6 @@ public TextComponent copyComponent(String text) { } - private void logException(Throwable throwable) { logger.error("An error occurred while executing a command", throwable); } diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/PunishmentParser.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/PunishmentParser.java new file mode 100644 index 0000000..8953a0e --- /dev/null +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/PunishmentParser.java @@ -0,0 +1,76 @@ +/* + * This file is part of Necrify (formerly Velocity Punishment), which is licensed under the MIT license. + * + * Copyright (c) 2022-2024 JvstvsHD + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package de.jvstvshd.necrify.common.commands; + +import de.jvstvshd.necrify.api.Necrify; +import de.jvstvshd.necrify.api.punishment.Punishment; +import de.jvstvshd.necrify.api.user.NecrifyUser; +import de.jvstvshd.necrify.common.util.Util; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.parser.ArgumentParseResult; +import org.incendo.cloud.parser.ArgumentParser; + +import java.util.concurrent.CompletableFuture; + +public class PunishmentParser implements ArgumentParser.FutureArgumentParser { + + private final Necrify necrify; + + public PunishmentParser(Necrify necrify) { + this.necrify = necrify; + } + + @Override + public @NonNull CompletableFuture<@NonNull ArgumentParseResult> parseFuture(@NonNull CommandContext commandContext, @NonNull CommandInput commandInput) { + var uuidString = commandInput.peekString(); + var uuid = Util.fromString(uuidString); + if (uuid.isEmpty()) { + return CompletableFuture.completedFuture(ArgumentParseResult.failure(new PunishmentParseException("command.parse.uuid.invalid", uuidString))); + } + return necrify.getPunishment(uuid.get()).handle((punishment, throwable) -> { + if (throwable != null) { + return ArgumentParseResult.failure(new PunishmentParseException("error.internal")); + } + return punishment.map(ArgumentParseResult::success) + .orElseGet(() -> ArgumentParseResult.failure(new PunishmentParseException("command.parse.punishment.notfound", uuidString))); + }); + } + + public static class PunishmentParseException extends Exception { + + private final String[] replacements; + + public PunishmentParseException(String message, String...replacements) { + super(message); + this.replacements = replacements; + } + + public String[] getReplacements() { + return replacements; + } + } +} diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/plugin/Util.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/util/Util.java similarity index 81% rename from necrify-common/src/main/java/de/jvstvshd/necrify/common/plugin/Util.java rename to necrify-common/src/main/java/de/jvstvshd/necrify/common/util/Util.java index 8b9184f..59055ac 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/plugin/Util.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/util/Util.java @@ -22,7 +22,7 @@ * SOFTWARE. */ -package de.jvstvshd.necrify.common.plugin; +package de.jvstvshd.necrify.common.util; import de.jvstvshd.necrify.api.message.MessageProvider; import net.kyori.adventure.text.Component; @@ -32,6 +32,7 @@ import net.kyori.adventure.text.event.HoverEventSource; import net.kyori.adventure.text.format.NamedTextColor; +import java.util.Optional; import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; @@ -62,4 +63,20 @@ public static TextComponent copyComponent(String text, MessageProvider provider) return Component.text(text).clickEvent(ClickEvent.suggestCommand(text)) .hoverEvent((HoverEventSource) op -> HoverEvent.showText(provider.provide("commands.general.copy").color(NamedTextColor.GREEN))); } + + public static Optional fromString(String uuidString) { + UUID uuid; + try { + uuid = UUID.fromString(uuidString); + } catch (IllegalArgumentException e) { + try { + uuid = UUID.fromString(uuidString.replaceAll( + "(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", + "$1-$2-$3-$4-$5")); + } catch (Exception ex) { + return Optional.empty(); + } + } + return Optional.of(uuid); + } } \ No newline at end of file diff --git a/necrify-common/src/main/resources/translations/de.properties b/necrify-common/src/main/resources/translations/de.properties index f0747c8..5254535 100644 --- a/necrify-common/src/main/resources/translations/de.properties +++ b/necrify-common/src/main/resources/translations/de.properties @@ -12,6 +12,8 @@ command.mute.usage=Bitte benutze /mute [Grund] command.mute.success=Du hast Spieler {0}/{1} für {2} gemutet. command.punishment.usage=Bitte benutze /punishment oder command.punishment.not-banned=Dieser Spieler ist derzeit nicht gebannt. +command.punishment.not-muted=Dieser Spieler ist derzeit nicht gemutet. +command.punishment.cancel.success=Die Strafe wurde erfolgreich abgebrochen. command.punishment.punishments=Dieser Spieler hat derzeit {0} laufende Bestrafungen. command.punishment.uuid-parse-error='{0}' ist keine valide UUID. command.punishment.unknown-option=Unbekannte Option: {0} @@ -21,11 +23,12 @@ command.tempban.success=Du hast den Spieler {0}/{1} f command.tempmute.usage=Bitte benutze /tempmute [Grund]. command.tempmute.success=Du hast den Spieler {0}/{1} für {2} bis {3} gemutet. command.unban.usage=Bitte benutze /unban . -command.unban.multiple-bans=Dieser Spieler wurde mehrfach bestraft. command.unban.success=Der Spieler wurde entbannt. +command.unban.multiple-bans=Dieser Spieler wurde mehrfach gebannt. command.unmute.usage=Bitte benutze /unban . -command.unmute.not-muted=Der Spieler ist derzeit nicht gemutet. -command.unmute.multiple-mutes=Dieser Spieler wurde mehrfach bestraft. +command.unmute.success=Der Spieler ist nun nicht mehr gemutet. +command.unmute.multiple-mutes=Dieser Spieler wurde mehrfach gemutet. +command.user.delete.success=Der User wurde inklusive all seiner Strafen erfolgreich gelöscht. command.whitelist.usage=Bitte nutze /whitelist [add|remove] command.whitelist.status=Der Whitelist-Status des Spielers {0} ist folgender: {1}. command.whitelist.success=Der Eintrag wurde erfolgreich aktualisiert. diff --git a/necrify-common/src/main/resources/translations/en.properties b/necrify-common/src/main/resources/translations/en.properties index 487b744..6136665 100644 --- a/necrify-common/src/main/resources/translations/en.properties +++ b/necrify-common/src/main/resources/translations/en.properties @@ -11,6 +11,8 @@ command.mute.usage=Please use /mute [reason] command.mute.success=You have muted the player {0}/{1} for {2}. command.punishment.usage=Please use /punishment or command.punishment.not-banned=This player is not banned at the moment. +command.punishment.not-muted=This player is not muted at the moment. +command.punishment.cancel.success=The punishment has been successfully cancelled. command.punishment.punishments=This player has {0} punishments. command.punishment.uuid-parse-error=Could not parse string '{0}' as uuid. command.punishment.unknown-option=Unknown option: {0} @@ -20,10 +22,10 @@ command.tempban.success=You have banned the player {0}/{1} for {2} until {3}. command.tempmute.usage=Please use /tempmute [reason]. command.tempmute.success=You have mute the player {0}/{1} for {2} until {3}. command.unban.usage=Please use /unban . -command.unban.multiple-bans=This player has been banned multiple times. command.unban.success=The player was unbanned. +command.unban.multiple-bans=This player has been banned multiple times. command.unmute.usage=Please use /unban . -command.unmute.not-muted=This player is not muted. +command.unmute.success=The player is no longer muted. command.unmute.multiple-mutes=This player has been muted multiple times. command.whitelist.usage=Please use /whitelist [add|remove] command.whitelist.status={0}'s whitelist status: {1}. diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/NecrifyVelocityPlugin.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/NecrifyVelocityPlugin.java index f2d4a5a..643877b 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/NecrifyVelocityPlugin.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/NecrifyVelocityPlugin.java @@ -361,10 +361,15 @@ public void setUserManager(@NotNull UserManager userManager) { @SuppressWarnings("unchecked") @Override public CompletableFuture> getPunishment(@NotNull UUID punishmentId) { - return Util.executeAsync(() -> (Optional) Query.query("SELECT u.* FROM punishment.necrify_user u " + "INNER JOIN punishment.necrify_punishment p ON u.uuid = p.uuid " + "WHERE p.punishment_id = ?;").single(Call.of().bind(punishmentId, Adapters.UUID_ADAPTER)).map(row -> { - var userId = row.getObject(1, UUID.class); - return createUser(userId).getPunishment(punishmentId).orElse(null); - }).first(), getExecutor()); + System.out.println(punishmentId); + return Util.executeAsync(() -> (Optional) Query + .query("SELECT u.* FROM punishment.necrify_user u INNER JOIN punishment.necrify_punishment p ON u.uuid = p.uuid WHERE p.punishment_id = ?;") + .single(Call.of().bind(punishmentId, Adapters.UUID_ADAPTER)) + .map(row -> { + var userId = row.getObject(1, UUID.class); + System.out.println("User " + userId); + return createUser(userId).getPunishment(punishmentId).orElse(null); + }).first(), getExecutor()); } public NecrifyUser createUser(CommandSource source) {