From c059880eceb3824c78f0cd01a25af08fdc68f427 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Elias=20M=C3=BCller?= <elias@elias-mueller.com>
Date: Sun, 10 Dec 2023 00:22:14 +0100
Subject: [PATCH] Cleanup and refactoring

---
 .../spawn/appliances/event/Event.java         |  8 ++--
 .../appliances/playerlimit/PlayerLimit.java   |  5 +--
 .../playerlimit/PlayerLimiterListener.java    | 24 +++++++++++
 .../{command => }/SetPlayerLimitCommand.java  |  2 +-
 .../listener/PlayerJoinLimiterListener.java   | 18 --------
 .../appliances/projectStart/ProjectStart.java |  6 +--
 .../spawn/appliances/report/Report.java       |  3 +-
 .../report/{command => }/ReportCommand.java   |  2 +-
 .../spawn/appliances/restart/Restart.java     | 20 ++++-----
 .../spawn/appliances/tablist/Tablist.java     | 34 ++++++++-------
 .../appliances/titleClear/TitleClear.java     |  4 ++
 .../titleClear/TitleClearListener.java        |  2 +-
 .../appliances/whitelist/LoginException.java  | 18 --------
 .../whitelist/PlayerJoinListener.java         | 13 ++----
 .../spawn/appliances/whitelist/Whitelist.java | 25 +++++------
 .../MoveWorldMuseumVillagerCommand.java       |  2 +-
 .../appliances/worldmuseum/WorldMuseum.java   |  9 ++--
 .../util/{ => entity}/DisplayVillager.java    |  3 +-
 .../DismissInventoryOpenFromHolder.java       |  2 +-
 .../PlayerInteractAtEntityEventListener.java  |  2 +-
 .../spawn/util/{ => server}/Floodgate.java    |  2 +-
 .../util/{ => server}/PluginMessage.java      |  2 +-
 .../spawn/util/{ => text}/ColorUtil.java      |  3 +-
 .../spawn/util/{ => text}/ComponentUtil.java  |  2 +-
 .../spawn/util/{ => text}/Countdown.java      |  2 +-
 .../spawn/util/text/DisconnectInfo.java       | 41 +++++++++++++++++++
 .../util/{ => text}/RainbowComponent.java     |  2 +-
 .../spawn/util/{ => world}/BlockCycle.java    |  2 +-
 .../spawn/util/{ => world}/ChunkUtils.java    |  2 +-
 29 files changed, 140 insertions(+), 120 deletions(-)
 create mode 100644 src/main/java/eu/mhsl/craftattack/spawn/appliances/playerlimit/PlayerLimiterListener.java
 rename src/main/java/eu/mhsl/craftattack/spawn/appliances/playerlimit/{command => }/SetPlayerLimitCommand.java (90%)
 delete mode 100644 src/main/java/eu/mhsl/craftattack/spawn/appliances/playerlimit/listener/PlayerJoinLimiterListener.java
 rename src/main/java/eu/mhsl/craftattack/spawn/appliances/report/{command => }/ReportCommand.java (97%)
 delete mode 100644 src/main/java/eu/mhsl/craftattack/spawn/appliances/whitelist/LoginException.java
 rename src/main/java/eu/mhsl/craftattack/spawn/appliances/worldmuseum/{command => }/MoveWorldMuseumVillagerCommand.java (91%)
 rename src/main/java/eu/mhsl/craftattack/spawn/util/{ => entity}/DisplayVillager.java (96%)
 rename src/main/java/eu/mhsl/craftattack/spawn/util/{commonListeners => listener}/DismissInventoryOpenFromHolder.java (92%)
 rename src/main/java/eu/mhsl/craftattack/spawn/util/{commonListeners => listener}/PlayerInteractAtEntityEventListener.java (93%)
 rename src/main/java/eu/mhsl/craftattack/spawn/util/{ => server}/Floodgate.java (93%)
 rename src/main/java/eu/mhsl/craftattack/spawn/util/{ => server}/PluginMessage.java (91%)
 rename src/main/java/eu/mhsl/craftattack/spawn/util/{ => text}/ColorUtil.java (82%)
 rename src/main/java/eu/mhsl/craftattack/spawn/util/{ => text}/ComponentUtil.java (98%)
 rename src/main/java/eu/mhsl/craftattack/spawn/util/{ => text}/Countdown.java (98%)
 create mode 100644 src/main/java/eu/mhsl/craftattack/spawn/util/text/DisconnectInfo.java
 rename src/main/java/eu/mhsl/craftattack/spawn/util/{ => text}/RainbowComponent.java (95%)
 rename src/main/java/eu/mhsl/craftattack/spawn/util/{ => world}/BlockCycle.java (94%)
 rename src/main/java/eu/mhsl/craftattack/spawn/util/{ => world}/ChunkUtils.java (93%)

diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/event/Event.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/event/Event.java
index 924949c..72c8c11 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/event/Event.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/event/Event.java
@@ -7,10 +7,10 @@ import eu.mhsl.craftattack.spawn.appliances.event.command.EventCommand;
 import eu.mhsl.craftattack.spawn.appliances.event.command.EventEndSessionCommand;
 import eu.mhsl.craftattack.spawn.appliances.event.command.EventOpenSessionCommand;
 import eu.mhsl.craftattack.spawn.appliances.event.command.MoveEventVillagerCommand;
-import eu.mhsl.craftattack.spawn.util.DisplayVillager;
-import eu.mhsl.craftattack.spawn.util.PluginMessage;
-import eu.mhsl.craftattack.spawn.util.commonListeners.DismissInventoryOpenFromHolder;
-import eu.mhsl.craftattack.spawn.util.commonListeners.PlayerInteractAtEntityEventListener;
+import eu.mhsl.craftattack.spawn.util.entity.DisplayVillager;
+import eu.mhsl.craftattack.spawn.util.server.PluginMessage;
+import eu.mhsl.craftattack.spawn.util.listener.DismissInventoryOpenFromHolder;
+import eu.mhsl.craftattack.spawn.util.listener.PlayerInteractAtEntityEventListener;
 import net.kyori.adventure.text.Component;
 import net.kyori.adventure.text.format.NamedTextColor;
 import org.bukkit.entity.Player;
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/playerlimit/PlayerLimit.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/playerlimit/PlayerLimit.java
index a2d72ab..9ab5e31 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/playerlimit/PlayerLimit.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/playerlimit/PlayerLimit.java
@@ -1,10 +1,7 @@
 package eu.mhsl.craftattack.spawn.appliances.playerlimit;
 
-import eu.mhsl.craftattack.spawn.Main;
 import eu.mhsl.craftattack.spawn.appliance.Appliance;
 import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
-import eu.mhsl.craftattack.spawn.appliances.playerlimit.command.SetPlayerLimitCommand;
-import eu.mhsl.craftattack.spawn.appliances.playerlimit.listener.PlayerJoinLimiterListener;
 import eu.mhsl.craftattack.spawn.config.Configuration;
 import org.bukkit.Bukkit;
 import org.bukkit.event.Listener;
@@ -34,7 +31,7 @@ public class PlayerLimit extends Appliance {
     @Override
     protected @NotNull List<Listener> eventHandlers() {
         return List.of(
-                new PlayerJoinLimiterListener()
+                new PlayerLimiterListener()
         );
     }
 
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/playerlimit/PlayerLimiterListener.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/playerlimit/PlayerLimiterListener.java
new file mode 100644
index 0000000..a1010fb
--- /dev/null
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/playerlimit/PlayerLimiterListener.java
@@ -0,0 +1,24 @@
+package eu.mhsl.craftattack.spawn.appliances.playerlimit;
+
+import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
+import eu.mhsl.craftattack.spawn.util.text.DisconnectInfo;
+import org.bukkit.Bukkit;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
+
+public class PlayerLimiterListener extends ApplianceListener<PlayerLimit> {
+    @EventHandler
+    public void onLogin(AsyncPlayerPreLoginEvent playerPreLoginEvent) {
+        playerPreLoginEvent.kickMessage(
+                new DisconnectInfo(
+                        "Hohe Serverauslastung",
+                        "Der Server ist momentan an seiner Kapazitätsgrenze angelangt!",
+                        "Bitte versuche es zu einem späteren Zeitpunkt erneut.",
+                        playerPreLoginEvent.getUniqueId()
+                ).getComponent()
+        );
+
+        if(Bukkit.getOnlinePlayers().size() >= getAppliance().getLimit())
+            playerPreLoginEvent.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_FULL);
+    }
+}
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/playerlimit/command/SetPlayerLimitCommand.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/playerlimit/SetPlayerLimitCommand.java
similarity index 90%
rename from src/main/java/eu/mhsl/craftattack/spawn/appliances/playerlimit/command/SetPlayerLimitCommand.java
rename to src/main/java/eu/mhsl/craftattack/spawn/appliances/playerlimit/SetPlayerLimitCommand.java
index 4d90408..f767eb7 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/playerlimit/command/SetPlayerLimitCommand.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/playerlimit/SetPlayerLimitCommand.java
@@ -1,4 +1,4 @@
-package eu.mhsl.craftattack.spawn.appliances.playerlimit.command;
+package eu.mhsl.craftattack.spawn.appliances.playerlimit;
 
 import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
 import eu.mhsl.craftattack.spawn.appliances.playerlimit.PlayerLimit;
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/playerlimit/listener/PlayerJoinLimiterListener.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/playerlimit/listener/PlayerJoinLimiterListener.java
deleted file mode 100644
index aa5383e..0000000
--- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/playerlimit/listener/PlayerJoinLimiterListener.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package eu.mhsl.craftattack.spawn.appliances.playerlimit.listener;
-
-import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
-import eu.mhsl.craftattack.spawn.appliances.playerlimit.PlayerLimit;
-import net.kyori.adventure.text.Component;
-import net.kyori.adventure.text.format.NamedTextColor;
-import org.bukkit.Bukkit;
-import org.bukkit.event.EventHandler;
-import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
-import org.bukkit.event.player.PlayerLoginEvent;
-
-public class PlayerJoinLimiterListener extends ApplianceListener<PlayerLimit> {
-    @EventHandler
-    public void onLogin(PlayerLoginEvent playerPreLoginEvent) {
-        playerPreLoginEvent.kickMessage(Component.text("Der Server ist derzeit voll! Versuche es bitte später erneut.", NamedTextColor.RED));
-        if(Bukkit.getOnlinePlayers().size() >= getAppliance().getLimit()) playerPreLoginEvent.setResult(PlayerLoginEvent.Result.KICK_FULL);
-    }
-}
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/projectStart/ProjectStart.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/projectStart/ProjectStart.java
index b84996d..1da05ff 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/projectStart/ProjectStart.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/projectStart/ProjectStart.java
@@ -7,9 +7,9 @@ import eu.mhsl.craftattack.spawn.appliances.projectStart.command.ProjectStartCom
 import eu.mhsl.craftattack.spawn.appliances.projectStart.command.ProjectStartResetCommand;
 import eu.mhsl.craftattack.spawn.appliances.projectStart.listener.PlayerInvincibleListener;
 import eu.mhsl.craftattack.spawn.config.Configuration;
