chat settings are synced with velocity again (join/leave, mentions)

This commit is contained in:
MineTec committed 2026-10-17 22:57:42 +02:00
1 parent ca0e364d85
commit 4a87bb3fad
6 files changed
+154 -4

No files matched your search

@@ -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<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 "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;
}
}
@@ -1,7 +1,9 @@
package eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.networkChat; 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.ByteArrayDataOutput;
import com.google.common.io.ByteStreams; 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.Main;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance; import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import net.kyori.adventure.text.Component; 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 * 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 * Join/Leave-Meldungen gelten im ganzen Netzwerk. Hier wird der Chat nur verworfen, Velocity bekommt
* bekommt die Rang-Prefixe für den Namen. * die Rang-Prefixe für den Namen, und die Chat-Einstellungen werden mit Velocity abgeglichen.
* <p>
* Kanal {@value #channel}, Nachrichten (UTF-Typ, dann Daten):
* <ul>
* <li>→ Proxy {@code prefix}: Adventure-JSON oder leer</li>
* <li>→ Proxy {@code settings-hello} beim Join, {@code settings-set} nach einer Änderung: Join/Leave,
* Erwähnungen hervorheben, Erwähnungston (je boolean). Velocity speichert sie; kennt es schon
* Werte, antwortet es auf das Hello mit {@code settings}.</li>
* <li>← Proxy {@code settings}: gespeicherte Werte, werden hier übernommen</li>
* </ul>
*/ */
public class NetworkChat extends Appliance { public class NetworkChat extends Appliance {
public static final String channel = "craftattack:chat"; public static final String channel = "craftattack:chat";
@@ -30,6 +41,12 @@ public class NetworkChat extends Appliance {
@Override @Override
public void onEnable() { public void onEnable() {
Bukkit.getMessenger().registerOutgoingPluginChannel(Main.instance(), channel); 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) { public void setPrefix(Player player, @Nullable Component prefix) {
@@ -39,12 +56,37 @@ public class NetworkChat extends Appliance {
} }
void sendPrefix(Player 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()); Component prefix = this.prefixes.get(player.getUniqueId());
ByteArrayDataOutput output = ByteStreams.newDataOutput(); ByteArrayDataOutput output = ByteStreams.newDataOutput();
output.writeUTF("prefix"); output.writeUTF("prefix");
output.writeUTF(prefix == null ? "" : GsonComponentSerializer.gson().serialize(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()); player.sendPluginMessage(Main.instance(), channel, output.toByteArray());
} }
@@ -43,6 +43,7 @@ class NetworkChatListener extends ApplianceListener<NetworkChat> {
public void onRegisterChannel(PlayerRegisterChannelEvent event) { public void onRegisterChannel(PlayerRegisterChannelEvent event) {
if(!event.getChannel().equals(NetworkChat.channel)) return; if(!event.getChannel().equals(NetworkChat.channel)) return;
this.getAppliance().sendPrefix(event.getPlayer()); this.getAppliance().sendPrefix(event.getPlayer());
this.getAppliance().sendSettings(event.getPlayer(), "settings-hello");
} }
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST)
@@ -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;
}
}
@@ -26,11 +26,13 @@ public class Settings extends Appliance {
public enum Key { public enum Key {
TechnicalTab, TechnicalTab,
ShowJoinAndLeaveMessages,
EnablePortableCrafting, EnablePortableCrafting,
EnableSettingsShortcut, EnableSettingsShortcut,
AutoShulker, AutoShulker,
SignEdit, SignEdit,
HotbarReplacer, HotbarReplacer,
ChatMentions,
DoubleDoors, DoubleDoors,
KnockDoors, KnockDoors,
BorderWarning, BorderWarning,
@@ -110,6 +112,18 @@ public class Settings extends Appliance {
return clazz.cast(setting.state()); return clazz.cast(setting.state());
} }
/** Setzt einen Wert von außen (z. B. vom Proxy), ohne die Change-Listener auszulösen. */
public <T> 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) { public void openSettings(Player player) {
List<Setting<?>> settings = this.getSettings(player); List<Setting<?>> settings = this.getSettings(player);
Inventory inventory = Bukkit.createInventory(null, this.calculateInvSize(settings), Component.text("Einstellungen")); Inventory inventory = Bukkit.createInventory(null, this.calculateInvSize(settings), Component.text("Einstellungen"));
@@ -62,6 +62,12 @@ public abstract class Setting<TDataType> {
Settings.instance().invokeChangeListener(p, this.getClass()); 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() { public ItemStack buildItem() {
ItemStack stack = new ItemStack(this.icon(), 1); ItemStack stack = new ItemStack(this.icon(), 1);
stack.setItemMeta(this.buildMeta(stack.getItemMeta())); stack.setItemMeta(this.buildMeta(stack.getItemMeta()));