TeamSpeak: Backoff beim Verbinden der Raum-Clients, kürzere Nicknames, alle Channels abonnieren, Anwesenheit loggen
This commit is contained in:
1 parent
3c3a9dd836
commit
ba22c9c79f
6 files changed
+38
-8
No files matched your search
@@ -22,8 +22,8 @@ teamspeak:
|
||||
parentChannel: "Crosstalk"
|
||||
# Name des Beobachter-Clients, der die Channels im Blick behält.
|
||||
nickname: "Crosstalk"
|
||||
# Name der Raum-Clients; %room% wird durch den Raumnamen ersetzt.
|
||||
roomNickname: "%room% (Minecraft/Discord)"
|
||||
# Name der Raum-Clients; %room% wird durch den Raumnamen ersetzt (TeamSpeak erlaubt höchstens 30 Zeichen).
|
||||
roomNickname: "%room% (MC/DC)"
|
||||
# Der TS6-Server auf mhsl.eu verlangt Level 15 (Fehler 519 "could not validate client identity").
|
||||
securityLevel: 15
|
||||
# Optional, falls der Server eine bestimmte Client-Version verlangt:
|
||||
@@ -37,7 +37,7 @@ discord:
|
||||
# In dieser Kategorie legt die Bridge fehlende Raum-Channels an (leer = ohne Kategorie).
|
||||
category: "000000000000000000"
|
||||
# Server-Nickname der Bots; %room% wird durch den Raumnamen ersetzt.
|
||||
nickname: "%room% (Minecraft/TS)"
|
||||
nickname: "%room% (MC/TS)"
|
||||
|
||||
rooms:
|
||||
- key: crosstalk-1
|
||||
|
||||
@@ -38,6 +38,7 @@ public final class Bridge {
|
||||
/** Zeitpunkte, bis zu denen Raum bzw. Bot noch gebraucht werden (für das Nachlaufen). */
|
||||
private static final class Presence {
|
||||
long roomWanted, teamspeakWanted, discordWanted;
|
||||
String lastCounts = "";
|
||||
boolean teamspeakConnected, discordConnected;
|
||||
}
|
||||
|
||||
@@ -84,6 +85,11 @@ public final class Bridge {
|
||||
int mc = plugin.minecraftMembers(room.key());
|
||||
int ts = teamspeak != null ? teamspeak.humansIn(room) : 0;
|
||||
int dc = bot != null ? bot.humans() : 0;
|
||||
String counts = "Minecraft " + mc + ", TeamSpeak " + ts + ", Discord " + dc;
|
||||
if (!counts.equals(p.lastCounts)) {
|
||||
LOG.info("{}: {}", room.name(), counts);
|
||||
p.lastCounts = counts;
|
||||
}
|
||||
int sides = (mc > 0 ? 1 : 0) + (ts > 0 ? 1 : 0) + (dc > 0 ? 1 : 0);
|
||||
|
||||
if (sides >= 2) p.roomWanted = now;
|
||||
|
||||
@@ -73,9 +73,9 @@ public record BridgeConfig(
|
||||
integer(root, "lingerSeconds", 15) * 1000L,
|
||||
new Teamspeak(string(ts, "host", ""), integer(ts, "port", 9987), string(ts, "password", ""),
|
||||
string(ts, "parentChannel", "0"), string(ts, "nickname", "Crosstalk"),
|
||||
string(ts, "roomNickname", "%room% (Minecraft/Discord)"), integer(ts, "securityLevel", 15), version),
|
||||
string(ts, "roomNickname", "%room% (MC/DC)"), integer(ts, "securityLevel", 15), version),
|
||||
new Discord(string(dc, "guild", ""), string(dc, "category", ""),
|
||||
string(dc, "nickname", "%room% (Minecraft/TS)")),
|
||||
string(dc, "nickname", "%room% (MC/TS)")),
|
||||
List.copyOf(rooms));
|
||||
}
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ public final class DiscordRoomBot implements AudioSendHandler, AudioReceiveHandl
|
||||
LOG.info("{}: Discord-Bot {} bereit, Channel #{}", room.name(), jda.getSelfUser().getName(), channel.getName());
|
||||
|
||||
String nickname = config.nickname().replace("%room%", room.name());
|
||||
if (nickname.length() > 32) nickname = nickname.substring(0, 32);
|
||||
if (!nickname.equals(guild.getSelfMember().getNickname())) {
|
||||
guild.getSelfMember().modifyNickname(nickname).queue(null,
|
||||
e -> LOG.debug("{}: Nickname nicht gesetzt: {}", room.name(), e.getMessage()));
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.function.Consumer;
|
||||
/** Ein TS-Client im Raum-Channel: spielt die Mischung aus Minecraft und Discord ab und hört zu. */
|
||||
final class TeamspeakRoomClient implements Microphone, TS3Listener {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TeamspeakRoomClient.class);
|
||||
private static final int MAX_NICKNAME = 30;
|
||||
|
||||
private final BridgeConfig.Teamspeak config;
|
||||
private final Room room;
|
||||
@@ -46,7 +47,8 @@ final class TeamspeakRoomClient implements Microphone, TS3Listener {
|
||||
static LocalTeamspeakClientSocket newSocket(BridgeConfig.Teamspeak config, LocalIdentity identity, String nickname) {
|
||||
LocalTeamspeakClientSocket socket = new LocalTeamspeakClientSocket();
|
||||
socket.setIdentity(identity);
|
||||
socket.setNickname(nickname);
|
||||
// Längere Nicknames lehnt der Server mit "invalid parameter size" ab
|
||||
socket.setNickname(nickname.length() > MAX_NICKNAME ? nickname.substring(0, MAX_NICKNAME) : nickname);
|
||||
BridgeConfig.ClientVersion version = config.clientVersion();
|
||||
if (version != null) socket.setClientVersion(version.platform(), version.version(), version.sign());
|
||||
socket.setExceptionHandler(e -> LOG.debug("ts3j: {}", e.toString()));
|
||||
|
||||
@@ -44,6 +44,8 @@ public final class TeamspeakSide implements TS3Listener {
|
||||
private static final long RECONNECT_SECONDS = 30;
|
||||
/** client_type 0 = normaler Client, 1 = ServerQuery. */
|
||||
private static final int CLIENT_TYPE_VOICE = 0;
|
||||
private static final long RETRY_MIN_MILLIS = 30_000;
|
||||
private static final long RETRY_MAX_MILLIS = 300_000;
|
||||
|
||||
private record TsClient(int channel, int type) {}
|
||||
private record TsChannel(int parent, String name) {}
|
||||
@@ -58,6 +60,9 @@ public final class TeamspeakSide implements TS3Listener {
|
||||
private final Map<Integer, TsChannel> channels = new ConcurrentHashMap<>();
|
||||
private final Map<String, Integer> roomChannels = new ConcurrentHashMap<>();
|
||||
private final Map<String, TeamspeakRoomClient> roomClients = new ConcurrentHashMap<>();
|
||||
private final Set<String> pendingConnect = ConcurrentHashMap.newKeySet();
|
||||
private final Map<String, Long> backoff = new ConcurrentHashMap<>();
|
||||
private final Map<String, Long> nextAttempt = new ConcurrentHashMap<>();
|
||||
|
||||
private volatile LocalTeamspeakClientSocket observer;
|
||||
private volatile boolean ready;
|
||||
@@ -106,9 +111,18 @@ public final class TeamspeakSide implements TS3Listener {
|
||||
return count;
|
||||
}
|
||||
|
||||
/** Raum-Client verbinden oder trennen. Läuft asynchron, mehrfaches Aufrufen schadet nicht. */
|
||||
/**
|
||||
* Raum-Client verbinden oder trennen. Läuft asynchron, mehrfaches Aufrufen schadet nicht.
|
||||
* Nach einem Fehlschlag wird mit wachsendem Abstand neu versucht, sonst greift der Flood-Schutz
|
||||
* des Servers und bannt die IP.
|
||||
*/
|
||||
public void setRoomConnected(Room room, boolean connected) {
|
||||
if (connected) {
|
||||
if (roomClients.containsKey(room.key()) || System.currentTimeMillis() < nextAttempt.getOrDefault(room.key(), 0L)) return;
|
||||
if (!pendingConnect.add(room.key())) return;
|
||||
}
|
||||
executor.execute(() -> {
|
||||
pendingConnect.remove(room.key());
|
||||
TeamspeakRoomClient client = roomClients.get(room.key());
|
||||
if (connected && client == null && ready) {
|
||||
Integer channel = roomChannels.get(room.key());
|
||||
@@ -118,9 +132,14 @@ public final class TeamspeakSide implements TS3Listener {
|
||||
loadIdentity("ts-" + room.key() + ".ini"), this::onRoomClientLost);
|
||||
roomClients.put(room.key(), client);
|
||||
client.connect();
|
||||
backoff.remove(room.key());
|
||||
LOG.info("{}: TeamSpeak-Client im Channel {}", room.name(), channel);
|
||||
} catch (Exception e) {
|
||||
LOG.warn("{}: TeamSpeak-Client konnte nicht verbinden: {}", room.name(), e.toString());
|
||||
long wait = Math.min(backoff.getOrDefault(room.key(), RETRY_MIN_MILLIS / 2) * 2, RETRY_MAX_MILLIS);
|
||||
backoff.put(room.key(), wait);
|
||||
nextAttempt.put(room.key(), System.currentTimeMillis() + wait);
|
||||
LOG.warn("{}: TeamSpeak-Client konnte nicht verbinden ({}), neuer Versuch in {} s",
|
||||
room.name(), e.toString(), wait / 1000);
|
||||
roomClients.remove(room.key());
|
||||
if (client != null) client.disconnect();
|
||||
}
|
||||
@@ -151,6 +170,8 @@ public final class TeamspeakSide implements TS3Listener {
|
||||
socket.addListener(this);
|
||||
observer = socket;
|
||||
socket.connect(new InetSocketAddress(config.host(), config.port()), emptyToNull(config.password()), 15000L);
|
||||
// Clients in nicht abonnierten Channels meldet der Server nicht
|
||||
socket.subscribeAll();
|
||||
awaitInitialState();
|
||||
LOG.info("TeamSpeak verbunden mit {}:{} ({} Channels, {} Clients sichtbar)",
|
||||
config.host(), config.port(), channels.size(), clients.size());
|
||||
|
||||
Reference in new issue
Block a user