diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 9f4197d..a441313 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
 networkTimeout=10000
 validateDistributionUrl=true
 zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew b/gradlew
index fcb6fca..b740cf1 100755
--- a/gradlew
+++ b/gradlew
@@ -55,7 +55,7 @@
 #       Darwin, MinGW, and NonStop.
 #
 #   (3) This script is generated from the Groovy template
-#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
 #       within the Gradle project.
 #
 #       You can find Gradle at https://github.com/gradle/gradle/.
@@ -83,7 +83,8 @@ done
 # This is normally unused
 # shellcheck disable=SC2034
 APP_BASE_NAME=${0##*/}
-APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
 
 # Use the maximum available, or set MAX_FD != -1 to use that value.
 MAX_FD=maximum
@@ -144,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
     case $MAX_FD in #(
       max*)
         # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
-        # shellcheck disable=SC3045
+        # shellcheck disable=SC2039,SC3045
         MAX_FD=$( ulimit -H -n ) ||
             warn "Could not query maximum file descriptor limit"
     esac
@@ -152,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
       '' | soft) :;; #(
       *)
         # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
-        # shellcheck disable=SC3045
+        # shellcheck disable=SC2039,SC3045
         ulimit -n "$MAX_FD" ||
             warn "Could not set maximum file descriptor limit to $MAX_FD"
     esac
@@ -201,11 +202,11 @@ fi
 # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
 DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
 
-# Collect all arguments for the java command;
-#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
-#     shell script including quotes and variable substitutions, so put them in
-#     double quotes to make sure that they get re-expanded; and
-#   * put everything else in single quotes, so that it's not re-expanded.
+# Collect all arguments for the java command:
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+#     and any embedded shellness will be escaped.
+#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+#     treated as '${Hostname}' itself on the command line.
 
 set -- \
         "-Dorg.gradle.appname=$APP_BASE_NAME" \
diff --git a/gradlew.bat b/gradlew.bat
index 93e3f59..25da30d 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
 %JAVA_EXE% -version >NUL 2>&1
 if %ERRORLEVEL% equ 0 goto execute
 
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
 
 goto fail
 
