diff --git a/build.gradle.kts b/build.gradle.kts index adedbe96..c9378c28 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,7 +14,7 @@ plugins { } group = "de.jvstvshd.necrify" -version = "1.2.0-rc.1" +version = "1.2.0" subprojects { apply { @@ -24,9 +24,6 @@ subprojects { plugin() } indraSpotlessLicenser { - //licenseHeaderFile(rootProject.files("HEADER.txt")) - //headerFormat { doubleSlash() } - languageFormatOverride("pebble") { starSlash() } newLine(true) } java { diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/Punishment.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/Punishment.java index 330059f0..87c04961 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/Punishment.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/Punishment.java @@ -150,7 +150,7 @@ default Punishment getSuccessorOrNull() { * After calling this method, do not access the data of successor anymore as it may be changed. Instead, use {@link #getSuccessor()} * or {@link #getSuccessorOrNull()} as soon as the future completes. * - * @param successor the successor of this punishment + * @param successor the successor of this punishment or null to remove the successor * @return a {@link CompletableFuture} containing this punishment with the successor set * @throws UnsupportedOperationException if the underlying punishment does not support succeeding punishments (e.g. Kicks) * @throws IllegalArgumentException if {@code successor} is not related to this punishment or if the succeeding punishment is not applied the same user @@ -158,7 +158,7 @@ default Punishment getSuccessorOrNull() { * @since 1.2.0 */ @NotNull - CompletableFuture setSuccessor(@NotNull Punishment successor); + CompletableFuture setSuccessor(Punishment successor); /** * Returns the creation time of this punishment. diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/util/ReasonHolder.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/util/ReasonHolder.java index 0f05feff..10976fdc 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/util/ReasonHolder.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/util/ReasonHolder.java @@ -31,12 +31,19 @@ public interface ReasonHolder { /** - * Gets the reason of this punishment. + * Gets the reason of this punishment. All placeholders will be replaced with their actual values. * @return the reason of this punishment as as component + * */ @NotNull Component getReason(); + /** + * Gets the reason of this punishment. All placeholders will remain as they are. + * @return the reason of this punishment as as component + */ + @NotNull + Component getRawReason(); /** * Creates the full reason inclusive when the ban ends (or that the ban is permanent). * 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 01906b41..ccc4457c 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 @@ -21,6 +21,7 @@ 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.PunishmentType; import de.jvstvshd.necrify.api.punishment.PunishmentTypeRegistry; import de.jvstvshd.necrify.api.punishment.StandardPunishmentType; import de.jvstvshd.necrify.api.user.NecrifyUser; @@ -136,13 +137,7 @@ public final void registerCommands(CommandManager manager, boolean } //TODO: Move config to necrify-common - public String getDefaultReason(StandardPunishmentType type) { - return "You were " + switch (type) { - case KICK -> "kicked from the server."; - case TEMPORARY_BAN, PERMANENT_BAN -> "banned from the server."; - case TEMPORARY_MUTE, PERMANENT_MUTE -> "muted."; - } + ""; - } + public abstract String getDefaultReason(PunishmentType type); @SuppressWarnings("ConstantValue") public static String buildInfo() { 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 5926ba5b..16f247dd 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 @@ -285,6 +285,9 @@ public void userCommand( Util.copyComponent(target.getUuid().toString(), provider).color(NamedTextColor.YELLOW), Component.text(punishments.size())).color(NamedTextColor.GRAY)); for (Punishment punishment : punishments) { + if (punishment.getPredecessor() != null) { + continue; + } Component component = buildComponent(PunishmentHelper.buildPunishmentData(punishment, plugin.getMessageProvider()), punishment); sender.sendMessage(component); } @@ -408,6 +411,9 @@ private void removePunishments(NecrifyUser source, List punishments, if (punishments.size() > 1) { source.sendMessage(plugin.getMessageProvider().provide("command." + values[0] + ".multiple-" + values[1] + "s").color(NamedTextColor.YELLOW)); for (Punishment punishment : punishments) { + if (punishment.getPredecessor() != null) { + continue; + } source.sendMessage(buildComponent(PunishmentHelper.buildPunishmentData(punishment, plugin.getMessageProvider()), punishment)); } } else { diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/io/Adapters.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/io/Adapters.java index 4343a218..3571970a 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/io/Adapters.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/io/Adapters.java @@ -20,6 +20,7 @@ import de.chojo.sadu.core.conversion.UUIDConverter; import de.chojo.sadu.queries.api.call.adapter.Adapter; +import de.chojo.sadu.queries.api.call.adapter.AdapterMapping; import de.chojo.sadu.queries.call.adapter.UUIDAdapter; import java.sql.JDBCType; @@ -38,4 +39,16 @@ public class Adapters { default -> preparedStatement.setBytes(parameterIndex, UUIDConverter.convert(x)); } }, Types.BINARY); + + public static final Adapter UUID_NULL_ADAPTER = new Adapter() { + @Override + public AdapterMapping mapping() { + return null; + } + + @Override + public int type() { + return Types.NULL; + } + }; } 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 b83a346e..1242f61b 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 @@ -27,6 +27,7 @@ import de.jvstvshd.necrify.api.user.NecrifyUser; import de.jvstvshd.necrify.common.AbstractNecrifyPlugin; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextReplacementConfig; import net.kyori.adventure.text.minimessage.MiniMessage; import org.intellij.lang.annotations.Language; import org.jetbrains.annotations.NotNull; @@ -89,6 +90,11 @@ public AbstractPunishment(@NotNull NecrifyUser user, return reason; } + @Override + public @NotNull Component getRawReason() { + return reason; + } + public ExecutorService getExecutor() { return executor; } 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 96bdd7e3..6269b111 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 @@ -33,6 +33,8 @@ import de.jvstvshd.necrify.common.io.Adapters; import de.jvstvshd.necrify.common.util.Util; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextReplacementConfig; +import net.kyori.adventure.text.format.NamedTextColor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -81,6 +83,15 @@ protected void checkValidity() { } } + @Override + public @NotNull Component getReason() { + return super.getReason().replaceText(TextReplacementConfig + .builder() + .matchLiteral("") + .replacement(Component.text(getDuration().remainingDuration(), NamedTextColor.YELLOW)) + .build()); + } + @Override public final @NotNull CompletableFuture change(@NotNull PunishmentDuration newDuration, @Nullable LocalDateTime creationTime, Component newReason) throws PunishmentException { if (!getType().isBan() && !getType().isMute()) { @@ -103,9 +114,9 @@ protected void checkValidity() { .withUser(getUser()) .withReason(newReason) .withDuration(newDuration) + .withCreationTime(newCreatedAt) .withPunishmentUuid(getPunishmentUuid()) - .withSuccessor(getSuccessorOrNull()) - .withCreationTime(getCreationTime()); + .withSuccessor(getSuccessorOrNull()); Punishment punishment; if (getType().isBan()) { punishment = builder.buildBan(); @@ -125,24 +136,12 @@ protected CompletableFuture applyCancellation() throws PunishmentExc if (hasSuccessor()) { updateSuccessor().join(); } - var predecessor = getUser().getPunishments().stream() - .filter(punishment -> punishment.getSuccessorOrNull() != null && punishment.getSuccessorOrNull().equals(this)).findFirst().orElse(null); + var predecessor = getPredecessor(); if (predecessor != null) { - Query.query(APPLY_SUCCESSOR) - .single(Call.of().bind(null, new Adapter() { - @Override - public AdapterMapping mapping() { - return null; - } - - @Override - public int type() { - return Types.NULL; - } - }).bind(predecessor.getPunishmentUuid(), Adapters.UUID_ADAPTER)) - .update(); if (hasSuccessor()) { predecessor.setSuccessor(getSuccessor()).join(); + } else { + predecessor.setSuccessor(null); } } Query.query(APPLY_CANCELLATION) @@ -175,7 +174,7 @@ protected CompletableFuture applyPunishment() throws PunishmentExcep .bind(getUser().getUuid(), Adapters.UUID_ADAPTER) .bind(getType().getId()) .bind(duration.expirationAsTimestamp()) - .bind(convertReason(getReason())) + .bind(convertReason(getRawReason())) .bind(getPunishmentUuid(), Adapters.UUID_ADAPTER) .bind(Timestamp.valueOf(getCreationTime()))) .insert(); @@ -184,7 +183,15 @@ protected CompletableFuture applyPunishment() throws PunishmentExcep } @Override - public @NotNull CompletableFuture setSuccessor(@NotNull Punishment successor) { + public @NotNull CompletableFuture setSuccessor(Punishment successor) { + if (successor == null) { + return Util.executeAsync(() -> { + Query.query(APPLY_SUCCESSOR) + .single(Call.of().bind(null, Adapters.UUID_NULL_ADAPTER).bind(getPunishmentUuid(), Adapters.UUID_ADAPTER)) + .update(); + return this; + }, getExecutor()); + } if (!getType().getRelatedTypes().contains(successor.getType())) { throw new IllegalArgumentException("successor punishment is not related to this punishment"); } @@ -198,7 +205,6 @@ protected CompletableFuture applyPunishment() throws PunishmentExcep Query.query(APPLY_SUCCESSOR) .single(Call.of().bind(successor.getPunishmentUuid(), Adapters.UUID_ADAPTER).bind(getPunishmentUuid(), Adapters.UUID_ADAPTER)) .update(); - setSuccessor0(successor); LocalDateTime successorNewExpiration; if (successor instanceof TemporalPunishment temporalSuccessor) { var total = temporalSuccessor.totalDuration(); @@ -214,7 +220,10 @@ protected CompletableFuture applyPunishment() throws PunishmentExcep .bind(successor.getPunishmentUuid(), Adapters.UUID_ADAPTER)) .update(); if (successor instanceof TemporalPunishment temporalSuccessor) { - temporalSuccessor.change(PunishmentDuration.from(successorNewExpiration), issuanceSuccessor, successor.getReason()).join(); + var newSuccessor = temporalSuccessor.change(PunishmentDuration.from(successorNewExpiration), issuanceSuccessor, successor.getReason()).join(); + setSuccessor0(newSuccessor); + } else { + setSuccessor0(successor); } return this; }, getExecutor()); 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 f4a7dc10..a2d2e338 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 @@ -62,16 +62,19 @@ public MessagingChannelCommunicator(ProxyServer server, AbstractNecrifyPlugin pl /** * Recalculates the mute information for the specified user and sends the updated mute information to all registered servers. * This will inform those servers only about expiration and reason. The reason is a translated complete reason. + * * @param user the user to recalculate the mute information for */ - public void recalculateMuteInformation(NecrifyUser user) { - List mutes = user.getPunishments(StandardPunishmentType.PERMANENT_MUTE, StandardPunishmentType.TEMPORARY_MUTE); - final Mute mute = Util.getLongestPunishment(mutes); - if (mute == null) - return; - Component deny = ChainedPunishment.of(mute, plugin).createFullReason(user.getLocale()); - var serialized = MiniMessage.miniMessage().serialize(GlobalTranslator.render(deny, user.getLocale())); + public void recalculateMuteInformation(NecrifyUser user) { try { + List mutes = user.getPunishments(StandardPunishmentType.PERMANENT_MUTE, StandardPunishmentType.TEMPORARY_MUTE); + final Mute mute = Util.getLongestPunishment(mutes); + if (mute == null) { + queueMute(new MuteData(user.getUuid(), null, null, MuteData.RESET, null)); + return; + } + Component deny = ChainedPunishment.of(mute, plugin).createFullReason(user.getLocale()); + var serialized = MiniMessage.miniMessage().serialize(GlobalTranslator.render(deny, user.getLocale())); queueMute(new MuteData(user.getUuid(), serialized, mute.getDuration().expiration(), MuteData.RECALCULATION, mute.getPunishmentUuid())); } catch (Exception e) { logger.error("Could not queue mute for player {}", user.getUuid(), e); 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 039f82ce..e684f5ac 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 @@ -57,6 +57,7 @@ import de.jvstvshd.necrify.api.message.MessageProvider; import de.jvstvshd.necrify.api.punishment.Punishment; import de.jvstvshd.necrify.api.punishment.PunishmentManager; +import de.jvstvshd.necrify.api.punishment.PunishmentType; import de.jvstvshd.necrify.api.punishment.util.PlayerResolver; import de.jvstvshd.necrify.api.user.NecrifyUser; import de.jvstvshd.necrify.api.user.UserManager; @@ -474,4 +475,9 @@ public NecrifyKick createKick(Component reason, NecrifyUser user, UUID punishmen public Set> getOnlinePlayers() { return server.getAllPlayers().stream().map(player -> Pair.of(player.getUsername(), player.getUniqueId())).collect(Collectors.toSet()); } + + @Override + public String getDefaultReason(PunishmentType type) { + return configurationManager.getConfiguration().getPunishmentConfigData().getPunishmentMessages().get(type.getId()); + } } diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/config/ConfigData.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/config/ConfigData.java index db91a2ab..a0a54420 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/config/ConfigData.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/config/ConfigData.java @@ -32,6 +32,9 @@ public class ConfigData { @JsonProperty("default-language") private final Locale defaultLanguage; + @JsonProperty("punishment") + private final PunishmentConfigData punishmentConfigData; + @JsonProperty("whitelist-activated") @JsonAlias("whitelistActivated") private boolean whitelistActivated; @@ -40,15 +43,16 @@ public class ConfigData { @JsonAlias("allowTopLevelCommands") private boolean allowTopLevelCommands; - public ConfigData(DataBaseData dataBaseData, Locale defaultLanguage, boolean whitelistActivated, boolean allowTopLevelCommands) { + public ConfigData(DataBaseData dataBaseData, Locale defaultLanguage, PunishmentConfigData punishmentConfigData, boolean whitelistActivated, boolean allowTopLevelCommands) { this.dataBaseData = dataBaseData; this.defaultLanguage = defaultLanguage; + this.punishmentConfigData = punishmentConfigData; this.whitelistActivated = whitelistActivated; this.allowTopLevelCommands = allowTopLevelCommands; } public ConfigData() { - this(new DataBaseData(), Locale.ENGLISH, false, true); + this(new DataBaseData(), Locale.ENGLISH, new PunishmentConfigData(), false, true); } public final DataBaseData getDataBaseData() { @@ -74,4 +78,8 @@ public boolean isAllowTopLevelCommands() { public void setAllowTopLevelCommands(boolean allowTopLevelCommands) { this.allowTopLevelCommands = allowTopLevelCommands; } + + public PunishmentConfigData getPunishmentConfigData() { + return punishmentConfigData; + } } diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/config/DataBaseData.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/config/DataBaseData.java index 5e4da027..a5c8f547 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/config/DataBaseData.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/config/DataBaseData.java @@ -29,6 +29,7 @@ import java.util.Locale; public class DataBaseData { + private final String host; private final String password; private final String username; diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/config/PunishmentConfigData.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/config/PunishmentConfigData.java new file mode 100644 index 00000000..e69d702c --- /dev/null +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/config/PunishmentConfigData.java @@ -0,0 +1,43 @@ +/* + * This file is part of Necrify (formerly Velocity Punishment), a plugin designed to manage player's punishments for the platforms Velocity and partly Paper. + * Copyright (C) 2022-2024 JvstvsHD + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package de.jvstvshd.necrify.velocity.config; + +import java.util.Map; + +public class PunishmentConfigData { + + private final Map punishmentMessages; + + public PunishmentConfigData(Map punishmentMessages) { + this.punishmentMessages = punishmentMessages; + } + + public PunishmentConfigData() { + this.punishmentMessages = Map.of( + 1, "You are banned.", + 2, "You were permanently banned.", + 3, "You were muted.", + 4, "You were permanently muted.", + 5, "You were kicked."); + } + + public Map getPunishmentMessages() { + return punishmentMessages; + } +} 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 3a02a86d..fa929442 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 @@ -38,6 +38,7 @@ import de.jvstvshd.necrify.common.user.UserLoader; import de.jvstvshd.necrify.common.util.Util; import de.jvstvshd.necrify.velocity.NecrifyVelocityPlugin; +import org.greenrobot.eventbus.ThreadMode; import org.intellij.lang.annotations.Language; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -252,7 +253,7 @@ public void onPlayerJoin(LoginEvent event) { user.setPlayer(player); } - @org.greenrobot.eventbus.Subscribe + @org.greenrobot.eventbus.Subscribe(priority = Integer.MAX_VALUE) public void onUserLoaded(UserLoadedEvent event) { if (event.getOrigin().originatesFrom(getClass())) return; var user = event.getUser(); @@ -261,7 +262,7 @@ public void onUserLoaded(UserLoadedEvent event) { } } - @org.greenrobot.eventbus.Subscribe + @org.greenrobot.eventbus.Subscribe(priority = Integer.MAX_VALUE) public void onPunishmentEnforced(PunishmentPersecutedEvent event) { var punishment = event.getPunishment(); if (punishment.getUser() instanceof VelocityUser user) { @@ -269,7 +270,7 @@ public void onPunishmentEnforced(PunishmentPersecutedEvent event) { } } - @org.greenrobot.eventbus.Subscribe + @org.greenrobot.eventbus.Subscribe(priority = Integer.MAX_VALUE) public void onPunishmentCancelled(PunishmentCancelledEvent event) { var punishment = event.getPunishment(); if (punishment.getUser() instanceof VelocityUser user) { @@ -277,7 +278,7 @@ public void onPunishmentCancelled(PunishmentCancelledEvent event) { } } - @org.greenrobot.eventbus.Subscribe + @org.greenrobot.eventbus.Subscribe(priority = Integer.MAX_VALUE) public void onPunishmentChanged(PunishmentChangedEvent event) { var punishment = event.getPunishment(); if (punishment.getUser() instanceof VelocityUser user) {