From d6e56ba2fc746d98132842cf5d4c05a1f49ba575 Mon Sep 17 00:00:00 2001 From: JvstvsHD <79066214+JvstvsHD@users.noreply.github.com> Date: Wed, 7 Aug 2024 20:52:07 +0200 Subject: [PATCH] Implement punishment successors You can now chain punishments via /necrify punishment chain . Automatic questioning about whether to chain punishments still needs to be added. --- HEADER.txt | 44 ++++--- LICENSE | 2 +- build.gradle.kts | 26 ++-- buildSrc/src/main/kotlin/Version.kt | 8 +- .../java/de/jvstvshd/necrify/api/Necrify.java | 1 - .../necrify/api/PunishmentException.java | 1 - .../duration/AbsolutePunishmentDuration.java | 1 - .../duration/PermanentPunishmentDuration.java | 3 +- .../api/duration/PunishmentDuration.java | 12 +- .../duration/RelativePunishmentDuration.java | 3 +- .../necrify/api/event/EventDispatcher.java | 1 - .../necrify/api/event/NecrifyEvent.java | 1 - .../necrify/api/event/Slf4jLogger.java | 1 - .../api/event/origin/ClassEventOrigin.java | 1 - .../necrify/api/event/origin/EventOrigin.java | 1 - .../api/event/origin/StringEventOrigin.java | 1 - .../punishment/PunishmentCancelledEvent.java | 1 - .../punishment/PunishmentChangedEvent.java | 1 - .../api/event/punishment/PunishmentEvent.java | 1 - .../punishment/PunishmentPersecutedEvent.java | 1 - .../api/event/state/NecrifyDisabledEvent.java | 1 - .../event/state/NecrifyDisablingEvent.java | 1 - .../event/state/NecrifyInitializedEvent.java | 1 - .../state/NecrifyPreInitializationEvent.java | 1 - .../api/event/state/NecrifyStateEvent.java | 1 - .../api/event/user/UserCreatedEvent.java | 1 - .../api/event/user/UserDeletedEvent.java | 1 - .../necrify/api/event/user/UserEvent.java | 1 - .../api/event/user/UserLoadedEvent.java | 1 - .../necrify/api/message/MessageProvider.java | 1 - .../jvstvshd/necrify/api/punishment/Ban.java | 1 - .../jvstvshd/necrify/api/punishment/Kick.java | 4 +- .../jvstvshd/necrify/api/punishment/Mute.java | 1 - .../necrify/api/punishment/Punishment.java | 41 +++++- .../api/punishment/PunishmentFactory.java | 1 - .../api/punishment/PunishmentManager.java | 1 - .../api/punishment/PunishmentType.java | 13 +- .../punishment/PunishmentTypeRegistry.java | 1 - .../punishment/StandardPunishmentType.java | 19 +-- .../api/punishment/TemporalPunishment.java | 24 +++- .../api/punishment/util/PlayerResolver.java | 1 - .../api/punishment/util/ReasonHolder.java | 1 - .../necrify/api/user/CommandSender.java | 1 - .../necrify/api/user/NecrifyUser.java | 1 - .../necrify/api/user/UserDeletionReason.java | 1 - .../api/user/UserLoadOrderCoordinator.java | 122 ++++++++++++++++++ .../necrify/api/user/UserManager.java | 1 - .../necrify/api/event/NecrifyEventTest.java | 1 - .../necrify/common/AbstractNecrifyPlugin.java | 2 +- .../common/commands/NecrifyCommand.java | 67 ++++++++-- .../common/commands/NecrifyUserParser.java | 1 - .../commands/PunishmentDurationParser.java | 1 - .../common/commands/PunishmentParser.java | 1 - .../commands/UserNotFoundParseException.java | 1 - .../jvstvshd/necrify/common/io/Adapters.java | 2 +- .../necrify/common/plugin/MuteData.java | 1 - .../common/punishment/AbstractPunishment.java | 51 ++++++-- .../AbstractTemporalPunishment.java | 112 ++++++++++++++-- .../necrify/common/punishment/NecrifyBan.java | 5 +- .../common/punishment/NecrifyKick.java | 6 +- .../common/punishment/NecrifyMute.java | 7 +- .../punishment/NecrifyPunishmentFactory.java | 8 +- .../common/punishment/PunishmentBuilder.java | 16 ++- .../common/user/AbstractConsoleUser.java | 1 - .../necrify/common/user/MojangAPI.java | 1 - .../necrify/common/user/UserLoader.java | 112 ++++++++++++++++ .../necrify/common/util/PunishmentHelper.java | 63 +++++++-- .../de/jvstvshd/necrify/common/util/Util.java | 25 +++- .../database/postgresql/1/patch_1.sql | 8 +- necrify-paper/build.gradle.kts | 10 +- .../necrify/paper/NecrifyPaperPlugin.java | 3 +- .../paper/NecrifyPaperPluginBootstrap.java | 1 - .../paper/NecrifyPaperPluginLoader.java | 1 - .../necrify/paper/listeners/ChatListener.java | 1 - .../listeners/MessagingChannelListener.java | 1 - .../paper/listeners/MuteInformation.java | 1 - necrify-velocity/build.gradle.kts | 10 +- .../MessagingChannelCommunicator.java | 1 - .../velocity/NecrifyVelocityPlugin.java | 16 ++- .../necrify/velocity/commands/BanCommand.java | 1 - .../velocity/commands/KickCommand.java | 1 - .../velocity/commands/MuteCommand.java | 1 - .../velocity/commands/PunishmentCommand.java | 3 +- .../commands/PunishmentRemovalCommand.java | 1 - .../velocity/commands/TempbanCommand.java | 1 - .../velocity/commands/TempmuteCommand.java | 1 - .../velocity/commands/WhitelistCommand.java | 3 +- .../necrify/velocity/config/ConfigData.java | 1 - .../velocity/config/ConfigurationManager.java | 3 +- .../necrify/velocity/config/DataBaseData.java | 1 - .../velocity/impl/DefaultPlayerResolver.java | 1 - .../impl/DefaultPunishmentManager.java | 3 +- .../necrify/velocity/impl/VelocityKick.java | 6 +- .../velocity/internal/PunishmentHelper.java | 1 - .../necrify/velocity/internal/Util.java | 3 +- .../velocity/listener/ConnectListener.java | 1 - .../ResourceBundleMessageProvider.java | 4 +- .../velocity/user/VelocityConsoleUser.java | 1 - .../necrify/velocity/user/VelocityUser.java | 21 --- .../velocity/user/VelocityUserManager.java | 36 ++++-- 100 files changed, 737 insertions(+), 249 deletions(-) create mode 100644 necrify-api/src/main/java/de/jvstvshd/necrify/api/user/UserLoadOrderCoordinator.java create mode 100644 necrify-common/src/main/java/de/jvstvshd/necrify/common/user/UserLoader.java diff --git a/HEADER.txt b/HEADER.txt index 2387238..4e80eba 100644 --- a/HEADER.txt +++ b/HEADER.txt @@ -1,21 +1,23 @@ -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. +/* + * 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. + */ \ No newline at end of file diff --git a/LICENSE b/LICENSE index a3c774a..9107dc9 100644 --- a/LICENSE +++ b/LICENSE @@ -18,4 +18,4 @@ 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. +SOFTWARE. \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 0e5e14a..a430e9f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,13 +1,13 @@ +import com.diffplug.gradle.spotless.SpotlessPlugin import io.papermc.hangarpublishplugin.model.Platforms -import org.cadixdev.gradle.licenser.Licenser -import java.io.ByteArrayOutputStream import java.util.* plugins { `maven-publish` signing - id("org.cadixdev.licenser") version "0.6.1" id("io.papermc.hangar-publish-plugin") version "0.1.2" + id("io.github.goooler.shadow") version "8.1.8" apply false + id("com.diffplug.spotless") version "6.25.0" java } @@ -16,16 +16,15 @@ version = "1.2.0-SNAPSHOT" subprojects { apply { - plugin() plugin() plugin() plugin("java") + plugin() } - - license { - header(rootProject.file("HEADER.txt")) - include("**/*.java") - newLine(true) + spotless { + java { + licenseHeaderFile(rootProject.file("HEADER.txt")) + } } java { toolchain.languageVersion = JavaLanguageVersion.of(21) @@ -105,14 +104,15 @@ hangarPublish { version.set(buildVersion()) channel.set(if (!isRelease()) "Snapshot" else "Release") id.set("necrify") - apiKey.set("5eb868d8-6dbf-4d5e-92be-6cf2c0126818.cfb7e524-0b1f-48c9-9ac4-8f31f0fa1f80") - //apiKey.set(System.getenv("HANGAR_API_TOKEN")) + apiKey.set(System.getenv("HANGAR_API_TOKEN")) if (!isRelease()) { - changelog.set(latestGitCommitMessage()) + changelog.set(changelogMessage()) + } else { + changelog.set("Changes will be provided shortly.\nComplete changelog can be found on GitHub: https://www.github.com/JvstvsHD/necrify/releases/tag/v$version") } platforms { register(Platforms.PAPER) { - jar.set(project(":necrify-paper").tasks.jar.flatMap { it.archiveFile }) + jar.set((project(":necrify-paper").tasks.getByName("shadowJar") as Jar).archiveFile) val versions: List = (property("paperVersion") as String) .split(",") .map { it.trim() } diff --git a/buildSrc/src/main/kotlin/Version.kt b/buildSrc/src/main/kotlin/Version.kt index 3d8ac8f..312dd87 100644 --- a/buildSrc/src/main/kotlin/Version.kt +++ b/buildSrc/src/main/kotlin/Version.kt @@ -17,6 +17,10 @@ class Version(val project: Project) { return executeGitCommand("log", "-1", "--pretty=%B") } + fun latestCommitHash(): String { + return executeGitCommand("rev-parse", "HEAD") + } + val versionString: String = project.version as String val isRelease: Boolean = !versionString.contains('-') @@ -30,6 +34,8 @@ class Version(val project: Project) { fun Project.buildVersion() = Version(this).suffixedVersion -fun Project.latestGitCommitMessage() = Version(this).latestCommitMessage() +fun Project.changelogMessage() = with(Version(this)) { + "https://github.com/JvstvsHD/necrify/commit/${latestCommitHash()}: ${latestCommitMessage()}" +} fun Project.isRelease() = Version(this).isRelease \ No newline at end of file diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/Necrify.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/Necrify.java index 87480dd..16ebde7 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/Necrify.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/Necrify.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api; import de.jvstvshd.necrify.api.event.EventDispatcher; diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/PunishmentException.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/PunishmentException.java index 8d9a9a8..bdf88a5 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/PunishmentException.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/PunishmentException.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api; public class PunishmentException extends RuntimeException { diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/duration/AbsolutePunishmentDuration.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/duration/AbsolutePunishmentDuration.java index 31049e0..62f6052 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/duration/AbsolutePunishmentDuration.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/duration/AbsolutePunishmentDuration.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.duration; import org.jetbrains.annotations.NotNull; diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/duration/PermanentPunishmentDuration.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/duration/PermanentPunishmentDuration.java index 4943dda..24309d5 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/duration/PermanentPunishmentDuration.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/duration/PermanentPunishmentDuration.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.duration; import org.jetbrains.annotations.NotNull; @@ -88,4 +87,4 @@ public int hashCode() { public String toString() { return "PermanentPunishmentDuration{} " + super.toString(); } -} \ No newline at end of file +} 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 c0d5401..f22934d 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 @@ -21,12 +21,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.duration; import java.sql.Timestamp; import java.time.Duration; import java.time.LocalDateTime; +import java.time.temporal.Temporal; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -182,6 +182,16 @@ default PunishmentDuration initialDuration() { throw new UnsupportedOperationException("Initial durations are not stored."); } + /** + * The remaining duration as a {@link Duration} object. + * @return the remaining duration + * @since 1.2.0 + * @see Duration#between(Temporal, Temporal) + */ + default Duration javaDuration() { + return Duration.between(LocalDateTime.now(), expiration()); + } + /** * A class for parsing player inputs into a valid {@link PunishmentDuration}. * Allowed format: {@code \d+[smhdSMHD]} (e.g. 1m (one minute), 2d (two days), 1d6h (one day and six hours) or 1h30m (one hour and thirty minutes)) diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/duration/RelativePunishmentDuration.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/duration/RelativePunishmentDuration.java index 9839312..c088662 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/duration/RelativePunishmentDuration.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/duration/RelativePunishmentDuration.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.duration; import org.jetbrains.annotations.NotNull; @@ -148,4 +147,4 @@ private String normalizeTimeUnit(long value) { return "0" + s; return s; } -} \ No newline at end of file +} diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/EventDispatcher.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/EventDispatcher.java index 264df2d..1ba7252 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/EventDispatcher.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/EventDispatcher.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.event; import org.greenrobot.eventbus.EventBus; 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 f4fc952..3e227d6 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 @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.event; import de.jvstvshd.necrify.api.event.origin.EventOrigin; diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/Slf4jLogger.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/Slf4jLogger.java index 3c5d8b6..2d2c7c1 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/Slf4jLogger.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/Slf4jLogger.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.event; import org.greenrobot.eventbus.Logger; diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/origin/ClassEventOrigin.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/origin/ClassEventOrigin.java index 025c5ba..6f7b98e 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/origin/ClassEventOrigin.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/origin/ClassEventOrigin.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.event.origin; public record ClassEventOrigin(Class origin) implements EventOrigin { 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 248677d..c41b9bb 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 @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.event.origin; public interface EventOrigin { diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/origin/StringEventOrigin.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/origin/StringEventOrigin.java index 4eb80c5..87c63f4 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/origin/StringEventOrigin.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/origin/StringEventOrigin.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.event.origin; public record StringEventOrigin(String origin) implements EventOrigin { diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/punishment/PunishmentCancelledEvent.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/punishment/PunishmentCancelledEvent.java index e349553..6d5148b 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/punishment/PunishmentCancelledEvent.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/punishment/PunishmentCancelledEvent.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.event.punishment; import de.jvstvshd.necrify.api.punishment.Punishment; diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/punishment/PunishmentChangedEvent.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/punishment/PunishmentChangedEvent.java index 102777d..3d153c5 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/punishment/PunishmentChangedEvent.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/punishment/PunishmentChangedEvent.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.event.punishment; import de.jvstvshd.necrify.api.punishment.Punishment; diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/punishment/PunishmentEvent.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/punishment/PunishmentEvent.java index 55710f1..1e635d7 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/punishment/PunishmentEvent.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/punishment/PunishmentEvent.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.event.punishment; import de.jvstvshd.necrify.api.event.user.UserEvent; diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/punishment/PunishmentPersecutedEvent.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/punishment/PunishmentPersecutedEvent.java index 8c0d4db..1809861 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/punishment/PunishmentPersecutedEvent.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/punishment/PunishmentPersecutedEvent.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.event.punishment; import de.jvstvshd.necrify.api.punishment.Punishment; diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/state/NecrifyDisabledEvent.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/state/NecrifyDisabledEvent.java index 91c0dc4..4b8e932 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/state/NecrifyDisabledEvent.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/state/NecrifyDisabledEvent.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.event.state; /** diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/state/NecrifyDisablingEvent.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/state/NecrifyDisablingEvent.java index 6c2cf60..1fba900 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/state/NecrifyDisablingEvent.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/state/NecrifyDisablingEvent.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.event.state; /** diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/state/NecrifyInitializedEvent.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/state/NecrifyInitializedEvent.java index 99eeb71..29503a4 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/state/NecrifyInitializedEvent.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/state/NecrifyInitializedEvent.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.event.state; /** diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/state/NecrifyPreInitializationEvent.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/state/NecrifyPreInitializationEvent.java index 2217585..9fc154c 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/state/NecrifyPreInitializationEvent.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/state/NecrifyPreInitializationEvent.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.event.state; /** diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/state/NecrifyStateEvent.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/state/NecrifyStateEvent.java index 135245b..e7ee106 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/state/NecrifyStateEvent.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/state/NecrifyStateEvent.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.event.state; import de.jvstvshd.necrify.api.event.NecrifyEvent; diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/user/UserCreatedEvent.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/user/UserCreatedEvent.java index 3ff084f..ff1dcbf 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/user/UserCreatedEvent.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/user/UserCreatedEvent.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.event.user; import de.jvstvshd.necrify.api.user.NecrifyUser; diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/user/UserDeletedEvent.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/user/UserDeletedEvent.java index 9f33992..2e20601 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/user/UserDeletedEvent.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/user/UserDeletedEvent.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.event.user; import de.jvstvshd.necrify.api.user.NecrifyUser; diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/user/UserEvent.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/user/UserEvent.java index d03c35d..c1b5362 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/user/UserEvent.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/user/UserEvent.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.event.user; import de.jvstvshd.necrify.api.event.NecrifyEvent; diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/user/UserLoadedEvent.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/user/UserLoadedEvent.java index 6b10daa..b665bd4 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/user/UserLoadedEvent.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/event/user/UserLoadedEvent.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.event.user; import de.jvstvshd.necrify.api.user.NecrifyUser; diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/message/MessageProvider.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/message/MessageProvider.java index a56e14a..45b3d02 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/message/MessageProvider.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/message/MessageProvider.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.message; import net.kyori.adventure.text.Component; diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/Ban.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/Ban.java index 85e014d..735f18c 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/Ban.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/Ban.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.punishment; public interface Ban extends TemporalPunishment { diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/Kick.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/Kick.java index d4c8bfd..6abf614 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/Kick.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/Kick.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.punishment; import de.jvstvshd.necrify.api.PunishmentException; @@ -66,8 +65,9 @@ default Punishment getSuccessor() { throw new UnsupportedOperationException("kick lasts only one moment"); } + @NotNull @Override - default void setSuccessor(@NotNull Punishment successor) { + default CompletableFuture setSuccessor(@NotNull Punishment successor) { throw new UnsupportedOperationException("kick lasts only one moment"); } } diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/Mute.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/Mute.java index d92802b..6478866 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/Mute.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/Mute.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.punishment; public interface Mute extends TemporalPunishment { 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 e4f0ede..86f6298 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 @@ -21,16 +21,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.punishment; import de.jvstvshd.necrify.api.PunishmentException; +import de.jvstvshd.necrify.api.duration.PunishmentDuration; import de.jvstvshd.necrify.api.punishment.util.ReasonHolder; import de.jvstvshd.necrify.api.user.NecrifyUser; import net.kyori.adventure.text.Component; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.time.Instant; import java.time.LocalDateTime; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -39,6 +40,9 @@ * Super interface for all sort of punishments..
* To punish a player, obtain a {@link NecrifyUser} instance from {@link de.jvstvshd.necrify.api.user.UserManager} and * select an adequate method (e.g. {@link NecrifyUser#banPermanent(Component)}) for the punishment you wish.
+ * @implSpec This classes instances expire either immediately (have no duration) or do not in an infinite amount of time. It is + * extremely important that this behaviour is guaranteed as otherwise this will result in unexpected behaviour. If your punishment + * instance does expire in a definite amount of time, use {@link TemporalPunishment} instead. */ public interface Punishment extends ReasonHolder { @@ -128,12 +132,45 @@ public interface Punishment extends ReasonHolder { @NotNull Punishment getSuccessor(); + /** + * Returns the successor of this punishment or null if there is no successor. This is used to chain punishments so that one punishment of the same kind + * is paused until the previous one is finished. This may especially be useful for punishments of differing reasons. + * @return the successor of this punishment or null if there is no successor + */ + @Nullable + default Punishment getSuccessorOrNull() { + if (hasSuccessor()) + return getSuccessor(); + return null; + } + /** * Sets the successor of this punishment. This is used to chain punishments so that one punishment of the same kind * is paused until the previous one is finished. This may especially be useful for punishments of differing reasons. * * @param successor the successor of this punishment * @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 + * @throws IllegalStateException if {@code successor} is in a circular chain with this punishment + * @since 1.2.0 + */ + @NotNull + CompletableFuture setSuccessor(@NotNull Punishment successor); + + /** + * Returns the creation time of this punishment. + * @since 1.2.0 + * @return the creation time of this punishment + * @throws IllegalStateException if the punishment has not been created yet + * @see #hasBeenCreated() + */ + @NotNull + LocalDateTime getCreationTime(); + + /** + * Returns whether this punishment has been created or not. + * @return true, if this punishment has been created, otherwise false + * @since 1.2.0 */ - void setSuccessor(@NotNull Punishment successor); + boolean hasBeenCreated(); } diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/PunishmentFactory.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/PunishmentFactory.java index 8ed8e55..23f29e1 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/PunishmentFactory.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/PunishmentFactory.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.punishment; import org.jetbrains.annotations.NotNull; diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/PunishmentManager.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/PunishmentManager.java index 4b3e89a..32086c1 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/PunishmentManager.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/PunishmentManager.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.punishment; import de.jvstvshd.necrify.api.duration.PunishmentDuration; 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 f0b0487..0eb4dcb 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 @@ -21,11 +21,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.punishment; import de.jvstvshd.necrify.api.Necrify; +import org.jetbrains.annotations.NotNull; +import java.util.List; import java.util.Map; /** @@ -80,4 +81,14 @@ default StandardPunishmentType standard() { throw new IllegalStateException("Punishment type is not a standard punishment type."); } } + + /** + * Creates a list that contains all punishment types that are related to this one in the sense that they are + * similar in nature. This is useful for e.g. a permanent ban being related to a temporary ban when having to check + * something for both types. + * @return a list of related punishment types + * @since 1.2.0 + */ + @NotNull + List getRelatedTypes(); } diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/PunishmentTypeRegistry.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/PunishmentTypeRegistry.java index 63bafb2..115654f 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/PunishmentTypeRegistry.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/PunishmentTypeRegistry.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.punishment; import org.jetbrains.annotations.NotNull; 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 ef518bf..4f1e356 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 @@ -21,17 +21,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.punishment; -import de.jvstvshd.necrify.api.Necrify; -import de.jvstvshd.necrify.api.duration.PunishmentDuration; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.MiniMessage; +import org.jetbrains.annotations.NotNull; -import java.sql.Timestamp; -import java.util.Map; -import java.util.UUID; +import java.util.List; /** * A collection of standard punishment types which are also used by the default implementation of the punishment system. @@ -67,4 +61,13 @@ public String getName() { public int getId() { return id; } + + @Override + public @NotNull List getRelatedTypes() { + return switch (this) { + case TEMPORARY_BAN, PERMANENT_BAN -> List.of(TEMPORARY_BAN, PERMANENT_BAN); + case TEMPORARY_MUTE, PERMANENT_MUTE -> List.of(TEMPORARY_MUTE, PERMANENT_MUTE); + default -> List.of(this); + }; + } } 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 e9bd5de..05e0eed 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 @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.punishment; import de.jvstvshd.necrify.api.PunishmentException; @@ -30,10 +29,15 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.time.LocalDateTime; import java.util.concurrent.CompletableFuture; /** * An interface containing some methods to only punish a player for a defined duration. + * @implSpec Instances of this interface must expire sometime in the future. This means that you may use non-permanent + * {@link PunishmentDuration punishment durations} for this type of punishment; but you may also use + * {@link de.jvstvshd.necrify.api.duration.PermanentPunishmentDuration permanent ones} as long as they expire sometime, + * which may be the year 10,000 or so. If your punishment does not expire or has no infinite duration, use {@link Punishment} instead. * * @see Ban * @see Mute @@ -54,16 +58,26 @@ public interface TemporalPunishment extends Punishment { /** * Changes the duration and reason of this punishment. This method can be used if a player created an appeal an it was accepted. * - * @param newDuration the new duration of this punishment - * @param newReason the new reason which should be displayed to the player, or null if it should remain the same + * @param newDuration the new duration of the punishment (relative to the point of punishment issuance) + * @param creationTime the new creation time of the punishment, or null if it should remain the same + * @param newReason the new reason which should be displayed to the player, or null if it should remain the same * @return a {@link CompletableFuture} containing the new punishment * @see #cancel() * @see #change(Component) */ - CompletableFuture change(@NotNull PunishmentDuration newDuration, @Nullable Component newReason) throws PunishmentException; + CompletableFuture change(@NotNull PunishmentDuration newDuration, @Nullable LocalDateTime creationTime, @Nullable Component newReason) throws PunishmentException; @Override - default CompletableFuture change(@Nullable Component newReason) { + default CompletableFuture change(@Nullable Component newReason) { return null; } + + /** + * Returns the total duration of this punishment from {@link #getCreationTime()} until the expiration date. + * + * @return the total duration of this punishment + * @since 1.2.0 + */ + @NotNull + PunishmentDuration totalDuration(); } diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/util/PlayerResolver.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/util/PlayerResolver.java index 35c2176..43507e5 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/util/PlayerResolver.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/punishment/util/PlayerResolver.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.punishment.util; import org.jetbrains.annotations.ApiStatus; 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 07d6a75..035c6d2 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 @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.punishment.util; import net.kyori.adventure.text.Component; 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 89bf910..52ac06e 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 @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.user; import de.jvstvshd.necrify.api.message.MessageProvider; 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 a359c03..c3ea133 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 @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.user; import de.jvstvshd.necrify.api.duration.PunishmentDuration; diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/user/UserDeletionReason.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/user/UserDeletionReason.java index 3caeead..6e4f619 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/user/UserDeletionReason.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/user/UserDeletionReason.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.user; public interface UserDeletionReason { diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/user/UserLoadOrderCoordinator.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/user/UserLoadOrderCoordinator.java new file mode 100644 index 0000000..0ca81a8 --- /dev/null +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/user/UserLoadOrderCoordinator.java @@ -0,0 +1,122 @@ +/* + * 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.api.user; + +import de.jvstvshd.necrify.api.punishment.Punishment; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * A utility class that is responsible for ordering a list of punishments to be loaded in the correct order. This reflects + * the {@link Punishment#getSuccessor() succession order of punishments} so that every punishment can be instantiated with + * the instance of its successor and is not referencing a null value. + *

This method only accepts the generic {@code Map} also taken by + * {@link de.jvstvshd.necrify.api.punishment.PunishmentTypeRegistry#createPunishment(int, Map)} since there are no punishment instances at this point.

+ *

This class uses the Kahn's algorithm to perform a topological sort on the list of punishments. The algorithm is + * guaranteed to work if the graph is a directed acyclic graph (DAG). If the graph contains a cycle, the algorithm will + * throw an {@link IllegalStateException}.

+ *

See here fore more information

+ * @since 1.2.0 + */ +public final class UserLoadOrderCoordinator { + + private UserLoadOrderCoordinator() { + throw new UnsupportedOperationException("This class cannot be instantiated"); + } + + /** + * Sorts the list of punishments topologically. This method utilizes the Kahn's algorithm to sort the list of + * punishments in the correct order. The list of nodes must contain the following key-value-pairs: + *
    + *
  • {@code punishmentUuid} - the {@link UUID} of the punishment
  • + *
  • {@code successorId} - the {@link UUID} of the successor punishment
  • + *
  • Any other key-value-pairs that are required for the instantiation of the punishment may remain in this map
  • + *
+ * If the upper conditions are not met, the method will yield unexpected results. + * @param nodes the list of nodes to be sorted + * @return the sorted list of nodes that respects the succession order of the punishments + * @throws IllegalStateException if the graph contains a cycle + */ + @NotNull + public static List> topologicalSort(@NotNull List> nodes) { + Map inDegree = new HashMap<>(); + Map> graph = new HashMap<>(); + List> sortedList = new ArrayList<>(); + + for (Map node : nodes) { + UUID nodeId = (UUID) node.get("punishmentUuid"); + UUID successorId = (UUID) node.get("successorId"); + inDegree.putIfAbsent(nodeId, 0); + graph.putIfAbsent(nodeId, new ArrayList<>()); + + if (successorId != null) { + graph.get(nodeId).add(successorId); + inDegree.put(successorId, inDegree.getOrDefault(successorId, 0) + 1); + } + } + + Queue queue = new LinkedList<>(); + for (UUID nodeId : inDegree.keySet()) { + if (inDegree.get(nodeId) == 0) { + queue.add(nodeId); + } + } + + while (!queue.isEmpty()) { + UUID nodeId = queue.poll(); + sortedList.add(findNodeById(nodes, nodeId)); + + for (UUID neighbor : graph.get(nodeId)) { + inDegree.put(neighbor, inDegree.get(neighbor) - 1); + if (inDegree.get(neighbor) == 0) { + queue.add(neighbor); + } + } + } + + if (sortedList.size() != nodes.size()) { + throw new IllegalStateException("Graph has at least one cycle"); + } + Collections.reverse(sortedList); + return sortedList; + } + + /** + * Finds a node by its {@link UUID punishmentUuid} in the list of nodes. + * @param nodes the list of nodes + * @param nodeId the {@link UUID punishmentUuid} of the node to be found + * @return the node with the given {@link UUID punishmentUuid} or {@code null} if no such node exists + */ + @Nullable + private static Map findNodeById(List> nodes, UUID nodeId) { + for (Map node : nodes) { + if (nodeId.equals(node.get("punishmentUuid"))) { + return node; + } + } + return null; + } +} diff --git a/necrify-api/src/main/java/de/jvstvshd/necrify/api/user/UserManager.java b/necrify-api/src/main/java/de/jvstvshd/necrify/api/user/UserManager.java index d6a3e97..08a9cc5 100644 --- a/necrify-api/src/main/java/de/jvstvshd/necrify/api/user/UserManager.java +++ b/necrify-api/src/main/java/de/jvstvshd/necrify/api/user/UserManager.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.user; import org.jetbrains.annotations.NotNull; diff --git a/necrify-api/src/test/java/de/jvstvshd/necrify/api/event/NecrifyEventTest.java b/necrify-api/src/test/java/de/jvstvshd/necrify/api/event/NecrifyEventTest.java index 1fe49d7..719ab2c 100644 --- a/necrify-api/src/test/java/de/jvstvshd/necrify/api/event/NecrifyEventTest.java +++ b/necrify-api/src/test/java/de/jvstvshd/necrify/api/event/NecrifyEventTest.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.api.event; import de.jvstvshd.necrify.api.event.state.NecrifyInitializedEvent; 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 dfa8607..1f5089b 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,10 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.common; import de.jvstvshd.necrify.api.Necrify; +import de.jvstvshd.necrify.api.PunishmentException; import de.jvstvshd.necrify.api.duration.PunishmentDuration; import de.jvstvshd.necrify.api.punishment.Punishment; import de.jvstvshd.necrify.api.punishment.PunishmentType; 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 c950a6f..0ca158a 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 @@ -21,12 +21,12 @@ * 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.duration.PunishmentDuration; import de.jvstvshd.necrify.api.message.MessageProvider; import de.jvstvshd.necrify.api.punishment.Punishment; +import de.jvstvshd.necrify.api.punishment.PunishmentType; import de.jvstvshd.necrify.api.punishment.StandardPunishmentType; import de.jvstvshd.necrify.api.user.NecrifyUser; import de.jvstvshd.necrify.api.user.UserDeletionReason; @@ -59,6 +59,7 @@ import java.util.List; import java.util.Locale; import java.util.Objects; +import java.util.concurrent.CompletableFuture; public class NecrifyCommand { @@ -220,12 +221,13 @@ public void unmuteCommand( //Informational/other - @Command("necrify punishment [option]") + @Command("necrify punishment [option] [otherPunishment]") @Permission(value = {"necrify.command.punishment", "necrify.admin"}, mode = Permission.Mode.ANY_OF) public void punishmentCommand( NecrifyUser sender, @Argument(value = "punishmentId", description = "Punishment to manage") Punishment punishmentParsed, - @Argument(value = "option", description = "Option to manage the punishment", suggestions = "suggestPunishmentCommandOptions") @Default(value = "info") String option + @Argument(value = "option", description = "Option to manage the punishment", suggestions = "suggestPunishmentCommandOptions") @Default(value = "info") String option, + @Argument(value = "otherPunishment", description = "Another punishment to chain") Punishment otherPunishment ) { switch (option) { case "info" -> @@ -238,6 +240,27 @@ public void punishmentCommand( sender.sendMessage(provider.provide("command.punishment.cancel.success").color(NamedTextColor.GREEN)); }, plugin.getService()); case "change" -> sender.sendMessage(miniMessage("Soon (TM)").color(NamedTextColor.LIGHT_PURPLE)); + case "chain" -> { + if (!punishmentParsed.getType().getRelatedTypes().contains(otherPunishment.getType())) { + sender.sendMessage(provider.provide("command.punishment.chain.unrelated-types").color(NamedTextColor.RED)); + return; + } + if (!punishmentParsed.getUser().equals(otherPunishment.getUser())) { + sender.sendMessage(provider.provide("command.punishment.chain.user-mismatch").color(NamedTextColor.RED)); + return; + } + if (Util.circularSuccessionChain(punishmentParsed, otherPunishment)) { + sender.sendMessage(provider.provide("command.punishment.circular-chain").color(NamedTextColor.RED)); + return; + } + punishmentParsed.setSuccessor(otherPunishment).whenComplete((unused, th) -> { + if (th != null) { + logException(sender, th); + return; + } + sender.sendMessage(provider.provide("command.punishment.chain.success").color(NamedTextColor.GREEN)); + }); + } default -> sender.sendMessage(unknownOption(option, PUNISHMENT_COMMAND_OPTIONS)); } } @@ -401,15 +424,31 @@ private void removePunishments(NecrifyUser source, List punishments, } } + private void tryChainPunishments(NecrifyUser sender, NecrifyUser target, Punishment newPunishment) { + var types = newPunishment.getType().getRelatedTypes(); + var matchingPunishments = target.getPunishments(types.toArray(new PunishmentType[0])); + if (matchingPunishments.isEmpty()) { + return; + } + var unchainedPunishments = matchingPunishments.stream().filter(punishment -> !punishment.hasSuccessor()).toList(); + if (unchainedPunishments.isEmpty()) { + //This should not happen since the chained punishment only gets activated after is predecessor runs out + throw new IllegalStateException("No unchained punishments found. Did you forget to remove a reference?"); + } + sender.sendMessage(provider.provide("command.punishment.chain.info")); + for (Punishment unchainedPunishment : unchainedPunishments) { + sender.sendMessage(provider.provide("command.punishment.chain", + Component.text(unchainedPunishment.getPunishmentUuid().toString()).color(NamedTextColor.YELLOW)).color(NamedTextColor.GRAY) + .clickEvent(ClickEvent.runCommand("/necrify punishment " + unchainedPunishment.getPunishmentUuid().toString().toLowerCase(Locale.ROOT) + + " chain " + newPunishment.getPunishmentUuid().toString().toLowerCase(Locale.ROOT))) + .hoverEvent((HoverEventSource) op -> HoverEvent.showText(provider.provide("command.punishment.chain.hover").color(NamedTextColor.GREEN)))); + } + } + //Communication, messaging, logging private Component buildComponent(Component dataComponent, Punishment punishment) { - var clickToRemove = provider.provide("command.punishment.click-to-remove"); - return dataComponent.append( - clickToRemove - .color(NamedTextColor.RED) - .clickEvent(ClickEvent.runCommand("/necrify punishment " + punishment.getPunishmentUuid().toString().toLowerCase(Locale.ROOT) + " remove")) - .hoverEvent((HoverEventSource) op -> HoverEvent.showText(clickToRemove.color(NamedTextColor.GREEN)))); + return dataComponent; } private Component reasonOrDefaultTo(String reason, StandardPunishmentType type) { @@ -431,11 +470,11 @@ public TextComponent copyComponent(String text) { private Component whitelistStatus(NecrifyUser user) { var whitelisted = user.isWhitelisted(); return provider.provide("command.whitelist.status", - Component.text(Objects.requireNonNullElse(user.getUsername(), "Unknown Username")).color(NamedTextColor.YELLOW), - provider - .unprefixedProvider() - .provide("whitelist.status." + (whitelisted ? "whitelisted" : "disallowed")) - .color(whitelisted ? NamedTextColor.GREEN : NamedTextColor.RED)) + Component.text(Objects.requireNonNullElse(user.getUsername(), "Unknown Username")).color(NamedTextColor.YELLOW), + provider + .unprefixedProvider() + .provide("whitelist.status." + (whitelisted ? "whitelisted" : "disallowed")) + .color(whitelisted ? NamedTextColor.GREEN : NamedTextColor.RED)) .color(NamedTextColor.GRAY); } 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 298c5f5..c454e88 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 @@ -21,7 +21,6 @@ * 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/PunishmentDurationParser.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/commands/PunishmentDurationParser.java index 5ee4466..ffeaa1b 100644 --- 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 @@ -21,7 +21,6 @@ * 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; 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 index 5aefa02..2c8e099 100644 --- 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 @@ -21,7 +21,6 @@ * 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; 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 c74bdeb..b554535 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 @@ -21,7 +21,6 @@ * 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/io/Adapters.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/io/Adapters.java index d120cd6..3b86292 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 @@ -21,13 +21,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.common.io; import de.chojo.sadu.queries.api.call.adapter.Adapter; import java.sql.PreparedStatement; import java.sql.Types; +import java.util.Objects; import java.util.UUID; public class Adapters { diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/plugin/MuteData.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/plugin/MuteData.java index a9ba051..cb369ac 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/plugin/MuteData.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/plugin/MuteData.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.common.plugin; import com.fasterxml.jackson.annotation.JsonIgnore; 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 5a22859..8227202 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 @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.common.punishment; @@ -38,6 +37,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.time.LocalDateTime; import java.util.NoSuchElementException; import java.util.Objects; import java.util.UUID; @@ -48,28 +48,38 @@ public abstract class AbstractPunishment implements Punishment { private final Component reason; - private final ExecutorService service; + private final ExecutorService executor; private final NecrifyUser user; private final UUID punishmentUuid; private final MessageProvider messageProvider; private final EventDispatcher eventDispatcher; private final AbstractNecrifyPlugin plugin; + private LocalDateTime creationTime; private Punishment successor; @Language("sql") protected final static String APPLY_PUNISHMENT = "INSERT INTO punishment.necrify_punishment" + - " (uuid, type, expiration, reason, punishment_id) VALUES (?, ?, ?, ?, ?)"; + " (uuid, type, expiration, reason, punishment_id, issued_at) VALUES (?, ?, ?, ?, ?, ?)"; @Language("sql") protected final static String APPLY_CANCELLATION = "DELETE FROM punishment.necrify_punishment WHERE punishment_id = ?"; @Language("sql") - protected final static String APPLY_CHANGE = "UPDATE necrify_punishment SET reason = ?, expiration = ?, permanent = ? WHERE punishment_id = ?"; - private final boolean validity; + protected final static String APPLY_CHANGE = "UPDATE punishment.necrify_punishment SET reason = ?, expiration = ?, issued_at = ? WHERE punishment_id = ?"; + @Language("sql") + protected final static String APPLY_SUCCESSOR = "UPDATE punishment.necrify_punishment SET successor = ? WHERE punishment_id = ?"; + @Language("sql") + protected final static String APPLY_TIMESTAMP_UPDATE = "UPDATE punishment.necrify_punishment SET expiration = ?, issued_at = ? WHERE punishment_id = ?"; + private final boolean validity; - public AbstractPunishment(@NotNull NecrifyUser user, @NotNull Component reason, @NotNull UUID punishmentUuid, @NotNull AbstractNecrifyPlugin plugin, @Nullable Punishment successor) { + public AbstractPunishment(@NotNull NecrifyUser user, + @NotNull Component reason, + @NotNull UUID punishmentUuid, + @NotNull AbstractNecrifyPlugin plugin, + @Nullable Punishment successor, + @Nullable LocalDateTime issuedAt) { this.reason = Objects.requireNonNull(reason, "punishment must be reasoned"); - this.service = plugin.getService(); + this.executor = plugin.getService(); 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; @@ -77,14 +87,15 @@ public AbstractPunishment(@NotNull NecrifyUser user, @NotNull Component reason, this.messageProvider = plugin.getMessageProvider(); this.eventDispatcher = plugin.getEventDispatcher(); this.plugin = plugin; + this.creationTime = issuedAt; } public @NotNull Component getReason() { return reason; } - public ExecutorService getService() { - return service; + public ExecutorService getExecutor() { + return executor; } protected CompletableFuture executeAsync(Callable task, ExecutorService executorService) { @@ -118,6 +129,7 @@ public boolean isValid() { @Override public final CompletableFuture punish() { + creationTime = LocalDateTime.now(); return applyPunishment().whenCompleteAsync((punishment, throwable) -> { if (punishment != null) { plugin.getEventDispatcher().dispatch(new PunishmentPersecutedEvent(punishment)); @@ -144,7 +156,7 @@ protected CompletableFuture applyCancellation() { public String toString() { return "AbstractPunishment{" + "reason=" + reason + - ", service=" + service + + ", service=" + executor + ", userUuid=" + user.getUuid() + ", userName=" + user.getUsername() + ", punishmentUuid=" + punishmentUuid + @@ -156,9 +168,8 @@ public String toString() { @Override public final boolean equals(Object o) { if (this == o) return true; - if (!(o instanceof AbstractPunishment that)) return false; - - return punishmentUuid.equals(that.punishmentUuid); + if (!(o instanceof Punishment that)) return false; + return punishmentUuid.equals(that.getPunishmentUuid()); } @Override @@ -206,7 +217,19 @@ public boolean hasSuccessor() { } @Override - public void setSuccessor(@NotNull Punishment successor) { + public @NotNull LocalDateTime getCreationTime() { + if (creationTime == null) { + throw new IllegalStateException("punishment has not been created yet"); + } + return creationTime; + } + + @Override + public boolean hasBeenCreated() { + return creationTime != null; + } + + void setSuccessor0(Punishment successor) { this.successor = successor; } } 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 183f6d0..0f17ae6 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 @@ -21,10 +21,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.common.punishment; import de.chojo.sadu.queries.api.call.Call; +import de.chojo.sadu.queries.api.call.adapter.Adapter; +import de.chojo.sadu.queries.api.call.adapter.AdapterMapping; import de.chojo.sadu.queries.api.query.Query; import de.jvstvshd.necrify.api.PunishmentException; import de.jvstvshd.necrify.api.duration.PunishmentDuration; @@ -35,9 +36,15 @@ import de.jvstvshd.necrify.api.user.NecrifyUser; import de.jvstvshd.necrify.common.AbstractNecrifyPlugin; import de.jvstvshd.necrify.common.io.Adapters; +import de.jvstvshd.necrify.common.util.Util; import net.kyori.adventure.text.Component; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.sql.Timestamp; +import java.sql.Types; +import java.time.Duration; +import java.time.LocalDateTime; import java.util.Objects; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -46,8 +53,8 @@ public abstract class AbstractTemporalPunishment extends AbstractPunishment impl private final PunishmentDuration duration; - public AbstractTemporalPunishment(NecrifyUser user, Component reason, UUID punishmentUuid, PunishmentDuration duration, AbstractNecrifyPlugin plugin, Punishment successor) { - super(user, reason, punishmentUuid, plugin, successor); + public AbstractTemporalPunishment(NecrifyUser user, Component reason, UUID punishmentUuid, PunishmentDuration duration, AbstractNecrifyPlugin plugin, Punishment successor, LocalDateTime issuedAt) { + super(user, reason, punishmentUuid, plugin, successor, issuedAt); this.duration = Objects.requireNonNull(duration, "temporal punishment must have a duration"); } @@ -75,23 +82,30 @@ protected void checkValidity() { } @Override - public final CompletableFuture change(@NotNull PunishmentDuration newDuration, Component newReason) throws PunishmentException { + public final CompletableFuture change(@NotNull PunishmentDuration newDuration, @Nullable LocalDateTime creationTime, Component newReason) throws PunishmentException { if (!getType().isBan() && !getType().isMute()) { throw new IllegalStateException("only bans and mutes can be changed"); } return executeAsync(() -> { + var newCreatedAt = creationTime == null ? getCreationTime() : creationTime; + var newRsn = newReason == null ? getReason() : newReason; Query.query(APPLY_CHANGE) .single(Call.of() - .bind(convertReason(newReason)) + .bind(convertReason(newRsn)) .bind(newDuration.expirationAsTimestamp()) - .bind(newDuration.isPermanent()) + .bind(Timestamp.valueOf(newCreatedAt)) .bind(getPunishmentUuid(), Adapters.UUID_ADAPTER)) .update(); + if (hasSuccessor()) { //we have to update the successor's time of issuance and expiration accordingly + updateSuccessor().join(); + } var builder = new PunishmentBuilder(getPlugin()) .withUser(getUser()) .withReason(newReason) .withDuration(newDuration) - .withPunishmentUuid(getPunishmentUuid()); + .withPunishmentUuid(getPunishmentUuid()) + .withSuccessor(getSuccessorOrNull()) + .withCreationTime(getCreationTime()); Punishment punishment; if (getType().isBan()) { punishment = builder.buildBan(); @@ -102,17 +116,50 @@ public final CompletableFuture change(@NotNull PunishmentDuration ne } getPlugin().getEventDispatcher().dispatch(new PunishmentChangedEvent(punishment, this)); return punishment; - }, getService()); + }, getExecutor()); } @Override protected CompletableFuture applyCancellation() throws PunishmentException { return executeAsync(() -> { + if (hasSuccessor()) { + updateSuccessor().join(); + } + var predecessor = getUser().getPunishments().stream() + .filter(punishment -> punishment.getSuccessorOrNull() != null && punishment.getSuccessorOrNull().equals(this)).findFirst().orElse(null); + 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(); + } + } Query.query(APPLY_CANCELLATION) .single(Call.of().bind(getPunishmentUuid(), Adapters.UUID_ADAPTER)) .delete(); return this; - }, getService()); + }, getExecutor()); + } + + private CompletableFuture updateSuccessor() { + var successor = getSuccessor(); + if (successor instanceof TemporalPunishment temporalSuccessor) { + var total = temporalSuccessor.totalDuration(); + LocalDateTime newExpiration = LocalDateTime.now().plus(total.javaDuration()); + temporalSuccessor.change(PunishmentDuration.from(newExpiration), LocalDateTime.now(), successor.getReason()).join(); + } //we just assume that everything except a TemporalPunishment expires immediately or not in an infinite time + return CompletableFuture.completedFuture(successor); } @Override @@ -129,9 +176,52 @@ protected CompletableFuture applyPunishment() throws PunishmentExcep .bind(getType().getId()) .bind(duration.expirationAsTimestamp()) .bind(convertReason(getReason())) - .bind(getPunishmentUuid(), Adapters.UUID_ADAPTER)) + .bind(getPunishmentUuid(), Adapters.UUID_ADAPTER) + .bind(Timestamp.valueOf(getCreationTime()))) .insert(); return this; - }, getService()); + }, getExecutor()); + } + + @Override + public @NotNull CompletableFuture setSuccessor(@NotNull Punishment successor) { + if (!getType().getRelatedTypes().contains(successor.getType())) { + throw new IllegalArgumentException("successor punishment is not related to this punishment"); + } + if (!getUser().equals(successor.getUser())) { + throw new IllegalArgumentException("successor punishment is not for the same user"); + } + if (Util.circularSuccessionChain(this, successor)) { + throw new IllegalStateException("circular successor chain detected"); + } + return Util.executeAsync(() -> { + Query.query(APPLY_SUCCESSOR) + .single(Call.of().bind(successor.getUuid(), Adapters.UUID_ADAPTER).bind(getPunishmentUuid(), Adapters.UUID_ADAPTER)) + .update(); + setSuccessor0(successor); + LocalDateTime successorNewExpiration; + if (successor instanceof TemporalPunishment temporalSuccessor) { + var total = temporalSuccessor.totalDuration(); + successorNewExpiration = duration.expiration().plus(total.javaDuration()); + } else { + successorNewExpiration = PunishmentDuration.permanent().expiration(); + } + var issuanceSuccessor = duration.expiration(); + Query.query(APPLY_TIMESTAMP_UPDATE) + .single(Call.of() + .bind(Timestamp.valueOf(successorNewExpiration)) + .bind(issuanceSuccessor) + .bind(successor.getPunishmentUuid(), Adapters.UUID_ADAPTER)) + .update(); + if (successor instanceof TemporalPunishment temporalSuccessor) { + temporalSuccessor.change(PunishmentDuration.from(successorNewExpiration), issuanceSuccessor, successor.getReason()).join(); + } + return this; + }, getExecutor()); + } + + @Override + public @NotNull PunishmentDuration totalDuration() { + return PunishmentDuration.fromDuration(Duration.between(getCreationTime(), getDuration().expiration())); } } 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 25bbf7e..3452f61 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 @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.common.punishment; import de.jvstvshd.necrify.api.PunishmentException; @@ -45,8 +44,8 @@ public class NecrifyBan extends AbstractTemporalPunishment implements Ban { - public NecrifyBan(NecrifyUser user, Component reason, UUID punishmentUuid, PunishmentDuration duration, AbstractNecrifyPlugin plugin, Punishment successor) { - super(user, reason, punishmentUuid, duration, plugin, successor); + public NecrifyBan(NecrifyUser user, Component reason, UUID punishmentUuid, PunishmentDuration duration, AbstractNecrifyPlugin plugin, Punishment successor, LocalDateTime issuedAt) { + super(user, reason, punishmentUuid, duration, plugin, successor, issuedAt); } @Override diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/NecrifyKick.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/NecrifyKick.java index ab7cf1f..bf0a451 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/NecrifyKick.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/NecrifyKick.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.common.punishment; import de.jvstvshd.necrify.api.punishment.Kick; @@ -32,13 +31,14 @@ import net.kyori.adventure.text.Component; import org.jetbrains.annotations.NotNull; +import java.time.LocalDateTime; import java.util.Locale; import java.util.UUID; public abstract class NecrifyKick extends AbstractPunishment implements Kick { - public NecrifyKick(NecrifyUser user, Component reason, UUID punishmentUuid, AbstractNecrifyPlugin plugin) { - super(user, reason, punishmentUuid, plugin, null); + public NecrifyKick(NecrifyUser user, Component reason, UUID punishmentUuid, AbstractNecrifyPlugin plugin, LocalDateTime issuedAt) { + super(user, reason, punishmentUuid, plugin, null, issuedAt); } @Override 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 7f67b8d..b6d86dd 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 @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.common.punishment; import de.jvstvshd.necrify.api.duration.PunishmentDuration; @@ -43,8 +42,8 @@ public class NecrifyMute extends AbstractTemporalPunishment implements Mute { - public NecrifyMute(NecrifyUser user, Component reason, UUID punishmentUuid, PunishmentDuration duration, AbstractNecrifyPlugin plugin, Punishment successor) { - super(user, reason, punishmentUuid, duration, plugin, successor); + public NecrifyMute(NecrifyUser user, Component reason, UUID punishmentUuid, PunishmentDuration duration, AbstractNecrifyPlugin plugin, Punishment successor, LocalDateTime creationTime) { + super(user, reason, punishmentUuid, duration, plugin, successor, creationTime); } @Override @@ -75,4 +74,4 @@ public boolean isPermanent() { return getMessageProvider().provide("punishment.mute.temp.full-reason", Component.text(getDuration().remainingDuration()).color(NamedTextColor.YELLOW), getReason(), until); } } -} \ No newline at end of file +} diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/NecrifyPunishmentFactory.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/NecrifyPunishmentFactory.java index 3a8f3a1..0b0abb7 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/NecrifyPunishmentFactory.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/punishment/NecrifyPunishmentFactory.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.common.punishment; import de.jvstvshd.necrify.api.duration.PunishmentDuration; @@ -33,6 +32,7 @@ import net.kyori.adventure.text.Component; import org.jetbrains.annotations.NotNull; +import java.time.LocalDateTime; import java.util.Map; import java.util.UUID; @@ -50,11 +50,15 @@ public NecrifyPunishmentFactory(AbstractNecrifyPlugin plugin) { final Component reason = (Component) data.get("reason"); final UUID punishmentUuid = (UUID) data.get("punishmentUuid"); final NecrifyUser user = (NecrifyUser) data.get("user"); + final Punishment successor = (Punishment) data.get("successor"); + final LocalDateTime creationTime = (LocalDateTime) data.get("issued_at"); var builder = PunishmentBuilder.newBuilder(plugin) .withDuration(duration) .withReason(reason) .withUser(user) - .withPunishmentUuid(punishmentUuid); + .withPunishmentUuid(punishmentUuid) + .withSuccessor(successor) + .withCreationTime(creationTime); Punishment punishment; switch (type.standard()) { case TEMPORARY_BAN, PERMANENT_BAN -> punishment = builder.buildBan(); 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 dd6c57b..53808d5 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 @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.common.punishment; import de.jvstvshd.necrify.api.duration.PunishmentDuration; @@ -30,6 +29,7 @@ import de.jvstvshd.necrify.common.AbstractNecrifyPlugin; import net.kyori.adventure.text.Component; +import java.time.LocalDateTime; import java.util.UUID; public class PunishmentBuilder { @@ -40,6 +40,7 @@ public class PunishmentBuilder { private PunishmentDuration duration; private UUID punishmentUuid; private Punishment successor; + private LocalDateTime creationTime; public PunishmentBuilder(AbstractNecrifyPlugin plugin) { this.plugin = plugin; @@ -94,9 +95,18 @@ public PunishmentBuilder withSuccessor(Punishment successor) { return this; } + public LocalDateTime creationTime() { + return creationTime; + } + + public PunishmentBuilder withCreationTime(LocalDateTime creationTime) { + this.creationTime = creationTime; + return this; + } + public NecrifyBan buildBan() { validateValues(); - return new NecrifyBan(user, reason, punishmentUuid, duration.absolute(), plugin, successor); + return new NecrifyBan(user, reason, punishmentUuid, duration.absolute(), plugin, successor, creationTime); } /** @@ -111,7 +121,7 @@ public NecrifyKick buildKick() { public NecrifyMute buildMute() { validateValues(); - return new NecrifyMute(user, reason, punishmentUuid, duration.absolute(), plugin, successor); + return new NecrifyMute(user, reason, punishmentUuid, duration.absolute(), plugin, successor, creationTime); } private void validateValues() { 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 3e15bc8..74cc3b4 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 @@ -21,7 +21,6 @@ * 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-common/src/main/java/de/jvstvshd/necrify/common/user/MojangAPI.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/user/MojangAPI.java index 5201b4f..b044687 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/user/MojangAPI.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/user/MojangAPI.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.common.user; import com.google.gson.JsonElement; diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/user/UserLoader.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/user/UserLoader.java new file mode 100644 index 0000000..d72a61f --- /dev/null +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/user/UserLoader.java @@ -0,0 +1,112 @@ +/* + * 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.chojo.sadu.mapper.wrapper.Row; +import de.jvstvshd.necrify.api.duration.PunishmentDuration; +import de.jvstvshd.necrify.api.punishment.Punishment; +import de.jvstvshd.necrify.api.punishment.PunishmentTypeRegistry; +import de.jvstvshd.necrify.api.punishment.StandardPunishmentType; +import de.jvstvshd.necrify.api.user.NecrifyUser; +import de.jvstvshd.necrify.api.user.UserLoadOrderCoordinator; +import de.jvstvshd.necrify.common.util.Util; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; + +import java.sql.SQLException; +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.util.*; + +public final class UserLoader { + + private final List> data = new ArrayList<>(); + private final NecrifyUser user; + + public UserLoader(NecrifyUser user) { + this.user = user; + } + + public void addData(Map data) { + this.data.add(data); + } + + public Void addDataFromRow(Row row) { + try { + final StandardPunishmentType type = PunishmentTypeRegistry.getType(row.getInt(1)).standard(); + final Timestamp timestamp = row.getTimestamp(2); + final PunishmentDuration duration = PunishmentDuration.fromTimestamp(timestamp); + final Component reason = MiniMessage.miniMessage().deserialize(row.getString(3)); + final UUID punishmentUuid = row.getObject(4, UUID.class); + final UUID successorId = row.getObject(5, UUID.class); + final LocalDateTime issuedAt = row.getTimestamp(6).toLocalDateTime(); + var data = new HashMap() { + { + put("type", type); + put("duration", duration); + put("reason", reason); + put("punishmentUuid", punishmentUuid); + put("user", user); + put("successorId", successorId); + put("issued_at", issuedAt); + } + }; + addData(data); + } catch (SQLException e) { + throw new RuntimeException(e); + } + return null; + } + + public List loadPunishments() { + var ordered = UserLoadOrderCoordinator.topologicalSort(data); + Map loaded = new HashMap<>(); + for (Map dataMap : ordered) { + var type = (StandardPunishmentType) dataMap.get("type"); + if (dataMap.get("successorId") != null) { + var successor = loaded.get((UUID) dataMap.get("successorId")); + dataMap.put("successor", successor); + } + var punishment = PunishmentTypeRegistry.createPunishment(type, dataMap); + loaded.put(punishment.getPunishmentUuid(), punishment); + } + return new ArrayList<>(loaded.values()); + + } + + private String c(Punishment p) { + if (p == null) { + return "null"; + } + return p.getPunishmentUuid().toString(); + } + + public NecrifyUser getUser() { + return user; + } + + public List> getData() { + return data; + } +} 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 index 75dd0dc..67795c0 100644 --- 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 @@ -21,16 +21,23 @@ * 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.ComponentBuilder; +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.flattener.ComponentFlattener; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import java.time.format.DateTimeFormatter; +import java.util.Locale; + public class PunishmentHelper { private PunishmentHelper() { @@ -38,34 +45,72 @@ private PunishmentHelper() { } public static Component buildPunishmentData(Punishment punishment, MessageProvider provider) { - return Component.text() - .append(provider.provide("helper.type").color(NamedTextColor.AQUA), + return buildPunishmentData(punishment, provider, 0); + } + + public static Component buildPunishmentData(Punishment punishment, MessageProvider messageProvider, int indents) { + var provider = indents == 0 ? messageProvider : messageProvider.unprefixedProvider(); + var ic = indentComponent(indents); + var clickToRemove = provider.provide("command.punishment.click-to-remove"); + + var builder = Component.text() + .append(ic, + provider.provide("helper.type").color(NamedTextColor.AQUA), copyable("%s (%d)".formatted(punishment.getType().getName(), punishment.getType().getId()), NamedTextColor.YELLOW, provider), Component.newline(), + ic, provider.provide("helper.reason").color(NamedTextColor.AQUA), Util.copyComponent(punishment.getReason(), PlainTextComponentSerializer.plainText().serialize(punishment.getReason()), provider), Component.newline(), + ic, provider.prefixed(Component.text("ID: ")).color(NamedTextColor.AQUA), copyable(punishment.getPunishmentUuid().toString(), NamedTextColor.YELLOW, provider), Component.newline(), punishment instanceof TemporalPunishment temporalPunishment ? - buildPunishmentDataTemporal(temporalPunishment, provider) : Component.text(""), + buildPunishmentDataTemporal(temporalPunishment, provider, ic) : Component.text(""), + Component.newline(), + ic, + clickToRemove + .color(NamedTextColor.RED) + .clickEvent(ClickEvent.runCommand("/necrify punishment " + punishment.getPunishmentUuid().toString().toLowerCase(Locale.ROOT) + " remove")) + .hoverEvent((HoverEventSource) op -> HoverEvent.showText(clickToRemove.color(NamedTextColor.GREEN))), Component.newline() - ) - .build(); + ); + if (punishment.hasSuccessor()) { + int newIndenting; + if (indents == 0) { + newIndenting = 10; + } else { + newIndenting = indents + 3; + } + var child = buildPunishmentData(punishment.getSuccessor(), provider, newIndenting); + builder.append(ic, provider.provide("helper.successor").color(NamedTextColor.AQUA), Component.newline()) + .append(child); + } + return builder.build(); + } + + public static Component indentComponent(int n) { + if (n == 0) { + return Component.empty(); + } + return Component.text(" ".repeat(n) + "> ").color(NamedTextColor.GRAY); } - public static Component buildPunishmentDataTemporal(TemporalPunishment punishment, MessageProvider provider) { + public static Component buildPunishmentDataTemporal(TemporalPunishment punishment, MessageProvider provider, Component linePrefix) { if (punishment.isPermanent()) { return Component.text() - .append(provider.provide("helper.temporal.duration").color(NamedTextColor.AQUA), + .append(linePrefix, + provider.provide("helper.temporal.duration").color(NamedTextColor.AQUA), Component.text("PERMANENT").color(NamedTextColor.RED)) .build(); } return Component.text() - .append(provider.provide("helper.temporal.duration").color(NamedTextColor.AQUA), + .append(linePrefix, + provider.provide("helper.temporal.duration").color(NamedTextColor.AQUA), Component.text(punishment.getDuration().remainingDuration()).color(NamedTextColor.YELLOW), Component.newline(), + linePrefix, provider.provide("helper.temporal.end").color(NamedTextColor.AQUA), Component.text(punishment.getDuration().expirationAsString()).color(NamedTextColor.YELLOW)) .build(); diff --git a/necrify-common/src/main/java/de/jvstvshd/necrify/common/util/Util.java b/necrify-common/src/main/java/de/jvstvshd/necrify/common/util/Util.java index 855c417..e65b596 100644 --- a/necrify-common/src/main/java/de/jvstvshd/necrify/common/util/Util.java +++ b/necrify-common/src/main/java/de/jvstvshd/necrify/common/util/Util.java @@ -21,10 +21,10 @@ * 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 net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.event.ClickEvent; @@ -32,6 +32,7 @@ import net.kyori.adventure.text.event.HoverEventSource; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import org.jetbrains.annotations.Nullable; import java.util.Optional; import java.util.UUID; @@ -49,7 +50,7 @@ public static CompletableFuture executeAsync(Callable task, Executor s service.execute(() -> { try { cf.complete(task.call()); - } catch (Exception e) { + } catch (Throwable e) { cf.completeExceptionally(e); } }); @@ -85,4 +86,22 @@ public static Optional fromString(String uuidString) { } return Optional.of(uuid); } -} \ No newline at end of file + + public static boolean circularSuccessionChain(Punishment base, Punishment successor) { + Punishment successorsSuccessor = successor; + do { + if (successorsSuccessor.equals(base)) { + return true; + } + } while ((successorsSuccessor = successorOrNull(successorsSuccessor)) != null); + return false; + } + + @Nullable + public static Punishment successorOrNull(Punishment punishment) { + if (punishment.hasSuccessor()) { + return punishment.getSuccessor(); + } + return null; + } +} diff --git a/necrify-common/src/main/resources/database/postgresql/1/patch_1.sql b/necrify-common/src/main/resources/database/postgresql/1/patch_1.sql index 4cfe4cc..bf3968d 100644 --- a/necrify-common/src/main/resources/database/postgresql/1/patch_1.sql +++ b/necrify-common/src/main/resources/database/postgresql/1/patch_1.sql @@ -45,4 +45,10 @@ ALTER TABLE punishment.necrify_punishment UPDATE punishment.necrify_user SET whitelisted = TRUE WHERE uuid IN (SELECT uuid FROM punishment.necrify_whitelist); -DROP TABLE punishment.necrify_whitelist; \ No newline at end of file +DROP TABLE punishment.necrify_whitelist; +ALTER TABLE punishment.necrify_punishment ADD COLUMN IF NOT EXISTS issued_at TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP; +ALTER TABLE punishment.necrify_punishment + ADD CONSTRAINT unique_punishment_id UNIQUE (punishment_id); + +ALTER TABLE punishment.necrify_punishment + ADD COLUMN IF NOT EXISTS successor UUID DEFAULT NULL REFERENCES punishment.necrify_punishment (punishment_id) ON DELETE SET NULL; \ No newline at end of file diff --git a/necrify-paper/build.gradle.kts b/necrify-paper/build.gradle.kts index b4f51ba..3e2543c 100644 --- a/necrify-paper/build.gradle.kts +++ b/necrify-paper/build.gradle.kts @@ -1,10 +1,7 @@ -import io.papermc.hangarpublishplugin.model.Platforms -import java.io.ByteArrayOutputStream - plugins { java `java-library` - id("com.github.johnrengelman.shadow") version "7.1.2" + id("io.github.goooler.shadow") id("net.minecrell.plugin-yml.paper") version "0.6.0" id("io.papermc.hangar-publish-plugin") } @@ -47,6 +44,11 @@ tasks { } } +java { + withSourcesJar() + withJavadocJar() +} + tasks.getByName("test") { useJUnitPlatform() } diff --git a/necrify-paper/src/main/java/de/jvstvshd/necrify/paper/NecrifyPaperPlugin.java b/necrify-paper/src/main/java/de/jvstvshd/necrify/paper/NecrifyPaperPlugin.java index 0cbe840..65770fe 100644 --- a/necrify-paper/src/main/java/de/jvstvshd/necrify/paper/NecrifyPaperPlugin.java +++ b/necrify-paper/src/main/java/de/jvstvshd/necrify/paper/NecrifyPaperPlugin.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.paper; import de.jvstvshd.necrify.common.plugin.MuteData; @@ -52,4 +51,4 @@ public void onDisable() { public List cachedMutes() { return cachedMutes; } -} \ No newline at end of file +} diff --git a/necrify-paper/src/main/java/de/jvstvshd/necrify/paper/NecrifyPaperPluginBootstrap.java b/necrify-paper/src/main/java/de/jvstvshd/necrify/paper/NecrifyPaperPluginBootstrap.java index 330abcc..417307b 100644 --- a/necrify-paper/src/main/java/de/jvstvshd/necrify/paper/NecrifyPaperPluginBootstrap.java +++ b/necrify-paper/src/main/java/de/jvstvshd/necrify/paper/NecrifyPaperPluginBootstrap.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.paper; import io.papermc.paper.plugin.bootstrap.BootstrapContext; diff --git a/necrify-paper/src/main/java/de/jvstvshd/necrify/paper/NecrifyPaperPluginLoader.java b/necrify-paper/src/main/java/de/jvstvshd/necrify/paper/NecrifyPaperPluginLoader.java index 149d713..baebd6d 100644 --- a/necrify-paper/src/main/java/de/jvstvshd/necrify/paper/NecrifyPaperPluginLoader.java +++ b/necrify-paper/src/main/java/de/jvstvshd/necrify/paper/NecrifyPaperPluginLoader.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.paper; import com.google.gson.Gson; diff --git a/necrify-paper/src/main/java/de/jvstvshd/necrify/paper/listeners/ChatListener.java b/necrify-paper/src/main/java/de/jvstvshd/necrify/paper/listeners/ChatListener.java index 9c83b8e..0e45535 100644 --- a/necrify-paper/src/main/java/de/jvstvshd/necrify/paper/listeners/ChatListener.java +++ b/necrify-paper/src/main/java/de/jvstvshd/necrify/paper/listeners/ChatListener.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.paper.listeners; import de.jvstvshd.necrify.paper.NecrifyPaperPlugin; 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 574e334..2716dbb 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 @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.paper.listeners; import com.fasterxml.jackson.core.JsonProcessingException; 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 ff0cc6b..f8511fd 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 @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.paper.listeners; import de.jvstvshd.necrify.api.duration.PunishmentDuration; diff --git a/necrify-velocity/build.gradle.kts b/necrify-velocity/build.gradle.kts index 3915d19..ae8c074 100644 --- a/necrify-velocity/build.gradle.kts +++ b/necrify-velocity/build.gradle.kts @@ -1,7 +1,7 @@ plugins { java `java-library` - id("io.github.goooler.shadow") version "8.1.8" + id("io.github.goooler.shadow") id("xyz.jpenilla.run-velocity") version "2.3.0" id("io.papermc.hangar-publish-plugin") id("dev.vankka.dependencydownload.plugin") version "1.3.1" @@ -35,9 +35,6 @@ tasks { javadoc { dependsOn(generateRuntimeDownloadResourceForRuntimeDownload) } - /*generateRuntimeDownloadResourceForRuntimeDownload { - configuration(configurations.getByName("runtimeDownload")) - }*/ compileJava { options.encoding = "UTF-8" } @@ -59,7 +56,6 @@ tasks { dependencies { val prefix: (String) -> String = { "de.jvstvshd.necrify.lib.$it" } relocate("com.fasterxml.jackson", prefix("jackson")) - //relocate("com.github.benmanes.caffeine", prefix("caffeine")) relocate("com.google.errorprone", prefix("google.errorprone")) relocate("com.google.protobuf", prefix("google.protobuf")) relocate("com.mysql", prefix("mysql")) @@ -88,10 +84,6 @@ tasks { } } -fun allParents(dependency: ResolvedDependency): List { - return listOf() -} - java { withSourcesJar() withJavadocJar() 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 b5e5c79..e3f95d6 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 @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.velocity; import com.fasterxml.jackson.core.JsonProcessingException; 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 15f2df9..74b6290 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 @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.velocity; import com.github.benmanes.caffeine.cache.Caffeine; @@ -71,6 +70,7 @@ import de.jvstvshd.necrify.common.io.Adapters; import de.jvstvshd.necrify.common.plugin.MuteData; import de.jvstvshd.necrify.common.punishment.NecrifyKick; +import de.jvstvshd.necrify.common.user.UserLoader; import de.jvstvshd.necrify.velocity.commands.*; import de.jvstvshd.necrify.velocity.config.ConfigurationManager; import de.jvstvshd.necrify.velocity.impl.DefaultPlayerResolver; @@ -104,6 +104,7 @@ import java.nio.file.Path; import java.sql.SQLException; import java.time.Duration; +import java.time.LocalDateTime; import java.util.Collections; import java.util.Optional; import java.util.Set; @@ -162,6 +163,7 @@ public NecrifyVelocityPlugin(ProxyServer server, Logger logger, @DataDirectory P @Subscribe public void onProxyInitialization(ProxyInitializeEvent event) throws IOException { Thread.setDefaultUncaughtExceptionHandler((t, e) -> logger.error("An error occurred in thread {}", t.getName(), e)); + long start = System.currentTimeMillis(); try { DependencyManager manager = new DependencyManager(dataDirectory.resolve("cache")); manager.loadFromResource(getClass().getClassLoader().getResource("runtimeDownload.txt")); @@ -174,6 +176,7 @@ public void onProxyInitialization(ProxyInitializeEvent event) throws IOException logger.error("Could not load required dependencies. Aborting start-up", e); return; } + logger.info("Successfully loaded all dependencies in {}ms", System.currentTimeMillis() - start); try { configurationManager.load(); if (configurationManager.getConfiguration().isWhitelistActivated()) { @@ -421,6 +424,9 @@ public NecrifyUser createUser(CommandSource source) { * loaded asynchronously or not. If set to true, punishments will be loaded blocking. * @return the created user. */ + //TODO allow for other implementations of UserManager to work with this -- extract this into its own class (with + //TODO maybe some other internal stuff) and make it a component like user manager etc. + //TODO 06.08.2024 public NecrifyUser createUser(UUID userId, boolean loadPunishmentsDirectly) { var cachedUser = getUserManager().getUser(userId); if (cachedUser.isPresent()) { @@ -428,10 +434,12 @@ public NecrifyUser createUser(UUID userId, boolean loadPunishmentsDirectly) { } var user = new VelocityUser(userId, "unknown", false, this); Runnable loadPunishments = () -> { - Query.query("SELECT type, expiration, reason, punishment_id FROM punishment.necrify_punishment WHERE uuid = ?;") + var loader = new UserLoader(user); + Query.query("SELECT type, expiration, reason, punishment_id, successor, issued_at FROM punishment.necrify_punishment WHERE uuid = ?;") .single(Call.of().bind(userId, Adapters.UUID_ADAPTER)) - .map(user::addPunishment) + .map(loader::addDataFromRow) .all(); + ((VelocityUserManager) userManager).loadPunishmentsToUser(loader); getEventDispatcher().dispatch(new UserLoadedEvent(user).setOrigin(EventOrigin.ofClass(getClass()))); }; if (loadPunishmentsDirectly) { @@ -467,7 +475,7 @@ public void setEventDispatcher(@NotNull EventDispatcher eventDispatcher) { @Override public NecrifyKick createKick(Component reason, NecrifyUser user, UUID punishmentUuid) { - return new VelocityKick(user, reason, punishmentUuid, this); + return new VelocityKick(user, reason, punishmentUuid, this, LocalDateTime.now()); } @Override diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/BanCommand.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/BanCommand.java index 277b4d0..2c14398 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/BanCommand.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/BanCommand.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.velocity.commands; import com.mojang.brigadier.Command; diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/KickCommand.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/KickCommand.java index e4ccb9f..bb2d270 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/KickCommand.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/KickCommand.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.velocity.commands; import com.mojang.brigadier.Command; diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/MuteCommand.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/MuteCommand.java index 72ba06c..b1b2ede 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/MuteCommand.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/MuteCommand.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.velocity.commands; import com.mojang.brigadier.Command; diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/PunishmentCommand.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/PunishmentCommand.java index f936381..5ced80b 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/PunishmentCommand.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/PunishmentCommand.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.velocity.commands; import com.google.common.collect.ImmutableList; @@ -138,4 +137,4 @@ private static int execute(CommandContext context, NecrifyVelocit }); return Command.SINGLE_SUCCESS; } -} \ No newline at end of file +} 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 44df0c0..cc3793d 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 @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.velocity.commands; import com.mojang.brigadier.Command; diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/TempbanCommand.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/TempbanCommand.java index 5e25354..3937404 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/TempbanCommand.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/TempbanCommand.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.velocity.commands; import com.mojang.brigadier.Command; diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/TempmuteCommand.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/TempmuteCommand.java index 3fdd8f8..fecbffe 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/TempmuteCommand.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/TempmuteCommand.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.velocity.commands; import com.mojang.brigadier.Command; diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/WhitelistCommand.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/WhitelistCommand.java index 2b8c3ba..be7943e 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/WhitelistCommand.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/commands/WhitelistCommand.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.velocity.commands; import com.google.common.collect.ImmutableList; @@ -124,4 +123,4 @@ private static int execute(CommandContext context, NecrifyVelocit }, plugin.getService()); return Command.SINGLE_SUCCESS; } -} \ No newline at end of file +} 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 d4e0419..29ae863 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 @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.velocity.config; import com.fasterxml.jackson.annotation.JsonAlias; diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/config/ConfigurationManager.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/config/ConfigurationManager.java index fcbfe8e..5f27bc3 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/config/ConfigurationManager.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/config/ConfigurationManager.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.velocity.config; import com.fasterxml.jackson.databind.ObjectMapper; @@ -94,4 +93,4 @@ public void save() throws IOException { public ConfigData getConfiguration() { return configData; } -} \ No newline at end of file +} 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 efa6718..4f85c6c 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 @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.velocity.config; import com.fasterxml.jackson.annotation.JsonAlias; 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 0947128..3bedd45 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 @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.velocity.impl; import com.google.gson.JsonElement; 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 69d993f..db5a831 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 @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.velocity.impl; import com.velocitypowered.api.proxy.ProxyServer; @@ -110,4 +109,4 @@ public NecrifyVelocityPlugin plugin() { public CompletableFuture isBanned(UUID playerUuid, Executor 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/impl/VelocityKick.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/impl/VelocityKick.java index 6a697e4..1b13b34 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/impl/VelocityKick.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/impl/VelocityKick.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.velocity.impl; import de.jvstvshd.necrify.api.punishment.Punishment; @@ -31,13 +30,14 @@ import de.jvstvshd.necrify.velocity.user.VelocityUser; import net.kyori.adventure.text.Component; +import java.time.LocalDateTime; import java.util.UUID; import java.util.concurrent.CompletableFuture; public class VelocityKick extends NecrifyKick { - public VelocityKick(NecrifyUser user, Component reason, UUID punishmentUuid, AbstractNecrifyPlugin plugin) { - super(user, reason, punishmentUuid, plugin); + public VelocityKick(NecrifyUser user, Component reason, UUID punishmentUuid, AbstractNecrifyPlugin plugin, LocalDateTime issuedAt) { + super(user, reason, punishmentUuid, plugin, issuedAt); } @Override 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 351ae7f..67e1ed2 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 @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.velocity.internal; import com.mojang.brigadier.arguments.StringArgumentType; 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 609c246..017d5ab 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 @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.velocity.internal; import com.google.common.collect.ImmutableList; @@ -103,6 +102,7 @@ public static UUID parseUuid(String uuidString) { } } + @Deprecated(forRemoval = true) public static Optional fromString(String uuidString) { UUID uuid; try { @@ -124,6 +124,7 @@ public static TextComponent copyComponent(String text, MessageProvider provider) .hoverEvent((HoverEventSource) op -> HoverEvent.showText(provider.provide("commands.general.copy").color(NamedTextColor.GREEN))); } + @Deprecated(forRemoval = true) public static CompletableFuture executeAsync(Callable task, Executor service) { CompletableFuture cf = new CompletableFuture<>(); service.execute(() -> { diff --git a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/listener/ConnectListener.java b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/listener/ConnectListener.java index 6230cc2..e4b8c63 100644 --- a/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/listener/ConnectListener.java +++ b/necrify-velocity/src/main/java/de/jvstvshd/necrify/velocity/listener/ConnectListener.java @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.velocity.listener; import com.velocitypowered.api.event.ResultedEvent; 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 c5dd716..744f66e 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 @@ -21,7 +21,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - package de.jvstvshd.necrify.velocity.message; import de.jvstvshd.necrify.api.message.MessageProvider; @@ -163,6 +162,9 @@ private Locale orDefault(@Nullable Locale input) { @Override public MessageProvider unprefixedProvider() { + if (!autoPrefixed) { + return this; + } return new ResourceBundleMessageProvider(configData, false); } } 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 d67bb33..a34e17a 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 @@ -21,7 +21,6 @@ * 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; 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 b12e2f5..12f0d5d 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 @@ -21,7 +21,6 @@ * 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.google.common.collect.ImmutableList; @@ -211,26 +210,6 @@ public Player getPlayer() { return player; } - public Punishment addPunishment(Row row) throws SQLException { - final StandardPunishmentType type = PunishmentTypeRegistry.getType(row.getInt(1)).standard(); - final Timestamp timestamp = row.getTimestamp(2); - final PunishmentDuration duration = PunishmentDuration.fromTimestamp(timestamp); - final Component reason = MiniMessage.miniMessage().deserialize(row.getString(3)); - final UUID punishmentUuid = row.getObject(4, UUID.class); - final NecrifyUser user = this; - var data = new HashMap() { - { - put("duration", duration); - put("reason", reason); - put("punishmentUuid", punishmentUuid); - put("user", user); - } - }; - Punishment punishment = PunishmentTypeRegistry.createPunishment(type, data); - punishments.add(punishment); - return punishment; - } - @Override public boolean isWhitelisted() { return whitelisted; 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 bd95a3f..4b740c2 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 @@ -21,7 +21,6 @@ * 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.github.benmanes.caffeine.cache.Cache; @@ -29,25 +28,37 @@ import com.velocitypowered.api.event.connection.DisconnectEvent; import com.velocitypowered.api.event.connection.LoginEvent; import com.velocitypowered.api.proxy.ProxyServer; +import de.chojo.sadu.mapper.wrapper.Row; import de.chojo.sadu.queries.api.call.Call; import de.chojo.sadu.queries.api.query.Query; +import de.jvstvshd.necrify.api.duration.PunishmentDuration; import de.jvstvshd.necrify.api.event.origin.EventOrigin; import de.jvstvshd.necrify.api.event.punishment.PunishmentCancelledEvent; import de.jvstvshd.necrify.api.event.punishment.PunishmentChangedEvent; import de.jvstvshd.necrify.api.event.punishment.PunishmentPersecutedEvent; import de.jvstvshd.necrify.api.event.user.UserLoadedEvent; +import de.jvstvshd.necrify.api.punishment.Punishment; +import de.jvstvshd.necrify.api.punishment.PunishmentTypeRegistry; +import de.jvstvshd.necrify.api.punishment.StandardPunishmentType; import de.jvstvshd.necrify.api.user.NecrifyUser; +import de.jvstvshd.necrify.api.user.UserLoadOrderCoordinator; import de.jvstvshd.necrify.api.user.UserManager; import de.jvstvshd.necrify.common.io.Adapters; import de.jvstvshd.necrify.common.user.MojangAPI; +import de.jvstvshd.necrify.common.user.UserLoader; import de.jvstvshd.necrify.velocity.NecrifyVelocityPlugin; import de.jvstvshd.necrify.velocity.internal.Util; +import de.jvstvshd.necrify.velocity.user.VelocityUser; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.incendo.cloud.type.tuple.Pair; import org.intellij.lang.annotations.Language; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Optional; -import java.util.UUID; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; @@ -65,7 +76,7 @@ public class VelocityUserManager implements UserManager { @Language("sql") private static final String SELECT_USER_PUNISHMENTS_QUERY = - "SELECT type, expiration, reason, punishment_id FROM punishment.necrify_punishment WHERE uuid = ?;"; + "SELECT type, expiration, reason, punishment_id, successor, issued_at FROM punishment.necrify_punishment WHERE uuid = ?;"; @Language("sql") private static final String INSERT_NEW_USER = "INSERT INTO punishment.necrify_user (uuid, name, whitelisted) VALUES (?, ?, ?) ON CONFLICT (uuid) DO NOTHING;"; @@ -111,9 +122,11 @@ public VelocityUserManager(ExecutorService executor, ProxyServer server, Cache new VelocityUser(uuid, row.getString(1), row.getBoolean(2), player.orElse(null), plugin)) .first(); user.ifPresent(velocityUser -> { + var loader = new UserLoader(velocityUser); Query.query(SELECT_USER_PUNISHMENTS_QUERY) .single(Call.of().bind(uuid, Adapters.UUID_ADAPTER)) - .map(velocityUser::addPunishment).all(); + .map(loader::addDataFromRow).all(); + loadPunishmentsToUser(loader); }); //will cause compilation error: return user.map(this::cache); return user.map(velocityUser -> { @@ -124,7 +137,6 @@ public VelocityUserManager(ExecutorService executor, ProxyServer server, Cache> loadUser(@NotNull String player) { var cached = getUser(player); @@ -141,9 +153,11 @@ public VelocityUserManager(ExecutorService executor, ProxyServer server, Cache new VelocityUser(row.getObject(1, UUID.class), player, row.getBoolean(2), plugin)) .first(); user.ifPresent(velocityUser -> { + var loader = new UserLoader(velocityUser); Query.query(SELECT_USER_PUNISHMENTS_QUERY) .single(Call.of().bind(velocityUser.getUuid(), Adapters.UUID_ADAPTER)) - .map(velocityUser::addPunishment).all(); + .map(loader::addDataFromRow).all(); + loadPunishmentsToUser(loader); }); //will cause compilation error: return user.map(this::cache); return user.map(velocityUser -> { @@ -230,6 +244,12 @@ private VelocityUser cache(@NotNull VelocityUser user) { return user; } + public void loadPunishmentsToUser(UserLoader loader) { + for (Punishment loadedPunishment : loader.loadPunishments()) { + ((VelocityUser) loader.getUser()).addPunishment(loadedPunishment); + } + } + @Subscribe public void onPlayerDisconnect(DisconnectEvent event) { var player = event.getPlayer(); @@ -281,4 +301,4 @@ public void onPunishmentChanged(PunishmentChangedEvent event) { user.addPunishment(punishment); } } -} \ No newline at end of file +}