From c2204790524462b7ee7ed0988a98cb81d1a92a52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Mon, 27 Oct 2025 14:25:44 +0100 Subject: [PATCH 1/4] WIP: refactored report and feedback systems; updated repository models, APIs, and component utilities --- .../CraftAttackReportRepository.java | 5 +- .../api/repositories/ReportRepository.java | 17 +++-- .../metaGameplay/report/Report.java | 72 +++++++++++-------- .../spawn/core/util/text/ComponentUtil.java | 8 ++- .../api/repositories/FeedbackRepository.java | 18 +++-- .../api/repositories/WhitelistRepository.java | 13 ++-- .../metaGameplay/feedback/Feedback.java | 29 +++----- .../feedback/FeedbackCommand.java | 1 + .../feedback/RequestFeedbackCommand.java | 1 + .../appliances/tooling/strike/Strike.java | 21 ++++++ .../tooling/whitelist/Whitelist.java | 8 +-- 11 files changed, 110 insertions(+), 83 deletions(-) create mode 100644 craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/strike/Strike.java diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/api/repositories/CraftAttackReportRepository.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/api/repositories/CraftAttackReportRepository.java index 46723f5..232f4c7 100644 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/api/repositories/CraftAttackReportRepository.java +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/api/repositories/CraftAttackReportRepository.java @@ -11,15 +11,14 @@ public class CraftAttackReportRepository extends ReportRepository { public ReqResp queryReports(UUID player) { return this.get( - "report", - (parameters) -> parameters.addParameter("uuid", player.toString()), + "users/%s/reports".formatted(player.toString()), PlayerReports.class ); } public ReqResp createReport(ReportCreationInfo data) { return this.post( - "report", + "reports", data, ReportUrl.class ); diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/api/repositories/ReportRepository.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/api/repositories/ReportRepository.java index 4ed05cb..0e3fec5 100644 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/api/repositories/ReportRepository.java +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/api/repositories/ReportRepository.java @@ -23,19 +23,18 @@ public abstract class ReportRepository extends HttpRepository { public record PlayerReports( List from_self, - Object to_self + List to_self ) { public record Report( - @Nullable Reporter reported, - @NotNull String subject, - boolean draft, - @NotNull String status, + @Nullable UUID reported, + @NotNull String reason, + @Nullable Long created, + @Nullable Status status, @NotNull String url ) { - public record Reporter( - @NotNull String username, - @NotNull String uuid - ) { + public enum Status { + open, + closed, } } } diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/report/Report.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/report/Report.java index d5b1551..543abe0 100644 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/report/Report.java +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/report/Report.java @@ -6,6 +6,7 @@ import eu.mhsl.craftattack.spawn.core.api.client.ReqResp; import eu.mhsl.craftattack.spawn.common.api.repositories.CraftAttackReportRepository; import eu.mhsl.craftattack.spawn.core.appliance.Appliance; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand; +import eu.mhsl.craftattack.spawn.core.util.text.ComponentUtil; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.ComponentBuilder; import net.kyori.adventure.text.TextComponent; @@ -19,7 +20,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; +import java.util.Objects; import java.util.Optional; +import java.util.function.Function; public class Report extends Appliance { public static Component helpText() { @@ -78,7 +81,7 @@ public class Report extends Appliance { .appendNewline() .append( Component - .text(createdReport.data().url(), NamedTextColor.GRAY) // URL mit Weltkugel-Emoji + .text(createdReport.data().url(), NamedTextColor.GRAY) .clickEvent(ClickEvent.clickEvent(ClickEvent.Action.OPEN_URL, createdReport.data().url())) ) .appendNewline() @@ -128,43 +131,50 @@ public class Report extends Appliance { return; } - List reports = userReports - .data() - .from_self() - .stream() - .filter(report -> !report.draft()) - .toList() - .reversed(); + Function, List> filterClosed = reports -> reports.stream() + .filter(report -> Objects.equals(report.status(), ReportRepository.PlayerReports.Report.Status.closed)) + .toList(); - if(reports.isEmpty()) { - issuer.sendMessage( - Component.text() - .append(Component.text("Du hast noch niemanden reportet.", NamedTextColor.RED)) - .appendNewline() - .append(Component.text("Um jemanden zu melden, nutze /report", NamedTextColor.GRAY)) - ); - return; - } + List reportsToOthers = filterClosed.apply(userReports.data().from_self()).reversed(); + List reportsToSelf = filterClosed.apply(userReports.data().to_self()).reversed(); ComponentBuilder component = Component.text() - .append(Component.newline()) - .append(Component.text("Von dir erstellte Reports: ", NamedTextColor.GOLD)) - .appendNewline(); + .append(Component.text( + !reportsToSelf.isEmpty() + ? "Du wurdest insgesamt %d mal von anderen Spielern gemeldet.".formatted(reportsToSelf.size()) + : "Du wurdest von keinem anderen Spieler gemeldet.", + NamedTextColor.GOLD) + ); - reports.forEach(report -> { - component - .append(Component.text(" - ", NamedTextColor.WHITE)) - .append( - report.reported() != null - ? Component.text(report.reported().username(), NamedTextColor.WHITE) - : Component.text("Unbekannt", NamedTextColor.YELLOW) - ) - .append(Component.text(String.format(": %s", report.subject()), NamedTextColor.GRAY)) + component.appendNewline(); + + component.append(Component.text("Von dir erstellte Reports: ", NamedTextColor.GOLD)); + reportsToOthers.forEach(report -> { + Component button = Component.text("[\uD83D\uDC41/\uD83D\uDD8A]") .clickEvent(ClickEvent.openUrl(report.url())) - .hoverEvent(HoverEvent.showText(Component.text("Klicke, um den Report einzusehen.", NamedTextColor.GOLD))); - component.appendNewline(); + .hoverEvent(HoverEvent.showText(ComponentUtil.clickLink(report.url()))); + + Component reportedDisplayName = report.reported() != null + ? Component.text(Optional.ofNullable(Bukkit.getOfflinePlayer(report.reported()).getName()).orElse(report.reported().toString()), NamedTextColor.WHITE) + : Component.text("Unbekannt", NamedTextColor.YELLOW); + + component + .appendNewline() + .append(Component.text(" \u27A1 ", NamedTextColor.GRAY)) + .append(button) + .append(Component.text(" du gegen ", NamedTextColor.GRAY)) + .append(reportedDisplayName) + .append(Component.text(String.format(": %s", report.reason()), NamedTextColor.GRAY)); }); + if(reportsToOthers.isEmpty()) { + component + .appendNewline() + .append(Component.text("Du hast noch niemanden reportet.", NamedTextColor.RED)) + .appendNewline() + .append(Component.text("Um jemanden zu melden, nutze /report", NamedTextColor.GRAY)); + } + issuer.sendMessage(component.build()); } diff --git a/core/src/main/java/eu/mhsl/craftattack/spawn/core/util/text/ComponentUtil.java b/core/src/main/java/eu/mhsl/craftattack/spawn/core/util/text/ComponentUtil.java index 6be7844..d802b36 100644 --- a/core/src/main/java/eu/mhsl/craftattack/spawn/core/util/text/ComponentUtil.java +++ b/core/src/main/java/eu/mhsl/craftattack/spawn/core/util/text/ComponentUtil.java @@ -5,6 +5,7 @@ import eu.mhsl.craftattack.spawn.core.util.statistics.ServerMonitor; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.ComponentBuilder; import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextColor; import org.bukkit.Bukkit; @@ -19,7 +20,12 @@ import java.util.stream.Stream; public class ComponentUtil { public static TextComponent pleaseWait() { - return Component.text("Bitte warte einen Augenblick...", NamedTextColor.GRAY); + return Component.text("\uD83D\uDCBE Daten werden geladen... Warte einen Augenblick!", NamedTextColor.GRAY); + } + + public static TextComponent clickLink(String url) { + return Component.text("Klicke, um zu öffnen: \uD83D\uDD17[%s]".formatted(url)) + .clickEvent(ClickEvent.openUrl(url)); } public static Component clearedSpace() { diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/api/repositories/FeedbackRepository.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/api/repositories/FeedbackRepository.java index d8243a9..3e551a7 100644 --- a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/api/repositories/FeedbackRepository.java +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/api/repositories/FeedbackRepository.java @@ -1,13 +1,10 @@ package eu.mhsl.craftattack.spawn.craftattack.api.repositories; -import com.google.common.reflect.TypeToken; import eu.mhsl.craftattack.spawn.core.api.client.HttpRepository; import eu.mhsl.craftattack.spawn.core.api.client.ReqResp; import eu.mhsl.craftattack.spawn.common.api.CraftAttackApi; -import java.lang.reflect.Type; import java.util.List; -import java.util.Map; import java.util.UUID; public class FeedbackRepository extends HttpRepository { @@ -15,14 +12,15 @@ public class FeedbackRepository extends HttpRepository { super(CraftAttackApi.getBaseUri(), new RequestModifier(null, CraftAttackApi::withAuthorizationHeader)); } - public record Request(String event, List users) { + public record Request(String event, String title, List users) { } - public ReqResp> createFeedbackUrls(Request data) { - final Type responseType = new TypeToken>() { - }.getType(); - ReqResp rawData = this.post("feedback", data, Object.class); - // TODO: use convertToTypeToken from ReqResp - return new ReqResp<>(rawData.status(), this.gson.fromJson(this.gson.toJson(rawData.data()), responseType)); + public record Response(List feedback) { + public record Feedback(UUID uuid, String url) { + } + } + + public ReqResp createFeedbackUrls(Request data) { + return this.post("feedback", data, Response.class); } } diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/api/repositories/WhitelistRepository.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/api/repositories/WhitelistRepository.java index 8e3eda4..81aea86 100644 --- a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/api/repositories/WhitelistRepository.java +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/api/repositories/WhitelistRepository.java @@ -4,6 +4,7 @@ import eu.mhsl.craftattack.spawn.core.api.client.HttpRepository; import eu.mhsl.craftattack.spawn.core.api.client.ReqResp; import eu.mhsl.craftattack.spawn.common.api.CraftAttackApi; +import java.util.List; import java.util.UUID; public class WhitelistRepository extends HttpRepository { @@ -16,17 +17,15 @@ public class WhitelistRepository extends HttpRepository { String username, String firstname, String lastname, - Long banned_until, - Long outlawed_until + List strikes ) { + public record Strike(int at, int weight) { + } } - private record UserQuery(UUID uuid) {} - public ReqResp getUserData(UUID userId) { - return this.post( - "player", - new UserQuery(userId), + return this.get( + "users/%s".formatted(userId.toString()), UserData.class ); } diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/metaGameplay/feedback/Feedback.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/metaGameplay/feedback/Feedback.java index b588c96..37eb437 100644 --- a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/metaGameplay/feedback/Feedback.java +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/metaGameplay/feedback/Feedback.java @@ -1,7 +1,7 @@ package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.feedback; -import eu.mhsl.craftattack.spawn.core.Main; import eu.mhsl.craftattack.spawn.core.api.client.ReqResp; +import eu.mhsl.craftattack.spawn.core.util.text.ComponentUtil; import eu.mhsl.craftattack.spawn.craftattack.api.repositories.FeedbackRepository; import eu.mhsl.craftattack.spawn.core.appliance.Appliance; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand; @@ -9,7 +9,6 @@ import eu.mhsl.craftattack.spawn.core.api.HttpStatus; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.ComponentBuilder; import net.kyori.adventure.text.TextComponent; -import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.entity.Entity; @@ -18,32 +17,27 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; -import java.util.Map; -import java.util.UUID; public class Feedback extends Appliance { public Feedback() { super("feedback"); } - public void requestFeedback(String eventName, List receivers, @Nullable String question) { - ReqResp> response = this.queryRepository(FeedbackRepository.class).createFeedbackUrls( - new FeedbackRepository.Request(eventName, receivers.stream().map(Entity::getUniqueId).toList()) + public void requestFeedback(String eventName, String title, List receivers, @Nullable String question) { + ReqResp response = this.queryRepository(FeedbackRepository.class).createFeedbackUrls( + new FeedbackRepository.Request(eventName, title, receivers.stream().map(Entity::getUniqueId).toList()) ); - System.out.println(response.toString()); - System.out.println(response.status()); - - if(response.status() != HttpStatus.CREATED) throw new RuntimeException(); + if(response.status() != HttpStatus.OK) throw new RuntimeException(); Component border = Component.text("-".repeat(40), NamedTextColor.GRAY); receivers.forEach(player -> { - String feedbackUrl = response.data().get(player.getUniqueId()); - if(feedbackUrl == null) { - Main.logger().warning(String.format("FeedbackUrl not found for player '%s' from backend!", player.getUniqueId())); - return; - } + String feedbackUrl = response.data().feedback().stream() + .filter(feedback -> feedback.uuid().equals(player.getUniqueId())) + .findFirst() + .orElseThrow() + .url(); ComponentBuilder message = Component.text() .append(border) @@ -58,8 +52,7 @@ public class Feedback extends Appliance { message .append(Component.text("Klicke hier und gib uns Feedback, damit wir dein Spielerlebnis verbessern können!", NamedTextColor.DARK_GREEN) - .clickEvent(ClickEvent.openUrl(feedbackUrl))) - .hoverEvent(HoverEvent.showText(Component.text("Klicke, um Feedback zu geben."))) + .hoverEvent(HoverEvent.showText(ComponentUtil.clickLink(feedbackUrl)))) .appendNewline() .append(border); diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/metaGameplay/feedback/FeedbackCommand.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/metaGameplay/feedback/FeedbackCommand.java index a95f0b0..59abc5e 100644 --- a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/metaGameplay/feedback/FeedbackCommand.java +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/metaGameplay/feedback/FeedbackCommand.java @@ -22,6 +22,7 @@ class FeedbackCommand extends ApplianceCommand.PlayerChecked { Main.instance(), () -> this.getAppliance().requestFeedback( "self-issued-ingame", + "Dein Feedback an uns", List.of(this.getPlayer()), null ) diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/metaGameplay/feedback/RequestFeedbackCommand.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/metaGameplay/feedback/RequestFeedbackCommand.java index bc5df1c..f7be48c 100644 --- a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/metaGameplay/feedback/RequestFeedbackCommand.java +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/metaGameplay/feedback/RequestFeedbackCommand.java @@ -20,6 +20,7 @@ class RequestFeedbackCommand extends ApplianceCommand { Main.instance(), () -> this.getAppliance().requestFeedback( "admin-issued-ingame", + "Hilf uns dein Spielerlebnis zu verbessern!", new ArrayList<>(Bukkit.getOnlinePlayers()), String.join(" ", args) ) ); diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/strike/Strike.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/strike/Strike.java new file mode 100644 index 0000000..59e5010 --- /dev/null +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/strike/Strike.java @@ -0,0 +1,21 @@ +package eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.strike; + +import eu.mhsl.craftattack.spawn.core.appliance.Appliance; + +import java.time.Duration; +import java.util.Map; + +public class Strike extends Appliance { + public Strike() { + super("strike"); + } + + private final Map strikePunishmentMap = Map.of( + 1, Duration.ofHours(1), + 2, Duration.ofHours(24), + 3, Duration.ofDays(3), + 4, Duration.ofDays(7) + ); + + +} diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/whitelist/Whitelist.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/whitelist/Whitelist.java index be76411..0e55b0a 100644 --- a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/whitelist/Whitelist.java +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/whitelist/Whitelist.java @@ -47,7 +47,7 @@ public class Whitelist extends Appliance { player.getUniqueId() ); } - this.queryAppliance(Outlawed.class).updateForcedStatus(player, this.timestampRelevant(user.outlawed_until())); + this.queryAppliance(Outlawed.class).updateForcedStatus(player, this.timestampRelevant(0L)); // TODO String purePlayerName = Floodgate.isBedrock(player) ? Floodgate.getBedrockPlayer(player).getUsername() @@ -67,14 +67,14 @@ public class Whitelist extends Appliance { Main.instance().getLogger().info(String.format("Running integrityCheck for %s", name)); boolean overrideCheck = this.localConfig().getBoolean("overrideIntegrityCheck", false); WhitelistRepository.UserData user = overrideCheck - ? new WhitelistRepository.UserData(uuid, name, "", "", 0L, 0L) + ? new WhitelistRepository.UserData(uuid, name, "", "", List.of()) : this.fetchUserData(uuid); this.userData.put(uuid, user); Main.logger().info(String.format("got userdata %s", user.toString())); - if(this.timestampRelevant(user.banned_until())) { - Instant bannedDate = new Date(user.banned_until() * 1000L) + if(this.timestampRelevant(0L)) { //TODO + Instant bannedDate = new Date(0 * 1000L) // TODO .toInstant() .plus(1, ChronoUnit.HOURS); From 239094971c9f78d68518ab4d60fea00ede312c1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Sun, 2 Nov 2025 14:18:43 +0100 Subject: [PATCH 2/4] Revert "simplified event message handling logic in `ChatMessagesListener`" This reverts commit db13a9f0a2413df7178c5d5d27d78cfe5bde12ff. --- .../chatMessages/ChatMessagesListener.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMessages/ChatMessagesListener.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMessages/ChatMessagesListener.java index 740ed52..561c5bf 100644 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMessages/ChatMessagesListener.java +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMessages/ChatMessagesListener.java @@ -21,17 +21,18 @@ class ChatMessagesListener extends ApplianceListener { public void onPlayerChatEvent(AsyncChatEvent event) { event.renderer( (source, sourceDisplayName, message, viewer) -> - Component.text() + Component.text("") .append(this.getAppliance().getReportablePlayerName(source)) .append(Component.text(" > ").color(TextColor.color(Color.GRAY.asRGB()))) .append(message).color(TextColor.color(Color.SILVER.asRGB())) - .build() ); } @EventHandler(priority = EventPriority.HIGH) public void onPlayerJoin(PlayerJoinEvent event) { - if(event.joinMessage() == null) return; + boolean wasHidden = event.joinMessage() == null; + event.joinMessage(null); + if(wasHidden) return; IteratorUtil.onlinePlayers(player -> { if(!Settings.instance().getSetting(player, Settings.Key.ShowJoinAndLeaveMessages, Boolean.class)) return; player.sendMessage( @@ -44,7 +45,9 @@ class ChatMessagesListener extends ApplianceListener { @EventHandler public void onPlayerLeave(PlayerQuitEvent event) { - if(event.quitMessage() == null) return; + boolean wasHidden = event.quitMessage() == null; + event.quitMessage(null); + if(wasHidden) return; IteratorUtil.onlinePlayers(player -> { if(!Settings.instance().getSetting(player, Settings.Key.ShowJoinAndLeaveMessages, Boolean.class)) return; player.sendMessage( From 29a362b5809e6e7ed246978fcfc75e3e47b62590 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Sun, 9 Nov 2025 16:08:28 +0100 Subject: [PATCH 3/4] updated default shortcut setting value --- .../common/appliances/metaGameplay/infoBars/InfoBarSetting.java | 2 -- .../metaGameplay/settings/SettingsShortcutSetting.java | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/infoBars/InfoBarSetting.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/infoBars/InfoBarSetting.java index 6439b11..23214ad 100644 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/infoBars/InfoBarSetting.java +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/infoBars/InfoBarSetting.java @@ -46,6 +46,4 @@ public class InfoBarSetting extends MultiBoolSetting dataType() { return InfoBarConfiguration.class; } - - } diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/settings/SettingsShortcutSetting.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/settings/SettingsShortcutSetting.java index 4737ba4..7c15f64 100644 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/settings/SettingsShortcutSetting.java +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/settings/SettingsShortcutSetting.java @@ -25,7 +25,7 @@ public class SettingsShortcutSetting extends BoolSetting implements CategorizedS @Override protected Boolean defaultValue() { - return false; + return true; } @Override From 0ab67bb4263e3f4b46e8a01dd0ae453e0c31fbb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Sun, 9 Nov 2025 19:13:08 +0100 Subject: [PATCH 4/4] refactored strike handling logic; added ban determination and updated whitelist integration --- .../api/repositories/WhitelistRepository.java | 2 +- .../appliances/tooling/strike/Strike.java | 21 -------- .../appliances/tooling/strikes/Strikes.java | 48 +++++++++++++++++++ .../tooling/whitelist/Whitelist.java | 43 ++++++++--------- 4 files changed, 70 insertions(+), 44 deletions(-) delete mode 100644 craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/strike/Strike.java create mode 100644 craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/strikes/Strikes.java diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/api/repositories/WhitelistRepository.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/api/repositories/WhitelistRepository.java index 81aea86..cad137e 100644 --- a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/api/repositories/WhitelistRepository.java +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/api/repositories/WhitelistRepository.java @@ -19,7 +19,7 @@ public class WhitelistRepository extends HttpRepository { String lastname, List strikes ) { - public record Strike(int at, int weight) { + public record Strike(long at, int weight) { } } diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/strike/Strike.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/strike/Strike.java deleted file mode 100644 index 59e5010..0000000 --- a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/strike/Strike.java +++ /dev/null @@ -1,21 +0,0 @@ -package eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.strike; - -import eu.mhsl.craftattack.spawn.core.appliance.Appliance; - -import java.time.Duration; -import java.util.Map; - -public class Strike extends Appliance { - public Strike() { - super("strike"); - } - - private final Map strikePunishmentMap = Map.of( - 1, Duration.ofHours(1), - 2, Duration.ofHours(24), - 3, Duration.ofDays(3), - 4, Duration.ofDays(7) - ); - - -} diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/strikes/Strikes.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/strikes/Strikes.java new file mode 100644 index 0000000..b52ba90 --- /dev/null +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/strikes/Strikes.java @@ -0,0 +1,48 @@ +package eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.strikes; + +import eu.mhsl.craftattack.spawn.core.appliance.Appliance; +import eu.mhsl.craftattack.spawn.craftattack.api.repositories.WhitelistRepository; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.time.Duration; +import java.time.Instant; +import java.util.List; +import java.util.Map; + +public class Strikes extends Appliance { + public Strikes() { + super("strike"); + } + + private static final BanInfo falseInfo = new BanInfo(false, null, false); + public record BanInfo(boolean isBanned, @Nullable Instant bannedUntil, boolean isPermanent) { + } + + private final Map strikePunishmentMap = Map.of( + 1, Duration.ofHours(1), + 2, Duration.ofHours(24), + 3, Duration.ofDays(3), + 4, Duration.ofDays(7) + ); + + + public BanInfo isBanned(List strikes) { + if (strikes.isEmpty()) return falseInfo; + + int strikeCount = strikes.stream().mapToInt(WhitelistRepository.UserData.Strike::weight).sum(); + if (strikeCount < 1) return falseInfo; + if (strikeCount > this.strikePunishmentMap.size()) return new BanInfo(true, null, true); + + Instant lastPunishment = strikes.stream() + .map(strike -> Instant.ofEpochMilli(strike.at())) + .max(Instant::compareTo) + .orElse(Instant.EPOCH); + + Duration duration = this.strikePunishmentMap.get(strikeCount); + Instant bannedUntil = lastPunishment.plus(duration); + + boolean stillBanned = bannedUntil.isAfter(Instant.now()); + + return new BanInfo(stillBanned, bannedUntil, false); + } +} diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/whitelist/Whitelist.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/whitelist/Whitelist.java index 0e55b0a..0e5d690 100644 --- a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/whitelist/Whitelist.java +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/tooling/whitelist/Whitelist.java @@ -8,21 +8,18 @@ import eu.mhsl.craftattack.spawn.core.appliance.Appliance; import eu.mhsl.craftattack.spawn.core.api.HttpStatus; import eu.mhsl.craftattack.spawn.core.util.server.Floodgate; import eu.mhsl.craftattack.spawn.core.util.text.DisconnectInfo; -import eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.outlawed.Outlawed; +import eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.strikes.Strikes; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.Listener; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.time.Instant; -import java.time.ZoneOffset; +import javax.inject.Provider; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; -import java.time.temporal.ChronoUnit; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.UUID; +import java.util.*; import java.util.logging.Level; public class Whitelist extends Appliance { @@ -47,7 +44,6 @@ public class Whitelist extends Appliance { player.getUniqueId() ); } - this.queryAppliance(Outlawed.class).updateForcedStatus(player, this.timestampRelevant(0L)); // TODO String purePlayerName = Floodgate.isBedrock(player) ? Floodgate.getBedrockPlayer(player).getUsername() @@ -73,17 +69,25 @@ public class Whitelist extends Appliance { this.userData.put(uuid, user); Main.logger().info(String.format("got userdata %s", user.toString())); - if(this.timestampRelevant(0L)) { //TODO - Instant bannedDate = new Date(0 * 1000L) // TODO - .toInstant() - .plus(1, ChronoUnit.HOURS); + Strikes.BanInfo banInfo = Main.instance().getAppliance(Strikes.class).isBanned(user.strikes()); + Main.logger().info(String.format("got baninfo %s", banInfo.toString())); - DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd.MM.yyyy").withZone(ZoneOffset.UTC); - DateTimeFormatter timeFormat = DateTimeFormatter.ofPattern("HH:mm").withZone(ZoneOffset.UTC); + if (banInfo.isBanned()) { + Provider zoned = () -> { + Objects.requireNonNull(banInfo.bannedUntil(), "Cannot get zoned date time from null"); + return banInfo.bannedUntil().atZone(ZoneId.of("Europe/Berlin")); + }; + + DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd.MM.yyyy"); + DateTimeFormatter timeFormat = DateTimeFormatter.ofPattern("HH:mm"); + + String unbanText = banInfo.bannedUntil() != null + ? String.format("Dein Bann läuft am %s um %s ab!", dateFormat.format(zoned.get()), timeFormat.format(zoned.get())) + : "Bandauer ist unbekannt."; throw new DisconnectInfo.Throwable( "Du wurdest vom Server gebannt.", - String.format("Dein Bann läuft am %s um %s ab!", dateFormat.format(bannedDate), timeFormat.format(bannedDate)), + banInfo.isPermanent() ? "Dein Bann läuft nicht ab!" : unbanText, "Wende dich an einen Admin für weitere Informationen.", uuid ); @@ -94,18 +98,13 @@ public class Whitelist extends Appliance { Main.instance().getLogger().log(Level.SEVERE, e, e::getMessage); throw new DisconnectInfo.Throwable( "Interner Serverfehler", - "Deine Anmeldedaten konnten nicht abgerufen/ überprüft werden.", + "Deine Anmeldedaten konnten nicht abgerufen/überprüft werden.", "Versuche es später erneut oder kontaktiere einen Admin!", uuid ); } } - private boolean timestampRelevant(Long timestamp) { - if(timestamp == null) return false; - return timestamp > System.currentTimeMillis() / 1000L; - } - private WhitelistRepository.UserData fetchUserData(UUID uuid) throws DisconnectInfo.Throwable { ReqResp response = this.queryRepository(WhitelistRepository.class).getUserData(uuid);