@@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
 
 if exist "%JAVA_EXE%" goto execute
 
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
 
 goto fail
 
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/Main.java b/src/main/java/eu/mhsl/craftattack/spawn/Main.java
index 56d8198..00f837d 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/Main.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/Main.java
@@ -19,7 +19,6 @@ public final class Main extends JavaPlugin {
     private static Logger logger;
 
     private List<Appliance> appliances;
-    private HttpServer httpApi;
 
     @Override
     public void onEnable() {
@@ -62,7 +61,7 @@ public final class Main extends JavaPlugin {
         Main.logger().info(String.format("Initialized %d appliances!", appliances.size()));
 
         Main.logger().info("Starting HTTP API...");
-        this.httpApi = new HttpServer();
+        new HttpServer();
 
         getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
         Main.logger().info("Startup complete!");
@@ -76,7 +75,7 @@ public final class Main extends JavaPlugin {
             appliance.onDisable();
             appliance.destruct(this);
         });
-        this.httpApi.stop();
+
         HandlerList.unregisterAll(this);
         Bukkit.getScheduler().cancelTasks(this);
         Main.logger().info("Disabled " + appliances.size() + " appliances!");
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/api/HttpServer.java b/src/main/java/eu/mhsl/craftattack/spawn/api/HttpServer.java
index 4961f1d..412d95b 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/api/HttpServer.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/api/HttpServer.java
@@ -24,10 +24,6 @@ public class HttpServer {
         Main.instance().getAppliances().forEach(appliance -> appliance.httpApi(new ApiBuilder(appliance)));
     }
 
-    public void stop() {
-        Spark.stop();
-    }
-
     public record Response(Status status, Object error, Object response) {
         public enum Status {
             FAILURE,
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/acInform/AcInform.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/acInform/AcInform.java
new file mode 100644
index 0000000..b7db6f2
--- /dev/null
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/acInform/AcInform.java
@@ -0,0 +1,132 @@
+package eu.mhsl.craftattack.spawn.appliances.acInform;
+
+import eu.mhsl.craftattack.spawn.appliance.Appliance;
+import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
+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 org.bukkit.Bukkit;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+
+public class AcInform extends Appliance {
+    public void processCommand(@NotNull String[] args) {
+        String anticheatName = null;
+        String playerName = null;
+        String checkName = null;
+        Integer violationCount = null;
+
+        for(int i = 0; i < args.length; i++) {
+            if(!args[i].startsWith("--")) continue;
+            if(i == args.length-1) continue;
+            String nextArgument = args[i+1];
+            if(nextArgument.startsWith("--")) continue;
+
+            switch(args[i]) {
+                case "--anticheatName" -> anticheatName = nextArgument;
+                case "--playerName" -> playerName = nextArgument;
+                case "--check" -> checkName = nextArgument;
+                case "--violationCount" -> violationCount = Integer.valueOf(nextArgument);
+            }
+        }
+
+        this.notifyAdmins(anticheatName, playerName, checkName, violationCount);
+    }
+
+    public void notifyAdmins(@Nullable String anticheatName, @Nullable String playerName, @Nullable String checkName, @Nullable Integer violationCount) {
+        ComponentBuilder<TextComponent, TextComponent.Builder> component = Component.text();
+        Component prefix = Component.text("# ", NamedTextColor.DARK_RED);
+        NamedTextColor textColor = NamedTextColor.GRAY;
+
+        if(playerName == null || playerName.isBlank()) throw new ApplianceCommand.Error("acinform command needs a player (--playerName)");
+
+        if(anticheatName != null && !anticheatName.isBlank()) {
+            component.append(
+                Component.newline()
+                    .append(prefix)
+                    .append(Component.text("[", textColor))
+                    .append(Component.text("Anticheat", NamedTextColor.RED))
+                    .append(Component.text("] ", textColor))
+                    .append(Component.text(anticheatName, NamedTextColor.WHITE))
+                    .append(Component.text(":", textColor))
+            );
+        }
+
+        component.append(
+            Component.newline()
+                .append(prefix)
+                .append(Component.text("Player ", textColor))
+                .append(Component.text(playerName, NamedTextColor.WHITE))
+                .append(Component.text(" "))
+        );
+
+        if(checkName == null || checkName.isBlank()) {
+            component.append(Component.text("got detected by Anticheat", textColor));
+        } else if(violationCount != null){
+            component.append(
+                Component.text("failed ", textColor)
+                    .append(Component.text(String.format("%sx ", violationCount), NamedTextColor.WHITE))
+                    .append(Component.text("'", textColor))
+                    .append(Component.text(checkName, NamedTextColor.WHITE))
+                    .append(Component.text("'", textColor))
+            );
+        } else {
+            component.append(
+                Component.text("failed ", textColor)
+                    .append(Component.text("'", textColor))
+                    .append(Component.text(checkName, NamedTextColor.WHITE))
+                    .append(Component.text("'", textColor))
+            );
+        }
+
+        component.append(
+            Component.newline()
+                .append(prefix)
+
+                .append(Component.text("[", NamedTextColor.GRAY))
+                .append(Component.text("Report", NamedTextColor.GOLD))
+                .append(Component.text("]", NamedTextColor.GRAY))
+                .clickEvent(ClickEvent.suggestCommand(String.format("/report %s anticheat %s flagged %s", playerName, anticheatName, checkName)))
+        );
+
+        component.append(
+            Component.text(" [", NamedTextColor.GRAY)
+                .append(Component.text("Kick", NamedTextColor.GOLD))
+                .append(Component.text("]", NamedTextColor.GRAY))
+                .clickEvent(ClickEvent.suggestCommand(String.format("/kick %s", playerName)))
+        );
+
+        component.append(
+            Component.text(" [", NamedTextColor.GRAY)
+                .append(Component.text("Panic Ban", NamedTextColor.GOLD))
+                .append(Component.text("]", NamedTextColor.GRAY))
+                .clickEvent(ClickEvent.suggestCommand(String.format("/panicban %s", playerName)))
+        );
+
+        component.append(
+            Component.text(" [", NamedTextColor.GRAY)
+                .append(Component.text("Teleport", NamedTextColor.GOLD))
+                .append(Component.text("]", NamedTextColor.GRAY))
+                .clickEvent(ClickEvent.suggestCommand(String.format("/tp %s", playerName)))
+        );
+
+        component.appendNewline();
+        TextComponent finalMessage = component.build();
+
+        Bukkit.getOnlinePlayers().stream()
+            .filter(player -> player.hasPermission("admin"))
+            .forEach(player -> player.sendMessage(finalMessage));
+
+    }
+
+    @Override
+    protected @NotNull List<ApplianceCommand<?>> commands() {
+        return List.of(
+            new AcInformCommand()
+        );
+    }
+}
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/acInform/AcInformCommand.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/acInform/AcInformCommand.java
new file mode 100644
index 0000000..149cd46
--- /dev/null
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/acInform/AcInformCommand.java
@@ -0,0 +1,19 @@
+package eu.mhsl.craftattack.spawn.appliances.acInform;
+
+import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+
+public class AcInformCommand extends ApplianceCommand<AcInform> {
+    public AcInformCommand() {
+        super("acInform");
+    }
+
+    @Override
+    protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
+        if(sender instanceof Player) throw new ApplianceCommand.Error("Dieser Command ist nicht für Spieler!");
+        getAppliance().processCommand(args);
+    }
+}
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/adminChat/AdminChat.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/adminChat/AdminChat.java
new file mode 100644
index 0000000..8891bba
--- /dev/null
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/adminChat/AdminChat.java
@@ -0,0 +1,37 @@
+package eu.mhsl.craftattack.spawn.appliances.adminChat;
+
+import eu.mhsl.craftattack.spawn.appliance.Appliance;
+import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.event.ClickEvent;
+import net.kyori.adventure.text.format.NamedTextColor;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+public class AdminChat extends Appliance {
+    public void sendMessage(Player sender, String message) {
+        Component privatePrefix = Component
+            .text("[Admin] ", NamedTextColor.LIGHT_PURPLE)
+            .clickEvent(ClickEvent.suggestCommand(AdminChatCommand.commandName));
+
+        Bukkit.getOnlinePlayers().stream()
+            .filter(player -> player.hasPermission("admin"))
+            .forEach(player -> {
+                Component formattedMessage = Component.text()
+                    .append(privatePrefix)
+                    .append(sender.displayName())
+                    .append(Component.text(" > ", NamedTextColor.DARK_GRAY))
+                    .append(Component.text(message))
+                    .build();
+                player.sendMessage(formattedMessage);
+            });
+    }
+
+    @Override
+    protected @NotNull List<ApplianceCommand<?>> commands() {
+        return List.of(new AdminChatCommand());
+    }
+}
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/adminChat/AdminChatCommand.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/adminChat/AdminChatCommand.java
new file mode 100644
index 0000000..71b2522
--- /dev/null
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/adminChat/AdminChatCommand.java
@@ -0,0 +1,22 @@
+package eu.mhsl.craftattack.spawn.appliances.adminChat;
+
+import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.jetbrains.annotations.NotNull;
+
+public class AdminChatCommand extends ApplianceCommand.PlayerChecked<AdminChat> {
+    public static final String commandName = "adminchat";
+
+    public AdminChatCommand() {
+        super(commandName);
+    }
+
+    @Override
+    protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
+        if(!sender.hasPermission("admin")) return;
+
+        String message = String.join(" ", args);
+        getAppliance().sendMessage(getPlayer(), message);
+    }
+}
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/afkTag/AfkResetListener.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/afkTag/AfkResetListener.java
new file mode 100644
index 0000000..4c758f9
--- /dev/null
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/afkTag/AfkResetListener.java
@@ -0,0 +1,30 @@
+package eu.mhsl.craftattack.spawn.appliances.afkTag;
+
+import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
+import io.papermc.paper.event.player.AsyncChatEvent;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.player.PlayerInteractEvent;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerMoveEvent;
+
+public class AfkResetListener extends ApplianceListener<AfkTag> {
+    @EventHandler
+    public void onMove(PlayerMoveEvent event) {
+        getAppliance().resetTiming(event.getPlayer());
+    }
+
+    @EventHandler
+    public void onInteract(PlayerInteractEvent event) {
+        getAppliance().resetTiming(event.getPlayer());
+    }
+
+    @EventHandler
+    public void onChat(AsyncChatEvent event) {
+        getAppliance().resetTiming(event.getPlayer());
+    }
+
+    @EventHandler
+    public void onJoin(PlayerJoinEvent event) {
+        getAppliance().resetTiming(event.getPlayer());
+    }
+}
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/afkTag/AfkTag.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/afkTag/AfkTag.java
new file mode 100644
index 0000000..538efcd
--- /dev/null
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/afkTag/AfkTag.java
@@ -0,0 +1,71 @@
+package eu.mhsl.craftattack.spawn.appliances.afkTag;
+
+import eu.mhsl.craftattack.spawn.Main;
+import eu.mhsl.craftattack.spawn.appliance.Appliance;
+import eu.mhsl.craftattack.spawn.appliances.displayName.DisplayName;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.event.HoverEvent;
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.kyori.adventure.util.Ticks;
+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.util.List;
+import java.util.WeakHashMap;
+
+public class AfkTag extends Appliance implements DisplayName.DisplayNamed {
+    private final WeakHashMap<Player, Long> afkTimings = new WeakHashMap<>();
+    private static final int updateIntervalSeconds = 30;
+    private static final int afkWhenMillis = 5 * 60 * 1000;
+
+    @Override
+    public void onEnable() {
+        Bukkit.getScheduler().runTaskTimerAsynchronously(
+            Main.instance(),
+            this::checkAfkPlayers,
+            Ticks.TICKS_PER_SECOND,
+            Ticks.TICKS_PER_SECOND * updateIntervalSeconds
+        );
+    }
+
+    public void resetTiming(Player player) {
+        boolean wasAfk = isAfk(player);
+        this.afkTimings.put(player, System.currentTimeMillis());
+        if (wasAfk) this.updateAfkPrefix(player);
+    }
+
+    private void checkAfkPlayers() {
+        this.afkTimings.keySet().forEach((player) -> {
+            if(!isAfk(player)) return;
+            this.updateAfkPrefix(player);
+        });
+    }
+
+    private boolean isAfk(Player player) {
+        if(player.isSleeping()) return false;
+
+        long lastTimeActive = this.afkTimings.getOrDefault(player, 0L);
+        long timeSinceLastActive = System.currentTimeMillis() - lastTimeActive;
+        return timeSinceLastActive >= afkWhenMillis;
+    }
+
+    private void updateAfkPrefix(Player player) {
+        Main.instance().getAppliance(DisplayName.class).update(player);
+    }
+
+    @Override
+    public @Nullable Component getNamePrefix(Player player) {
+        if(isAfk(player)) return Component.text("[ᵃᶠᵏ]", NamedTextColor.GRAY)
+            .hoverEvent(HoverEvent.showText(Component.text("Der Spieler ist AFK")));
+
+        return null;
+    }
+
+    @Override
+    protected @NotNull List<Listener> listeners() {
+        return List.of(new AfkResetListener());
+    }
+}
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/displayName/DisplayName.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/displayName/DisplayName.java
index 429cfdd..8d19a54 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/displayName/DisplayName.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/displayName/DisplayName.java
@@ -4,7 +4,9 @@ import eu.mhsl.craftattack.spawn.Main;
 import eu.mhsl.craftattack.spawn.appliance.Appliance;
 import eu.mhsl.craftattack.spawn.appliances.adminMarker.AdminMarker;
 import eu.mhsl.craftattack.spawn.appliances.adminMarker.AdminMarkerListener;
+import eu.mhsl.craftattack.spawn.appliances.afkTag.AfkTag;
 import eu.mhsl.craftattack.spawn.appliances.outlawed.Outlawed;
+import eu.mhsl.craftattack.spawn.appliances.sleepTag.SleepTag;
 import eu.mhsl.craftattack.spawn.appliances.yearRank.YearRank;
 import net.kyori.adventure.text.Component;
 import net.kyori.adventure.text.ComponentBuilder;
@@ -13,17 +15,24 @@ import net.kyori.adventure.text.format.TextColor;
 import org.bukkit.entity.Player;
 import org.bukkit.event.Listener;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import java.util.List;
 import java.util.function.Supplier;
 import java.util.logging.Level;
 
 public class DisplayName extends Appliance {
+    public interface DisplayNamed {
+        @Nullable Component getNamePrefix(Player player);
+    }
+
     public void update(Player player) {
         TextColor playerColor = queryAppliance(AdminMarker.class).getPlayerColor(player);
         List<Supplier<Component>> prefixes = List.of(
             () -> queryAppliance(Outlawed.class).getNamePrefix(player),
-            () -> queryAppliance(YearRank.class).getNamePrefix(player)
+            () -> queryAppliance(YearRank.class).getNamePrefix(player),
+            () -> queryAppliance(AfkTag.class).getNamePrefix(player),
+            () -> queryAppliance(SleepTag.class).getNamePrefix(player)
         );
 
         ComponentBuilder<TextComponent, TextComponent.Builder> playerName = Component.text();
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/endPrevent/EndPrevent.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/endPrevent/EndPrevent.java
new file mode 100644
index 0000000..2013ad9
--- /dev/null
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/endPrevent/EndPrevent.java
@@ -0,0 +1,39 @@
+package eu.mhsl.craftattack.spawn.appliances.endPrevent;
+
+import eu.mhsl.craftattack.spawn.appliance.Appliance;
+import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
+import eu.mhsl.craftattack.spawn.config.Configuration;
+import org.bukkit.event.Listener;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+public class EndPrevent extends Appliance {
+    private final String endDisabledKey = "endDisabled";
+    private boolean endDisabled;
+
+    public EndPrevent() {
+        super("endPrevent");
+        this.endDisabled = localConfig().getBoolean(endDisabledKey);
+    }
+
+    public void setEndDisabled(boolean disabled) {
+        localConfig().set(endDisabledKey, disabled);
+        Configuration.saveChanges();
+        this.endDisabled = disabled;
+    }
+
+    public boolean isEndDisabled() {
+        return endDisabled;
+    }
+
+    @Override
+    protected @NotNull List<Listener> listeners() {
+        return List.of(new PreventEnderEyeUseListener());
+    }
+
+    @Override
+    protected @NotNull List<ApplianceCommand<?>> commands() {
+        return List.of(new EndPreventCommand());
+    }
+}
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/endPrevent/EndPreventCommand.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/endPrevent/EndPreventCommand.java
new file mode 100644
index 0000000..863b703
--- /dev/null
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/endPrevent/EndPreventCommand.java
@@ -0,0 +1,37 @@
+package eu.mhsl.craftattack.spawn.appliances.endPrevent;
+
+import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.Map;
+
+public class EndPreventCommand extends ApplianceCommand<EndPrevent> {
+    private final Map<String, Boolean> arguments = Map.of("preventEnd", true, "allowEnd", false);
+
+    public EndPreventCommand() {
+        super("endPrevent");
+    }
+
+    @Override
+    protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
+        if(args.length == 1 && arguments.containsKey(args[0])) {
+            getAppliance().setEndDisabled(arguments.get(args[0]));
+            sender.sendMessage(Component.text("Setting updated!", NamedTextColor.GREEN));
+        }
+        sender.sendMessage(Component.text(
+            String.format("The End is %s!", getAppliance().isEndDisabled() ? "open" : "closed"),
+            NamedTextColor.GOLD
+        ));
+    }
+
+    @Override
+    public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
+        return arguments.keySet().stream().toList();
+    }
+}
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/endPrevent/PreventEnderEyeUseListener.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/endPrevent/PreventEnderEyeUseListener.java
new file mode 100644
index 0000000..078c848
--- /dev/null
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/endPrevent/PreventEnderEyeUseListener.java
@@ -0,0 +1,23 @@
+package eu.mhsl.craftattack.spawn.appliances.endPrevent;
+
+import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import org.bukkit.Material;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.player.PlayerInteractEvent;
+
+public class PreventEnderEyeUseListener extends ApplianceListener<EndPrevent> {
+    @EventHandler
+    public void onEnderEyeInteraction(PlayerInteractEvent event) {
+        if(event.getClickedBlock() == null) return;
+        if(!event.getClickedBlock().getType().equals(Material.END_PORTAL_FRAME)) return;
+        if(event.getItem() == null) return;
+        if(!event.getItem().getType().equals(Material.ENDER_EYE)) return;
+
+        if(!getAppliance().isEndDisabled()) return;
+
+        event.setCancelled(true);
+        event.getPlayer().sendActionBar(Component.text("Das End ist noch nicht freigeschaltet!", NamedTextColor.RED));
+    }
+}
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/infoBars/Bar.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/infoBars/Bar.java
new file mode 100644
index 0000000..503a674
--- /dev/null
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/infoBars/Bar.java
@@ -0,0 +1,67 @@
+package eu.mhsl.craftattack.spawn.appliances.infoBars;
+
+import eu.mhsl.craftattack.spawn.Main;
+import net.kyori.adventure.bossbar.BossBar;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.util.Ticks;
+import org.bukkit.Bukkit;
+import org.bukkit.scheduler.BukkitTask;
+
+import java.time.Duration;
+import java.time.temporal.ChronoUnit;
+
+public abstract class Bar {
+    private BossBar bossBar;
+    private final BukkitTask updateTask;
+
+    public Bar() {
+        long refreshRateInTicks = this.refresh().get(ChronoUnit.SECONDS) * Ticks.TICKS_PER_SECOND;
+        this.updateTask = Bukkit.getScheduler().runTaskTimerAsynchronously(
+            Main.instance(),
+            this::update,
+            refreshRateInTicks,
+            refreshRateInTicks
+        );
+    }
+
+    public BossBar getBossBar() {
+        if(this.bossBar == null) this.bossBar = this.createBar();
+        return this.bossBar;
+    }
+
+    private BossBar createBar() {
+        return BossBar.bossBar(
+            this.title(),
+            this.correctedProgress(),
+            this.color(),
+            this.overlay()
+        );
+    }
+
+    private void update() {
+        if(this.bossBar == null) return;
+
+        this.beforeRefresh();
+        this.bossBar.name(this.title());
+        this.bossBar.progress(this.correctedProgress());
+        this.bossBar.color(this.color());
+        this.bossBar.overlay(this.overlay());
+    }
+
+    public void stopUpdate() {
+        this.updateTask.cancel();
+    }
+
+    private float correctedProgress() {
+        return Math.clamp(this.progress(), 0, 1);
+    }
+
+    protected void beforeRefresh() {}
+    protected abstract Duration refresh();
+    protected abstract String name();
+
+    protected abstract Component title();
+    protected abstract float progress();
+    protected abstract BossBar.Color color();
+    protected abstract BossBar.Overlay overlay();
+}
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/infoBars/InfoBarCommand.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/infoBars/InfoBarCommand.java
new file mode 100644
index 0000000..d3aa097
--- /dev/null
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/infoBars/InfoBarCommand.java
@@ -0,0 +1,32 @@
+package eu.mhsl.craftattack.spawn.appliances.infoBars;
+
+import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+public class InfoBarCommand extends ApplianceCommand.PlayerChecked<InfoBars> {
+    public InfoBarCommand() {
+        super("infobar");
+    }
+
+    @Override
+    protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
+        if(args.length == 0) throw new Error("<show|hide|hideall> [bar name]");
+        switch(args[0]) {
+            case "hideAll" -> getAppliance().hideAll(getPlayer());
+            case "show" -> getAppliance().show(getPlayer(), args[1]);
+            case "hide" -> getAppliance().hide(getPlayer(), args[1]);
+            default -> throw new Error("Erlaubte Optionen sind 'show', 'hide', 'hideAll'!");
+        }
+    }
+
+    @Override
+    public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
+        if(args.length == 1) return List.of("show", "hide", "hideAll");
+        return getAppliance().getInfoBars().stream().map(Bar::name).toList();
+    }
+}
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/infoBars/InfoBars.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/infoBars/InfoBars.java
new file mode 100644
index 0000000..5e08c60
--- /dev/null
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/infoBars/InfoBars.java
@@ -0,0 +1,91 @@
+package eu.mhsl.craftattack.spawn.appliances.infoBars;
+
+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.infoBars.bars.MsptBar;
+import eu.mhsl.craftattack.spawn.appliances.infoBars.bars.PlayerCounterBar;
+import eu.mhsl.craftattack.spawn.appliances.infoBars.bars.TpsBar;
+import org.bukkit.NamespacedKey;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Listener;
+import org.bukkit.persistence.PersistentDataContainer;
+import org.bukkit.persistence.PersistentDataType;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class InfoBars extends Appliance {
+    private final NamespacedKey infoBarKey = new NamespacedKey(Main.instance(), "infobars");
+    private final List<Bar> infoBars = List.of(
+        new TpsBar(),
+        new MsptBar(),
+        new PlayerCounterBar()
+    );
+
+    public void showAll(Player player) {
+        this.getStoredBars(player).forEach(bar -> this.show(player, bar));
+    }
+
+    public void hideAll(Player player) {
+        this.getStoredBars(player).forEach(bar -> this.hide(player, bar));
+        this.setStoredBars(player, List.of());
+    }
+
+    public void show(Player player, String bar) {
+        this.validateBarName(bar);
+        List<String> existingBars = new ArrayList<>(this.getStoredBars(player));
+        existingBars.add(bar);
+        player.showBossBar(this.getBarByName(bar).getBossBar());
+        this.setStoredBars(player, existingBars);
+    }
+
+    public void hide(Player player, String bar) {
+        this.validateBarName(bar);
+        List<String> existingBars = new ArrayList<>(this.getStoredBars(player));
+        existingBars.remove(bar);
+        player.hideBossBar(this.getBarByName(bar).getBossBar());
+        this.setStoredBars(player, existingBars);
+    }
+
+    private List<String> getStoredBars(Player player) {
+        PersistentDataContainer container = player.getPersistentDataContainer();
+        if(!container.has(infoBarKey)) return List.of();
+        return container.get(infoBarKey, PersistentDataType.LIST.strings());
+    }
+
+    private void setStoredBars(Player player, List<String> bars) {
+        player.getPersistentDataContainer().set(infoBarKey, PersistentDataType.LIST.strings(), bars);
+    }
+
+    private Bar getBarByName(String name) {
+        return infoBars.stream()
+            .filter(bar -> bar.name().equalsIgnoreCase(name))
+            .findFirst()
+            .orElse(null);
+    }
+
+    private void validateBarName(String name) {
+        if(getBarByName(name) == null) throw new ApplianceCommand.Error(String.format("Ungültiger infobar name '%s'", name));
+    }
+
+    public List<Bar> getInfoBars() {
+        return infoBars;
+    }
+
+    @Override
+    public void onDisable() {
+        infoBars.forEach(Bar::stopUpdate);
+    }
+
+    @Override
+    protected @NotNull List<Listener> listeners() {
+        return List.of(new ShowPreviousBarsListener());
+    }
+
+    @Override
+    protected @NotNull List<ApplianceCommand<?>> commands() {
+        return List.of(new InfoBarCommand());
+    }
+}
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/infoBars/ShowPreviousBarsListener.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/infoBars/ShowPreviousBarsListener.java
new file mode 100644
index 0000000..fa5006a
--- /dev/null
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/infoBars/ShowPreviousBarsListener.java
@@ -0,0 +1,12 @@
+package eu.mhsl.craftattack.spawn.appliances.infoBars;
+
+import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.player.PlayerJoinEvent;
+
+public class ShowPreviousBarsListener extends ApplianceListener<InfoBars> {
+    @EventHandler
+    public void onJoin(PlayerJoinEvent event) {
+        getAppliance().showAll(event.getPlayer());
+    }
+}
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/infoBars/bars/MsptBar.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/infoBars/bars/MsptBar.java
new file mode 100644
index 0000000..60046a5
--- /dev/null
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/infoBars/bars/MsptBar.java
@@ -0,0 +1,57 @@
+package eu.mhsl.craftattack.spawn.appliances.infoBars.bars;
+
+import eu.mhsl.craftattack.spawn.appliances.infoBars.Bar;
+import eu.mhsl.craftattack.spawn.util.statistics.ServerMonitor;
+import eu.mhsl.craftattack.spawn.util.text.ColorUtil;
+import net.kyori.adventure.bossbar.BossBar;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+
+import java.time.Duration;
+
+public class MsptBar extends Bar {
+    @Override
+    protected Duration refresh() {
+        return Duration.ofSeconds(3);
+    }
+
+    @Override
+    protected String name() {
+        return "mspt";
+    }
+
+    @Override
+    protected Component title() {
+        return Component.text()
+            .append(Component.text("M"))
+            .append(Component.text("illi", NamedTextColor.GRAY))
+            .append(Component.text("S"))
+            .append(Component.text("econds ", NamedTextColor.GRAY))
+            .append(Component.text("P"))
+            .append(Component.text("er ", NamedTextColor.GRAY))
+            .append(Component.text("T"))
+            .append(Component.text("ick", NamedTextColor.GRAY))
+            .append(Component.text(": "))
+            .append(Component.text(String.format("%.2f", this.currentMSPT()), ColorUtil.msptColor(this.currentMSPT())))
+            .build();
+    }
+
+    @Override
+    protected float progress() {
+        return this.currentMSPT() / 50f;
+    }
+
+    @Override
+    protected BossBar.Color color() {
+        return BossBar.Color.BLUE;
+    }
+
+    @Override
+    protected BossBar.Overlay overlay() {
+        return BossBar.Overlay.PROGRESS;
+    }
+
+    private float currentMSPT() {
+        return ServerMonitor.getServerMSPT();
+    }
+}
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/infoBars/bars/PlayerCounterBar.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/infoBars/bars/PlayerCounterBar.java
new file mode 100644
index 0000000..32c6966
--- /dev/null
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/infoBars/bars/PlayerCounterBar.java
@@ -0,0 +1,56 @@
+package eu.mhsl.craftattack.spawn.appliances.infoBars.bars;
+
+import eu.mhsl.craftattack.spawn.Main;
+import eu.mhsl.craftattack.spawn.appliances.infoBars.Bar;
+import eu.mhsl.craftattack.spawn.appliances.playerlimit.PlayerLimit;
+import eu.mhsl.craftattack.spawn.util.text.ColorUtil;
+import net.kyori.adventure.bossbar.BossBar;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.TextColor;
+import org.bukkit.Bukkit;
+
+import java.time.Duration;
+
+public class PlayerCounterBar extends Bar {
+    @Override
+    protected Duration refresh() {
+        return Duration.ofSeconds(3);
+    }
+
+    @Override
+    protected String name() {
+        return "playerCounter";
+    }
+
+    @Override
+    protected Component title() {
+        TextColor color = ColorUtil.mapGreenToRed(this.getCurrentPlayerCount(), 0, this.getMaxPlayerCount(), true);
+        return Component.text()
+            .append(Component.text("Spieler online: "))
+            .append(Component.text(this.getCurrentPlayerCount(), color))
+            .build();
+    }
+
+    @Override
+    protected float progress() {
+        return (float) this.getCurrentPlayerCount() / this.getMaxPlayerCount();
+    }
+
+    @Override
+    protected BossBar.Color color() {
+        return BossBar.Color.BLUE;
+    }
+
+    @Override
+    protected BossBar.Overlay overlay() {
+        return BossBar.Overlay.PROGRESS;
+    }
+
+    private int getCurrentPlayerCount() {
+        return Bukkit.getOnlinePlayers().size();
+    }
+
+    private int getMaxPlayerCount() {
+        return Main.instance().getAppliance(PlayerLimit.class).getLimit();
+    }
+}
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/infoBars/bars/TpsBar.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/infoBars/bars/TpsBar.java
new file mode 100644
index 0000000..0d82b40
--- /dev/null
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/infoBars/bars/TpsBar.java
@@ -0,0 +1,55 @@
+package eu.mhsl.craftattack.spawn.appliances.infoBars.bars;
+
+import eu.mhsl.craftattack.spawn.appliances.infoBars.Bar;
+import eu.mhsl.craftattack.spawn.util.text.ColorUtil;
+import net.kyori.adventure.bossbar.BossBar;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import org.bukkit.Bukkit;
+
+import java.time.Duration;
+
+public class TpsBar extends Bar {
+    @Override
+    protected Duration refresh() {
+        return Duration.ofSeconds(3);
+    }
+
+    @Override
+    protected String name() {
+        return "tps";
+    }
+
+    @Override
+    protected Component title() {
+        return Component.text()
+            .append(Component.text("T"))
+            .append(Component.text("icks ", NamedTextColor.GRAY))
+            .append(Component.text("P"))
+            .append(Component.text("er ", NamedTextColor.GRAY))
+            .append(Component.text("S"))
+            .append(Component.text("econds", NamedTextColor.GRAY))
+            .append(Component.text(": "))
+            .append(Component.text(String.format("%.2f", this.currentTps()), ColorUtil.tpsColor(this.currentTps())))
+            .build();
+    }
+
+    @Override
+    protected float progress() {
+        return this.currentTps() / 20;
+    }
+
+    @Override
+    protected BossBar.Color color() {
+        return BossBar.Color.BLUE;
+    }
+
+    @Override
+    protected BossBar.Overlay overlay() {
+        return BossBar.Overlay.NOTCHED_20;
+    }
+
+    private float currentTps() {
+        return (float) Bukkit.getTPS()[0];
+    }
+}
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/maintenance/MaintenanceCommand.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/maintenance/MaintenanceCommand.java
index 9372b17..38ad1d9 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/maintenance/MaintenanceCommand.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/maintenance/MaintenanceCommand.java
@@ -1,6 +1,8 @@
 package eu.mhsl.craftattack.spawn.appliances.maintenance;
 
 import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
 import org.bukkit.command.Command;
 import org.bukkit.command.CommandSender;
 import org.jetbrains.annotations.NotNull;
