Update ts3j: resend like the official client; clean up failed connects
- ts3j: native resend timing, INIT1 resends, one clientinitiv per handshake, non-blocking disconnect. - Ignore errors from a connect attempt that has been superseded or cancelled. - Show the whole cause chain in network error messages. - -Dts3j.debug=true enables ts3j's protocol logging. - Tests run with a throwaway user.home instead of the real profile. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -105,6 +105,11 @@ public final class TeamspeakConnection implements TS3Listener {
|
||||
private ExecutorService eventExecutor;
|
||||
|
||||
private volatile boolean connected;
|
||||
/**
|
||||
* The connect in progress. A disconnect clears it and a newer connect replaces it, which
|
||||
* tells a superseded attempt that is still failing not to clean up after itself.
|
||||
*/
|
||||
private volatile Object connectAttempt;
|
||||
private volatile int selfClientId = -1;
|
||||
/** Package-private: read directly by {@link ConnectionStatsCollector}. */
|
||||
volatile long connectedAtMs;
|
||||
@@ -240,6 +245,8 @@ public final class TeamspeakConnection implements TS3Listener {
|
||||
|
||||
private void doConnect(String address, int port, String nickname, String password, LocalIdentity withIdentity,
|
||||
String channel, String channelPassword) {
|
||||
Object attempt = new Object();
|
||||
connectAttempt = attempt;
|
||||
try {
|
||||
identity = withIdentity;
|
||||
serverHost = address;
|
||||
@@ -290,7 +297,7 @@ public final class TeamspeakConnection implements TS3Listener {
|
||||
// A late packet dispatched after close() rejects on the shut-down
|
||||
// command executor; harmless during teardown.
|
||||
if (t instanceof java.util.concurrent.RejectedExecutionException) return;
|
||||
ui.onError("Network error: " + rootMessage(t));
|
||||
ui.onError("Network error: " + messageChain(t));
|
||||
});
|
||||
|
||||
applyClientVersion();
|
||||
@@ -331,6 +338,9 @@ public final class TeamspeakConnection implements TS3Listener {
|
||||
// Only captures if this connection holds the microphone.
|
||||
applyMicrophoneState();
|
||||
} catch (Exception e) {
|
||||
// Whoever took over reports on their own; cleaning up here would tear down what a newer
|
||||
// connect has set up in the meantime.
|
||||
if (connectAttempt != attempt) return;
|
||||
connected = false;
|
||||
ui.onError("Connection failed: " + rootMessage(e) + versionHint(e));
|
||||
safeCleanup();
|
||||
@@ -505,6 +515,7 @@ public final class TeamspeakConnection implements TS3Listener {
|
||||
* close) where the JVM must not exit before the leave notification is sent.
|
||||
*/
|
||||
public void disconnectBlocking(String reason) {
|
||||
connectAttempt = null;
|
||||
leaving = true;
|
||||
try {
|
||||
if (client != null) client.disconnect(reason);
|
||||
@@ -1667,6 +1678,18 @@ public final class TeamspeakConnection implements TS3Listener {
|
||||
return s == null ? "" : s;
|
||||
}
|
||||
|
||||
/** Every message along the cause chain, outermost first: network errors are vague without context. */
|
||||
private static String messageChain(Throwable t) {
|
||||
StringBuilder chain = new StringBuilder();
|
||||
for (Throwable c = t; c != null; c = c.getCause() == c ? null : c.getCause()) {
|
||||
String m = c.getMessage();
|
||||
if (m == null || chain.indexOf(m) >= 0) continue;
|
||||
if (chain.length() > 0) chain.append(": ");
|
||||
chain.append(m);
|
||||
}
|
||||
return chain.length() > 0 ? chain.toString() : t.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
private static String rootMessage(Throwable t) {
|
||||
Throwable r = t;
|
||||
while (r.getCause() != null && r.getCause() != r) r = r.getCause();
|
||||
|
||||
@@ -34,6 +34,12 @@
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>${surefire.version}</version>
|
||||
<configuration>
|
||||
<!-- The stores resolve ~/.ts3jclient once per JVM; keep every test out of the real profile. -->
|
||||
<systemPropertyVariables>
|
||||
<user.home>${project.build.directory}/test-home</user.home>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
|
||||
@@ -15,9 +15,10 @@ import javax.swing.SwingUtilities;
|
||||
public final class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// ts3j is verbose by default; keep the console quiet unless debugging.
|
||||
// ts3j is verbose by default; keep the console quiet unless asked for its protocol log
|
||||
// (-Dts3j.debug=true, written to stderr).
|
||||
try {
|
||||
com.github.manevolent.ts3j.util.Ts3Debugging.setEnabled(false);
|
||||
com.github.manevolent.ts3j.util.Ts3Debugging.setEnabled(Boolean.getBoolean("ts3j.debug"));
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user