diff --git a/src/main/java/com/github/manevolent/ts3j/event/DisconnectedEvent.java b/src/main/java/com/github/manevolent/ts3j/event/DisconnectedEvent.java new file mode 100644 index 0000000..c4313a0 --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/event/DisconnectedEvent.java @@ -0,0 +1,45 @@ +package com.github.manevolent.ts3j.event; + +/* + * #%L + * TeamSpeak 3 Java API + * %% + * Copyright (C) 2014 Bert De Geyter + * %% + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * #L% + */ + +import java.util.Map; + +public class DisconnectedEvent extends BaseEvent { + + public DisconnectedEvent(Map map) { + super(map); + } + + public int getReasonId() { + return getInt("reasonid"); + } + + @Override + public void fire(TS3Listener listener) { + listener.onDisconnected(this); + } +} diff --git a/src/main/java/com/github/manevolent/ts3j/event/TS3Listener.java b/src/main/java/com/github/manevolent/ts3j/event/TS3Listener.java index 27f7d8e..cc78d02 100644 --- a/src/main/java/com/github/manevolent/ts3j/event/TS3Listener.java +++ b/src/main/java/com/github/manevolent/ts3j/event/TS3Listener.java @@ -63,4 +63,6 @@ public interface TS3Listener { void onClientChannelGroupChanged(ClientChannelGroupChangedEvent e); void onClientChanged(ClientUpdatedEvent e); + + void onDisconnected(DisconnectedEvent e); } diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/AbstractTeamspeakClientSocket.java b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/AbstractTeamspeakClientSocket.java index ef04b09..446d921 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/AbstractTeamspeakClientSocket.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/AbstractTeamspeakClientSocket.java @@ -37,6 +37,8 @@ import java.util.concurrent.*; public abstract class AbstractTeamspeakClientSocket extends AbstractTeamspeakSocket implements TeamspeakClientSocket { + public static long TIMEOUT = 30000L; + private static final byte[] INIT1_MAC = new byte[] { 0x54, 0x53, 0x33, 0x49, 0x4E, 0x49, 0x54, 0x31 }; @@ -66,6 +68,7 @@ public abstract class AbstractTeamspeakClientSocket private int clientId = 0; private int packetId = 0; + private long lastResponse; private long lastPing; private final NetworkReader networkReader = new NetworkReader(); @@ -396,7 +399,7 @@ public abstract class AbstractTeamspeakClientSocket response = new PacketResponse( networkPacket, sendQueue, - networkPacket.getHeader().getType().canResend() ? 10 : 0 + networkPacket.getHeader().getType().canResend() ? 30 : 0 ) ); } else @@ -408,13 +411,14 @@ public abstract class AbstractTeamspeakClientSocket // If necessary, fulfill promise for the packet acknowledgement or throw TimeoutException if (response != null) { - while (response.getRetries() < response.getMaxTries()) { + while (response.getRetries() < response.getMaxTries() && + getState() != ClientConnectionState.DISCONNECTED) { try { if (getState() == ClientConnectionState.DISCONNECTED) throw new IllegalStateException("no longer connected"); // Wait one cycle for an ACK - response.waitForAcknowledgement(1000); + response.waitForAcknowledgement(1000L); break; } catch (TimeoutException e) { if (response.willResend()) @@ -530,8 +534,13 @@ public abstract class AbstractTeamspeakClientSocket networkPacket = readNetworkPacket((int) timeout); } catch (SocketTimeoutException e) { if (getState() == ClientConnectionState.CONNECTED) { - writePacket(new PacketBody4Ping(getRole().getOut())); - lastPing = System.currentTimeMillis(); + if (System.currentTimeMillis() - lastResponse >= TIMEOUT) { + Ts3Debugging.debug("Connection timed out."); + setState(ClientConnectionState.DISCONNECTED); // Timeout + } else { + writePacket(new PacketBody4Ping(getRole().getOut())); + lastPing = System.currentTimeMillis(); + } } continue; @@ -593,16 +602,28 @@ public abstract class AbstractTeamspeakClientSocket switch (packet.getHeader().getType()) { case ACK: response = sendQueue.get(((PacketBody6Ack)packet.getBody()).getPacketId()); - if (response == null) - Ts3Debugging.debug("Unrecognized ACK: " + ((PacketBody6Ack)packet.getBody()).getPacketId()); + if (response == null) { + Ts3Debugging.debug("Unrecognized ACK: " + ((PacketBody6Ack) packet.getBody()).getPacketId()); + } else { + lastResponse = System.currentTimeMillis(); + } + handle = false; break; case ACK_LOW: + lastResponse = System.currentTimeMillis(); + response = sendQueueLow.get(((PacketBody7AckLow)packet.getBody()).getPacketId()); - if (response == null) - Ts3Debugging.debug("Unrecognized ACK_LOW: " + ((PacketBody6Ack)packet.getBody()).getPacketId()); + if (response == null) { + Ts3Debugging.debug("Unrecognized ACK_LOW: " + ((PacketBody6Ack) packet.getBody()).getPacketId()); + } else { + lastResponse = System.currentTimeMillis(); + } + handle = false; break; + case PONG: + lastResponse = System.currentTimeMillis(); default: handle = true; response = null; diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/LocalTeamspeakClientSocket.java b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/LocalTeamspeakClientSocket.java index 7cd417d..bb94e57 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/LocalTeamspeakClientSocket.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/LocalTeamspeakClientSocket.java @@ -9,9 +9,7 @@ import com.github.manevolent.ts3j.command.*; import com.github.manevolent.ts3j.command.parameter.CommandSingleParameter; import com.github.manevolent.ts3j.command.response.AbstractCommandResponse; import com.github.manevolent.ts3j.command.response.CommandResponse; -import com.github.manevolent.ts3j.event.ChannelListEvent; -import com.github.manevolent.ts3j.event.TS3Event; -import com.github.manevolent.ts3j.event.TS3Listener; +import com.github.manevolent.ts3j.event.*; import com.github.manevolent.ts3j.identity.LocalIdentity; import com.github.manevolent.ts3j.identity.Uid; import com.github.manevolent.ts3j.protocol.NetworkPacket; @@ -58,7 +56,7 @@ public class LocalTeamspeakClientSocket private List listeners = new LinkedList<>(); - private int acceptingReturnCode = 0; + private int acceptingReturnCode = Integer.MAX_VALUE; private int lastReturnCode = 0; private Integer serverId = null; @@ -80,6 +78,27 @@ public class LocalTeamspeakClientSocket }); namedProcessors.put("channellistfinished", new ChannelListFinishedHandler()); + + namedProcessors.put("notifyclientleftview", new CommandProcessor() { + @Override + public void process(AbstractTeamspeakClientSocket client, SingleCommand command) + throws CommandProcessException { + int clientId = Integer.parseInt(command.get("clid").getValue()); + if (clientId == getClientId()) { + try { + setState(ClientConnectionState.DISCONNECTED); + } catch (Exception e) { + throw new RuntimeException(e); + } + + for (TS3Listener listener : listeners) + listener.onDisconnected(new DisconnectedEvent(command.toMap())); + } else { + for (TS3Listener listener : listeners) + listener.onClientLeave(new ClientLeaveEvent(command.toMap())); + } + } + }); } public void addListener(TS3Listener listener) { @@ -164,6 +183,9 @@ public class LocalTeamspeakClientSocket setClientId(0); setPacketId(0); setGenerationId(0); + serverId = null; + awaitingCommands.clear(); + acceptingReturnCode = lastReturnCode = 0; setOption("client.hostname", remote.getHostString()); @@ -745,4 +767,24 @@ public class LocalTeamspeakClientSocket executeCommand(command).complete(); } + + public void disconnect() + throws + IOException, TimeoutException, + ExecutionException, InterruptedException { + switch (getState()) { + case CONNECTING: + case RETRIEVING_DATA: + waitForState(ClientConnectionState.CONNECTED, 10000L); + case CONNECTED: + writePacket(new PacketBody2Command( + ProtocolRole.CLIENT, + new SingleCommand("clientdisconnect", ProtocolRole.CLIENT)) + ); + + break; + } + } + + } diff --git a/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java b/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java index 992d6df..88f863a 100644 --- a/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java +++ b/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java @@ -40,16 +40,9 @@ public class ServerConnectionTest { assertEquals(client.getState(), ClientConnectionState.CONNECTED); - client.sendServerMessage("Heyyyy"); + client.disconnect(); - while (true) { - Thread.sleep(1000L); - - for (Channel channel : client.listChannels()) { - Ts3Debugging.debug(channel.getName()); - } - - } + client.waitForState(ClientConnectionState.DISCONNECTED, 10000L); } catch (Throwable ex) { ex.printStackTrace(); }