chat moves to the velocity plugin chat-bridge: backend only silences chat and sends rank prefixes
This commit is contained in:
1 parent
fb88856925
commit
ca0e364d85
18 files changed
+149
-717
No files matched your search
-66
@@ -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<String> playerNames;
|
||||
|
||||
public String formatPlayer(String name) {
|
||||
return "@" + name;
|
||||
}
|
||||
|
||||
public List<String> 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<String> 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<Listener> listeners() {
|
||||
return List.of(new ChatMentionListener());
|
||||
}
|
||||
}
|
||||
-56
@@ -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<ChatMention> {
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
@EventHandler
|
||||
public void coloringEvent(AsyncChatDecorateEvent event) {
|
||||
String message = PlainTextComponentSerializer.plainText().serialize(event.result());
|
||||
List<String> words = List.of(message.split(" "));
|
||||
List<String> 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();
|
||||
}
|
||||
}
|
||||
-49
@@ -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<ChatMentionSetting.ChatMentionConfig> 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;
|
||||
}
|
||||
}
|
||||
-36
@@ -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<Listener> listeners() {
|
||||
return List.of(new ChatMessagesListener());
|
||||
}
|
||||
}
|
||||
-74
@@ -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<ChatMessages> {
|
||||
@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()))
|
||||
);
|
||||
}
|
||||
}
|
||||
-38
@@ -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;
|
||||
}
|
||||
}
|
||||
+17
-3
@@ -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<TextComponent, TextComponent.Builder> playerName = Component.text();
|
||||
prefixes.stream()
|
||||
List<Component> prefixComponents = prefixes.stream()
|
||||
.map(prefixed -> prefixed.getNamePrefix(player))
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(prefix -> playerName
|
||||
.toList();
|
||||
|
||||
ComponentBuilder<TextComponent, TextComponent.Builder> 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(
|
||||
|
||||
+59
@@ -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<UUID, Component> 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<Listener> listeners() {
|
||||
return List.of(new NetworkChatListener());
|
||||
}
|
||||
}
|
||||
+73
@@ -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<NetworkChat> {
|
||||
// Vanilla-Befehle, die am Proxy vorbei in den lokalen Chat schreiben; /msg, /tell, /w fängt Velocity ab
|
||||
private static final Set<String> 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()))
|
||||
);
|
||||
}
|
||||
}
|
||||
-194
@@ -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<Player, List<Conversation>> replyMapping = new WeakHashMap<>();
|
||||
|
||||
public void reply(Player sender, String message) {
|
||||
this.replyMapping.computeIfAbsent(sender, player -> new ArrayList<>());
|
||||
|
||||
List<Conversation> replyList = this.replyMapping.get(sender);
|
||||
|
||||
List<Conversation> 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<Conversation> 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<TextComponent, TextComponent.Builder> 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<String> 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<Conversation> 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<Conversation> 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<String> 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<ApplianceCommand<?>> commands() {
|
||||
return List.of(
|
||||
new PrivateMessageCommand(),
|
||||
new PrivateReplyCommand()
|
||||
);
|
||||
}
|
||||
}
|
||||
-18
@@ -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<PrivateMessage> {
|
||||
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));
|
||||
}
|
||||
}
|
||||
-18
@@ -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<PrivateMessage> {
|
||||
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));
|
||||
}
|
||||
}
|
||||
-2
@@ -26,13 +26,11 @@ public class Settings extends Appliance {
|
||||
|
||||
public enum Key {
|
||||
TechnicalTab,
|
||||
ShowJoinAndLeaveMessages,
|
||||
EnablePortableCrafting,
|
||||
EnableSettingsShortcut,
|
||||
AutoShulker,
|
||||
SignEdit,
|
||||
HotbarReplacer,
|
||||
ChatMentions,
|
||||
DoubleDoors,
|
||||
KnockDoors,
|
||||
BorderWarning,
|
||||
|
||||
-37
@@ -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<ApplianceCommand<?>> commands() {
|
||||
return List.of(new AdminChatCommand());
|
||||
}
|
||||
}
|
||||
-22
@@ -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<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);
|
||||
this.getAppliance().sendMessage(this.getPlayer(), message);
|
||||
}
|
||||
}
|
||||
-44
@@ -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<ApplianceCommand<?>> commands() {
|
||||
return List.of(new MuteCommand());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull List<Listener> listeners() {
|
||||
return List.of(new ChatMuteListener());
|
||||
}
|
||||
}
|
||||
-25
@@ -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<ChatMute> {
|
||||
@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
|
||||
));
|
||||
}
|
||||
}
|
||||
-35
@@ -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<ChatMute> {
|
||||
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> <duration in hours>");
|
||||
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<String> 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;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user