diff --git a/ts3-client/core/src/main/java/com/ts3client/net/ConnectionEventHandler.java b/ts3-client/core/src/main/java/com/ts3client/net/ConnectionEventHandler.java index f573fd9..910a471 100644 --- a/ts3-client/core/src/main/java/com/ts3client/net/ConnectionEventHandler.java +++ b/ts3-client/core/src/main/java/com/ts3client/net/ConnectionEventHandler.java @@ -314,12 +314,12 @@ final class ConnectionEventHandler implements TS3Listener { // Both fields travel together, so an absent message here means "no message" // — which `has` cannot tell from "not reported". c.awayMessage = c.away ? TeamspeakConnection.orEmpty(e.get("client_away_message")) : ""; - } else if (has(e, "client_away_message")) { - c.awayMessage = e.get("client_away_message"); + } else if (reported(e, "client_away_message")) { + c.awayMessage = TeamspeakConnection.orEmpty(e.get("client_away_message")); } if (has(e, "client_talk_power")) c.talkPower = e.getInt("client_talk_power"); - if (has(e, "client_description")) { - c.description = e.get("client_description"); + if (reported(e, "client_description")) { + c.description = TeamspeakConnection.orEmpty(e.get("client_description")); conn.ui.onInfoUpdated(); } // The answer to clientgetvariables. @@ -330,7 +330,7 @@ final class ConnectionEventHandler implements TS3Listener { } if (has(e, "client_is_channel_commander")) c.channelCommander = e.getBoolean("client_is_channel_commander"); - if (has(e, "client_flag_avatar")) c.avatarFlag = TeamspeakConnection.orEmpty(e.get("client_flag_avatar")); + if (reported(e, "client_flag_avatar")) c.avatarFlag = TeamspeakConnection.orEmpty(e.get("client_flag_avatar")); announceClientUpdate(e, c, renamed, oldName); conn.ui.onModelChanged(); } @@ -398,7 +398,7 @@ final class ConnectionEventHandler implements TS3Listener { ChannelNode ch = conn.getModel().getChannel(safeInt(e, "cid")); if (ch == null) return; // The answer to channelgetdescription; an empty description comes as a bare key. - if (e.getMap().containsKey("channel_description")) { + if (reported(e, "channel_description")) { ch.description = TeamspeakConnection.orEmpty(e.get("channel_description")); ch.descriptionLoaded = true; conn.ui.onInfoUpdated(); @@ -425,7 +425,7 @@ final class ConnectionEventHandler implements TS3Listener { private static void applyChannelProperties(ChannelNode ch, BaseEvent e) { if (has(e, "channel_name")) ch.name = e.get("channel_name"); // An emptied topic arrives as a bare key, which has() would skip. - if (e.getMap().containsKey("channel_topic")) ch.topic = TeamspeakConnection.orEmpty(e.get("channel_topic")); + if (reported(e, "channel_topic")) ch.topic = TeamspeakConnection.orEmpty(e.get("channel_topic")); if (has(e, "channel_icon_id")) ch.iconId = TeamspeakConnection.safeLong(e, "channel_icon_id"); if (has(e, "channel_flag_password")) ch.hasPassword = e.getBoolean("channel_flag_password"); if (has(e, "channel_flag_permanent")) ch.permanent = e.getBoolean("channel_flag_permanent"); @@ -681,6 +681,11 @@ final class ConnectionEventHandler implements TS3Listener { return value != null && !value.isEmpty(); } + /** Whether the event carries a field at all, even empty: for text that can be cleared. */ + private static boolean reported(BaseEvent e, String key) { + return e.getMap().containsKey(key); + } + /** Parses a comma-separated id list (e.g. server groups "6,12,15"). */ private static int[] parseIntList(String csv) { if (csv == null || csv.isEmpty()) return new int[0]; diff --git a/ts3-client/core/src/test/java/com/ts3client/net/EventFieldsTest.java b/ts3-client/core/src/test/java/com/ts3client/net/EventFieldsTest.java index f38c1d5..1439128 100644 --- a/ts3-client/core/src/test/java/com/ts3client/net/EventFieldsTest.java +++ b/ts3-client/core/src/test/java/com/ts3client/net/EventFieldsTest.java @@ -1,12 +1,16 @@ package com.ts3client.net; +import com.github.manevolent.ts3j.command.MultiCommand; import com.github.manevolent.ts3j.event.ClientUpdatedEvent; +import com.github.manevolent.ts3j.protocol.ProtocolRole; import org.junit.jupiter.api.Test; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Pins the ts3j behaviour {@code ConnectionEventHandler.has(...)} exists for: a field @@ -22,4 +26,15 @@ class EventFieldsTest { assertEquals("", e.get("client_is_recording")); assertEquals("", e.get("client_nickname")); } + + /** What {@code reported(...)} relies on: a cleared field still arrives, just without a value. */ + @Test + void clearedFieldsAreStillInTheMap() { + Map fields = MultiCommand.parse(ProtocolRole.SERVER, + "notifyclientupdated clid=7 client_flag_avatar client_description=").simplifyOne().toMap(); + ClientUpdatedEvent e = new ClientUpdatedEvent(fields); + assertTrue(e.getMap().containsKey("client_flag_avatar")); + assertTrue(e.getMap().containsKey("client_description")); + assertFalse(e.getMap().containsKey("client_away_message")); + } }