-import eu.mhsl.craftattack.spawn.util.BlockCycle;
-import eu.mhsl.craftattack.spawn.util.ComponentUtil;
-import eu.mhsl.craftattack.spawn.util.Countdown;
+import eu.mhsl.craftattack.spawn.util.world.BlockCycle;
+import eu.mhsl.craftattack.spawn.util.text.ComponentUtil;
+import eu.mhsl.craftattack.spawn.util.text.Countdown;
 import eu.mhsl.craftattack.spawn.util.IteratorUtil;
 import net.kyori.adventure.sound.Sound;
 import net.kyori.adventure.text.Component;
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/report/Report.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/report/Report.java
index 1612b46..9cc227d 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/report/Report.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/report/Report.java
@@ -3,7 +3,6 @@ package eu.mhsl.craftattack.spawn.appliances.report;
 import com.google.gson.Gson;
 import eu.mhsl.craftattack.spawn.appliance.Appliance;
 import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
-import eu.mhsl.craftattack.spawn.appliances.report.command.ReportCommand;
 import net.kyori.adventure.text.Component;
 import net.kyori.adventure.text.event.ClickEvent;
 import net.kyori.adventure.text.event.HoverEvent;
@@ -75,7 +74,7 @@ public class Report extends Appliance {
             this.printResultMessage(issuer, httpResponse);
         } catch (IOException | InterruptedException e) {
             issuer.sendMessage(
-                    Component.text("Internal server error: " + e.getMessage()).color(NamedTextColor.RED)
+                    Component.text("Internal server description: " + e.getMessage()).color(NamedTextColor.RED)
             );
             throw new RuntimeException(e);
         }
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/report/command/ReportCommand.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/report/ReportCommand.java
similarity index 97%
rename from src/main/java/eu/mhsl/craftattack/spawn/appliances/report/command/ReportCommand.java
rename to src/main/java/eu/mhsl/craftattack/spawn/appliances/report/ReportCommand.java
index ec23e65..d4c93e2 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/report/command/ReportCommand.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/report/ReportCommand.java
@@ -1,4 +1,4 @@
-package eu.mhsl.craftattack.spawn.appliances.report.command;
+package eu.mhsl.craftattack.spawn.appliances.report;
 
 import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
 import eu.mhsl.craftattack.spawn.appliances.report.Report;
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/restart/Restart.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/restart/Restart.java
index 69347aa..d3a0edb 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/restart/Restart.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/restart/Restart.java
@@ -4,7 +4,8 @@ import eu.mhsl.craftattack.spawn.appliance.Appliance;
 import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
 import eu.mhsl.craftattack.spawn.appliances.restart.command.CancelRestartCommand;
 import eu.mhsl.craftattack.spawn.appliances.restart.command.ScheduleRestartCommand;
