diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/networkChat/ChatMentionSetting.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/networkChat/ChatMentionSetting.java new file mode 100644 index 0000000..b74238e --- /dev/null +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/networkChat/ChatMentionSetting.java @@ -0,0 +1,49 @@ +package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.networkChat; + +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 "Wirst du im Chat erwähnt, wird dein Name hervorgehoben und du hörst 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/networkChat/NetworkChat.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/networkChat/NetworkChat.java index 0d1decf..57e0b2b 100644 --- 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 @@ -1,7 +1,9 @@ package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.networkChat; +import com.google.common.io.ByteArrayDataInput; import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteStreams; +import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings; import eu.mhsl.craftattack.spawn.core.Main; import eu.mhsl.craftattack.spawn.core.appliance.Appliance; import net.kyori.adventure.text.Component; @@ -19,8 +21,17 @@ 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. + * Join/Leave-Meldungen gelten im ganzen Netzwerk. Hier wird der Chat nur verworfen, Velocity bekommt + * die Rang-Prefixe für den Namen, und die Chat-Einstellungen werden mit Velocity abgeglichen. + *

+ * Kanal {@value #channel}, Nachrichten (UTF-Typ, dann Daten): + *

*/ public class NetworkChat extends Appliance { public static final String channel = "craftattack:chat"; @@ -30,6 +41,12 @@ public class NetworkChat extends Appliance { @Override public void onEnable() { Bukkit.getMessenger().registerOutgoingPluginChannel(Main.instance(), channel); + Bukkit.getMessenger().registerIncomingPluginChannel(Main.instance(), channel, (channel, player, message) -> this.receive(player, message)); + + Settings.instance().declareSetting(ShowJoinAndLeaveMessagesSetting.class); + Settings.instance().declareSetting(ChatMentionSetting.class); + Settings.instance().addChangeListener(ShowJoinAndLeaveMessagesSetting.class, player -> this.sendSettings(player, "settings-set")); + Settings.instance().addChangeListener(ChatMentionSetting.class, player -> this.sendSettings(player, "settings-set")); } public void setPrefix(Player player, @Nullable Component prefix) { @@ -39,12 +56,37 @@ public class NetworkChat extends Appliance { } 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)); + this.send(player, output); + } + + void sendSettings(Player player, String type) { + ChatMentionSetting.ChatMentionConfig mentions = Settings.instance() + .getSetting(player, Settings.Key.ChatMentions, ChatMentionSetting.ChatMentionConfig.class); + ByteArrayDataOutput output = ByteStreams.newDataOutput(); + output.writeUTF(type); + output.writeBoolean(Settings.instance().getSetting(player, Settings.Key.ShowJoinAndLeaveMessages, Boolean.class)); + output.writeBoolean(mentions.applyMentions()); + output.writeBoolean(mentions.notifyOnMention()); + this.send(player, output); + } + + private void receive(Player player, byte[] message) { + ByteArrayDataInput input = ByteStreams.newDataInput(message); + if(!input.readUTF().equals("settings")) return; + boolean joinAndLeave = input.readBoolean(); + boolean applyMentions = input.readBoolean(); + boolean notifyOnMention = input.readBoolean(); + Settings.instance().setSetting(player, Settings.Key.ShowJoinAndLeaveMessages, joinAndLeave); + Settings.instance().setSetting(player, Settings.Key.ChatMentions, new ChatMentionSetting.ChatMentionConfig(applyMentions, notifyOnMention)); + } + + private void send(Player player, ByteArrayDataOutput output) { + // Velocity meldet den Kanal erst kurz nach dem Join an, dann folgt PlayerRegisterChannelEvent + if(!player.getListeningPluginChannels().contains(channel)) return; player.sendPluginMessage(Main.instance(), channel, output.toByteArray()); } 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 index aa375d7..31fde0f 100644 --- 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 @@ -43,6 +43,7 @@ class NetworkChatListener extends ApplianceListener { public void onRegisterChannel(PlayerRegisterChannelEvent event) { if(!event.getChannel().equals(NetworkChat.channel)) return; this.getAppliance().sendPrefix(event.getPlayer()); + this.getAppliance().sendSettings(event.getPlayer(), "settings-hello"); } @EventHandler(priority = EventPriority.HIGHEST) diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/networkChat/ShowJoinAndLeaveMessagesSetting.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/networkChat/ShowJoinAndLeaveMessagesSetting.java new file mode 100644 index 0000000..0c9b3df --- /dev/null +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/networkChat/ShowJoinAndLeaveMessagesSetting.java @@ -0,0 +1,38 @@ +package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.networkChat; + +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/settings/Settings.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/settings/Settings.java index 146987e..e2cb94e 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,11 +26,13 @@ public class Settings extends Appliance { public enum Key { TechnicalTab, + ShowJoinAndLeaveMessages, EnablePortableCrafting, EnableSettingsShortcut, AutoShulker, SignEdit, HotbarReplacer, + ChatMentions, DoubleDoors, KnockDoors, BorderWarning, @@ -110,6 +112,18 @@ public class Settings extends Appliance { return clazz.cast(setting.state()); } + /** Setzt einen Wert von außen (z. B. vom Proxy), ohne die Change-Listener auszulösen. */ + public void setSetting(Player player, Key key, T value) { + Setting setting = this.getSettings(player).stream() + .filter(s -> Objects.equals(s.getKey(), key)) + .findFirst() + .orElseThrow(); + + if(!setting.dataType().isInstance(value)) + throw new IllegalStateException("Tried to set Setting with Datatype " + value.getClass().getSimpleName() + " but expected " + setting.dataType().getSimpleName()); + setting.restore(player, value); + } + public void openSettings(Player player) { List> settings = this.getSettings(player); Inventory inventory = Bukkit.createInventory(null, this.calculateInvSize(settings), Component.text("Einstellungen")); diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/settings/datatypes/Setting.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/settings/datatypes/Setting.java index 24e28bb..c555a46 100644 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/settings/datatypes/Setting.java +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/metaGameplay/settings/datatypes/Setting.java @@ -62,6 +62,12 @@ public abstract class Setting { Settings.instance().invokeChangeListener(p, this.getClass()); } + @SuppressWarnings("unchecked") + public void restore(Player p, Object value) { + this.state = (TDataType) value; + this.toStorage(p.getPersistentDataContainer(), this.state); + } + public ItemStack buildItem() { ItemStack stack = new ItemStack(this.icon(), 1); stack.setItemMeta(this.buildMeta(stack.getItemMeta()));