From 3c3a9dd8365226b26e89a81df8da55fd94da2c9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Sun, 18 Oct 2026 13:14:43 +0200 Subject: [PATCH] TeamSpeak: auf Channel-Liste warten, kein doppelter Neuversuch --- .../bridge/teamspeak/TeamspeakSide.java | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/bridge/src/main/java/eu/mhsl/voicebridge/bridge/teamspeak/TeamspeakSide.java b/bridge/src/main/java/eu/mhsl/voicebridge/bridge/teamspeak/TeamspeakSide.java index f3cbe64..887575a 100644 --- a/bridge/src/main/java/eu/mhsl/voicebridge/bridge/teamspeak/TeamspeakSide.java +++ b/bridge/src/main/java/eu/mhsl/voicebridge/bridge/teamspeak/TeamspeakSide.java @@ -81,7 +81,7 @@ public final class TeamspeakSide implements TS3Listener { if (!ready || socket == null || rooms.stream().allMatch(room -> roomChannels.containsKey(room.key()))) return; try { resolveChannels(socket); - if (parentChannel > 0) socket.joinChannel(parentChannel, null); + joinParent(socket); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (Exception e) { @@ -151,11 +151,12 @@ public final class TeamspeakSide implements TS3Listener { socket.addListener(this); observer = socket; socket.connect(new InetSocketAddress(config.host(), config.port()), emptyToNull(config.password()), 15000L); + awaitInitialState(); LOG.info("TeamSpeak verbunden mit {}:{} ({} Channels, {} Clients sichtbar)", config.host(), config.port(), channels.size(), clients.size()); parentChannel = resolveParent(); resolveChannels(socket); - if (parentChannel > 0) socket.joinChannel(parentChannel, null); + joinParent(socket); ready = true; } catch (Exception e) { LOG.warn("TeamSpeak-Verbindung fehlgeschlagen ({}), neuer Versuch in {} s", e.toString(), RECONNECT_SECONDS); @@ -164,6 +165,17 @@ public final class TeamspeakSide implements TS3Listener { } } + /** ts3j liefert die Channel- und Client-Liste asynchron aus; warten, bis sie sich nicht mehr ändert. */ + private void awaitInitialState() throws InterruptedException { + int last = -1; + for (int i = 0; i < 50; i++) { + int now = channels.size() + clients.size(); + if (now == last && now > 0) return; + last = now; + Thread.sleep(200); + } + } + private void resolveChannels(LocalTeamspeakClientSocket socket) throws InterruptedException { for (Room room : rooms) { if (roomChannels.containsKey(room.key())) continue; @@ -202,6 +214,12 @@ public final class TeamspeakSide implements TS3Listener { } } + /** Der Beobachter wartet im Eltern-Channel; wer einen Channel anlegt, kann dorthin verschoben werden. */ + private void joinParent(LocalTeamspeakClientSocket socket) throws Exception { + TsClient self = clients.get(socket.getClientId()); + if (parentChannel > 0 && (self == null || self.channel() != parentChannel)) socket.joinChannel(parentChannel, null); + } + /** {@code parentChannel} ist eine Channel-ID oder der Name eines Channels auf oberster Ebene. */ private int resolveParent() throws IOException { String configured = config.parentChannel(); @@ -230,6 +248,7 @@ public final class TeamspeakSide implements TS3Listener { LocalTeamspeakClientSocket socket = observer; observer = null; if (socket != null) { + socket.removeListener(this); try { socket.disconnect(); } catch (Exception ignored) { @@ -321,6 +340,9 @@ public final class TeamspeakSide implements TS3Listener { @Override public void onDisconnected(DisconnectedEvent e) { + // Beim fehlgeschlagenen Verbinden plant connectObserver den Neuversuch selbst + if (!ready) return; + ready = false; LOG.warn("TeamSpeak-Beobachter getrennt, neuer Versuch in {} s", RECONNECT_SECONDS); executor.execute(this::disconnectObserver); executor.schedule(this::connectObserver, RECONNECT_SECONDS, TimeUnit.SECONDS);