-import eu.mhsl.craftattack.spawn.util.Countdown;
+import eu.mhsl.craftattack.spawn.util.text.Countdown;
+import eu.mhsl.craftattack.spawn.util.text.DisconnectInfo;
 import eu.mhsl.craftattack.spawn.util.IteratorUtil;
 import net.kyori.adventure.text.Component;
 import net.kyori.adventure.text.format.NamedTextColor;
@@ -49,16 +50,13 @@ public class Restart extends Appliance {
     }
 
     private void onDone() {
-        IteratorUtil.onlinePlayers(player -> {
-            player.kick(
-                    Component.text()
-                            .appendNewline().appendNewline()
-                            .append(Component.text("Serverneustart", NamedTextColor.DARK_RED)).appendNewline().appendNewline()
-                            .append(Component.text("Wir sind gleich wieder online!", NamedTextColor.GOLD)).appendNewline()
-                            .append(Component.text("Verbinde Dich dann erneut.", NamedTextColor.GRAY)).appendNewline()
-                            .build()
-            );
-        });
+        IteratorUtil.onlinePlayers(
+            player -> new DisconnectInfo(
+                "Serverneustart",
+                "Wir sind gleich wieder online!",
+                "Verbinde Dich dann erneut.",
+                player.getUniqueId()
+            ).applyKick(player));
         Bukkit.shutdown();
     }
 
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/tablist/Tablist.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/tablist/Tablist.java
index 7e2baf3..19c6e77 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/tablist/Tablist.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/tablist/Tablist.java
@@ -3,8 +3,9 @@ package eu.mhsl.craftattack.spawn.appliances.tablist;
 import eu.mhsl.craftattack.spawn.Main;
 import eu.mhsl.craftattack.spawn.appliance.Appliance;
 import eu.mhsl.craftattack.spawn.appliances.report.Report;
-import eu.mhsl.craftattack.spawn.util.ComponentUtil;
-import eu.mhsl.craftattack.spawn.util.RainbowComponent;
+import eu.mhsl.craftattack.spawn.util.text.ComponentUtil;
+import eu.mhsl.craftattack.spawn.util.IteratorUtil;
+import eu.mhsl.craftattack.spawn.util.text.RainbowComponent;
 import net.kyori.adventure.text.Component;
 import net.kyori.adventure.text.format.NamedTextColor;
 import net.kyori.adventure.util.Ticks;
@@ -18,13 +19,16 @@ import java.util.List;
 
 public class Tablist extends Appliance {
     private final int refreshRate = Ticks.TICKS_PER_SECOND * 3;
-
     private final RainbowComponent serverName = new RainbowComponent(" CraftAttack 6 ", 7, 3);
-    private final Component hostName = Component.text("mhsl.eu").color(NamedTextColor.GOLD);
+
     @Override
     public void onEnable() {
-        Runnable updateAll = () -> Bukkit.getOnlinePlayers().forEach(this::update);
-        Bukkit.getScheduler().runTaskTimerAsynchronously(Main.instance(), updateAll, refreshRate, refreshRate);
+        Bukkit.getScheduler().runTaskTimerAsynchronously(
+                Main.instance(),
+                () -> IteratorUtil.onlinePlayers(this::update),
+                refreshRate,
+                refreshRate
+        );
     }
 
     public void update(Player player) {
@@ -32,23 +36,17 @@ public class Tablist extends Appliance {
         updateFooter(player);
     }
 
-    public void updateHeader(Player player) {
+    private void updateHeader(Player player) {
         player.sendPlayerListHeader(
                 Component.newline()
-                        .append(serverName.getRainbowState())
-                        .appendNewline()
-                        .append(hostName)
-                        .appendNewline()
-                        .appendNewline()
-                        .append(ComponentUtil.getFormattedMSPT())
-                        .appendNewline()
-                        .appendNewline()
-                        .append(ComponentUtil.getFormattedPing(player))
-                        .appendNewline()
+                        .append(serverName.getRainbowState()).appendNewline()
+                        .append(Component.text("mhsl.eu", NamedTextColor.GOLD)).appendNewline().appendNewline()
+                        .append(ComponentUtil.getFormattedMSPT()).appendNewline().appendNewline()
+                        .append(ComponentUtil.getFormattedPing(player)).appendNewline()
         );
     }
 
-    public void updateFooter(Player player) {
+    private void updateFooter(Player player) {
         player.sendPlayerListFooter(Report.helpText());
     }
 
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/titleClear/TitleClear.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/titleClear/TitleClear.java
index aea4652..9088402 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/titleClear/TitleClear.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/titleClear/TitleClear.java
@@ -1,12 +1,16 @@
 package eu.mhsl.craftattack.spawn.appliances.titleClear;
 
 import eu.mhsl.craftattack.spawn.appliance.Appliance;
+import org.bukkit.entity.Player;
 import org.bukkit.event.Listener;
 import org.jetbrains.annotations.NotNull;
 
 import java.util.List;
 
 public class TitleClear extends Appliance {
+    public void clearTitle(Player player) {
+        player.clearTitle();
+    }
     @Override
     protected @NotNull List<Listener> eventHandlers() {
         return List.of(
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/titleClear/TitleClearListener.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/titleClear/TitleClearListener.java
index fc20ae8..81996ab 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/titleClear/TitleClearListener.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/titleClear/TitleClearListener.java
@@ -7,6 +7,6 @@ import org.bukkit.event.player.PlayerJoinEvent;
 public class TitleClearListener extends ApplianceListener<TitleClear> {
     @EventHandler
     public void onPlayerJoin(PlayerJoinEvent event) {
-        event.getPlayer().resetTitle();
+        getAppliance().clearTitle(event.getPlayer());
     }
 }
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/whitelist/LoginException.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/whitelist/LoginException.java
deleted file mode 100644
index c84c513..0000000
--- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/whitelist/LoginException.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package eu.mhsl.craftattack.spawn.appliances.whitelist;
-
-import java.util.UUID;
-
-public class LoginException extends Exception {
-    public String title;
-    public String error;
-    public UUID user;
-    public String solution;
-
-    public LoginException(String title, String error, UUID user, String solution) {
-        super(error);
-        this.title = title;
-        this.error = error;
-        this.user = user;
-        this.solution = solution;
-    }
-}
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/whitelist/PlayerJoinListener.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/whitelist/PlayerJoinListener.java
index 12254fb..9aed863 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/whitelist/PlayerJoinListener.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/whitelist/PlayerJoinListener.java
@@ -1,8 +1,7 @@
 package eu.mhsl.craftattack.spawn.appliances.whitelist;
 
 import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
-import net.kyori.adventure.text.Component;
-import net.kyori.adventure.text.format.NamedTextColor;
+import eu.mhsl.craftattack.spawn.util.text.DisconnectInfo;
 import org.bukkit.event.EventHandler;
 import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
 
@@ -11,16 +10,10 @@ public class PlayerJoinListener extends ApplianceListener<Whitelist> {
     public void preLoginEvent(AsyncPlayerPreLoginEvent preLoginEvent) {
         try {
             getAppliance().login(preLoginEvent);
-        } catch (LoginException e) {
+        } catch (DisconnectInfo.Throwable e) {
             preLoginEvent.disallow(
                     AsyncPlayerPreLoginEvent.Result.KICK_WHITELIST,
-                    Component.text()
-                            .appendNewline().appendNewline()
-                            .append(Component.text(e.title, NamedTextColor.DARK_RED)).appendNewline()
-                            .append(Component.text(e.error, NamedTextColor.RED)).appendNewline().appendNewline()
-                            .append(Component.text(e.solution, NamedTextColor.GRAY)).appendNewline().appendNewline()
-                            .append(Component.text(e.user.toString(), NamedTextColor.DARK_GRAY)).appendNewline()
-                            .build()
+                    e.getDisconnectScreen().getComponent()
             );
         }
     }
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/whitelist/Whitelist.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/whitelist/Whitelist.java
index a1def1d..2d17e06 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/whitelist/Whitelist.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/whitelist/Whitelist.java
@@ -2,6 +2,7 @@ package eu.mhsl.craftattack.spawn.appliances.whitelist;
 
 import com.google.gson.Gson;
 import eu.mhsl.craftattack.spawn.appliance.Appliance;
+import eu.mhsl.craftattack.spawn.util.text.DisconnectInfo;
 import org.apache.http.client.utils.URIBuilder;
 import org.bukkit.event.Listener;
 import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
@@ -26,31 +27,31 @@ public class Whitelist extends Appliance {
         super("whitelist");
     }
 
-    public void login(AsyncPlayerPreLoginEvent login) throws LoginException {
+    public void login(AsyncPlayerPreLoginEvent login) throws DisconnectInfo.Throwable {
         try {
             UserData data = this.fetchUserData(login.getUniqueId());
 
             if(!data.username.equalsIgnoreCase(login.getName()))
-                throw new LoginException(
+                throw new DisconnectInfo.Throwable(
                         "Nutzername geändert",
                         String.format("Der Name '%s' stimmt nicht mit '%s' überein.", data.username, login.getName()),
-                        login.getUniqueId(),
-                        "Bitte kontaktiere einen Admin, um Deine Anmeldedaten zu aktualisieren!"
+                        "Bitte kontaktiere einen Admin, um Deine Anmeldedaten zu aktualisieren!",
+                        login.getUniqueId()
                 );
 
-        } catch (LoginException e) {
+        } catch (DisconnectInfo.Throwable e) {
             throw e;
         } catch (Exception e) {
-            throw new LoginException(
+            throw new DisconnectInfo.Throwable(
                     "Interner Serverfehler",
                     "Deine Zugangsdaten konnten nicht abgerufen werden.",
-                    login.getUniqueId(),
-                    "Versuche es später erneut oder kontaktiere einen Admin!"
+                    "Versuche es später erneut oder kontaktiere einen Admin!",
+                    login.getUniqueId()
             );
         }
     }
 
-    private UserData fetchUserData(UUID uuid) throws LoginException {
+    private UserData fetchUserData(UUID uuid) throws DisconnectInfo.Throwable {
         URIBuilder uriBuilder = new URIBuilder(apiEndpoint);
         uriBuilder.addParameter("uuid", uuid.toString());
 
@@ -65,11 +66,11 @@ public class Whitelist extends Appliance {
             HttpResponse<String> httpResponse = client.send(httpRequest, HttpResponse.BodyHandlers.ofString());
 
             if(httpResponse.statusCode() == 400)
-                throw new LoginException(
+                throw new DisconnectInfo.Throwable(
                         "Nicht angemeldet",
                         "Du bist derzeit nicht als Teilnehmer des CraftAttack-Projektes registriert!",
-                        uuid,
-                        "Melde Dich bei einem Admin für eine nachträgliche Anmeldung."
+                        "Melde Dich bei einem Admin für eine nachträgliche Anmeldung.",
+                        uuid
                 );
 
             return new Gson().fromJson(httpResponse.body(), UserData.class);
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/worldmuseum/command/MoveWorldMuseumVillagerCommand.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/worldmuseum/MoveWorldMuseumVillagerCommand.java
similarity index 91%
rename from src/main/java/eu/mhsl/craftattack/spawn/appliances/worldmuseum/command/MoveWorldMuseumVillagerCommand.java
rename to src/main/java/eu/mhsl/craftattack/spawn/appliances/worldmuseum/MoveWorldMuseumVillagerCommand.java
index ad3560a..4b1d713 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/worldmuseum/command/MoveWorldMuseumVillagerCommand.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/worldmuseum/MoveWorldMuseumVillagerCommand.java
@@ -1,4 +1,4 @@
-package eu.mhsl.craftattack.spawn.appliances.worldmuseum.command;
+package eu.mhsl.craftattack.spawn.appliances.worldmuseum;
 
 import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
 import eu.mhsl.craftattack.spawn.appliances.worldmuseum.WorldMuseum;
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/worldmuseum/WorldMuseum.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/worldmuseum/WorldMuseum.java
index 4d56b63..0d6bef5 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/worldmuseum/WorldMuseum.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/worldmuseum/WorldMuseum.java
@@ -2,11 +2,10 @@ package eu.mhsl.craftattack.spawn.appliances.worldmuseum;
 
 import eu.mhsl.craftattack.spawn.appliance.Appliance;
 import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
-import eu.mhsl.craftattack.spawn.appliances.worldmuseum.command.MoveWorldMuseumVillagerCommand;
-import eu.mhsl.craftattack.spawn.util.DisplayVillager;
-import eu.mhsl.craftattack.spawn.util.PluginMessage;
-import eu.mhsl.craftattack.spawn.util.commonListeners.DismissInventoryOpenFromHolder;
-import eu.mhsl.craftattack.spawn.util.commonListeners.PlayerInteractAtEntityEventListener;
+import eu.mhsl.craftattack.spawn.util.entity.DisplayVillager;
+import eu.mhsl.craftattack.spawn.util.server.PluginMessage;
+import eu.mhsl.craftattack.spawn.util.listener.DismissInventoryOpenFromHolder;
+import eu.mhsl.craftattack.spawn.util.listener.PlayerInteractAtEntityEventListener;
 import net.kyori.adventure.text.Component;
 import net.kyori.adventure.text.format.NamedTextColor;
 import org.bukkit.Bukkit;
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/util/DisplayVillager.java b/src/main/java/eu/mhsl/craftattack/spawn/util/entity/DisplayVillager.java
similarity index 96%
rename from src/main/java/eu/mhsl/craftattack/spawn/util/DisplayVillager.java
rename to src/main/java/eu/mhsl/craftattack/spawn/util/entity/DisplayVillager.java
index 6f62fcc..324ba42 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/util/DisplayVillager.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/util/entity/DisplayVillager.java
@@ -1,7 +1,8 @@
-package eu.mhsl.craftattack.spawn.util;
+package eu.mhsl.craftattack.spawn.util.entity;
 
 import eu.mhsl.craftattack.spawn.config.ConfigUtil;
 import eu.mhsl.craftattack.spawn.config.Configuration;
+import eu.mhsl.craftattack.spawn.util.world.ChunkUtils;
 import org.bukkit.Location;
 import org.bukkit.configuration.ConfigurationSection;
 import org.bukkit.entity.EntityType;
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/util/commonListeners/DismissInventoryOpenFromHolder.java b/src/main/java/eu/mhsl/craftattack/spawn/util/listener/DismissInventoryOpenFromHolder.java
similarity index 92%
rename from src/main/java/eu/mhsl/craftattack/spawn/util/commonListeners/DismissInventoryOpenFromHolder.java
rename to src/main/java/eu/mhsl/craftattack/spawn/util/listener/DismissInventoryOpenFromHolder.java
index 4acf048..6c798a1 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/util/commonListeners/DismissInventoryOpenFromHolder.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/util/listener/DismissInventoryOpenFromHolder.java
@@ -1,4 +1,4 @@
-package eu.mhsl.craftattack.spawn.util.commonListeners;
+package eu.mhsl.craftattack.spawn.util.listener;
 
 import org.bukkit.entity.Entity;
 import org.bukkit.event.EventHandler;
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/util/commonListeners/PlayerInteractAtEntityEventListener.java b/src/main/java/eu/mhsl/craftattack/spawn/util/listener/PlayerInteractAtEntityEventListener.java
similarity index 93%
rename from src/main/java/eu/mhsl/craftattack/spawn/util/commonListeners/PlayerInteractAtEntityEventListener.java
rename to src/main/java/eu/mhsl/craftattack/spawn/util/listener/PlayerInteractAtEntityEventListener.java
index d9bd98f..dc16bee 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/util/commonListeners/PlayerInteractAtEntityEventListener.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/util/listener/PlayerInteractAtEntityEventListener.java
@@ -1,4 +1,4 @@
-package eu.mhsl.craftattack.spawn.util.commonListeners;
+package eu.mhsl.craftattack.spawn.util.listener;
 
 import org.bukkit.event.EventHandler;
 import org.bukkit.event.Listener;
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/util/Floodgate.java b/src/main/java/eu/mhsl/craftattack/spawn/util/server/Floodgate.java
similarity index 93%
rename from src/main/java/eu/mhsl/craftattack/spawn/util/Floodgate.java
rename to src/main/java/eu/mhsl/craftattack/spawn/util/server/Floodgate.java
index 5299b00..19782c7 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/util/Floodgate.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/util/server/Floodgate.java
@@ -1,4 +1,4 @@
-package eu.mhsl.craftattack.spawn.util;
+package eu.mhsl.craftattack.spawn.util.server;
 
 import org.bukkit.entity.Player;
 import org.geysermc.floodgate.api.FloodgateApi;
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/util/PluginMessage.java b/src/main/java/eu/mhsl/craftattack/spawn/util/server/PluginMessage.java
similarity index 91%
rename from src/main/java/eu/mhsl/craftattack/spawn/util/PluginMessage.java
rename to src/main/java/eu/mhsl/craftattack/spawn/util/server/PluginMessage.java
index 24561a0..5becb22 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/util/PluginMessage.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/util/server/PluginMessage.java
@@ -1,4 +1,4 @@
-package eu.mhsl.craftattack.spawn.util;
+package eu.mhsl.craftattack.spawn.util.server;
 
 import com.google.common.io.ByteArrayDataOutput;
 import com.google.common.io.ByteStreams;
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/util/ColorUtil.java b/src/main/java/eu/mhsl/craftattack/spawn/util/text/ColorUtil.java
similarity index 82%
rename from src/main/java/eu/mhsl/craftattack/spawn/util/ColorUtil.java
rename to src/main/java/eu/mhsl/craftattack/spawn/util/text/ColorUtil.java
index e1b6450..737564c 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/util/ColorUtil.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/util/text/ColorUtil.java
@@ -1,5 +1,6 @@
-package eu.mhsl.craftattack.spawn.util;
+package eu.mhsl.craftattack.spawn.util.text;
 
+import eu.mhsl.craftattack.spawn.util.NumberUtil;
 import net.kyori.adventure.text.format.TextColor;
 
 import java.awt.*;
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/util/ComponentUtil.java b/src/main/java/eu/mhsl/craftattack/spawn/util/text/ComponentUtil.java
similarity index 98%
rename from src/main/java/eu/mhsl/craftattack/spawn/util/ComponentUtil.java
rename to src/main/java/eu/mhsl/craftattack/spawn/util/text/ComponentUtil.java
index 2071824..f406135 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/util/ComponentUtil.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/util/text/ComponentUtil.java
@@ -1,4 +1,4 @@
-package eu.mhsl.craftattack.spawn.util;
+package eu.mhsl.craftattack.spawn.util.text;
 
 import net.kyori.adventure.text.Component;
 import net.kyori.adventure.text.format.NamedTextColor;
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/util/Countdown.java b/src/main/java/eu/mhsl/craftattack/spawn/util/text/Countdown.java
similarity index 98%
rename from src/main/java/eu/mhsl/craftattack/spawn/util/Countdown.java
rename to src/main/java/eu/mhsl/craftattack/spawn/util/text/Countdown.java
index 5202f4e..5fc8b9a 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/util/Countdown.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/util/text/Countdown.java
@@ -1,4 +1,4 @@
-package eu.mhsl.craftattack.spawn.util;
+package eu.mhsl.craftattack.spawn.util.text;
 
 import eu.mhsl.craftattack.spawn.Main;
 import net.kyori.adventure.text.Component;
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/util/text/DisconnectInfo.java b/src/main/java/eu/mhsl/craftattack/spawn/util/text/DisconnectInfo.java
new file mode 100644
index 0000000..d41f9e1
--- /dev/null
+++ b/src/main/java/eu/mhsl/craftattack/spawn/util/text/DisconnectInfo.java
@@ -0,0 +1,41 @@
+package eu.mhsl.craftattack.spawn.util.text;
+
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import org.bukkit.entity.Player;
+
+import java.util.UUID;
+
+public record DisconnectInfo(String error, String description, String help, UUID user) {
+    public void applyKick(Player player) {
+        player.kick(this.getComponent());
+    }
+    public Component getComponent() {
+        return Component.text()
+                .appendNewline().appendNewline()
+                .append(Component.text(this.error, NamedTextColor.DARK_RED)).appendNewline()
+                .append(Component.text(this.description, NamedTextColor.RED)).appendNewline().appendNewline()
+                .append(Component.text(this.help, NamedTextColor.GRAY)).appendNewline().appendNewline()
+                .append(Component.text(this.user.toString(), NamedTextColor.DARK_GRAY)).appendNewline()
+                .build();
+    }
+
+    public static class Throwable extends Exception {
+        public String error;
+        public String description;
+        public String help;
+        public UUID user;
+
+        public Throwable(String error, String description, String help, UUID user) {
+            super(description);
+            this.error = error;
+            this.description = description;
+            this.help = help;
+            this.user = user;
+        }
+
+        public DisconnectInfo getDisconnectScreen() {
+            return new DisconnectInfo(this.error, this.description, this.help, this.user);
+        }
+    }
+}
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/util/RainbowComponent.java b/src/main/java/eu/mhsl/craftattack/spawn/util/text/RainbowComponent.java
similarity index 95%
rename from src/main/java/eu/mhsl/craftattack/spawn/util/RainbowComponent.java
rename to src/main/java/eu/mhsl/craftattack/spawn/util/text/RainbowComponent.java
index 180001d..218e8bf 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/util/RainbowComponent.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/util/text/RainbowComponent.java
@@ -1,4 +1,4 @@
-package eu.mhsl.craftattack.spawn.util;
+package eu.mhsl.craftattack.spawn.util.text;
 
 import net.kyori.adventure.text.Component;
 import net.kyori.adventure.text.format.TextColor;
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/util/BlockCycle.java b/src/main/java/eu/mhsl/craftattack/spawn/util/world/BlockCycle.java
similarity index 94%
rename from src/main/java/eu/mhsl/craftattack/spawn/util/BlockCycle.java
rename to src/main/java/eu/mhsl/craftattack/spawn/util/world/BlockCycle.java
index 3072e0f..92b0699 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/util/BlockCycle.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/util/world/BlockCycle.java
@@ -1,4 +1,4 @@
-package eu.mhsl.craftattack.spawn.util;
+package eu.mhsl.craftattack.spawn.util.world;
 
 import org.bukkit.Location;
 import org.bukkit.Material;
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/util/ChunkUtils.java b/src/main/java/eu/mhsl/craftattack/spawn/util/world/ChunkUtils.java
similarity index 93%
rename from src/main/java/eu/mhsl/craftattack/spawn/util/ChunkUtils.java
rename to src/main/java/eu/mhsl/craftattack/spawn/util/world/ChunkUtils.java
index 5558bad..7218ecf 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/util/ChunkUtils.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/util/world/ChunkUtils.java
@@ -1,4 +1,4 @@
-package eu.mhsl.craftattack.spawn.util;
+package eu.mhsl.craftattack.spawn.util.world;
 
 import org.bukkit.Chunk;
 import org.bukkit.Location;