@@ -10,7 +12,7 @@ import java.util.List;
 import java.util.Map;
 
 public class MaintenanceCommand extends ApplianceCommand<Maintenance> {
-    Map<String, Boolean> arguments = Map.of("enable", true, "disable", false);
+    private final Map<String, Boolean> arguments = Map.of("enable", true, "disable", false);
 
     public MaintenanceCommand() {
         super("maintanance");
@@ -18,9 +20,14 @@ public class MaintenanceCommand extends ApplianceCommand<Maintenance> {
 
     @Override
     protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
-        if(args.length != 1 || !arguments.containsKey(args[0])) throw new Error("Argument 'enable' oder 'disable' gefordert!");
-        getAppliance().setState(arguments.get(args[0]));
-        sender.sendMessage(String.format("Maintanance: %b", getAppliance().isInMaintenance()));
+        if(args.length == 1 && arguments.containsKey(args[0])) {
+            getAppliance().setState(arguments.get(args[0]));
+            sender.sendMessage(Component.text("Maintanance mode updated!", NamedTextColor.GREEN));
+        }
+        sender.sendMessage(Component.text(
+            String.format("Maintanance mode is %b", getAppliance().isInMaintenance()),
+            NamedTextColor.GOLD
+        ));
     }
 
     @Override
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/maintenance/PreventMaintenanceJoinListener.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/maintenance/PreventMaintenanceJoinListener.java
index 9990d23..aacc3bd 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/maintenance/PreventMaintenanceJoinListener.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/maintenance/PreventMaintenanceJoinListener.java
@@ -12,9 +12,9 @@ public class PreventMaintenanceJoinListener extends ApplianceListener<Maintenanc
         if(event.getPlayer().hasPermission("bypassMaintainance")) return;
 
         DisconnectInfo disconnectInfo = new DisconnectInfo(
-            "Wartunsarbeiten",
+            "Server für Spieler nicht verfügbar",
             "Zurzeit können nur Admins dem Server beitreten!",
-            "Bitte warte bis die Warungsarbeiten wieder deaktiviert werden.",
+            "Bitte versuche es später erneut.",
             event.getPlayer().getUniqueId()
         );
 
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/outlawed/Outlawed.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/outlawed/Outlawed.java
index e71ffc0..3fb23fb 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/outlawed/Outlawed.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/outlawed/Outlawed.java
@@ -17,7 +17,7 @@ import org.jetbrains.annotations.NotNull;
 
 import java.util.*;
 
-public class Outlawed extends Appliance {
+public class Outlawed extends Appliance implements DisplayName.DisplayNamed {
     public final int timeoutInMs = 1000 * 60 * 60 * 6;
     private final Map<UUID, Long> timeouts = new HashMap<>();
 
@@ -110,6 +110,7 @@ public class Outlawed extends Appliance {
         };
     }
 
+    @Override
     public Component getNamePrefix(Player player) {
         if(isOutlawed(player)) {
             return Component.text("[☠]", NamedTextColor.RED)
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/playtime/Playtime.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/playtime/Playtime.java
new file mode 100644
index 0000000..6a59694
--- /dev/null
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/playtime/Playtime.java
@@ -0,0 +1,32 @@
+package eu.mhsl.craftattack.spawn.appliances.playtime;
+
+import eu.mhsl.craftattack.spawn.appliance.Appliance;
+import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
+import eu.mhsl.craftattack.spawn.util.text.DataSizeConverter;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.kyori.adventure.util.Ticks;
+import org.bukkit.OfflinePlayer;
+import org.bukkit.Statistic;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+import java.util.Objects;
+
+public class Playtime extends Appliance {
+    public Component getFormattedPlaytime(OfflinePlayer player) {
+        int playtimeInTicks = player.getStatistic(Statistic.PLAY_ONE_MINUTE);
+        String playtime = DataSizeConverter.formatSecondsToHumanReadable(playtimeInTicks / Ticks.TICKS_PER_SECOND);
+        return Component.text()
+            .append(Component.text("Der Spieler ", NamedTextColor.GRAY))
+            .append(Component.text(Objects.requireNonNull(player.getName())))
+            .append(Component.text(" hat eine Spielzeit von ", NamedTextColor.GRAY))
+            .append(Component.text(playtime, NamedTextColor.GOLD))
+            .build();
+    }
+
+    @Override
+    protected @NotNull List<ApplianceCommand<?>> commands() {
+        return List.of(new PlaytimeCommand());
+    }
+}
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/playtime/PlaytimeCommand.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/playtime/PlaytimeCommand.java
new file mode 100644
index 0000000..03cffc7
--- /dev/null
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/playtime/PlaytimeCommand.java
@@ -0,0 +1,31 @@
+package eu.mhsl.craftattack.spawn.appliances.playtime;
+
+import eu.mhsl.craftattack.spawn.Main;
+import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import org.bukkit.Bukkit;
+import org.bukkit.OfflinePlayer;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.jetbrains.annotations.NotNull;
+
+public class PlaytimeCommand extends ApplianceCommand.PlayerChecked<Playtime> {
+    public PlaytimeCommand() {
+        super("playtime");
+    }
+
+    @Override
+    protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
+        String playerName = args.length == 1 ? args[0] : getPlayer().getName();
+
+        Bukkit.getScheduler().runTaskAsynchronously(Main.instance(), () -> {
+            OfflinePlayer player = Bukkit.getOfflinePlayer(playerName);
+            if (!player.hasPlayedBefore()) {
+                sender.sendMessage(Component.text("Der Spieler existiert nicht!", NamedTextColor.RED));
+                return;
+            }
+            sender.sendMessage(getAppliance().getFormattedPlaytime(player));
+        });
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/projectStart/listener/PlayerInvincibleListener.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/projectStart/listener/PlayerInvincibleListener.java
index 4fd1c30..89e116f 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/projectStart/listener/PlayerInvincibleListener.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/projectStart/listener/PlayerInvincibleListener.java
@@ -11,16 +11,17 @@ import org.bukkit.event.entity.FoodLevelChangeEvent;
 public class PlayerInvincibleListener extends ApplianceListener<ProjectStart> {
     @EventHandler
     public void onDamage(EntityDamageEvent event) {
-        if(event.getEntity() instanceof Player) event.setCancelled(getAppliance().isEnabled());
+        if(!(event.getEntity() instanceof Player)) return;
+        if(getAppliance().isEnabled()) event.setCancelled(true);
     }
 
     @EventHandler
     public void onHunger(FoodLevelChangeEvent event) {
-        event.setCancelled(getAppliance().isEnabled());
+        if(getAppliance().isEnabled()) event.setCancelled(true);
     }
 
     @EventHandler
     public void onHit(PrePlayerAttackEntityEvent event) {
-        event.setCancelled(getAppliance().isEnabled());
+        if(getAppliance().isEnabled()) event.setCancelled(true);
     }
 }
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/sleepTag/SleepStateChangeListener.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/sleepTag/SleepStateChangeListener.java
new file mode 100644
index 0000000..4179307
--- /dev/null
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/sleepTag/SleepStateChangeListener.java
@@ -0,0 +1,19 @@
+package eu.mhsl.craftattack.spawn.appliances.sleepTag;
+
+import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.player.PlayerBedEnterEvent;
+import org.bukkit.event.player.PlayerBedLeaveEvent;
+
+public class SleepStateChangeListener extends ApplianceListener<SleepTag> {
+    @EventHandler
+    public void onBedEnter(PlayerBedEnterEvent event) {
+        if(!event.getBedEnterResult().equals(PlayerBedEnterEvent.BedEnterResult.OK)) return;
+        getAppliance().updateSleeping(event.getPlayer(), true);
+    }
+
+    @EventHandler
+    public void onBedLeave(PlayerBedLeaveEvent event) {
+        getAppliance().updateSleeping(event.getPlayer(), false);
+    }
+}
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/sleepTag/SleepTag.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/sleepTag/SleepTag.java
new file mode 100644
index 0000000..1d92aff
--- /dev/null
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/sleepTag/SleepTag.java
@@ -0,0 +1,69 @@
+package eu.mhsl.craftattack.spawn.appliances.sleepTag;
+
+import eu.mhsl.craftattack.spawn.Main;
+import eu.mhsl.craftattack.spawn.appliance.Appliance;
+import eu.mhsl.craftattack.spawn.appliances.displayName.DisplayName;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.event.HoverEvent;
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.kyori.adventure.util.Ticks;
+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.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class SleepTag extends Appliance implements DisplayName.DisplayNamed {
+    private final Set<Player> sleepingPlayers = new HashSet<>();
+
+    @Override
+    public void onEnable() {
+        Bukkit.getScheduler().runTaskTimerAsynchronously(
+            Main.instance(),
+            this::cleanup,
+            Ticks.TICKS_PER_SECOND * 60,
+            Ticks.TICKS_PER_SECOND * 60
+        );
+    }
+
+    public void updateSleeping(Player player, boolean isSleeping) {
+        if(isSleeping) {
+            this.sleepingPlayers.add(player);
+        } else {
+            this.sleepingPlayers.remove(player);
+        }
+        this.updateDisplayName(player);
+    }
+
+    private void updateDisplayName(Player player) {
+        Main.instance().getAppliance(DisplayName.class).update(player);
+    }
+
+    private void cleanup() {
+        List<Player> invalidEntries = this.sleepingPlayers.stream()
+            .filter(player -> !player.isConnected())
+            .filter(player -> !player.isSleeping())
+            .toList();
+
+        invalidEntries.forEach(this.sleepingPlayers::remove);
+        invalidEntries.forEach(this::updateDisplayName);
+    }
+
+    @Override
+    public @Nullable Component getNamePrefix(Player player) {
+        if(this.sleepingPlayers.contains(player))
+            return Component.text("[\uD83D\uDCA4]", NamedTextColor.GRAY)
+                .hoverEvent(HoverEvent.showText(Component.text("Der Spieler liegt in einem Bett")));
+
+        return null;
+    }
+
+    @Override
+    protected @NotNull List<Listener> listeners() {
+        return List.of(new SleepStateChangeListener());
+    }
+}
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/yearRank/YearRank.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/yearRank/YearRank.java
index 75e3acb..0b97f9b 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/yearRank/YearRank.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/yearRank/YearRank.java
@@ -3,6 +3,7 @@ package eu.mhsl.craftattack.spawn.appliances.yearRank;
 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.displayName.DisplayName;
 import net.kyori.adventure.text.Component;
 import net.kyori.adventure.text.TextComponent;
 import net.kyori.adventure.text.event.ClickEvent;
@@ -15,7 +16,7 @@ import org.jetbrains.annotations.Nullable;
 import java.io.File;
 import java.util.*;
 
-public class YearRank extends Appliance {
+public class YearRank extends Appliance implements DisplayName.DisplayNamed {
     record CraftAttackYear(String name) {}
     private final Map<UUID, List<CraftAttackYear>> rankMap = new HashMap<>();
 
@@ -44,6 +45,7 @@ public class YearRank extends Appliance {
         });
     }
 
+    @Override
     public @Nullable Component getNamePrefix(Player player) {
         if(!rankMap.containsKey(player.getUniqueId())) return null;
         int yearCount = rankMap.get(player.getUniqueId()).size();
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/util/statistics/ServerMonitor.java b/src/main/java/eu/mhsl/craftattack/spawn/util/statistics/ServerMonitor.java
new file mode 100644
index 0000000..6c3fc89
--- /dev/null
+++ b/src/main/java/eu/mhsl/craftattack/spawn/util/statistics/ServerMonitor.java
@@ -0,0 +1,12 @@
+package eu.mhsl.craftattack.spawn.util.statistics;
+
+import org.bukkit.Bukkit;
+
+import java.util.Arrays;
+
+public class ServerMonitor {
+    public static float getServerMSPT() {
+        long[] times = Bukkit.getServer().getTickTimes();
+        return ((float) Arrays.stream(times).sum() / times.length) * 1.0E-6f;
+    }
+}
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/util/text/ColorUtil.java b/src/main/java/eu/mhsl/craftattack/spawn/util/text/ColorUtil.java
index e64aa99..2fd9f47 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/util/text/ColorUtil.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/util/text/ColorUtil.java
@@ -15,4 +15,13 @@ public class ColorUtil {
 
         return TextColor.color(Color.getHSBColor(hue / 360, 1f, 1f).getRGB());
     }
+
+    public static TextColor msptColor(float mspt) {
+        if(mspt > 50) return TextColor.color(255, 0, 0);
+        return ColorUtil.mapGreenToRed(mspt, 25, 60, true);
+    }
+
+    public static TextColor tpsColor(float tps) {
+        return ColorUtil.mapGreenToRed(tps, 15, 20, false);
+    }
 }
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/util/text/ComponentUtil.java b/src/main/java/eu/mhsl/craftattack/spawn/util/text/ComponentUtil.java
index 978ddd5..2364b27 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/util/text/ComponentUtil.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/util/text/ComponentUtil.java
@@ -1,6 +1,7 @@
 package eu.mhsl.craftattack.spawn.util.text;
 
 import eu.mhsl.craftattack.spawn.util.statistics.NetworkMonitor;
+import eu.mhsl.craftattack.spawn.util.statistics.ServerMonitor;
 import net.kyori.adventure.text.Component;
 import net.kyori.adventure.text.ComponentBuilder;
 import net.kyori.adventure.text.TextComponent;
@@ -12,7 +13,6 @@ import org.bukkit.entity.Player;
 import java.awt.*;
 import java.lang.management.OperatingSystemMXBean;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -55,15 +55,12 @@ public class ComponentUtil {
     }
 
     public static Component getFormattedTickTimes(boolean detailed) {
-        long[] times = Bukkit.getServer().getTickTimes();
-        float mspt = ((float) Arrays.stream(times).sum() / times.length) * 1.0E-6f;
+        float mspt = ServerMonitor.getServerMSPT();
         float roundedMspt = Math.round(mspt * 100f) / 100f;
         int loadPercentage = (int) (Math.min(100, (mspt / 50.0) * 100));
         float roundedTPS = Math.round(Bukkit.getTPS()[0] * 100f) / 100f;
 
-        TextColor msptColor = ColorUtil.mapGreenToRed(roundedMspt, 0, 50, true);
         TextColor percentageColor = ColorUtil.mapGreenToRed(loadPercentage, 80, 100, true);
-        TextColor tpsColor = ColorUtil.mapGreenToRed(roundedTPS, 15, 20, false);
 
         ComponentBuilder<TextComponent, TextComponent.Builder> tickTimes = Component.text()
             .append(Component.text("Serverlast: ", NamedTextColor.GRAY))
@@ -72,12 +69,12 @@ public class ComponentUtil {
 
         if(detailed) {
             tickTimes
-                .append(Component.text(roundedMspt + "mspt", msptColor))
+                .append(Component.text(roundedMspt + "mspt", ColorUtil.msptColor(mspt)))
                 .append(Component.text(" | ", NamedTextColor.GRAY));
         }
 
         return tickTimes
-            .append(Component.text(roundedTPS + "tps", tpsColor))
+            .append(Component.text(roundedTPS + "tps", ColorUtil.tpsColor(roundedTPS)))
             .build();
     }
 
diff --git a/src/main/java/eu/mhsl/craftattack/spawn/util/text/DataSizeConverter.java b/src/main/java/eu/mhsl/craftattack/spawn/util/text/DataSizeConverter.java
index 2154c3c..43a24a8 100644
--- a/src/main/java/eu/mhsl/craftattack/spawn/util/text/DataSizeConverter.java
+++ b/src/main/java/eu/mhsl/craftattack/spawn/util/text/DataSizeConverter.java
@@ -38,4 +38,18 @@ public class DataSizeConverter {
         hours %= 60;
         return String.format("%dd%dh%dm%ds", days, hours, minutes, seconds);
     }
+
+    public static String formatSecondsToHumanReadable(int seconds) {
+        if (seconds < 0) return "unsupported";
+
+        int minutes = seconds / 60;
+        int hours = minutes / 60;
+        int days = hours / 24;
+
+        seconds %= 60;
+        minutes %= 60;
+        hours %= 60;
+
+        return String.format("%dd %dh %dm %ds", days, hours, minutes, seconds);
+    }
 }
\ No newline at end of file
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index a6240d1..476f32b 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -66,4 +66,7 @@ packselect:
         author: "Pack Author(s)"
         url: "https://example.com/download/pack.zip"
         hash: "" # SHA1 hash of ZIP file (will be auto determined by the server on startup when not set)
-        icon: "" # base64 player-head texture, can be obtained from sites like https://minecraft-heads.com/ under developers > Value
\ No newline at end of file
+        icon: "" # base64 player-head texture, can be obtained from sites like https://minecraft-heads.com/ under developers > Value
+
+endPrevent:
+  endDisabled: true
\ No newline at end of file
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 0a3437b..b4a0607 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -41,4 +41,10 @@ commands:
   maintanance:
   yearRank:
   msg:
-  r:
\ No newline at end of file
+  r:
+  playtime:
+  adminchat:
+    aliases: [ "sc" ]
+  acInform:
+  infobar:
+  endPrevent: