From ca0e364d85cddb65844f9017645ce4123d6872b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Sat, 17 Oct 2026 22:41:48 +0200 Subject: [PATCH] chat moves to the velocity plugin chat-bridge: backend only silences chat and sends rank prefixes --- .../metaGameplay/chatMention/ChatMention.java | 66 ------ .../chatMention/ChatMentionListener.java | 56 ----- .../chatMention/ChatMentionSetting.java | 49 ----- .../chatMessages/ChatMessages.java | 36 ---- .../chatMessages/ChatMessagesListener.java | 74 ------- .../ShowJoinAndLeaveMessagesSetting.java | 38 ---- .../metaGameplay/displayName/DisplayName.java | 24 ++- .../metaGameplay/networkChat/NetworkChat.java | 59 ++++++ .../networkChat/NetworkChatListener.java | 73 +++++++ .../privateMessage/PrivateMessage.java | 194 ------------------ .../commands/PrivateMessageCommand.java | 18 -- .../commands/PrivateReplyCommand.java | 18 -- .../metaGameplay/settings/Settings.java | 2 - .../tooling/adminChat/AdminChat.java | 37 ---- .../tooling/adminChat/AdminChatCommand.java | 22 -- .../appliances/tooling/chatMute/ChatMute.java | 44 ---- .../tooling/chatMute/ChatMuteListener.java | 25 --- .../tooling/chatMute/MuteCommand.java | 35 ---- 18 files changed, 151 insertions(+), 719 deletions(-) delete mode 100644 common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMention/ChatMention.java delete mode 100644 common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMention/ChatMentionListener.java delete mode 100644 common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMention/ChatMentionSetting.java delete mode 100644 common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMessages/ChatMessages.java delete mode 100644 common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMessages/ChatMessagesListener.java delete mode 100644 common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMessages/ShowJoinAndLeaveMessagesSetting.java create mode 100644 common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/networkChat/NetworkChat.java create mode 100644 common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/networkChat/NetworkChatListener.java delete mode 100644 common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/privateMessage/PrivateMessage.java delete mode 100644 common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/privateMessage/commands/PrivateMessageCommand.java delete mode 100644 common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/privateMessage/commands/PrivateReplyCommand.java delete mode 100644 common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/adminChat/AdminChat.java delete mode 100644 common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/adminChat/AdminChatCommand.java delete mode 100644 common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/chatMute/ChatMute.java delete mode 100644 common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/chatMute/ChatMuteListener.java delete mode 100644 common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/chatMute/MuteCommand.java diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMention/ChatMention.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMention/ChatMention.java deleted file mode 100644 index 964c3d2..0000000 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMention/ChatMention.java +++ /dev/null @@ -1,66 +0,0 @@ -package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMention; - -import eu.mhsl.craftattack.spawn.core.Main; -import eu.mhsl.craftattack.spawn.core.appliance.Appliance; -import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings; -import net.kyori.adventure.sound.Sound; -import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; -import org.bukkit.event.Listener; -import org.jetbrains.annotations.NotNull; - -import java.util.Arrays; -import java.util.List; -import java.util.Objects; - -public class ChatMention extends Appliance { - private static List playerNames; - - public String formatPlayer(String name) { - return "@" + name; - } - - public List getPlayerNames() { - return playerNames; - } - - public void refreshPlayers() { - Bukkit.getScheduler().runTaskAsynchronously( - Main.instance(), - () -> playerNames = Arrays.stream(Bukkit.getOfflinePlayers()) - .map(OfflinePlayer::getName) - .filter(Objects::nonNull) - .toList() - ); - } - - public void notifyPlayers(List playerNames) { - playerNames.stream() - .distinct() - .map(Bukkit::getPlayer) - .filter(Objects::nonNull) - .filter(player -> Settings.instance() - .getSetting(player, Settings.Key.ChatMentions, ChatMentionSetting.ChatMentionConfig.class) - .notifyOnMention() - ) - .forEach(player -> player.playSound( - Sound.sound( - org.bukkit.Sound.ENTITY_EXPERIENCE_ORB_PICKUP, - Sound.Source.PLAYER, - 1.0f, - 1.0f - ) - )); - } - - @Override - public void onEnable() { - Settings.instance().declareSetting(ChatMentionSetting.class); - this.refreshPlayers(); - } - - @Override - protected @NotNull List listeners() { - return List.of(new ChatMentionListener()); - } -} diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMention/ChatMentionListener.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMention/ChatMentionListener.java deleted file mode 100644 index be1fcf5..0000000 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMention/ChatMentionListener.java +++ /dev/null @@ -1,56 +0,0 @@ -package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMention; - -import eu.mhsl.craftattack.spawn.core.Main; -import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener; -import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMessages.ChatMessages; -import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings; -import eu.mhsl.craftattack.spawn.core.util.text.ComponentUtil; -import io.papermc.paper.event.player.AsyncChatDecorateEvent; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; -import org.bukkit.event.EventHandler; -import org.bukkit.event.player.PlayerJoinEvent; - -import java.util.ArrayList; -import java.util.List; - -class ChatMentionListener extends ApplianceListener { - @SuppressWarnings("UnstableApiUsage") - @EventHandler - public void coloringEvent(AsyncChatDecorateEvent event) { - String message = PlainTextComponentSerializer.plainText().serialize(event.result()); - List words = List.of(message.split(" ")); - List mentioned = new ArrayList<>(); - - ChatMentionSetting.ChatMentionConfig config = Settings.instance() - .getSetting(event.player(), Settings.Key.ChatMentions, ChatMentionSetting.ChatMentionConfig.class); - ChatMessages chatMessages = Main.instance().getAppliance(ChatMessages.class); - - Component result = words.stream() - .map(word -> { - String wordWithoutAnnotation = word.replace("@", ""); - boolean isPlayer = this.getAppliance().getPlayerNames().contains(wordWithoutAnnotation); - if(isPlayer && config.applyMentions()) { - mentioned.add(wordWithoutAnnotation); - Component mention = Component.text( - this.getAppliance().formatPlayer(wordWithoutAnnotation), - NamedTextColor.GOLD - ); - return chatMessages.addReportActions(mention, wordWithoutAnnotation); - } else { - return Component.text(word); - } - }) - .reduce(ComponentUtil::appendWithSpace) - .orElseThrow(); - - this.getAppliance().notifyPlayers(mentioned); - event.result(result); - } - - @EventHandler - public void onJoin(PlayerJoinEvent event) { - this.getAppliance().refreshPlayers(); - } -} diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMention/ChatMentionSetting.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMention/ChatMentionSetting.java deleted file mode 100644 index bba73b4..0000000 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMention/ChatMentionSetting.java +++ /dev/null @@ -1,49 +0,0 @@ -package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMention; - -import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.CategorizedSetting; -import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.SettingCategory; -import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings; -import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes.MultiBoolSetting; -import org.bukkit.Material; - -public class ChatMentionSetting extends MultiBoolSetting implements CategorizedSetting { - @Override - public SettingCategory category() { - return SettingCategory.Visuals; - } - - public record ChatMentionConfig( - @DisplayName("Spielernamen hervorheben") boolean applyMentions, - @DisplayName("Benachrichtigungston") boolean notifyOnMention - ) { - } - - public ChatMentionSetting() { - super(Settings.Key.ChatMentions); - } - - @Override - protected String title() { - return "Erwähnungen im Chat"; - } - - @Override - protected String description() { - return "Erwähnungen werden automatisch im Chat angewandt und der Empfänger erhält einen Signalton"; - } - - @Override - protected Material icon() { - return Material.FEATHER; - } - - @Override - protected ChatMentionConfig defaultValue() { - return new ChatMentionConfig(true, true); - } - - @Override - public Class dataType() { - return ChatMentionConfig.class; - } -} diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMessages/ChatMessages.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMessages/ChatMessages.java deleted file mode 100644 index e0d8946..0000000 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMessages/ChatMessages.java +++ /dev/null @@ -1,36 +0,0 @@ -package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMessages; - -import eu.mhsl.craftattack.spawn.core.appliance.Appliance; -import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.event.ClickEvent; -import net.kyori.adventure.text.event.HoverEvent; -import net.kyori.adventure.text.format.NamedTextColor; -import org.bukkit.entity.Player; -import org.bukkit.event.Listener; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -public class ChatMessages extends Appliance { - @Override - public void onEnable() { - Settings.instance().declareSetting(ShowJoinAndLeaveMessagesSetting.class); - } - - public Component getReportablePlayerName(Player player) { - return this.addReportActions(player.displayName(), player.getName()); - } - - public Component addReportActions(Component message, String username) { - return message - .hoverEvent(HoverEvent.showText(Component.text("Klicke, um diesen Spieler zu reporten").color(NamedTextColor.GOLD))) - .clickEvent(ClickEvent.suggestCommand(String.format("/report %s ", username))); - } - - @Override - @NotNull - protected List listeners() { - return List.of(new ChatMessagesListener()); - } -} diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMessages/ChatMessagesListener.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMessages/ChatMessagesListener.java deleted file mode 100644 index 561c5bf..0000000 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMessages/ChatMessagesListener.java +++ /dev/null @@ -1,74 +0,0 @@ -package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMessages; - -import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener; -import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings; -import eu.mhsl.craftattack.spawn.core.util.IteratorUtil; -import io.papermc.paper.event.player.AsyncChatEvent; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import net.kyori.adventure.text.format.TextColor; -import org.bukkit.Color; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.entity.PlayerDeathEvent; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerQuitEvent; - -import java.util.Optional; - -class ChatMessagesListener extends ApplianceListener { - @EventHandler - public void onPlayerChatEvent(AsyncChatEvent event) { - event.renderer( - (source, sourceDisplayName, message, viewer) -> - Component.text("") - .append(this.getAppliance().getReportablePlayerName(source)) - .append(Component.text(" > ").color(TextColor.color(Color.GRAY.asRGB()))) - .append(message).color(TextColor.color(Color.SILVER.asRGB())) - ); - } - - @EventHandler(priority = EventPriority.HIGH) - public void onPlayerJoin(PlayerJoinEvent event) { - boolean wasHidden = event.joinMessage() == null; - event.joinMessage(null); - if(wasHidden) return; - IteratorUtil.onlinePlayers(player -> { - if(!Settings.instance().getSetting(player, Settings.Key.ShowJoinAndLeaveMessages, Boolean.class)) return; - player.sendMessage( - Component - .text(">>> ").color(NamedTextColor.GREEN) - .append(this.getAppliance().getReportablePlayerName(event.getPlayer())) - ); - }); - } - - @EventHandler - public void onPlayerLeave(PlayerQuitEvent event) { - boolean wasHidden = event.quitMessage() == null; - event.quitMessage(null); - if(wasHidden) return; - IteratorUtil.onlinePlayers(player -> { - if(!Settings.instance().getSetting(player, Settings.Key.ShowJoinAndLeaveMessages, Boolean.class)) return; - player.sendMessage( - Component - .text("<<< ").color(NamedTextColor.RED) - .append(this.getAppliance().getReportablePlayerName(event.getPlayer())) - ); - }); - } - - @EventHandler - public void onDeath(PlayerDeathEvent event) { - event.deathMessage( - Component - .text("☠ ") - .append( - Optional - .ofNullable(event.deathMessage()) - .orElse(Component.text(event.getPlayer().getName())) - ) - .color(TextColor.color(Color.SILVER.asRGB())) - ); - } -} diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMessages/ShowJoinAndLeaveMessagesSetting.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMessages/ShowJoinAndLeaveMessagesSetting.java deleted file mode 100644 index 272d491..0000000 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/chatMessages/ShowJoinAndLeaveMessagesSetting.java +++ /dev/null @@ -1,38 +0,0 @@ -package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMessages; - -import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.CategorizedSetting; -import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.SettingCategory; -import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings; -import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes.BoolSetting; -import org.bukkit.Material; - -public class ShowJoinAndLeaveMessagesSetting extends BoolSetting implements CategorizedSetting { - public ShowJoinAndLeaveMessagesSetting() { - super(Settings.Key.ShowJoinAndLeaveMessages); - } - - @Override - protected String title() { - return "Join & Leave Nachrichten anzeigen"; - } - - @Override - protected String description() { - return "Zeige allgemeine Beitritts und Verlassensmeldungen im Chat"; - } - - @Override - protected Material icon() { - return Material.PLAYER_HEAD; - } - - @Override - protected Boolean defaultValue() { - return true; - } - - @Override - public SettingCategory category() { - return SettingCategory.Visuals; - } -} diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/displayName/DisplayName.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/displayName/DisplayName.java index 2e9ec2d..eb05be0 100644 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/displayName/DisplayName.java +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/displayName/DisplayName.java @@ -2,10 +2,12 @@ package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.displayName; import eu.mhsl.craftattack.spawn.core.Main; import eu.mhsl.craftattack.spawn.core.appliance.Appliance; +import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.networkChat.NetworkChat; import eu.mhsl.craftattack.spawn.core.util.server.Floodgate; import eu.mhsl.craftattack.spawn.core.util.text.ComponentUtil; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.ComponentBuilder; +import net.kyori.adventure.text.JoinConfiguration; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.format.NamedTextColor; @@ -49,15 +51,27 @@ public class DisplayName extends Appliance { .map(appliance -> (Prefixed) appliance) .toList(); - ComponentBuilder playerName = Component.text(); - prefixes.stream() + List prefixComponents = prefixes.stream() .map(prefixed -> prefixed.getNamePrefix(player)) .filter(Objects::nonNull) - .forEach(prefix -> playerName - .append(prefix) - .append(ComponentUtil.clearedSpace()) + .toList(); + + ComponentBuilder playerName = Component.text(); + prefixComponents.forEach(prefix -> playerName + .append(prefix) + .append(ComponentUtil.clearedSpace()) ); + // Den Chat-Namen baut Velocity selbst (Farbe, Bedrock), es braucht von hier nur die Prefixe + Main.instance().getAppliances().stream() + .filter(NetworkChat.class::isInstance) + .map(NetworkChat.class::cast) + .findFirst() + .ifPresent(networkChat -> networkChat.setPrefix( + player, + prefixComponents.isEmpty() ? null : Component.join(JoinConfiguration.separator(ComponentUtil.clearedSpace()), prefixComponents) + )); + if(Floodgate.isBedrock(player)) { playerName .append( diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/networkChat/NetworkChat.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/networkChat/NetworkChat.java new file mode 100644 index 0000000..0d1decf --- /dev/null +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/networkChat/NetworkChat.java @@ -0,0 +1,59 @@ +package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.networkChat; + +import com.google.common.io.ByteArrayDataOutput; +import com.google.common.io.ByteStreams; +import eu.mhsl.craftattack.spawn.core.Main; +import eu.mhsl.craftattack.spawn.core.appliance.Appliance; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +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.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Chat läuft über Velocity (Plugin chat-bridge): Der öffentliche Chat, /msg, /r, /adminchat, /mute und + * Join/Leave-Meldungen gelten im ganzen Netzwerk. Hier wird der Chat nur verworfen und Velocity + * bekommt die Rang-Prefixe für den Namen. + */ +public class NetworkChat extends Appliance { + public static final String channel = "craftattack:chat"; + + private final Map prefixes = new ConcurrentHashMap<>(); + + @Override + public void onEnable() { + Bukkit.getMessenger().registerOutgoingPluginChannel(Main.instance(), channel); + } + + public void setPrefix(Player player, @Nullable Component prefix) { + if(prefix == null) this.prefixes.remove(player.getUniqueId()); + else this.prefixes.put(player.getUniqueId(), prefix); + this.sendPrefix(player); + } + + void sendPrefix(Player player) { + // Velocity meldet den Kanal erst kurz nach dem Join an, dann folgt PlayerRegisterChannelEvent + if(!player.getListeningPluginChannels().contains(channel)) return; + Component prefix = this.prefixes.get(player.getUniqueId()); + ByteArrayDataOutput output = ByteStreams.newDataOutput(); + output.writeUTF("prefix"); + output.writeUTF(prefix == null ? "" : GsonComponentSerializer.gson().serialize(prefix)); + player.sendPluginMessage(Main.instance(), channel, output.toByteArray()); + } + + void forget(Player player) { + this.prefixes.remove(player.getUniqueId()); + } + + @Override + protected @NotNull List listeners() { + return List.of(new NetworkChatListener()); + } +} diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/networkChat/NetworkChatListener.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/networkChat/NetworkChatListener.java new file mode 100644 index 0000000..aa375d7 --- /dev/null +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/networkChat/NetworkChatListener.java @@ -0,0 +1,73 @@ +package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.networkChat; + +import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener; +import io.papermc.paper.event.player.AsyncChatEvent; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextColor; +import org.bukkit.Color; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.player.PlayerRegisterChannelEvent; + +import java.util.Locale; +import java.util.Optional; +import java.util.Set; + +class NetworkChatListener extends ApplianceListener { + // Vanilla-Befehle, die am Proxy vorbei in den lokalen Chat schreiben; /msg, /tell, /w fängt Velocity ab + private static final Set localChatCommands = Set.of( + "me", "minecraft:me", "teammsg", "tm", "minecraft:teammsg", "minecraft:tm", + "minecraft:msg", "minecraft:tell", "minecraft:w" + ); + + @EventHandler(priority = EventPriority.HIGHEST) + public void onChat(AsyncChatEvent event) { + // Velocity hat die Nachricht schon an alle verteilt + event.setCancelled(true); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onCommand(PlayerCommandPreprocessEvent event) { + String label = event.getMessage().substring(1).split(" ", 2)[0].toLowerCase(Locale.ROOT); + if(!localChatCommands.contains(label)) return; + event.setCancelled(true); + event.getPlayer().sendMessage(Component.text("Dieser Befehl ist hier nicht verfügbar.", NamedTextColor.RED)); + } + + @EventHandler + public void onRegisterChannel(PlayerRegisterChannelEvent event) { + if(!event.getChannel().equals(NetworkChat.channel)) return; + this.getAppliance().sendPrefix(event.getPlayer()); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onJoin(PlayerJoinEvent event) { + // Join- und Leave-Meldungen schreibt Velocity, beim Serverwechsel gar keine + event.joinMessage(null); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onQuit(PlayerQuitEvent event) { + event.quitMessage(null); + this.getAppliance().forget(event.getPlayer()); + } + + @EventHandler + public void onDeath(PlayerDeathEvent event) { + event.deathMessage( + Component + .text("☠ ") + .append( + Optional + .ofNullable(event.deathMessage()) + .orElse(Component.text(event.getPlayer().getName())) + ) + .color(TextColor.color(Color.SILVER.asRGB())) + ); + } +} diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/privateMessage/PrivateMessage.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/privateMessage/PrivateMessage.java deleted file mode 100644 index a571c53..0000000 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/privateMessage/PrivateMessage.java +++ /dev/null @@ -1,194 +0,0 @@ -package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage; - -import eu.mhsl.craftattack.spawn.core.Main; -import eu.mhsl.craftattack.spawn.core.appliance.Appliance; -import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand; -import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.chatMessages.ChatMessages; -import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage.commands.PrivateMessageCommand; -import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage.commands.PrivateReplyCommand; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.ComponentBuilder; -import net.kyori.adventure.text.TextComponent; -import net.kyori.adventure.text.event.ClickEvent; -import net.kyori.adventure.text.event.HoverEvent; -import net.kyori.adventure.text.format.NamedTextColor; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; - -import java.util.*; -import java.util.stream.Collectors; - -public class PrivateMessage extends Appliance { - public final int targetChangeTimeoutSeconds = 30; - public final int conversationTimeoutMinutes = 30; - - private record Conversation(UUID target, Long lastSet) { - } - - private final Map> replyMapping = new WeakHashMap<>(); - - public void reply(Player sender, String message) { - this.replyMapping.computeIfAbsent(sender, player -> new ArrayList<>()); - - List replyList = this.replyMapping.get(sender); - - List tooOldConversations = replyList.stream() - .filter(conversation -> conversation.lastSet < System.currentTimeMillis() - (this.conversationTimeoutMinutes * 60 * 1000)) - .toList(); - replyList.removeAll(tooOldConversations); - - if(replyList.isEmpty()) throw new ApplianceCommand.Error("Du führst aktuell keine Konversation."); - - Component privatePrefix = Component.text("[Privat] ", NamedTextColor.LIGHT_PURPLE); - - Conversation youngestEntry = replyList.stream() - .max(Comparator.comparingLong(o -> o.lastSet)) - .orElse(replyList.getLast()); - - if(message.isBlank()) { - Player currentTargetPlayer = Bukkit.getPlayer(youngestEntry.target()); - - Component currentTargetComponent = currentTargetPlayer != null - ? Main.instance().getAppliance(ChatMessages.class).getReportablePlayerName(currentTargetPlayer) - : Component.text("niemandem."); - - sender.sendMessage( - privatePrefix - .append(Component.text("Du schreibst aktuell mit ", NamedTextColor.GRAY)) - .append(currentTargetComponent) - ); - return; - } - - List oldConversations = replyList.stream() - .filter(conversation -> conversation.lastSet < System.currentTimeMillis() - (this.targetChangeTimeoutSeconds * 1000)) - .toList(); - - if(oldConversations.contains(youngestEntry) || replyList.size() == 1) { - Player target = Bukkit.getPlayer(youngestEntry.target()); - if(target == null) - throw new ApplianceCommand.Error("Der Spieler " + Bukkit.getOfflinePlayer(youngestEntry.target()).getName() + " ist nicht mehr verfügbar."); - - replyList.clear(); - this.sendWhisper(sender, new ResolvedPmUserArguments(target, message)); - return; - } - - ComponentBuilder component = Component.text(); - - component.append( - Component.newline() - .append(privatePrefix) - .append(Component.text("Das Ziel für /r hat sich bei dir in den letzten ", NamedTextColor.RED)) - .append(Component.text(String.valueOf(this.targetChangeTimeoutSeconds), NamedTextColor.RED)) - .append(Component.text(" Sekunden geändert. Wer soll deine Nachricht erhalten? ", NamedTextColor.RED)) - .appendNewline() - .appendNewline() - ); - - if(!oldConversations.isEmpty()) { - Conversation youngestOldConversation = oldConversations.stream() - .max(Comparator.comparingLong(o -> o.lastSet)) - .orElse(oldConversations.getLast()); - replyList.removeAll(oldConversations); - replyList.add(youngestOldConversation); - } - - List playerNames = replyList.stream() - .map(conversation -> Bukkit.getOfflinePlayer(conversation.target()).getName()) - .distinct() - .toList(); - - playerNames.forEach(playerName -> component.append( - Component.text("[") - .append(Component.text(playerName, NamedTextColor.GOLD)) - .append(Component.text("]")) - .clickEvent(ClickEvent.runCommand(String.format("/msg %s %s", playerName, message))) - .hoverEvent(HoverEvent.showText(Component.text("Klicke, um diesem Spieler zu schreiben.").color(NamedTextColor.GOLD)))) - .append(Component.text(" ")) - ); - component.appendNewline(); - - sender.sendMessage(component.build()); - - } - - public void sendWhisper(Player sender, ResolvedPmUserArguments userArguments) { - Conversation newReceiverConversation = new Conversation( - sender.getUniqueId(), - System.currentTimeMillis() - ); - - if(this.replyMapping.get(userArguments.receiver) != null) { - List oldEntries = this.replyMapping.get(userArguments.receiver).stream() - .filter(conversation -> conversation.target() == sender.getUniqueId()) - .toList(); - this.replyMapping.get(userArguments.receiver).removeAll(oldEntries); - } else { - this.replyMapping.put( - userArguments.receiver, - new ArrayList<>() - ); - } - - this.replyMapping.get(userArguments.receiver).add(newReceiverConversation); - - List senderConversationList = new ArrayList<>(); - senderConversationList.add( - new Conversation( - userArguments.receiver.getUniqueId(), - System.currentTimeMillis() - ) - ); - - this.replyMapping.put( - sender, - senderConversationList - ); - - ChatMessages chatMessages = Main.instance().getAppliance(ChatMessages.class); - Component privatePrefix = Component.text("[Privat] ", NamedTextColor.LIGHT_PURPLE); - - sender.sendMessage( - Component.text() - .append(privatePrefix.clickEvent(ClickEvent.suggestCommand(String.format("/msg %s ", userArguments.receiver.getName())))) - .append(sender.displayName()) - .append(Component.text(" zu ", NamedTextColor.GRAY)) - .append(chatMessages.getReportablePlayerName(userArguments.receiver)) - .append(Component.text(" > ", NamedTextColor.GRAY)) - .append(Component.text(userArguments.message)) - ); - userArguments.receiver.sendMessage( - Component.text() - .append(privatePrefix.clickEvent(ClickEvent.suggestCommand(String.format("/msg %s ", sender.getName())))) - .append(chatMessages.getReportablePlayerName(sender)) - .append(Component.text(" zu ", NamedTextColor.GRAY)) - .append(userArguments.receiver.displayName()) - .append(Component.text(" > ", NamedTextColor.GRAY)) - .append(Component.text(userArguments.message)) - ); - } - - public record ResolvedPmUserArguments(Player receiver, String message) { - } - - public ResolvedPmUserArguments resolveImplicit(String[] args) { - if(args.length < 2) - throw new ApplianceCommand.Error("Es muss ein Spieler sowie eine Nachricht angegeben werden."); - List arguments = List.of(args); - Player targetPlayer = Bukkit.getPlayer(arguments.getFirst()); - if(targetPlayer == null) - throw new ApplianceCommand.Error(String.format("Der Spieler %s konnte nicht gefunden werden.", arguments.getFirst())); - String message = arguments.stream().skip(1).collect(Collectors.joining(" ")); - return new ResolvedPmUserArguments(targetPlayer, message); - } - - @Override - protected @NotNull List> commands() { - return List.of( - new PrivateMessageCommand(), - new PrivateReplyCommand() - ); - } -} diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/privateMessage/commands/PrivateMessageCommand.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/privateMessage/commands/PrivateMessageCommand.java deleted file mode 100644 index 35df21e..0000000 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/privateMessage/commands/PrivateMessageCommand.java +++ /dev/null @@ -1,18 +0,0 @@ -package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage.commands; - -import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand; -import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage.PrivateMessage; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.jetbrains.annotations.NotNull; - -public class PrivateMessageCommand extends ApplianceCommand.PlayerChecked { - public PrivateMessageCommand() { - super("msg"); - } - - @Override - protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception { - this.getAppliance().sendWhisper(this.getPlayer(), this.getAppliance().resolveImplicit(args)); - } -} diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/privateMessage/commands/PrivateReplyCommand.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/privateMessage/commands/PrivateReplyCommand.java deleted file mode 100644 index 18f430c..0000000 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/privateMessage/commands/PrivateReplyCommand.java +++ /dev/null @@ -1,18 +0,0 @@ -package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage.commands; - -import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand; -import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.privateMessage.PrivateMessage; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.jetbrains.annotations.NotNull; - -public class PrivateReplyCommand extends ApplianceCommand.PlayerChecked { - public PrivateReplyCommand() { - super("r"); - } - - @Override - protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception { - this.getAppliance().reply(this.getPlayer(), String.join(" ", args)); - } -} diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/settings/Settings.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/settings/Settings.java index 2a6a0fc..146987e 100644 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/settings/Settings.java +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/settings/Settings.java @@ -26,13 +26,11 @@ public class Settings extends Appliance { public enum Key { TechnicalTab, - ShowJoinAndLeaveMessages, EnablePortableCrafting, EnableSettingsShortcut, AutoShulker, SignEdit, HotbarReplacer, - ChatMentions, DoubleDoors, KnockDoors, BorderWarning, diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/adminChat/AdminChat.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/adminChat/AdminChat.java deleted file mode 100644 index c3786ce..0000000 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/adminChat/AdminChat.java +++ /dev/null @@ -1,37 +0,0 @@ -package eu.mhsl.craftattack.spawn.common.appliances.tooling.adminChat; - -import eu.mhsl.craftattack.spawn.core.appliance.Appliance; -import eu.mhsl.craftattack.spawn.core.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> commands() { - return List.of(new AdminChatCommand()); - } -} diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/adminChat/AdminChatCommand.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/adminChat/AdminChatCommand.java deleted file mode 100644 index dfa9fda..0000000 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/adminChat/AdminChatCommand.java +++ /dev/null @@ -1,22 +0,0 @@ -package eu.mhsl.craftattack.spawn.common.appliances.tooling.adminChat; - -import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.jetbrains.annotations.NotNull; - -class AdminChatCommand extends ApplianceCommand.PlayerChecked { - 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); - this.getAppliance().sendMessage(this.getPlayer(), message); - } -} diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/chatMute/ChatMute.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/chatMute/ChatMute.java deleted file mode 100644 index 0ffe686..0000000 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/chatMute/ChatMute.java +++ /dev/null @@ -1,44 +0,0 @@ -package eu.mhsl.craftattack.spawn.common.appliances.tooling.chatMute; - -import eu.mhsl.craftattack.spawn.core.appliance.Appliance; -import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand; -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 org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.Locale; -import java.util.Objects; - -public class ChatMute extends Appliance { - private static final String namespace = ChatMute.class.getSimpleName().toLowerCase(Locale.ROOT); - public static NamespacedKey mutedUntilKey = new NamespacedKey(namespace, "mutedUntilMillis".toLowerCase()); - - public void mutePlayer(Player player, int durationHours) { - PersistentDataContainer container = player.getPersistentDataContainer(); - long mutedUntil = System.currentTimeMillis() + (long) durationHours * 60 * 60 * 1000; - container.set(ChatMute.mutedUntilKey, PersistentDataType.LONG, mutedUntil); - } - - public @Nullable Long muteStatus(Player player) { - PersistentDataContainer container = player.getPersistentDataContainer(); - if(!container.has(mutedUntilKey)) return null; - long mutedUntil = Objects.requireNonNull(container.get(mutedUntilKey, PersistentDataType.LONG)); - if(mutedUntil < System.currentTimeMillis()) return null; - return mutedUntil; - } - - @Override - protected @NotNull List> commands() { - return List.of(new MuteCommand()); - } - - @Override - protected @NotNull List listeners() { - return List.of(new ChatMuteListener()); - } -} diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/chatMute/ChatMuteListener.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/chatMute/ChatMuteListener.java deleted file mode 100644 index 0e57ba2..0000000 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/chatMute/ChatMuteListener.java +++ /dev/null @@ -1,25 +0,0 @@ -package eu.mhsl.craftattack.spawn.common.appliances.tooling.chatMute; - -import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener; -import eu.mhsl.craftattack.spawn.core.util.text.DataSizeConverter; -import io.papermc.paper.event.player.AsyncChatEvent; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import org.bukkit.event.EventHandler; -import org.jetbrains.annotations.Nullable; - -class ChatMuteListener extends ApplianceListener { - @EventHandler - public void onChat(AsyncChatEvent event) { - @Nullable Long muteDuration = this.getAppliance().muteStatus(event.getPlayer()); - if(muteDuration == null) return; - event.setCancelled(true); - event.getPlayer().sendMessage(Component.text( - String.format( - "Du bist für %s gestummt!", - DataSizeConverter.formatSecondsToHumanReadable((int) ((muteDuration - System.currentTimeMillis()) / 1000)) - ), - NamedTextColor.RED - )); - } -} diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/chatMute/MuteCommand.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/chatMute/MuteCommand.java deleted file mode 100644 index f9e3d86..0000000 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/chatMute/MuteCommand.java +++ /dev/null @@ -1,35 +0,0 @@ -package eu.mhsl.craftattack.spawn.common.appliances.tooling.chatMute; - -import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand; -import org.bukkit.Bukkit; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.Objects; - -class MuteCommand extends ApplianceCommand { - public MuteCommand() { - super("mute"); - } - - @Override - protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception { - if(args.length < 2) throw new Error("Syntax: mute "); - Player player = Objects.requireNonNull(Bukkit.getPlayer(args[0])); - int durationInHours = Integer.parseInt(args[1]); - this.getAppliance().mutePlayer(player, durationInHours); - sender.sendMessage(String.format("%s wurde für %d Stunden gestummt!", player.getName(), durationInHours)); - } - - @Override - public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - if(args.length == 2) { - return List.of("1", "2", "4", "8", "24", "48"); - } - return null; - } -}