diff --git a/LICENSE b/LICENSE
index 94a9ed0..8b28719 100644
--- a/LICENSE
+++ b/LICENSE
@@ -116,7 +116,7 @@ for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
+standard defined by a recognized standards buffer, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
diff --git a/pom.xml b/pom.xml
index 47d37bc..9b060b4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -35,5 +35,11 @@
bcprov-jdk15on
1.60
+
+
+ commons-lang
+ commons-lang
+ 2.6
+
\ No newline at end of file
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/Endpoint.java b/src/main/java/com/github/manevolent/ts3j/protocol/Endpoint.java
deleted file mode 100644
index a4e0553..0000000
--- a/src/main/java/com/github/manevolent/ts3j/protocol/Endpoint.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.github.manevolent.ts3j.protocol;
-
-import com.github.manevolent.ts3j.protocol.packet.Packet;
-
-import java.io.IOException;
-import java.util.function.Function;
-
-public interface Endpoint {
-
- SocketRole getRole();
-
- void send(Packet packet) throws IOException;
-
-}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/LocalEndpoint.java b/src/main/java/com/github/manevolent/ts3j/protocol/LocalEndpoint.java
deleted file mode 100644
index b3a020b..0000000
--- a/src/main/java/com/github/manevolent/ts3j/protocol/LocalEndpoint.java
+++ /dev/null
@@ -1,119 +0,0 @@
-package com.github.manevolent.ts3j.protocol;
-
-import com.github.manevolent.ts3j.identity.LocalIdentity;
-import com.github.manevolent.ts3j.protocol.header.HeaderFlag;
-import com.github.manevolent.ts3j.protocol.header.PacketHeader;
-import com.github.manevolent.ts3j.protocol.packet.PacketType;
-import com.github.manevolent.ts3j.protocol.packet.channel.PacketChannel;
-import com.github.manevolent.ts3j.protocol.packet.handler.PacketHandler;
-import com.github.manevolent.ts3j.protocol.socket.LocalTeamspeakSocket;
-import com.github.manevolent.ts3j.protocol.packet.channel.ReassemblyQueue;
-import com.github.manevolent.ts3j.util.Ts3Logging;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.SocketException;
-
-public abstract class LocalEndpoint implements Endpoint {
- private final LocalTeamspeakSocket socket;
-
- private LocalIdentity localIdentity = null;
-
- private final ReassemblyQueue reassemblyQueue = new ReassemblyQueue();
-
- private final Object runLock = new Object();
- private Thread receiverThread;
- private boolean running = false;
-
- protected LocalEndpoint(LocalTeamspeakSocket socket) {
- this.socket = socket;
- }
-
- protected LocalEndpoint(SocketRole socketRole) throws SocketException {
- this.socket = new LocalTeamspeakSocket(socketRole);
- }
-
- public InetSocketAddress getLocalAddress() {
- return getSocket().getLocalAddress();
- }
-
- public InetSocketAddress getRemoteAddress() {
- return getSocket().getLocalAddress();
- }
-
- protected final LocalTeamspeakSocket getSocket() {
- return socket;
- }
-
- protected abstract void acknowledge(NetworkPacket packet, PacketType ackType) throws IOException;
- protected abstract PacketChannel getChannel(NetworkPacket packet);
-
- public void setRunning(boolean running) {
- if (this.running != running) {
- this.running = running;
-
- if (running) {
- Ts3Logging.debug("Starting network receiver thread...");
-
- receiverThread = new Thread(new PacketReceiver());
- receiverThread.setDaemon(true);
- receiverThread.start();
- } else {
- if (receiverThread != null) {
- receiverThread.interrupt();
- receiverThread = null;
- }
- }
- }
- }
-
- @Override
- public SocketRole getRole() {
- return socket.getSocketRole();
- }
-
- private NetworkPacket receive() throws IOException {
- return socket.receive();
- }
-
- public LocalIdentity getLocalIdentity() {
- return localIdentity;
- }
-
- public void setLocalIdentity(LocalIdentity localIdentity) {
- this.localIdentity = localIdentity;
- }
-
- private class PacketReceiver implements Runnable {
- @Override
- public void run() {
- Ts3Logging.debug("Entering packet receiver");
-
- while (running) {
- try {
- NetworkPacket packet = receive();
- PacketChannel channel = getChannel(packet);
- if (channel == null) continue;
-
- channel.insert(packet);
-
- if (packet.getHeader().getType().getAcknolwedgedBy() != null)
- acknowledge(packet, packet.getHeader().getType().getAcknolwedgedBy());
-
- PacketHandler handler = channel.getHandler();
- if (handler != null) {
- while ((packet = channel.next()) != null) {
- handler.handlePacket(packet);
- }
- }
- } catch (Exception e) {
- if (e instanceof InterruptedException) continue;
-
- Ts3Logging.debug("Problem handling packet", e);
- }
- }
-
- Ts3Logging.debug("Leaving packet receiver");
- }
- }
-}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/NetworkPacket.java b/src/main/java/com/github/manevolent/ts3j/protocol/NetworkPacket.java
index 113ff48..4ca46bf 100644
--- a/src/main/java/com/github/manevolent/ts3j/protocol/NetworkPacket.java
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/NetworkPacket.java
@@ -1,127 +1,42 @@
package com.github.manevolent.ts3j.protocol;
import com.github.manevolent.ts3j.protocol.header.PacketHeader;
-import com.github.manevolent.ts3j.protocol.packet.Packet;
-import com.github.manevolent.ts3j.util.Ts3Logging;
+import java.net.DatagramPacket;
import java.nio.ByteBuffer;
-/**
- * Represents a fully contained network packet, traveling in or out.
- */
public final class NetworkPacket {
- private final ProtocolRole role;
+ private final DatagramPacket datagram;
+ private final PacketHeader header;
+ private final ByteBuffer buffer;
- private PacketHeader header;
- private Packet packet;
-
- public NetworkPacket(ProtocolRole role) {
- if (role == null) throw new NullPointerException("role");
-
- this.role = role;
+ public NetworkPacket(DatagramPacket datagram, PacketHeader header, ByteBuffer buffer) {
+ this.datagram = datagram;
+ this.header = header;
+ this.buffer = buffer;
}
- public ProtocolRole getRole() {
- return role;
+ /**
+ * Gets the original packet datagram.
+ * @return Packet datagram.
+ */
+ public DatagramPacket getDatagram() {
+ return datagram;
}
+ /**
+ * Gets the received header.
+ * @return Packet header.
+ */
public PacketHeader getHeader() {
return header;
}
- public void setHeader(PacketHeader header) {
- if (header == null) throw new NullPointerException("header");
-
- this.header = header;
- }
-
- public Packet getPacket() {
- return packet;
- }
-
- public void setPacket(Packet packet) {
- if (packet == null) throw new NullPointerException("packet");
-
- this.packet = packet;
- }
-
/**
- * Writes the existing data out to the specified byte buffer
- * @param buffer Buffer to write to
+ * Gets the original packet buffer.
+ * @return Packet buffer.
*/
- public ByteBuffer write(ByteBuffer buffer) {
- writeHeader(buffer);
- writeBody(buffer);
-
+ public ByteBuffer getBuffer() {
return buffer;
}
-
- public ByteBuffer writeHeader(ByteBuffer buffer) {
- if (getHeader() == null) throw new NullPointerException("header");
-
- getHeader().setType(getPacket().getType());
-
- getHeader().write(buffer);
-
- return buffer;
- }
-
- public ByteBuffer writeBody(ByteBuffer buffer) {
- if (getPacket() == null) throw new NullPointerException("packet");
-
- getPacket().write(buffer);
-
- return buffer;
- }
-
- /**
- * Reads data from the specified byte buffer, filling in the packet body as needed.
- * @param buffer Buffer to read from.
- */
- public ByteBuffer read(ByteBuffer buffer) {
- readHeader(buffer);
- readBody(buffer);
-
- return buffer;
- }
-
- public ByteBuffer readHeader(ByteBuffer buffer) {
- if (getHeader() == null) {
- try {
- setHeader(getRole().getHeaderClass().newInstance());
- } catch (ReflectiveOperationException e) {
- throw new RuntimeException(e);
- }
- }
-
- getHeader().read(buffer);
-
- return buffer;
- }
-
- public ByteBuffer readBody(ByteBuffer buffer) {
- if (getPacket() == null) {
- try {
- setPacket(
- getHeader().getType().getPacketClass()
- .getConstructor(ProtocolRole.class)
- .newInstance(getHeader().getRole())
- );
- } catch (ReflectiveOperationException e) {
- throw new RuntimeException(e);
- }
- }
-
- getPacket().read(buffer);
-
- return buffer;
- }
-
- /**
- * Gets the size of this packet. This method is generally only used when writing a fully defined packet.
- * @return packet size.
- */
- public int getSize() {
- return getHeader().getSize() + getPacket().getSize();
- }
}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/Packet.java b/src/main/java/com/github/manevolent/ts3j/protocol/Packet.java
new file mode 100644
index 0000000..8c1ce48
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/Packet.java
@@ -0,0 +1,127 @@
+package com.github.manevolent.ts3j.protocol;
+
+import com.github.manevolent.ts3j.protocol.header.PacketHeader;
+import com.github.manevolent.ts3j.protocol.packet.PacketBody;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Represents a fully contained network packet, traveling in or out.
+ */
+public final class Packet {
+ private final ProtocolRole role;
+
+ private PacketHeader header;
+ private PacketBody body;
+
+ public Packet(ProtocolRole role) {
+ if (role == null) throw new NullPointerException("role");
+
+ this.role = role;
+ }
+
+ public ProtocolRole getRole() {
+ return role;
+ }
+
+ public PacketHeader getHeader() {
+ return header;
+ }
+
+ public void setHeader(PacketHeader header) {
+ if (header == null) throw new NullPointerException("header");
+
+ this.header = header;
+ }
+
+ public PacketBody getBody() {
+ return body;
+ }
+
+ public void setBody(PacketBody packetBody) {
+ if (packetBody == null) throw new NullPointerException("packet");
+
+ this.body = packetBody;
+ }
+
+ /**
+ * Writes the existing data out to the specified byte buffer
+ * @param buffer Buffer to write to
+ */
+ public ByteBuffer write(ByteBuffer buffer) {
+ writeHeader(buffer);
+ writeBody(buffer);
+
+ return buffer;
+ }
+
+ public ByteBuffer writeHeader(ByteBuffer buffer) {
+ if (getHeader() == null) throw new NullPointerException("header");
+
+ if (getBody() != null)
+ getHeader().setType(getBody().getType());
+
+ getHeader().write(buffer);
+
+ return buffer;
+ }
+
+ public ByteBuffer writeBody(ByteBuffer buffer) {
+ if (getBody() == null) throw new NullPointerException("packet");
+
+ getBody().write(buffer);
+
+ return buffer;
+ }
+
+ /**
+ * Reads data from the specified byte buffer, filling in the packet body as needed.
+ * @param buffer Buffer to read from.
+ */
+ public ByteBuffer read(ByteBuffer buffer) {
+ readHeader(buffer);
+ readBody(buffer);
+
+ return buffer;
+ }
+
+ public ByteBuffer readHeader(ByteBuffer buffer) {
+ if (getHeader() == null) {
+ try {
+ setHeader(getRole().getHeaderClass().newInstance());
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ getHeader().read(buffer);
+
+ return buffer;
+ }
+
+ public ByteBuffer readBody(ByteBuffer buffer) {
+ if (getBody() == null) {
+ try {
+ setBody(
+ getHeader().getType().getPacketClass()
+ .getConstructor(ProtocolRole.class)
+ .newInstance(getHeader().getRole())
+ );
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ getBody().read(buffer);
+
+ return buffer;
+ }
+
+ /**
+ * Gets the size of this packet. This method is generally only used when writing a fully defined packet.
+ * @return packet size.
+ */
+ public int getSize() {
+ return (getHeader() == null ? 0 : getHeader().getSize()) + (getBody() == null ? 0 : getBody().getSize());
+ }
+}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/RemoteEndpoint.java b/src/main/java/com/github/manevolent/ts3j/protocol/RemoteEndpoint.java
deleted file mode 100644
index 3899f4c..0000000
--- a/src/main/java/com/github/manevolent/ts3j/protocol/RemoteEndpoint.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.github.manevolent.ts3j.protocol;
-
-import java.net.InetSocketAddress;
-
-public abstract class RemoteEndpoint implements Endpoint {
- public abstract InetSocketAddress getRemoteAddress();
-}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/client/ClientConnectionState.java b/src/main/java/com/github/manevolent/ts3j/protocol/client/ClientConnectionState.java
index 6979b0d..6104023 100644
--- a/src/main/java/com/github/manevolent/ts3j/protocol/client/ClientConnectionState.java
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/client/ClientConnectionState.java
@@ -1,29 +1,34 @@
package com.github.manevolent.ts3j.protocol.client;
-import com.github.manevolent.ts3j.protocol.packet.handler.client.LocalClientHandler;
-import com.github.manevolent.ts3j.protocol.packet.handler.client.LocalClientHandlerConnected;
-import com.github.manevolent.ts3j.protocol.packet.handler.client.LocalClientHandlerConnecting;
-import com.github.manevolent.ts3j.protocol.packet.handler.client.LocalClientHandlerDisconnected;
+import com.github.manevolent.ts3j.protocol.packet.handler.PacketHandler;
+import com.github.manevolent.ts3j.protocol.packet.handler.local.LocalClientHandler;
+import com.github.manevolent.ts3j.protocol.packet.handler.local.LocalClientHandlerConnected;
+import com.github.manevolent.ts3j.protocol.packet.handler.local.LocalClientHandlerConnecting;
+import com.github.manevolent.ts3j.protocol.packet.handler.local.LocalClientHandlerDisconnected;
+import com.github.manevolent.ts3j.protocol.socket.client.AbstractTeamspeakClientSocket;
+import com.github.manevolent.ts3j.protocol.socket.client.TeamspeakClientSocket;
+import org.apache.commons.lang.reflect.ConstructorUtils;
public enum ClientConnectionState {
CONNECTING(LocalClientHandlerConnecting.class),
+ NEGOTIATING(LocalClientHandlerConnecting.class),
CONNECTED(LocalClientHandlerConnected.class),
DISCONNECTED(LocalClientHandlerDisconnected.class);
- private final Class extends LocalClientHandler> clazz;
+ private final Class extends PacketHandler> clazz;
- ClientConnectionState(Class extends LocalClientHandler> clazz) {
+ ClientConnectionState(Class extends PacketHandler> clazz) {
this.clazz = clazz;
}
- public Class extends LocalClientHandler> getHandlerClass() {
+ public Class extends PacketHandler> getHandlerClass() {
return clazz;
}
- public LocalClientHandler createHandler(LocalTeamspeakClient client) {
+ public PacketHandler createHandler(TeamspeakClientSocket client) {
try {
- return clazz.getConstructor(LocalTeamspeakClient.class).newInstance(client);
+ return (PacketHandler) ConstructorUtils.invokeConstructor(clazz, client);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/client/LocalTeamspeakClient.java b/src/main/java/com/github/manevolent/ts3j/protocol/client/LocalTeamspeakClient.java
deleted file mode 100644
index ff9f0db..0000000
--- a/src/main/java/com/github/manevolent/ts3j/protocol/client/LocalTeamspeakClient.java
+++ /dev/null
@@ -1,218 +0,0 @@
-package com.github.manevolent.ts3j.protocol.client;
-
-import com.github.manevolent.ts3j.command.Command;
-import com.github.manevolent.ts3j.command.response.CommandResponse;
-import com.github.manevolent.ts3j.protocol.LocalEndpoint;
-import com.github.manevolent.ts3j.protocol.NetworkPacket;
-import com.github.manevolent.ts3j.protocol.ProtocolRole;
-import com.github.manevolent.ts3j.protocol.SocketRole;
-import com.github.manevolent.ts3j.protocol.header.ClientPacketHeader;
-import com.github.manevolent.ts3j.protocol.header.HeaderFlag;
-import com.github.manevolent.ts3j.protocol.packet.Packet6Ack;
-import com.github.manevolent.ts3j.protocol.packet.PacketType;
-import com.github.manevolent.ts3j.protocol.packet.channel.PacketChannel;
-import com.github.manevolent.ts3j.protocol.packet.handler.client.LocalClientHandler;
-import com.github.manevolent.ts3j.protocol.packet.transformation.PacketTransformation;
-import com.github.manevolent.ts3j.protocol.socket.LocalTeamspeakSocket;
-import com.github.manevolent.ts3j.protocol.packet.Packet;
-import com.github.manevolent.ts3j.util.Ts3Crypt;
-import com.github.manevolent.ts3j.util.Ts3Logging;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.SocketException;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.concurrent.TimeoutException;
-
-public class LocalTeamspeakClient extends LocalEndpoint implements TeamspeakClient {
- private final Object connectionStateLock = new Object();
- private final Map clientOptions = new LinkedHashMap<>();
- private final PacketChannel channel = new PacketChannel();
-
- private ClientConnectionState connectionState = null;
-
- private int packetId = 0;
- private int clientId = 0;
- private int returnCodeIndex = 0;
-
- public LocalTeamspeakClient(LocalTeamspeakSocket socket) {
- super(socket);
-
- if (socket.getSocketRole() != SocketRole.CLIENT)
- throw new IllegalArgumentException("invalid socket role: " + socket.getSocketRole().name());
-
- setConnectionState(ClientConnectionState.DISCONNECTED);
- }
-
- @Override
- public SocketRole getRole() {
- return SocketRole.CLIENT;
- }
-
- @Override
- protected void acknowledge(NetworkPacket packet, PacketType ackType) throws IOException {
- switch (ackType) {
- case ACK:
- Ts3Logging.debug("ACK " + packet.getHeader().getType().name());
-
- Packet6Ack ack = new Packet6Ack(ProtocolRole.CLIENT);
- ack.setPacketId(packet.getHeader().getPacketId());
- send(ack);
-
- break;
- }
- }
-
- // Just return the client's single channel (to the server) here
- @Override
- protected PacketChannel getChannel(NetworkPacket packet) {
- return channel;
- }
-
- @Override
- public Map getOptions() {
- return clientOptions;
- }
-
- public void setSecureParameters(Ts3Crypt.SecureChannelParameters parameters) {
- getSocket().setPacketTransformation(new PacketTransformation(parameters.getIvStruct()));
- }
-
- /**
- * Sends a command to the remote server.
- * @param command Command to send.
- * @return Command response object to track command response.
- * @throws IOException
- */
- public CommandResponse sendCommand(Command command) throws IOException {
- if (command.willExpectResponse() && !isConnected())
- throw new IOException("not connected");
-
- CommandResponse response;
-
- if (command.willExpectResponse()) {
- int returnCodeIndex = ++ this.returnCodeIndex;
- //command.appendParameter(new CommandSingleParameter("return_code", returnCodeIndex));
- response = new CommandResponse(command, returnCodeIndex);
- } else {
- response = new CommandResponse(command, -1);
- }
-
- String commandString = command.toString();
-
- response.setDispatchedTime(System.currentTimeMillis());
-
- return response;
- }
-
- public LocalTeamspeakClient() throws SocketException {
- this(new LocalTeamspeakSocket(SocketRole.CLIENT));
- }
-
- /**
- * Sends a packet to the server
- * @param packet Packet to send
- * @throws IOException
- */
- int z = 0;
- public void send(Packet packet) throws IOException {
- // Construct header
- ClientPacketHeader header = new ClientPacketHeader();
-
- header.setClientId(clientId);
- header.setPacketId(packetId);
-
- packet.setHeaderValues(header);
-
- if (connectionState == ClientConnectionState.CONNECTING) {
- header.setPacketId(z++);
- header.setPacketFlag(HeaderFlag.NEW_PROTOCOL, true);
- }
-
- getSocket().send(header, packet);
- }
-
- public void setConnectionState(ClientConnectionState state) {
- synchronized (connectionStateLock) {
- if (state != this.connectionState) {
- Ts3Logging.debug("State changing: " + state.name());
-
- boolean wasDisconnected = this.connectionState == ClientConnectionState.DISCONNECTED;
-
- this.connectionState = state;
-
- if (wasDisconnected)
- setRunning(true);
-
- if (channel.getHandler() != null)
- ((LocalClientHandler)channel.getHandler()).handleConnectionStateChanging(state);
-
- if (channel.getHandler() == null || channel.getHandler().getClass() != state.getHandlerClass()) {
- Ts3Logging.debug("Assigning " + state.getHandlerClass().getName() + " handler...");
-
- try {
- channel.setHandler(state.createHandler(this));
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
- }
-
- if (this.connectionState == ClientConnectionState.DISCONNECTED)
- clientOptions.clear();
-
- connectionStateLock.notifyAll();
-
- Ts3Logging.debug("State changed: " + state.name());
- }
- }
- }
-
- public ClientConnectionState getConnectionState() {
- return connectionState;
- }
-
- private void joinConnectionState(ClientConnectionState state, long wait)
- throws InterruptedException, TimeoutException {
- synchronized (connectionStateLock) {
- long start = System.currentTimeMillis();
-
- while (connectionState != state && System.currentTimeMillis() - start < wait) {
- connectionStateLock.wait(Math.max(0L, wait - (System.currentTimeMillis() - start)));
- }
-
- if (connectionState != state)
- throw new TimeoutException("timeout waiting for " + state.name() + " state");
- }
- }
-
- public boolean isConnected() {
- return connectionState == ClientConnectionState.CONNECTED;
- }
-
- /**
- * Initiates a connection to a server
- * @param remote remote sever to contact
- * @param password server password
- * @param timeout timeout, in milliseconds, to complete a connection.
- * @throws IOException
- */
- public void connect(InetSocketAddress remote, String password, long timeout) throws IOException {
- try {
- if (connectionState != ClientConnectionState.DISCONNECTED)
- throw new IllegalStateException(connectionState.name());
-
- setOption("client.hostname", remote.getHostString());
- setOption("client.password", password);
-
- getSocket().connect(remote);
-
- setConnectionState(ClientConnectionState.CONNECTING);
- joinConnectionState(ClientConnectionState.CONNECTED, timeout);
- } catch (Throwable e) {
- setConnectionState(ClientConnectionState.DISCONNECTED);
-
- throw new IOException(e);
- }
- }
-}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/client/TeamspeakClient.java b/src/main/java/com/github/manevolent/ts3j/protocol/client/TeamspeakClient.java
deleted file mode 100644
index d97ec7c..0000000
--- a/src/main/java/com/github/manevolent/ts3j/protocol/client/TeamspeakClient.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.github.manevolent.ts3j.protocol.client;
-
-import com.github.manevolent.ts3j.protocol.Endpoint;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-import java.util.Map;
-import java.util.function.Function;
-
-public interface TeamspeakClient extends Endpoint {
- void connect(InetSocketAddress remote, String password, long timeout) throws IOException;
-
- SocketAddress getRemoteAddress();
-
- boolean isConnected();
-
- Map getOptions();
-
- default T getOption(String key, Class clazz) {
- Object value = getOptions().get(key);
-
- if (value == null) return (T) null;
-
- else if (!value.getClass().isAssignableFrom(clazz)) {
- throw new ClassCastException(
- clazz.getName()
- + " is not assignable to option type " +
- value.getClass().getName()
- );
- } else
- return (T) value;
- }
-
- default Object setOption(String key, Object value) {
- return getOptions().put(key, value);
- }
-}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/header/ClientPacketHeader.java b/src/main/java/com/github/manevolent/ts3j/protocol/header/ClientPacketHeader.java
index 1b4c5ef..789955c 100644
--- a/src/main/java/com/github/manevolent/ts3j/protocol/header/ClientPacketHeader.java
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/header/ClientPacketHeader.java
@@ -1,6 +1,6 @@
package com.github.manevolent.ts3j.protocol.header;
-import com.github.manevolent.ts3j.protocol.packet.PacketType;
+import com.github.manevolent.ts3j.protocol.packet.PacketBodyType;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
import java.nio.ByteBuffer;
@@ -44,7 +44,7 @@ public class ClientPacketHeader extends PacketHeader {
setClientId((buffer.getShort() & 0xFFFF));
byte packetTypeAndFlags = buffer.get();
- setType(PacketType.fromId((packetTypeAndFlags & 0x0F))); // PT / Packet Type + Flags
+ setType(PacketBodyType.fromId((packetTypeAndFlags & 0x0F))); // PT / Packet Type + Flags
setPacketFlags(packetTypeAndFlags & 0xF0);
// (payload) -- see writeBody impl in higher levels
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/header/PacketHeader.java b/src/main/java/com/github/manevolent/ts3j/protocol/header/PacketHeader.java
index 491fd9e..77b04bf 100644
--- a/src/main/java/com/github/manevolent/ts3j/protocol/header/PacketHeader.java
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/header/PacketHeader.java
@@ -1,7 +1,7 @@
package com.github.manevolent.ts3j.protocol.header;
-import com.github.manevolent.ts3j.protocol.packet.PacketType;
-import com.github.manevolent.ts3j.protocol.packet.Packet;
+import com.github.manevolent.ts3j.protocol.packet.PacketBodyType;
+import com.github.manevolent.ts3j.protocol.packet.PacketBody;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
import java.nio.ByteBuffer;
@@ -13,7 +13,7 @@ public abstract class PacketHeader {
private int packetGeneration;
private byte[] mac = new byte[8]; // 8 bytes
private int packetId = 0;
- private PacketType type;
+ private PacketBodyType type;
private int packetFlags;
protected PacketHeader(ProtocolRole role) {
@@ -44,11 +44,11 @@ public abstract class PacketHeader {
}
}
- public PacketType getType() {
+ public PacketBodyType getType() {
return type;
}
- public void setType(PacketType type) {
+ public void setType(PacketBodyType type) {
this.type = type;
}
@@ -65,7 +65,7 @@ public abstract class PacketHeader {
}
public void setMac(byte[] mac) {
- Packet.assertArray("mac", mac, 8);
+ PacketBody.assertArray("mac", mac, 8);
this.mac = mac;
}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/header/ServerPacketHeader.java b/src/main/java/com/github/manevolent/ts3j/protocol/header/ServerPacketHeader.java
index ee90074..3bfb742 100644
--- a/src/main/java/com/github/manevolent/ts3j/protocol/header/ServerPacketHeader.java
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/header/ServerPacketHeader.java
@@ -1,8 +1,7 @@
package com.github.manevolent.ts3j.protocol.header;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
-import com.github.manevolent.ts3j.protocol.packet.PacketType;
-import com.github.manevolent.ts3j.util.Ts3Logging;
+import com.github.manevolent.ts3j.protocol.packet.PacketBodyType;
import java.nio.ByteBuffer;
@@ -40,7 +39,7 @@ public class ServerPacketHeader extends PacketHeader {
// https://github.com/ReSpeak/tsdeclarations/blob/master/ts3protocol.md
byte packetTypeAndFlags = buffer.get();
- setType(PacketType.fromId((packetTypeAndFlags & 0x0F))); // PT / Packet Type + Flags
+ setType(PacketBodyType.fromId((packetTypeAndFlags & 0x0F))); // PT / Packet Type + Flags
setPacketFlags(packetTypeAndFlags & 0xF0);
// (payload) -- see writeBody impl in higher levels
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody.java
similarity index 87%
rename from src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet.java
rename to src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody.java
index 82473cd..f238cf6 100644
--- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet.java
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody.java
@@ -5,16 +5,16 @@ import com.github.manevolent.ts3j.protocol.header.PacketHeader;
import java.nio.ByteBuffer;
-public abstract class Packet {
- private final PacketType type;
+public abstract class PacketBody {
+ private final PacketBodyType type;
private final ProtocolRole role;
- protected Packet(PacketType type, ProtocolRole role) {
+ protected PacketBody(PacketBodyType type, ProtocolRole role) {
this.type = type;
this.role = role;
}
- public PacketType getType() {
+ public PacketBodyType getType() {
return type;
}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet2Command.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody2Command.java
similarity index 81%
rename from src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet2Command.java
rename to src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody2Command.java
index 2d85ac3..46024c7 100644
--- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet2Command.java
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody2Command.java
@@ -2,16 +2,15 @@ package com.github.manevolent.ts3j.protocol.packet;
import com.github.manevolent.ts3j.command.SimpleCommand;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
-import com.github.manevolent.ts3j.util.Ts3Logging;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
-public class Packet2Command extends Packet {
+public class PacketBody2Command extends PacketBody {
private String text;
- public Packet2Command(ProtocolRole role) {
- super(PacketType.COMMAND, role);
+ public PacketBody2Command(ProtocolRole role) {
+ super(PacketBodyType.COMMAND, role);
}
public String getText() {
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet6Ack.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody6Ack.java
similarity index 69%
rename from src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet6Ack.java
rename to src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody6Ack.java
index 0ac69d7..d0ded14 100644
--- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet6Ack.java
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody6Ack.java
@@ -3,13 +3,18 @@ package com.github.manevolent.ts3j.protocol.packet;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-public class Packet6Ack extends Packet {
+public class PacketBody6Ack extends PacketBody {
private int packetId;
- public Packet6Ack(ProtocolRole role) {
- super(PacketType.ACK, role);
+ public PacketBody6Ack(ProtocolRole role) {
+ super(PacketBodyType.ACK, role);
+ }
+
+ public PacketBody6Ack(ProtocolRole role, int packetId) {
+ super(PacketBodyType.ACK, role);
+
+ setPacketId(packetId);
}
public int getPacketId() {
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet8Init1.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody8Init1.java
similarity index 91%
rename from src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet8Init1.java
rename to src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody8Init1.java
index 480477d..80826f2 100644
--- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet8Init1.java
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody8Init1.java
@@ -7,9 +7,9 @@ import com.github.manevolent.ts3j.protocol.header.PacketHeader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
-import static com.github.manevolent.ts3j.protocol.packet.PacketType.INIT1;
+import static com.github.manevolent.ts3j.protocol.packet.PacketBodyType.INIT1;
-public class Packet8Init1 extends Packet {
+public class PacketBody8Init1 extends PacketBody {
private static final byte[] LOW_LEVEL_MAC = new byte[] {
0x54, 0x53, 0x33, 0x49, 0x4E, 0x49, 0x54, 0x31
};
@@ -17,7 +17,7 @@ public class Packet8Init1 extends Packet {
private byte[] version = new byte[4]; // 4 bytes
private Step step; // step instance
- public Packet8Init1(ProtocolRole role) {
+ public PacketBody8Init1(ProtocolRole role) {
// sec. 2 of https://github.com/ReSpeak/tsdeclarations/blob/master/ts3protocol.md
// (The packet header values are set as following for all packets here:)
super(INIT1, role);
@@ -152,7 +152,7 @@ public class Packet8Init1 extends Packet {
}
public void setTimestamp(byte[] timestamp) {
- Packet.assertArray("timestamp", timestamp, 4);
+ PacketBody.assertArray("timestamp", timestamp, 4);
this.timestamp = timestamp;
}
@@ -161,7 +161,7 @@ public class Packet8Init1 extends Packet {
}
public void setRandom(byte[] random) {
- Packet.assertArray("random", random, 4);
+ PacketBody.assertArray("random", random, 4);
this.random = random;
}
@@ -199,7 +199,7 @@ public class Packet8Init1 extends Packet {
}
public void setA0reversed(byte[] a0reversed) {
- Packet.assertArray("a0reversed", a0reversed, 4);
+ PacketBody.assertArray("a0reversed", a0reversed, 4);
this.a0reversed = a0reversed;
}
@@ -208,7 +208,7 @@ public class Packet8Init1 extends Packet {
}
public void setServerStuff(byte[] serverStuff) {
- Packet.assertArray("serverStuff", serverStuff, 16);
+ PacketBody.assertArray("serverStuff", serverStuff, 16);
this.serverStuff = serverStuff;
}
@@ -244,7 +244,7 @@ public class Packet8Init1 extends Packet {
}
public void setA0reversed(byte[] a0reversed) {
- Packet.assertArray("a0reversed", a0reversed, 4);
+ PacketBody.assertArray("a0reversed", a0reversed, 4);
this.a0reversed = a0reversed;
}
@@ -253,7 +253,7 @@ public class Packet8Init1 extends Packet {
}
public void setServerStuff(byte[] serverStuff) {
- Packet.assertArray("serverStuff", serverStuff, 16);
+ PacketBody.assertArray("serverStuff", serverStuff, 16);
this.serverStuff = serverStuff;
}
@@ -290,7 +290,7 @@ public class Packet8Init1 extends Packet {
}
public void setX(byte[] x) {
- Packet.assertArray("x", x, 64);
+ PacketBody.assertArray("x", x, 64);
this.x = x;
}
@@ -299,7 +299,7 @@ public class Packet8Init1 extends Packet {
}
public void setN(byte[] n) {
- Packet.assertArray("n", n, 64);
+ PacketBody.assertArray("n", n, 64);
this.n = n;
}
@@ -308,7 +308,7 @@ public class Packet8Init1 extends Packet {
}
public void setServerStuff(byte[] serverStuff) {
- Packet.assertArray("serverStuff", serverStuff, 100);
+ PacketBody.assertArray("serverStuff", serverStuff, 100);
this.serverStuff = serverStuff;
}
@@ -359,7 +359,7 @@ public class Packet8Init1 extends Packet {
}
public void setX(byte[] x) {
- Packet.assertArray("x", x, 64);
+ PacketBody.assertArray("x", x, 64);
this.x = x;
}
@@ -368,7 +368,7 @@ public class Packet8Init1 extends Packet {
}
public void setN(byte[] n) {
- Packet.assertArray("n", n, 64);
+ PacketBody.assertArray("n", n, 64);
this.n = n;
}
@@ -377,7 +377,7 @@ public class Packet8Init1 extends Packet {
}
public void setServerStuff(byte[] serverStuff) {
- Packet.assertArray("serverStuff", serverStuff, 100);
+ PacketBody.assertArray("serverStuff", serverStuff, 100);
this.serverStuff = serverStuff;
}
@@ -394,7 +394,7 @@ public class Packet8Init1 extends Packet {
}
public void setY(byte[] y) {
- Packet.assertArray("y", y, 64);
+ PacketBody.assertArray("y", y, 64);
this.y = y;
}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBodyFragment.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBodyFragment.java
new file mode 100644
index 0000000..12cc5fc
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBodyFragment.java
@@ -0,0 +1,37 @@
+package com.github.manevolent.ts3j.protocol.packet;
+
+import com.github.manevolent.ts3j.protocol.ProtocolRole;
+
+import java.nio.ByteBuffer;
+
+public class PacketBodyFragment extends PacketBody {
+ private byte[] raw;
+
+ public PacketBodyFragment(PacketBodyType type, ProtocolRole role) {
+ super(type, role);
+ }
+
+ public byte[] getRaw() {
+ return raw;
+ }
+
+ public void setRaw(byte[] raw) {
+ this.raw = raw;
+ }
+
+ @Override
+ public void read(ByteBuffer buffer) {
+ raw = new byte[buffer.remaining()];
+ buffer.get(raw);
+ }
+
+ @Override
+ public void write(ByteBuffer buffer) {
+ buffer.put(raw);
+ }
+
+ @Override
+ public int getSize() {
+ return raw.length;
+ }
+}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBodyType.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBodyType.java
new file mode 100644
index 0000000..2ccadc7
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBodyType.java
@@ -0,0 +1,74 @@
+package com.github.manevolent.ts3j.protocol.packet;
+
+public enum PacketBodyType {
+ VOICE(0x0, null, null, false, false, false, false, false),
+ VOICE_WHISPER(0x1, null, null, false, false, false, false, false),
+ PONG(0x5, null, null, false, false, false, false, false),
+ PING(0x4, null, PONG, false, false, false, false, false),
+ ACK(0x6, PacketBody6Ack.class, null, true, true, false, false, true),
+ ACK_LOW(0x7, null, null, true, true, false, false, true),
+ INIT1(0x8, PacketBody8Init1.class, null, false, true, false, false, false),
+ COMMAND_LOW(0x3, null, ACK_LOW, true, true, true, true, true),
+ COMMAND(0x2, PacketBody2Command.class, ACK, true, true, true, true, true);
+
+ private final int index;
+ private final Class extends PacketBody> packetClass;
+
+ private final PacketBodyType acknolwedgedBy;
+ private final boolean encrypted, resend, splittable, compressible, mustEncrypt;
+
+ PacketBodyType(int index, Class extends PacketBody> packetClass,
+ PacketBodyType acknolwedgedBy,
+ boolean encrypted,
+ boolean resend,
+ boolean splittable,
+ boolean compressible, boolean mustEncrypt) {
+ this.index = index;
+ this.packetClass = packetClass;
+ this.acknolwedgedBy = acknolwedgedBy;
+ this.resend = resend;
+ this.encrypted = encrypted;
+ this.splittable = splittable;
+ this.compressible = compressible;
+ this.mustEncrypt = mustEncrypt;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public static PacketBodyType fromId(int index) {
+ for (PacketBodyType value : values())
+ if (value.getIndex() == index) return value;
+
+ throw new IllegalArgumentException("invalid index: " + index);
+ }
+
+ public Class extends PacketBody> getPacketClass() {
+ return packetClass;
+ }
+
+ public boolean canEncrypt() {
+ return encrypted;
+ }
+
+ public PacketBodyType getAcknolwedgedBy() {
+ return acknolwedgedBy;
+ }
+
+ public boolean canResend() {
+ return resend;
+ }
+
+ public boolean isSplittable() {
+ return splittable;
+ }
+
+ public boolean isCompressible() {
+ return compressible;
+ }
+
+ public boolean mustEncrypt() {
+ return mustEncrypt;
+ }
+}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketFragment.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketFragment.java
deleted file mode 100644
index 77822b5..0000000
--- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketFragment.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.github.manevolent.ts3j.protocol.packet;
-
-import com.github.manevolent.ts3j.protocol.ProtocolRole;
-
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-
-public class PacketFragment extends Packet {
- private byte[] fragment;
-
- public PacketFragment( PacketType type, ProtocolRole role) {
- super(type, role);
- }
-
- public byte[] getFragment() {
- return fragment;
- }
-
- public void setFragment(byte[] fragment) {
- this.fragment = fragment;
- }
-
- @Override
- public void read(ByteBuffer buffer) {
- fragment = new byte[buffer.remaining()];
- buffer.get(fragment);
- }
-
- @Override
- public void write(ByteBuffer buffer) {
- buffer.put(fragment);
- }
-
- @Override
- public int getSize() {
- return fragment.length;
- }
-}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketType.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketType.java
deleted file mode 100644
index 1b268af..0000000
--- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketType.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package com.github.manevolent.ts3j.protocol.packet;
-
-public enum PacketType {
- VOICE(0x0, null, null, false, false, false, false),
- VOICE_WHISPER(0x1, null, null, false, false, false, false),
- PONG(0x5, null, null, false, false, false, false),
- PING(0x4, null, PONG, false, false, false, false),
- ACK(0x6, Packet6Ack.class, null, true, true, false, false),
- ACK_LOW(0x7, null, null, true, true, false, false),
- INIT1(0x8, Packet8Init1.class, null, false, true, false, false),
- COMMAND_LOW(0x3, null, ACK_LOW, true, true, true, true),
- COMMAND(0x2, Packet2Command.class, ACK, true, true, true, true);
-
- private final int index;
- private final Class extends Packet> packetClass;
-
- private final PacketType acknolwedgedBy;
- private final boolean encrypted, resend, splittable, compressible;
-
- PacketType(int index, Class extends Packet> packetClass,
- PacketType acknolwedgedBy,
- boolean encrypted,
- boolean resend,
- boolean splittable,
- boolean compressible) {
- this.index = index;
- this.packetClass = packetClass;
- this.acknolwedgedBy = acknolwedgedBy;
- this.resend = resend;
- this.encrypted = encrypted;
- this.splittable = splittable;
- this.compressible = compressible;
- }
-
- public int getIndex() {
- return index;
- }
-
- public static PacketType fromId(int index) {
- for (PacketType value : values())
- if (value.getIndex() == index) return value;
-
- throw new IllegalArgumentException("invalid index: " + index);
- }
-
- public Class extends Packet> getPacketClass() {
- return packetClass;
- }
-
- public boolean isEncrypted() {
- return encrypted;
- }
-
- public PacketType getAcknolwedgedBy() {
- return acknolwedgedBy;
- }
-
- public boolean canResend() {
- return resend;
- }
-
- public boolean isSplittable() {
- return splittable;
- }
-
- public boolean isCompressible() {
- return compressible;
- }
-}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/channel/PacketChannel.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/channel/PacketChannel.java
deleted file mode 100644
index 849c248..0000000
--- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/channel/PacketChannel.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package com.github.manevolent.ts3j.protocol.packet.channel;
-
-import com.github.manevolent.ts3j.protocol.NetworkPacket;
-import com.github.manevolent.ts3j.protocol.header.HeaderFlag;
-import com.github.manevolent.ts3j.protocol.packet.handler.PacketHandler;
-
-import java.io.IOException;
-import java.util.NoSuchElementException;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-public final class PacketChannel {
- private final Queue receive = new ConcurrentLinkedQueue<>();
- private final ReassemblyQueue reassemblyQueue = new ReassemblyQueue();
-
- private PacketHandler handler;
-
- /**
- * Inserts a packet into the receive queue.
- * @param networkPacket Packet to insert.
- */
- public void insert(NetworkPacket networkPacket) {
- if ((networkPacket = reassemblyQueue.put(networkPacket)) != null)
- receive.add(networkPacket);
- }
-
- /**
- * Finds the next packet in the receive queue available for dequeue.
- * @return Network packet, null otherwise
- */
- public NetworkPacket next() {
- try {
- return receive.remove();
- } catch (NoSuchElementException ex) {
- return null;
- }
- }
-
- public PacketHandler getHandler() {
- return handler;
- }
-
- public void setHandler(PacketHandler handler) throws IOException {
- if (this.handler != handler) {
- if (this.handler != null) this.handler.onUnassigning();
- this.handler = handler;
- if (handler != null) handler.onAssigned();
- }
- }
-}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/channel/ReassemblyQueue.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/channel/ReassemblyQueue.java
deleted file mode 100644
index 066927d..0000000
--- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/channel/ReassemblyQueue.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package com.github.manevolent.ts3j.protocol.packet.channel;
-
-import com.github.manevolent.ts3j.protocol.NetworkPacket;
-import com.github.manevolent.ts3j.protocol.header.HeaderFlag;
-import com.github.manevolent.ts3j.protocol.packet.PacketFragment;
-import com.github.manevolent.ts3j.protocol.packet.PacketType;
-
-import java.nio.ByteBuffer;
-import java.util.*;
-
-public class ReassemblyQueue {
- private final Map queue = new LinkedHashMap<>();
- private final Map fragmentation = new LinkedHashMap<>();
-
- public NetworkPacket reassemble(NetworkPacket lastFragment) {
- // Pull out all other packets in the queue before this one which would also be fragmented
- List reassemblyList = new ArrayList<>();
- for (int packetId = lastFragment.getHeader().getPacketId();; packetId--) {
- NetworkPacket olderPacket = queue.get(packetId);
-
- if (olderPacket == null)
- break; // Chain breaks here
- else if (olderPacket.getHeader().getType() != lastFragment.getHeader().getType())
- continue; // skip
- else {
- reassemblyList.add(olderPacket);
- fragmentation.remove(packetId); // Don't reuse this packet
- }
- }
-
- // Reverse the collection
- Collections.reverse(reassemblyList);
-
- // Rebuild a master packet from the contents of all previous packet fragments
- int totalLength = reassemblyList.stream().mapToInt(x -> x.getPacket().getSize()).sum() +
- lastFragment.getPacket().getSize();
-
- if (totalLength <= 0) throw new IllegalArgumentException("reassembly too small: " + totalLength);
-
- ByteBuffer reassemblyBuffer = ByteBuffer.allocate(totalLength);
-
- for (NetworkPacket old : reassemblyList)
- old.writeBody(reassemblyBuffer);
-
- NetworkPacket firstPacket = reassemblyList.get(0);
- NetworkPacket reassembledPacket = new NetworkPacket(firstPacket.getRole());
- reassembledPacket.setHeader(firstPacket.getHeader());
- reassembledPacket.getHeader().setPacketFlag(HeaderFlag.FRAGMENTED, false);
- reassembledPacket.readBody((ByteBuffer) reassemblyBuffer.position(0));
-
- return reassembledPacket;
- }
-
- public NetworkPacket put(NetworkPacket networkPacket) {
- Boolean fragmentState = fragmentation.get(networkPacket.getHeader().getType());
-
- if (networkPacket.getHeader().getPacketFlag(HeaderFlag.FRAGMENTED)) {
- if (!(networkPacket.getPacket() instanceof PacketFragment))
- throw new IllegalArgumentException("packet fragment object is not instance of PacketFragment class");
-
- boolean fragmentationEnded = false;
-
- if (fragmentState == null)
- fragmentation.put(networkPacket.getHeader().getType(), fragmentState = true);
- else if (fragmentState) {
- // Fragmentation end
- fragmentationEnded = true;
-
- fragmentation.put(networkPacket.getHeader().getType(), fragmentState = false);
- }
-
- queue.put(networkPacket.getHeader().getPacketId(), networkPacket);
-
- if (fragmentationEnded)
- return reassemble(networkPacket);
-
- return null;
- } else {
- if (fragmentState != null && fragmentState) {
- queue.put(networkPacket.getHeader().getPacketId(), networkPacket);
-
- return null;
- } else {
- return networkPacket;
- }
- }
- }
-}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/PacketHandler.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/PacketHandler.java
index 871796a..10ff149 100644
--- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/PacketHandler.java
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/PacketHandler.java
@@ -1,30 +1,31 @@
package com.github.manevolent.ts3j.protocol.packet.handler;
-import com.github.manevolent.ts3j.protocol.NetworkPacket;
-import com.github.manevolent.ts3j.protocol.LocalEndpoint;
+import com.github.manevolent.ts3j.protocol.Packet;
+import com.github.manevolent.ts3j.protocol.socket.client.TeamspeakClientSocket;
import java.io.IOException;
+import java.util.concurrent.TimeoutException;
public abstract class PacketHandler {
- private final LocalEndpoint endpoint;
+ private final TeamspeakClientSocket clientSocket;
- protected PacketHandler(LocalEndpoint endpoint) {
- this.endpoint = endpoint;
+ protected PacketHandler(TeamspeakClientSocket clientSocket) {
+ this.clientSocket = clientSocket;
}
public void onUnassigning() throws IOException {
}
- public void onAssigned() throws IOException {
+ public void onAssigned() throws IOException, TimeoutException {
}
- public void handlePacket(NetworkPacket packet) throws IOException {
+ public void handlePacket(Packet packet) throws IOException, TimeoutException {
}
- public LocalEndpoint getEndpoint() {
- return endpoint;
+ public TeamspeakClientSocket getClient() {
+ return clientSocket;
}
}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnected.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnected.java
deleted file mode 100644
index d33da6b..0000000
--- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnected.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.github.manevolent.ts3j.protocol.packet.handler.client;
-
-import com.github.manevolent.ts3j.protocol.client.LocalTeamspeakClient;
-
-public class LocalClientHandlerConnected extends LocalClientHandler {
- public LocalClientHandlerConnected(LocalTeamspeakClient client) {
- super(client);
- }
-}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerDisconnected.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerDisconnected.java
deleted file mode 100644
index e57aa51..0000000
--- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerDisconnected.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.github.manevolent.ts3j.protocol.packet.handler.client;
-
-import com.github.manevolent.ts3j.protocol.client.LocalTeamspeakClient;
-
-public class LocalClientHandlerDisconnected extends LocalClientHandler {
- public LocalClientHandlerDisconnected(LocalTeamspeakClient client) {
- super(client);
- }
-}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandler.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandler.java
similarity index 55%
rename from src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandler.java
rename to src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandler.java
index c98c40f..3dd883a 100644
--- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandler.java
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandler.java
@@ -1,16 +1,14 @@
-package com.github.manevolent.ts3j.protocol.packet.handler.client;
+package com.github.manevolent.ts3j.protocol.packet.handler.local;
import com.github.manevolent.ts3j.protocol.client.ClientConnectionState;
-import com.github.manevolent.ts3j.protocol.client.LocalTeamspeakClient;
import com.github.manevolent.ts3j.protocol.packet.handler.PacketHandler;
-
-import java.io.IOException;
+import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket;
public abstract class LocalClientHandler extends PacketHandler {
- private final LocalTeamspeakClient client;
+ private final LocalTeamspeakClientSocket client;
- protected LocalClientHandler(LocalTeamspeakClient client) {
+ protected LocalClientHandler(LocalTeamspeakClientSocket client) {
super(client);
this.client = client;
@@ -20,7 +18,7 @@ public abstract class LocalClientHandler extends PacketHandler {
}
- protected LocalTeamspeakClient getClient() {
+ public LocalTeamspeakClientSocket getClient() {
return client;
}
}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandlerConnected.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandlerConnected.java
new file mode 100644
index 0000000..3d2d9a1
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandlerConnected.java
@@ -0,0 +1,9 @@
+package com.github.manevolent.ts3j.protocol.packet.handler.local;
+
+import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket;
+
+public class LocalClientHandlerConnected extends LocalClientHandler {
+ public LocalClientHandlerConnected(LocalTeamspeakClientSocket client) {
+ super(client);
+ }
+}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnecting.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandlerConnecting.java
similarity index 78%
rename from src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnecting.java
rename to src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandlerConnecting.java
index d83acb3..877478b 100644
--- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnecting.java
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandlerConnecting.java
@@ -1,14 +1,12 @@
-package com.github.manevolent.ts3j.protocol.packet.handler.client;
+package com.github.manevolent.ts3j.protocol.packet.handler.local;
import com.github.manevolent.ts3j.command.SimpleCommand;
import com.github.manevolent.ts3j.command.part.CommandSingleParameter;
-import com.github.manevolent.ts3j.license.License;
-import com.github.manevolent.ts3j.protocol.NetworkPacket;
+import com.github.manevolent.ts3j.protocol.Packet;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
-import com.github.manevolent.ts3j.protocol.client.ClientConnectionState;
-import com.github.manevolent.ts3j.protocol.client.LocalTeamspeakClient;
-import com.github.manevolent.ts3j.protocol.packet.Packet2Command;
-import com.github.manevolent.ts3j.protocol.packet.Packet8Init1;
+import com.github.manevolent.ts3j.protocol.packet.PacketBody2Command;
+import com.github.manevolent.ts3j.protocol.packet.PacketBody8Init1;
+import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket;
import com.github.manevolent.ts3j.util.Ts3Crypt;
import com.github.manevolent.ts3j.util.Ts3Logging;
import javafx.util.Pair;
@@ -16,27 +14,26 @@ import org.bouncycastle.math.ec.ECPoint;
import java.io.IOException;
import java.math.BigInteger;
-import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Base64;
-import java.util.List;
import java.util.Random;
+import java.util.concurrent.TimeoutException;
public class LocalClientHandlerConnecting extends LocalClientHandler {
private byte[] randomBytes;
private byte[] alphaBytes;
- public LocalClientHandlerConnecting(LocalTeamspeakClient client) {
+ public LocalClientHandlerConnecting(LocalTeamspeakClientSocket client) {
super(client);
}
@Override
- public void onAssigned() throws IOException {
- Packet8Init1 packet = new Packet8Init1(ProtocolRole.CLIENT);
+ public void onAssigned() throws IOException, TimeoutException {
+ PacketBody8Init1 packet = new PacketBody8Init1(ProtocolRole.CLIENT);
// Initialize connection
- Packet8Init1.Step0 step = new Packet8Init1.Step0();
+ PacketBody8Init1.Step0 step = new PacketBody8Init1.Step0();
Random random = new Random();
randomBytes = new byte[4];
random.nextBytes(randomBytes);
@@ -47,7 +44,7 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
sendInit1(packet);
}
- private void sendInit1(Packet8Init1 packet) throws IOException {
+ private void sendInit1(PacketBody8Init1 packet) throws IOException, TimeoutException {
Ts3Logging.debug("Connecting: sending " + packet.getClass().getSimpleName()
+ ":"
+ packet.getStep().getClass().getSimpleName() + "..."
@@ -55,19 +52,19 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
packet.setVersion(new byte[] { 0x09, (byte)0x83, (byte)0x8C, (byte)0xCF });
- getClient().send(packet);
+ getClient().writePacket(packet);
}
@Override
- public void handlePacket(NetworkPacket packet) throws IOException {
- Ts3Logging.debug("Connecting: handling " + packet.getPacket().getClass().getSimpleName());
+ public void handlePacket(Packet packet) throws IOException, TimeoutException {
+ Ts3Logging.debug("Connecting: handling " + packet.getBody().getClass().getSimpleName());
- if (packet.getPacket() instanceof Packet8Init1) {
- Packet8Init1 init1 = (Packet8Init1) packet.getPacket();
+ if (packet.getBody() instanceof PacketBody8Init1) {
+ PacketBody8Init1 init1 = (PacketBody8Init1) packet.getBody();
switch (init1.getStep().getNumber()) {
case 1:
- Packet8Init1.Step1 serverReplyStep1 = (Packet8Init1.Step1)init1.getStep();
+ PacketBody8Init1.Step1 serverReplyStep1 = (PacketBody8Init1.Step1)init1.getStep();
// Check nonce. It's received backwards, so walk backwards over the array received
for (int i = 0; i < 4; i ++) {
if (randomBytes[3 - i] != serverReplyStep1.getA0reversed()[i])
@@ -75,9 +72,9 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
}
// Build response
- Packet8Init1 response2 = new Packet8Init1(ProtocolRole.CLIENT);
+ PacketBody8Init1 response2 = new PacketBody8Init1(ProtocolRole.CLIENT);
- Packet8Init1.Step2 step2 = new Packet8Init1.Step2();
+ PacketBody8Init1.Step2 step2 = new PacketBody8Init1.Step2();
step2.setA0reversed(serverReplyStep1.getA0reversed());
step2.setServerStuff(serverReplyStep1.getServerStuff());
response2.setStep(step2);
@@ -85,7 +82,7 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
sendInit1(response2);
break;
case 3:
- Packet8Init1.Step3 serverReplyStep3 = (Packet8Init1.Step3)init1.getStep();
+ PacketBody8Init1.Step3 serverReplyStep3 = (PacketBody8Init1.Step3)init1.getStep();
// Calculate 'y'
// which is the result of x ^ (2 ^ level) % n as an unsigned
@@ -113,9 +110,9 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
);
// Build response
- Packet8Init1 response4 = new Packet8Init1(ProtocolRole.CLIENT);
+ PacketBody8Init1 response4 = new PacketBody8Init1(ProtocolRole.CLIENT);
- Packet8Init1.Step4 step4 = new Packet8Init1.Step4();
+ PacketBody8Init1.Step4 step4 = new PacketBody8Init1.Step4();
step4.setLevel(serverReplyStep3.getLevel());
step4.setX(serverReplyStep3.getX());
@@ -130,7 +127,7 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
initiv.add(new CommandSingleParameter("alpha", Base64.getEncoder().encodeToString(alphaBytes)));
- initiv.add(new CommandSingleParameter("omega", getClient().getLocalIdentity().getPublicKeyString()));
+ initiv.add(new CommandSingleParameter("omega", getClient().getIdentity().getPublicKeyString()));
initiv.add(new CommandSingleParameter("ot", "1")); // constant, set to 1
@@ -151,8 +148,8 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
throw new IllegalArgumentException("unexpected Init1 server step: " + init1.getStep().getNumber());
}
- } else if (packet.getPacket() instanceof Packet2Command) {
- SimpleCommand command = ((Packet2Command) packet.getPacket()).parse();
+ } else if (packet.getBody() instanceof PacketBody2Command) {
+ SimpleCommand command = ((PacketBody2Command) packet.getBody()).parse();
if (command.getName().equalsIgnoreCase("initivexpand2")) {
// 3.2.2 initivexpand2 (Client <- Server)
@@ -186,12 +183,12 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
byte[] toSign = new byte[86];
System.arraycopy(keyPair.getKey(), 0, toSign, 0, 32);
System.arraycopy(beta, 0, toSign, 32, 54);
- byte[] sign = getClient().getLocalIdentity().sign(toSign);
+ byte[] sign = getClient().getIdentity().sign(toSign);
Ts3Logging.debug("Created proof (clientek): " + Ts3Logging.getHex(sign));
// Send clientek (telling them the shared secret)
- Packet2Command clientek = new Packet2Command(ProtocolRole.CLIENT);
+ PacketBody2Command clientek = new PacketBody2Command(ProtocolRole.CLIENT);
clientek.setText(
new SimpleCommand(
"clientek", ProtocolRole.CLIENT,
@@ -199,12 +196,14 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
new CommandSingleParameter("proof", Base64.getEncoder().encodeToString(sign))
).build()
);
- getClient().send(clientek);
+ getClient().writePacket(clientek);
- Ts3Crypt.SecureChannelParameters secureChannelParameters =
- Ts3Crypt.cryptoInit2(license, alphaBytes, omega, proof, beta, keyPair.getValue());
+ getClient().setSecureParameters(
+ Ts3Crypt.cryptoInit2(license, alphaBytes, omega, proof, beta, keyPair.getValue())
+ );
+
+ PacketBody2Command clientinit = new PacketBody2Command(ProtocolRole.CLIENT);
- Packet2Command clientinit = new Packet2Command(ProtocolRole.CLIENT);
clientinit.setText(
new SimpleCommand(
"clientinit", ProtocolRole.CLIENT,
@@ -220,11 +219,12 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
new CommandSingleParameter("client_nickname_phonetic", "test"),
new CommandSingleParameter("client_meta_data", ""),
new CommandSingleParameter("client_default_token", ""),
- new CommandSingleParameter("client_key_offset", Long.toString(getClient().getLocalIdentity().getKeyOffset())),
+ new CommandSingleParameter("client_key_offset", Long.toString(getClient().getIdentity().getKeyOffset())),
new CommandSingleParameter("hwid", "87056c6e1268aaf5055abf8256415e0e,408978b6d98810cc03f0aa16a4c75600")
).build()
);
- getClient().send(clientinit);
+
+ getClient().writePacket(clientinit);
}
}
}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandlerDisconnected.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandlerDisconnected.java
new file mode 100644
index 0000000..30e6faa
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandlerDisconnected.java
@@ -0,0 +1,9 @@
+package com.github.manevolent.ts3j.protocol.packet.handler.local;
+
+import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket;
+
+public class LocalClientHandlerDisconnected extends LocalClientHandler {
+ public LocalClientHandlerDisconnected(LocalTeamspeakClientSocket client) {
+ super(client);
+ }
+}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandlerNegotiating.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandlerNegotiating.java
new file mode 100644
index 0000000..2d204bc
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandlerNegotiating.java
@@ -0,0 +1,9 @@
+package com.github.manevolent.ts3j.protocol.packet.handler.local;
+
+import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket;
+
+public class LocalClientHandlerNegotiating extends LocalClientHandler {
+ public LocalClientHandlerNegotiating(LocalTeamspeakClientSocket client) {
+ super(client);
+ }
+}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/transformation/PacketTransformation.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/transformation/PacketTransformation.java
index 1dccad8..9511b01 100644
--- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/transformation/PacketTransformation.java
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/transformation/PacketTransformation.java
@@ -1,6 +1,6 @@
package com.github.manevolent.ts3j.protocol.packet.transformation;
-import com.github.manevolent.ts3j.protocol.NetworkPacket;
+import com.github.manevolent.ts3j.protocol.Packet;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
import com.github.manevolent.ts3j.protocol.header.PacketHeader;
import com.github.manevolent.ts3j.util.Ts3Logging;
@@ -72,7 +72,7 @@ public class PacketTransformation {
return new Pair<>(key, nonce);
}
- public ByteBuffer encrypt(NetworkPacket packet) {
+ public ByteBuffer encrypt(Packet packet) {
Pair parameters = computeParameters(packet.getHeader());
// Write header (this is temporary)
@@ -93,7 +93,7 @@ public class PacketTransformation {
headerWithoutMac
);
- int dataLen = packet.getPacket().getSize();
+ int dataLen = packet.getBody().getSize();
ByteBuffer packetBuffer = packet.writeBody(ByteBuffer.allocate(dataLen).order(ByteOrder.BIG_ENDIAN));
byte[] result;
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/server/RemoteTeamspeakServer.java b/src/main/java/com/github/manevolent/ts3j/protocol/server/RemoteTeamspeakServer.java
deleted file mode 100644
index 4ca98b9..0000000
--- a/src/main/java/com/github/manevolent/ts3j/protocol/server/RemoteTeamspeakServer.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.github.manevolent.ts3j.protocol.server;
-
-import com.github.manevolent.ts3j.protocol.NetworkPacket;
-import com.github.manevolent.ts3j.protocol.RemoteEndpoint;
-import com.github.manevolent.ts3j.protocol.SocketRole;
-import com.github.manevolent.ts3j.protocol.client.LocalTeamspeakClient;
-import com.github.manevolent.ts3j.protocol.packet.Packet;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-
-public class RemoteTeamspeakServer extends RemoteEndpoint implements TeamspeakServer {
- private final LocalTeamspeakClient client;
- private final InetSocketAddress address;
-
- public RemoteTeamspeakServer(LocalTeamspeakClient client,
- InetSocketAddress address) {
- this.client = client;
- this.address = address;
- }
-
- @Override
- public SocketRole getRole() {
- return SocketRole.SERVER;
- }
-
- @Override
- public void send(Packet packet) throws IOException {
- client.send(packet);
- }
-
- @Override
- public InetSocketAddress getRemoteAddress() {
- return address;
- }
-}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/server/TeamspeakServer.java b/src/main/java/com/github/manevolent/ts3j/protocol/server/TeamspeakServer.java
deleted file mode 100644
index 2687855..0000000
--- a/src/main/java/com/github/manevolent/ts3j/protocol/server/TeamspeakServer.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.github.manevolent.ts3j.protocol.server;
-
-import com.github.manevolent.ts3j.protocol.Endpoint;
-
-import java.net.InetSocketAddress;
-
-public interface TeamspeakServer extends Endpoint {
-
-
-
-}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/socket/AbstractTeamspeakSocket.java b/src/main/java/com/github/manevolent/ts3j/protocol/socket/AbstractTeamspeakSocket.java
index 1716064..e1a7d1a 100644
--- a/src/main/java/com/github/manevolent/ts3j/protocol/socket/AbstractTeamspeakSocket.java
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/AbstractTeamspeakSocket.java
@@ -1,18 +1,15 @@
package com.github.manevolent.ts3j.protocol.socket;
-import com.github.manevolent.ts3j.protocol.packet.transformation.DefaultPacketTransformation;
-import com.github.manevolent.ts3j.protocol.packet.transformation.PacketTransformation;
+import com.github.manevolent.ts3j.protocol.SocketRole;
public abstract class AbstractTeamspeakSocket implements TeamspeakSocket {
- private PacketTransformation transformation = new DefaultPacketTransformation();
+ private final SocketRole role;
- @Override
- public PacketTransformation getPacketTransformation() {
- return transformation;
+ protected AbstractTeamspeakSocket(SocketRole role) {
+ this.role = role;
}
- @Override
- public void setPacketTransformation(PacketTransformation transformation) {
- this.transformation = transformation;
+ public SocketRole getRole() {
+ return role;
}
}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/socket/LocalTeamspeakSocket.java b/src/main/java/com/github/manevolent/ts3j/protocol/socket/LocalTeamspeakSocket.java
deleted file mode 100644
index 1c459c8..0000000
--- a/src/main/java/com/github/manevolent/ts3j/protocol/socket/LocalTeamspeakSocket.java
+++ /dev/null
@@ -1,176 +0,0 @@
-package com.github.manevolent.ts3j.protocol.socket;
-
-import com.github.manevolent.ts3j.protocol.NetworkPacket;
-import com.github.manevolent.ts3j.protocol.SocketRole;
-import com.github.manevolent.ts3j.protocol.header.HeaderFlag;
-import com.github.manevolent.ts3j.protocol.header.PacketHeader;
-import com.github.manevolent.ts3j.protocol.packet.Packet;
-import com.github.manevolent.ts3j.protocol.packet.PacketFragment;
-import com.github.manevolent.ts3j.protocol.packet.PacketType;
-import com.github.manevolent.ts3j.util.Ts3Logging;
-
-import java.io.IOException;
-import java.net.*;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
-/**
- * Low-level protocol socket (not a client, used for server implementation also)
- *
- * Used simply to transcribe packets on the network
- */
-public class LocalTeamspeakSocket extends AbstractTeamspeakSocket implements TeamspeakSocket {
- private final SocketRole socketRole;
-
- private final DatagramSocket datagramSocket;
- private final DatagramPacket datagramPacket = new DatagramPacket(new byte[1500], 1500);
-
- public LocalTeamspeakSocket(SocketRole socketRole, DatagramSocket datagramSocket) {
- this.socketRole = socketRole;
- this.datagramSocket = datagramSocket;
- }
-
- public LocalTeamspeakSocket(SocketRole socketRole) throws SocketException {
- this(socketRole, new DatagramSocket());
- }
-
- public SocketRole getSocketRole() {
- return socketRole;
- }
-
- public InetSocketAddress getLocalAddress() {
- return new InetSocketAddress(datagramSocket.getLocalAddress(), datagramSocket.getLocalPort());
- }
-
- public InetSocketAddress getRemoteAddress() {
- return new InetSocketAddress(datagramSocket.getInetAddress(), datagramSocket.getPort());
- }
-
- public void connect(SocketAddress remote) throws SocketException {
- datagramSocket.connect(remote);
- }
-
- /**
- * Writes an actual datagram to the network
- * @param packet Packet to write
- * @throws IOException
- */
- private void sendNetworkPacket(DatagramPacket packet) throws IOException {
- if (packet.getAddress() == null || packet.getPort() < 0)
- packet.setSocketAddress(datagramSocket.getRemoteSocketAddress());
-
- Ts3Logging.debug(
- "[NETWORK] WRITE Len=" + packet.getLength() + " to " + packet.getSocketAddress()
- );
-
- //Ts3Logging.debug(Ts3Logging.getHex(packet.getData(), packet.getLength()));
-
- datagramSocket.send(packet);
- }
-
- /**
- * Sends a NetworkPacket to the network, first converting it to a datagram.
- * @param header Header to send
- * @throws IOException
- */
- public void send(PacketHeader header, Packet packet) throws IOException {
- if (header == null)
- throw new NullPointerException("header");
- else if (header.getRole() != getSocketRole().getOut())
- throw new IllegalArgumentException("packet role mismatch: " +
- header.getRole().name() + " != " +
- getSocketRole().getOut().name());
-
- // Ensure type matches
- header.setType(packet.getType());
-
- // Construct a network packet
- NetworkPacket networkPacket = new NetworkPacket(header.getRole());
- networkPacket.setHeader(header);
- networkPacket.setPacket(packet);
-
- // Flush to a buffer
- ByteBuffer outputBuffer;
-
- if (networkPacket.getHeader().getType().isEncrypted() &&
- !networkPacket.getHeader().getPacketFlag(HeaderFlag.UNENCRYPTED)) {
- outputBuffer = getPacketTransformation().encrypt(networkPacket);
- } else {
- outputBuffer = ByteBuffer.allocate(networkPacket.getSize());
-
- outputBuffer.order(ByteOrder.BIG_ENDIAN);
- networkPacket.writeHeader(outputBuffer);
- networkPacket.writeBody(outputBuffer);
- }
-
- Ts3Logging.debug("[PROTOCOL] WRITE " + packet.getType().name());
-
- sendNetworkPacket(new DatagramPacket(outputBuffer.array(), outputBuffer.position()));
- }
-
- /**
- * Receives a datagram from the network.
- * @return Datagram.
- * @throws IOException
- */
- private DatagramPacket receiveNetworkPacket() throws IOException {
- datagramSocket.receive(datagramPacket);
-
- Ts3Logging.debug(
- "[NETWORK] READ Len=" + datagramPacket.getLength() + " from " + datagramPacket.getSocketAddress()
- );
-
- //Ts3Logging.debug(Ts3Logging.getHex(datagramPacket.getData(), datagramPacket.getLength()));
-
- return datagramPacket;
- }
-
- public NetworkPacket receive() throws IOException {
- while (true) {
- DatagramPacket datagramPacket = receiveNetworkPacket();
-
- NetworkPacket networkPacket = new NetworkPacket(getSocketRole().getIn());
-
- ByteBuffer buffer = ByteBuffer.wrap(datagramPacket.getData());
- networkPacket.readHeader(buffer);
-
- boolean fragment = networkPacket.getHeader().getPacketFlag(HeaderFlag.FRAGMENTED);
- if (fragment && !networkPacket.getHeader().getType().isSplittable()) continue; // drop packet
-
- ByteBuffer packetBuffer;
-
- if (networkPacket.getHeader().getType().isEncrypted()) {
- packetBuffer = ByteBuffer.wrap(
- getPacketTransformation().decrypt(
- networkPacket.getHeader(),
- buffer,
- datagramPacket.getLength() - networkPacket.getHeader().getSize()
- )
- );
- } else {
- packetBuffer = buffer;
- }
-
- if (fragment) {
- Ts3Logging.debug("[PROTOCOL] READ " +
- networkPacket.getHeader().getType().name() + " fragment");
-
- networkPacket.setPacket(
- new PacketFragment(
- networkPacket.getHeader().getType(),
- getSocketRole().getIn()
- )
- );
- } else {
- Ts3Logging.debug("[PROTOCOL] READ " +
- networkPacket.getHeader().getType().name());
- }
-
-
- networkPacket.readBody(packetBuffer);
-
- return networkPacket;
- }
- }
-
-}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/socket/TeamspeakSocket.java b/src/main/java/com/github/manevolent/ts3j/protocol/socket/TeamspeakSocket.java
index afd9f4d..d757af6 100644
--- a/src/main/java/com/github/manevolent/ts3j/protocol/socket/TeamspeakSocket.java
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/TeamspeakSocket.java
@@ -1,19 +1,14 @@
package com.github.manevolent.ts3j.protocol.socket;
import com.github.manevolent.ts3j.protocol.SocketRole;
-import com.github.manevolent.ts3j.protocol.header.PacketHeader;
-import com.github.manevolent.ts3j.protocol.packet.Packet;
-import com.github.manevolent.ts3j.protocol.packet.transformation.PacketTransformation;
-import java.io.IOException;
+import java.io.Closeable;
+import java.net.InetSocketAddress;
-public interface TeamspeakSocket {
- SocketRole getSocketRole();
+public interface TeamspeakSocket extends Closeable {
- void send(PacketHeader header, Packet packet) throws IOException;
+ SocketRole getRole();
- PacketTransformation getPacketTransformation();
-
- void setPacketTransformation(PacketTransformation transformation);
+ InetSocketAddress getLocalSocketAddress();
}
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
new file mode 100644
index 0000000..fa8b5ee
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/AbstractTeamspeakClientSocket.java
@@ -0,0 +1,452 @@
+package com.github.manevolent.ts3j.protocol.socket.client;
+
+import com.github.manevolent.ts3j.command.Command;
+import com.github.manevolent.ts3j.command.response.CommandResponse;
+import com.github.manevolent.ts3j.identity.Identity;
+import com.github.manevolent.ts3j.protocol.NetworkPacket;
+import com.github.manevolent.ts3j.protocol.Packet;
+import com.github.manevolent.ts3j.protocol.SocketRole;
+import com.github.manevolent.ts3j.protocol.client.ClientConnectionState;
+import com.github.manevolent.ts3j.protocol.header.HeaderFlag;
+import com.github.manevolent.ts3j.protocol.header.PacketHeader;
+import com.github.manevolent.ts3j.protocol.packet.PacketBody;
+import com.github.manevolent.ts3j.protocol.packet.PacketBody6Ack;
+import com.github.manevolent.ts3j.protocol.packet.PacketBodyFragment;
+import com.github.manevolent.ts3j.protocol.packet.PacketBodyType;
+import com.github.manevolent.ts3j.protocol.packet.handler.PacketHandler;
+import com.github.manevolent.ts3j.protocol.packet.handler.local.LocalClientHandler;
+import com.github.manevolent.ts3j.protocol.packet.transformation.DefaultPacketTransformation;
+import com.github.manevolent.ts3j.protocol.packet.transformation.PacketTransformation;
+import com.github.manevolent.ts3j.protocol.socket.AbstractTeamspeakSocket;
+import com.github.manevolent.ts3j.util.Ts3Crypt;
+import com.github.manevolent.ts3j.util.Ts3Logging;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.*;
+import java.util.concurrent.TimeoutException;
+
+public abstract class AbstractTeamspeakClientSocket
+ extends AbstractTeamspeakSocket
+ implements TeamspeakClientSocket {
+ private final Object connectionStateLock = new Object();
+ private final Map reassemblyQueue = new LinkedHashMap<>();
+ private final NetworkReader networkReader = new NetworkReader();
+ private final Map clientOptions = new LinkedHashMap<>();
+
+ private Identity identity = null;
+ private ClientConnectionState connectionState = null;
+ private PacketTransformation transformation = new DefaultPacketTransformation();
+ private PacketHandler handler;
+
+ private Thread networkThread = null;
+ private boolean reading = false;
+
+ protected AbstractTeamspeakClientSocket() {
+ super(SocketRole.CLIENT);
+
+ try {
+ setState(ClientConnectionState.DISCONNECTED);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ // Initialize reassembly queue
+ for (PacketBodyType bodyType : PacketBodyType.values())
+ if (bodyType.isSplittable())
+ reassemblyQueue.put(bodyType, new ReassemblyQueue());
+ }
+
+ protected void start() {
+ Thread networkThread = new Thread(networkReader);
+ networkThread.setDaemon(true);
+ networkThread.start();
+ }
+
+ @Override
+ public Map getOptions() {
+ return clientOptions;
+ }
+
+ public final void setSecureParameters(Ts3Crypt.SecureChannelParameters parameters) {
+ setTransformation(new PacketTransformation(parameters.getIvStruct()));
+ }
+
+ protected abstract NetworkPacket readNetworkPacket() throws IOException;
+
+ protected abstract void writeNetworkPacket(NetworkPacket packet) throws IOException;
+
+ public void setState(ClientConnectionState state) throws IOException, TimeoutException {
+ synchronized (connectionStateLock) {
+ if (state != this.connectionState) {
+ Ts3Logging.debug("State changing: " + state.name());
+
+ boolean wasDisconnected = this.connectionState == ClientConnectionState.DISCONNECTED;
+
+ if (wasDisconnected) setReading(true);
+
+ this.connectionState = state;
+
+ if (this.connectionState == ClientConnectionState.DISCONNECTED) setReading(false);
+
+ PacketHandler handler = getHandler();
+
+ if (handler != null)
+ ((LocalClientHandler) handler).handleConnectionStateChanging(state);
+
+ if (handler == null || handler.getClass() != state.getHandlerClass()) {
+ Ts3Logging.debug("Assigning " + state.getHandlerClass().getName() + " handler...");
+
+ setHandler(state.createHandler(this));
+ }
+
+ if (this.connectionState == ClientConnectionState.DISCONNECTED)
+ clientOptions.clear();
+
+ connectionStateLock.notifyAll();
+
+ Ts3Logging.debug("State changed: " + state.name());
+ }
+ }
+ }
+
+ public ClientConnectionState getState() {
+ return connectionState;
+ }
+
+ public void waitForState(ClientConnectionState state, long wait)
+ throws InterruptedException, TimeoutException {
+ synchronized (connectionStateLock) {
+ long start = System.currentTimeMillis();
+
+ while (connectionState != state && System.currentTimeMillis() - start < wait) {
+ connectionStateLock.wait(Math.max(0L, wait - (System.currentTimeMillis() - start)));
+ }
+
+ if (connectionState != state)
+ throw new TimeoutException("timeout waiting for " + state.name() + " state");
+ }
+ }
+
+ public boolean isConnected() {
+ return connectionState == ClientConnectionState.CONNECTED;
+ }
+
+ /**
+ * Sends a command to the remote server.
+ * @param command Command to send.
+ * @return Command response object to track command response.
+ * @throws IOException
+ */
+ public CommandResponse sendCommand(Command command) throws IOException {
+ if (command.willExpectResponse() && !isConnected())
+ throw new IOException("not connected");
+
+ CommandResponse response;
+
+ if (command.willExpectResponse()) {
+
+ //command.appendParameter(new CommandSingleParameter("return_code", returnCodeIndex));
+ response = new CommandResponse(command, 0); // TODO
+ } else {
+ response = new CommandResponse(command, -1);
+ }
+
+ String commandString = command.toString();
+
+ response.setDispatchedTime(System.currentTimeMillis());
+
+ return response;
+ }
+
+ @Override
+ public void writePacket(PacketBody body) throws IOException, TimeoutException {
+ PacketHeader header;
+
+ try {
+ header = body.getRole().getHeaderClass().newInstance();
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException(e);
+ }
+
+ // Construct a network packet
+ Packet packet = new Packet(body.getRole());
+ packet.setHeader(header);
+ packet.setBody(body);
+
+ writePacket(packet);
+ }
+
+ @Override
+ public void writePacket(Packet packet) throws IOException, TimeoutException {
+ if (packet.getHeader() == null)
+ throw new NullPointerException("header");
+ else if (packet.getHeader().getRole() != getRole().getOut())
+ throw new IllegalArgumentException("packet role mismatch: " +
+ packet.getHeader().getRole().name() + " != " +
+ getRole().getOut().name());
+
+ // Ensure type matches
+ packet.getHeader().setType(packet.getHeader().getType());
+
+ // Make sure new protocol is set if it must be set
+ if (getState() == ClientConnectionState.CONNECTING)
+ packet.getHeader().setPacketFlag(HeaderFlag.NEW_PROTOCOL, true);
+
+ // Set header values
+ packet.getBody().setHeaderValues(packet.getHeader());
+
+ // Flush to a buffer
+ ByteBuffer outputBuffer;
+
+ if (packet.getBody().getType().canEncrypt() &&
+ !packet.getHeader().getPacketFlag(HeaderFlag.UNENCRYPTED)) {
+ outputBuffer = getTransformation().encrypt(packet);
+ } else {
+ outputBuffer = ByteBuffer.allocate(packet.getSize());
+
+ outputBuffer.order(ByteOrder.BIG_ENDIAN);
+ packet.writeHeader(outputBuffer);
+ packet.writeBody(outputBuffer);
+ }
+
+ Ts3Logging.debug("[PROTOCOL] WRITE " + packet.getHeader().getType().name());
+
+ // Find if the packet must be acknowledged and create a promise for that action
+ // TODO
+
+ writeNetworkPacket(new NetworkPacket(
+ new DatagramPacket(outputBuffer.array(), outputBuffer.position()),
+ packet.getHeader(),
+ (ByteBuffer) outputBuffer.position(0)
+ ));
+
+ // If necessary, fulfill promise for the packet acknowledgement or throw TimeoutException
+ // TODO
+ }
+
+ private Packet readPacketIntl() throws IOException {
+ NetworkPacket networkPacket = readNetworkPacket();
+
+ if (networkPacket.getHeader() == null) throw new NullPointerException("header");
+
+ Packet packet = new Packet(networkPacket.getHeader().getRole());
+ packet.setHeader(networkPacket.getHeader());
+
+ boolean fragment = networkPacket.getHeader().getPacketFlag(HeaderFlag.FRAGMENTED);
+
+ if (networkPacket.getHeader().getType().isSplittable()) {
+ if (fragment)
+ throw new IllegalArgumentException("packet is fragment, but not splittable");
+ else {
+ fragment = reassemblyQueue.get(networkPacket.getHeader().getType()).isReassembling();
+ }
+ }
+
+ boolean encrypted = !networkPacket.getHeader().getPacketFlag(HeaderFlag.UNENCRYPTED) &&
+ networkPacket.getHeader().getType().canEncrypt();
+
+ if (networkPacket.getHeader().getType().mustEncrypt() && !encrypted)
+ throw new IllegalArgumentException("packet is unencrypted, but must encrypt");
+
+ ByteBuffer packetBuffer =
+ (ByteBuffer) networkPacket.getBuffer().position(networkPacket.getHeader().getSize());
+
+ // Decrypt
+ if (encrypted) {
+ packetBuffer = ByteBuffer.wrap(
+ getTransformation().decrypt(
+ networkPacket.getHeader(),
+ packetBuffer,
+ networkPacket.getDatagram().getLength() - networkPacket.getHeader().getSize()
+ )
+ );
+ }
+
+ // Fragment handling
+ if (fragment) {
+ Ts3Logging.debug("[PROTOCOL] READ " + networkPacket.getHeader().getType().name() + " fragment");
+
+ packet.setBody(
+ new PacketBodyFragment(
+ networkPacket.getHeader().getType(),
+ getRole().getIn()
+ )
+ );
+ } else {
+ Ts3Logging.debug("[PROTOCOL] READ " + networkPacket.getHeader().getType().name());
+ }
+
+ // Compression?
+ // TODO
+
+ // Read packet body
+ packet.readBody(packetBuffer);
+
+ return packet;
+ }
+
+ @Override
+ public Packet readPacket() throws IOException, TimeoutException {
+ while (true) {
+ Packet packet = readPacketIntl();
+
+ // Find if the packet must be acknowledged
+ PacketBodyType ackType = packet.getHeader().getType().getAcknolwedgedBy();
+ if (ackType != null) {
+ switch (ackType) {
+ case ACK:
+ writePacket(new PacketBody6Ack(getRole().getOut(), packet.getHeader().getPacketId()));
+ break;
+ }
+ }
+
+ // Find if the packet is itself an acknowledgement
+ switch (packet.getHeader().getType()) {
+ case ACK:
+ break;
+ case ACK_LOW:
+ break;
+ default:
+ getHandler().handlePacket(packet);
+ }
+ }
+ }
+
+ @Override
+ public PacketTransformation getTransformation() {
+ return transformation;
+ }
+
+ @Override
+ public void setTransformation(PacketTransformation transformation) {
+ this.transformation = transformation;
+ }
+
+ @Override
+ public PacketHandler getHandler() {
+ return handler;
+ }
+
+ protected void setHandler(PacketHandler handler) throws IOException, TimeoutException {
+ if (this.handler != handler) {
+ if (this.handler != null) this.handler.onUnassigning();
+ this.handler = handler;
+ if (handler != null) handler.onAssigned();
+ }
+ }
+
+ public Identity getIdentity() {
+ return identity;
+ }
+
+ public void setIdentity(Identity identity) {
+ this.identity = identity;
+ }
+
+ protected boolean isReading() {
+ return reading;
+ }
+
+ protected void setReading(boolean b) {
+ if (this.reading != b) {
+ this.reading = b;
+
+ if (!reading) {
+ if (networkThread != null) networkThread.interrupt();
+ } else {
+ start();
+ }
+ }
+ }
+
+ private class NetworkReader implements Runnable {
+ @Override
+ public void run() {
+ while (isReading()) {
+ try {
+ readPacket();
+ } catch (Exception e) {
+ if (e instanceof InterruptedException) continue;
+
+ Ts3Logging.debug("Problem handling packet", e);
+ }
+ }
+ }
+ }
+
+ private class ReassemblyQueue {
+ private final Map queue = new LinkedHashMap<>();
+ private boolean state = false;
+
+ public Packet reassemble(Packet lastFragment) {
+ // Pull out all other packets in the queue before this one which would also be fragmented
+ List reassemblyList = new ArrayList<>();
+ for (int packetId = lastFragment.getHeader().getPacketId();; packetId--) {
+ Packet olderPacket = queue.get(packetId);
+
+ if (olderPacket == null)
+ break; // Chain breaks here
+ else if (olderPacket.getHeader().getType() != lastFragment.getHeader().getType())
+ continue; // skip
+ else {
+ reassemblyList.add(olderPacket);
+ }
+ }
+
+ // Reverse the collection
+ Collections.reverse(reassemblyList);
+
+ // Rebuild a master packet from the contents of all previous packet fragments
+ int totalLength = reassemblyList.stream().mapToInt(x -> x.getBody().getSize()).sum() + lastFragment.getBody().getSize();
+ if (totalLength < 0) throw new IllegalArgumentException("reassembly too small: " + totalLength);
+
+ ByteBuffer reassemblyBuffer = ByteBuffer.allocate(totalLength);
+
+ for (Packet old : reassemblyList)
+ old.writeBody(reassemblyBuffer);
+
+ Packet firstPacket = reassemblyList.get(0);
+ Packet reassembledPacket = new Packet(firstPacket.getRole());
+ reassembledPacket.setHeader(firstPacket.getHeader());
+ reassembledPacket.getHeader().setPacketFlag(HeaderFlag.FRAGMENTED, false);
+ reassembledPacket.readBody((ByteBuffer) reassemblyBuffer.position(0));
+
+ return reassembledPacket;
+ }
+
+ public Packet put(Packet packet) {
+ if (packet.getHeader().getPacketFlag(HeaderFlag.FRAGMENTED)) {
+ boolean oldState = state;
+
+ state = !state;
+
+ if (!(packet.getBody() instanceof PacketBodyFragment))
+ throw new IllegalArgumentException("packet fragment object is not representative of a fragment");
+
+ boolean fragmentationEnded = oldState;
+
+ queue.put(packet.getHeader().getPacketId(), packet);
+
+ if (fragmentationEnded)
+ return reassemble(packet);
+
+ return null;
+ } else {
+ if (state) {
+ queue.put(packet.getHeader().getPacketId(), packet);
+
+ return null;
+ } else {
+ return packet;
+ }
+ }
+ }
+
+ public boolean isReassembling() {
+ return state;
+ }
+ }
+}
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
new file mode 100644
index 0000000..9f1f24b
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/LocalTeamspeakClientSocket.java
@@ -0,0 +1,124 @@
+package com.github.manevolent.ts3j.protocol.socket.client;
+
+import com.github.manevolent.ts3j.identity.LocalIdentity;
+import com.github.manevolent.ts3j.protocol.NetworkPacket;
+import com.github.manevolent.ts3j.protocol.client.ClientConnectionState;
+import com.github.manevolent.ts3j.protocol.header.PacketHeader;
+import com.github.manevolent.ts3j.util.Ts3Logging;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetSocketAddress;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.concurrent.TimeoutException;
+
+public class LocalTeamspeakClientSocket extends AbstractTeamspeakClientSocket {
+ private final DatagramSocket socket;
+
+ private final DatagramPacket packet = new DatagramPacket(new byte[500], 500);
+
+ {
+ try {
+ socket = new DatagramSocket();
+ } catch (SocketException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public LocalTeamspeakClientSocket() {
+ super();
+ }
+
+ @Override
+ protected NetworkPacket readNetworkPacket() throws IOException {
+ socket.receive(packet);
+
+ PacketHeader header;
+
+ try {
+ header = getRole().getIn().getHeaderClass().newInstance();
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException(e);
+ }
+
+ ByteBuffer buffer = ByteBuffer
+ .wrap(packet.getData(), packet.getOffset(), packet.getLength())
+ .order(ByteOrder.BIG_ENDIAN);
+
+ header.read(buffer);
+
+ return new NetworkPacket(
+ packet,
+ header,
+ buffer
+ );
+ }
+
+ @Override
+ protected void writeNetworkPacket(NetworkPacket packet) throws IOException {
+ socket.send(packet.getDatagram());
+ }
+
+
+ @Override
+ public LocalIdentity getIdentity() {
+ return (LocalIdentity) super.getIdentity();
+ }
+
+ public void setIdentity(LocalIdentity identity) {
+ super.setIdentity(identity);
+ }
+
+ /**
+ * Initiates a connection to a server
+ * @param remote remote sever to contact
+ * @param password server password
+ * @param timeout timeout, in milliseconds, to complete a connection.
+ * @throws IOException
+ */
+ public void connect(InetSocketAddress remote, String password, long timeout)
+ throws IOException, TimeoutException {
+ try {
+ Ts3Logging.debug("Connecting to " + remote + "...");
+
+ ClientConnectionState connectionState = getState();
+
+ if (connectionState != ClientConnectionState.DISCONNECTED)
+ throw new IllegalStateException(connectionState.name());
+
+ setOption("client.hostname", remote.getHostString());
+ setOption("client.password", password);
+
+ socket.connect(remote);
+
+ Ts3Logging.debug("Changing state...");
+ setState(ClientConnectionState.CONNECTING);
+
+ waitForState(ClientConnectionState.CONNECTED, timeout);
+ } catch (TimeoutException e) {
+ throw e;
+ } catch (Throwable e) {
+ setState(ClientConnectionState.DISCONNECTED);
+
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ protected boolean isReading() {
+ return socket.isConnected();
+ }
+
+ @Override
+ public InetSocketAddress getLocalSocketAddress() {
+ return (InetSocketAddress) socket.getLocalSocketAddress();
+ }
+
+ @Override
+ public void close() throws IOException {
+ socket.close();
+ }
+}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/TeamspeakClientSocket.java b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/TeamspeakClientSocket.java
new file mode 100644
index 0000000..bc643b5
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/TeamspeakClientSocket.java
@@ -0,0 +1,56 @@
+package com.github.manevolent.ts3j.protocol.socket.client;
+
+import com.github.manevolent.ts3j.identity.Identity;
+import com.github.manevolent.ts3j.protocol.Packet;
+import com.github.manevolent.ts3j.protocol.packet.PacketBody;
+import com.github.manevolent.ts3j.protocol.packet.handler.PacketHandler;
+import com.github.manevolent.ts3j.protocol.packet.transformation.PacketTransformation;
+import com.github.manevolent.ts3j.protocol.socket.TeamspeakSocket;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.Map;
+import java.util.concurrent.TimeoutException;
+
+public interface TeamspeakClientSocket extends TeamspeakSocket {
+
+ Map getOptions();
+
+ default T getOption(String key, Class clazz) {
+ Object value = getOptions().get(key);
+
+ if (value == null) return (T) null;
+
+ else if (!value.getClass().isAssignableFrom(clazz)) {
+ throw new ClassCastException(
+ clazz.getName()
+ + " is not assignable to option type " +
+ value.getClass().getName()
+ );
+ } else
+ return (T) value;
+ }
+
+ default Object setOption(String key, Object value) {
+ return getOptions().put(key, value);
+ }
+
+ boolean isConnected();
+
+ Identity getIdentity();
+
+ void setIdentity(Identity identity);
+
+ PacketTransformation getTransformation();
+
+ void setTransformation(PacketTransformation transformation);
+
+ PacketHandler getHandler();
+
+ Packet readPacket() throws IOException, TimeoutException;
+
+ void writePacket(PacketBody body) throws IOException, TimeoutException;
+
+ void writePacket(Packet packet) throws IOException, TimeoutException;
+
+}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/socket/server/TeamspeakServerSocket.java b/src/main/java/com/github/manevolent/ts3j/protocol/socket/server/TeamspeakServerSocket.java
new file mode 100644
index 0000000..1ba5437
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/server/TeamspeakServerSocket.java
@@ -0,0 +1,9 @@
+package com.github.manevolent.ts3j.protocol.socket.server;
+
+import com.github.manevolent.ts3j.protocol.socket.client.TeamspeakClientSocket;
+
+public interface TeamspeakServerSocket {
+
+ TeamspeakClientSocket accept();
+
+}
diff --git a/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java b/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java
index 7b448d9..9c3bc33 100644
--- a/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java
+++ b/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java
@@ -2,7 +2,7 @@ package com.github.manevolent.ts3j;
import com.github.manevolent.ts3j.identity.LocalIdentity;
import com.github.manevolent.ts3j.protocol.client.ClientConnectionState;
-import com.github.manevolent.ts3j.protocol.client.LocalTeamspeakClient;
+import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket;
import junit.framework.TestCase;
import java.net.InetSocketAddress;
@@ -16,9 +16,9 @@ public class ServerConnectionTest extends TestCase {
try {
LocalIdentity localIdentity = LocalIdentity.generateNew(8);
- LocalTeamspeakClient client = new LocalTeamspeakClient();
+ LocalTeamspeakClientSocket client = new LocalTeamspeakClientSocket();
- client.setLocalIdentity(localIdentity);
+ client.setIdentity(localIdentity);
client.connect(new InetSocketAddress(
"ts.teamlixo.net",
@@ -27,7 +27,7 @@ public class ServerConnectionTest extends TestCase {
10000L
);
- assertEquals(client.getConnectionState(), ClientConnectionState.CONNECTED);
+ assertEquals(client.getState(), ClientConnectionState.CONNECTED);
} catch (Throwable ex) {
ex.printStackTrace();
}