Adding missing files

This commit is contained in:
Manevolent
2018-07-23 15:47:21 -04:00
parent 11f5acd60f
commit 307cc7be61
13 changed files with 1254 additions and 0 deletions
@@ -0,0 +1,28 @@
package com.github.manevolent.ts3j;
import com.github.manevolent.ts3j.handler.*;
public enum ClientConnectionState {
CONNECTING(TeamspeakClientHandlerConnecting.class),
CONNECTED(TeamspeakClientHandlerConnected.class),
DISCONNECTED(TeamspeakClientHandlerDisconnected.class);
private final Class<? extends TeamspeakClientHandler> clazz;
ClientConnectionState(Class<? extends TeamspeakClientHandler> clazz) {
this.clazz = clazz;
}
public Class<? extends TeamspeakClientHandler> getHandlerClass() {
return clazz;
}
public TeamspeakClientHandler createHandler(Teamspeak3Client client) {
try {
return clazz.getConstructor(Teamspeak3Client.class).newInstance(client);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
}
}
@@ -0,0 +1,202 @@
package com.github.manevolent.ts3j;
import com.github.manevolent.ts3j.command.Command;
import com.github.manevolent.ts3j.command.part.CommandSingleParameter;
import com.github.manevolent.ts3j.command.response.CommandResponse;
import com.github.manevolent.ts3j.handler.TeamspeakClientHandler;
import com.github.manevolent.ts3j.protocol.NetworkPacket;
import com.github.manevolent.ts3j.protocol.SocketRole;
import com.github.manevolent.ts3j.protocol.Teamspeak3Socket;
import com.github.manevolent.ts3j.protocol.header.ClientPacketHeader;
import com.github.manevolent.ts3j.protocol.packet.Packet;
import com.github.manevolent.ts3j.util.Ts3Logging;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
public class Teamspeak3Client {
private final PacketReceiver packetReceiver = new PacketReceiver();
private final Teamspeak3Socket socket;
private final Object connectionStateLock = new Object();
private final Map<String, Object> clientOptions = new LinkedHashMap<>();
private TeamspeakClientHandler handler;
private InetSocketAddress remote = null;
private ClientConnectionState clientConnectionState = null;
private int returnCodeIndex = 0;
public Teamspeak3Client(DatagramSocket datagramSocket) throws SocketException {
this.socket = new Teamspeak3Socket(SocketRole.CLIENT, datagramSocket);
setClientConnectionState(ClientConnectionState.DISCONNECTED);
}
public Teamspeak3Client() throws SocketException {
this(new DatagramSocket());
}
public InetSocketAddress getRemote() {
return remote;
}
public void send(Packet packet) throws IOException {
// Construct header
ClientPacketHeader header = new ClientPacketHeader();
packet.setHeaderValues(header);
socket.send(header, packet);
}
protected void setClientConnectionState(ClientConnectionState state) {
synchronized (connectionStateLock) {
if (state != this.clientConnectionState) {
Ts3Logging.debug("State changing: " + state.name());
boolean wasDisconnected = this.clientConnectionState == ClientConnectionState.DISCONNECTED;
this.clientConnectionState = state;
if (wasDisconnected) {
Ts3Logging.debug("Starting network receiver thread...");
Thread receiverThread = new Thread(new PacketReceiver());
receiverThread.setDaemon(true);
receiverThread.start();
}
if (handler != null)
handler.handleConnectionStateChanging(state);
if (handler == null || handler.getClass() != state.getHandlerClass()) {
Ts3Logging.debug("Assigning " + state.getHandlerClass().getName() + " handler...");
handler = state.createHandler(this);
try {
handler.onAssigned();
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
if (this.clientConnectionState == ClientConnectionState.DISCONNECTED)
clientOptions.clear();
connectionStateLock.notifyAll();
Ts3Logging.debug("State changed: " + state.name());
}
}
}
public boolean isConnected() {
return clientConnectionState == ClientConnectionState.CONNECTED;
}
public ClientConnectionState getClientConnectionState() {
return clientConnectionState;
}
private void joinConnectionState(ClientConnectionState state, long wait)
throws InterruptedException, TimeoutException {
synchronized (connectionStateLock) {
long start = System.currentTimeMillis();
while (clientConnectionState != state && System.currentTimeMillis() - start < wait) {
connectionStateLock.wait(Math.max(0L, wait - (System.currentTimeMillis() - start)));
}
if (clientConnectionState != state)
throw new TimeoutException("timeout waiting for " + state.name() + " state");
}
}
public <T extends Object> T getOption(String key, Class<T> clazz) {
Object value = clientOptions.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;
}
public Object setOption(String key, Object value) {
return clientOptions.put(key, value);
}
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 void connect(InetSocketAddress remote, String password, long timeout) throws IOException {
try {
if (clientConnectionState != ClientConnectionState.DISCONNECTED)
throw new IllegalStateException(clientConnectionState.name());
setOption("client.hostname", remote.getHostString());
setOption("client.password", password);
socket.connect(remote);
setClientConnectionState(ClientConnectionState.CONNECTING);
joinConnectionState(ClientConnectionState.CONNECTED, timeout);
} catch (Throwable e) {
setClientConnectionState(ClientConnectionState.DISCONNECTED);
throw new IOException(e);
}
}
private void handlePacket(NetworkPacket packet) throws Exception {
if (handler != null) handler.handlePacket(packet);
}
private class PacketReceiver implements Runnable {
@Override
public void run() {
Ts3Logging.debug("Entering packet receiver");
while (clientConnectionState != ClientConnectionState.DISCONNECTED) {
try {
handlePacket(socket.receive());
} catch (Exception e) {
Ts3Logging.debug("Problem handling packet", e);
}
}
Ts3Logging.debug("Leaving packet receiver");
}
}
}
@@ -0,0 +1,115 @@
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.nio.ByteBuffer;
/**
* Represents a fully contained network packet, traveling in or out.
*/
public final class NetworkPacket {
private final ProtocolRole role;
private PacketHeader header;
private Packet packet;
public NetworkPacket(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 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
*/
public void write(ByteBuffer buffer) {
writeHeader(buffer);
writeBody(buffer);
}
public void writeHeader(ByteBuffer buffer) {
if (getHeader() == null) throw new NullPointerException("header");
getHeader().setType(getPacket().getType());
getHeader().write(buffer);
}
public void writeBody(ByteBuffer buffer) {
if (getPacket() == null) throw new NullPointerException("packet");
getPacket().write(buffer);
}
/**
* Reads data from the specified byte buffer, filling in the packet body as needed.
* @param buffer Buffer to read from.
*/
public void read(ByteBuffer buffer) {
readHeader(buffer);
readBody(buffer);
}
public void readHeader(ByteBuffer buffer) {
if (getHeader() == null) {
try {
setHeader(getRole().getHeaderClass().newInstance());
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
getHeader().read(buffer);
}
public void 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);
}
/**
* 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();
}
}
@@ -0,0 +1,20 @@
package com.github.manevolent.ts3j.protocol;
import com.github.manevolent.ts3j.protocol.header.ClientPacketHeader;
import com.github.manevolent.ts3j.protocol.header.PacketHeader;
import com.github.manevolent.ts3j.protocol.header.ServerPacketHeader;
public enum ProtocolRole {
CLIENT(ClientPacketHeader.class),
SERVER(ServerPacketHeader.class);
private final Class<? extends PacketHeader> headerClass;
ProtocolRole(Class<? extends PacketHeader> headerClass) {
this.headerClass = headerClass;
}
public Class<? extends PacketHeader> getHeaderClass() {
return headerClass;
}
}
@@ -0,0 +1,21 @@
package com.github.manevolent.ts3j.protocol;
public enum SocketRole {
CLIENT(ProtocolRole.SERVER, ProtocolRole.CLIENT),
SERVER(ProtocolRole.CLIENT, ProtocolRole.SERVER);
private final ProtocolRole in, out;
SocketRole(ProtocolRole in, ProtocolRole out) {
this.in = in;
this.out = out;
}
public ProtocolRole getIn() {
return in;
}
public ProtocolRole getOut() {
return out;
}
}
@@ -0,0 +1,119 @@
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.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketAddress;
import java.net.SocketException;
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 final class Teamspeak3Socket {
private final SocketRole socketRole;
private final DatagramSocket datagramSocket;
private final DatagramPacket datagramPacket = new DatagramPacket(new byte[1500], 1500);
public Teamspeak3Socket(SocketRole socketRole, DatagramSocket datagramSocket) {
this.socketRole = socketRole;
this.datagramSocket = datagramSocket;
}
public Teamspeak3Socket(SocketRole socketRole) throws SocketException {
this(socketRole, new DatagramSocket());
}
public SocketRole getSocketRole() {
return socketRole;
}
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() +
"\n" + 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());
NetworkPacket networkPacket = new NetworkPacket(header.getRole());
networkPacket.setHeader(header);
networkPacket.setPacket(packet);
if (networkPacket.getHeader().getMac() == null) {
// Generate mac?
}
ByteBuffer outputBuffer = ByteBuffer.allocate(networkPacket.getSize());
outputBuffer.order(ByteOrder.BIG_ENDIAN);
networkPacket.write(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() + " to " + datagramPacket.getSocketAddress() +
"\n" + Ts3Logging.getHex(datagramPacket.getData(), datagramPacket.getLength())
);
return datagramPacket;
}
public NetworkPacket receive() throws IOException {
DatagramPacket datagramPacket = receiveNetworkPacket();
NetworkPacket networkPacket = new NetworkPacket(getSocketRole().getIn());
ByteBuffer buffer = ByteBuffer.wrap(datagramPacket.getData());
networkPacket.readHeader(buffer);
Ts3Logging.debug("[PROTOCOL] READ " + networkPacket.getHeader().getType().name());
networkPacket.readBody(buffer);
return networkPacket;
}
}
@@ -0,0 +1,55 @@
package com.github.manevolent.ts3j.protocol.header;
import com.github.manevolent.ts3j.protocol.packet.PacketType;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
import java.nio.ByteBuffer;
public class ClientPacketHeader extends PacketHeader {
private int clientId;
public ClientPacketHeader() {
super(ProtocolRole.CLIENT);
}
public int getClientId() {
return clientId;
}
public void setClientId(int clientId) {
this.clientId = clientId;
}
@Override
protected void writeHeader(ByteBuffer buffer) {
super.writeHeader(buffer);
// https://github.com/ReSpeak/tsdeclarations/blob/master/ts3protocol.md
buffer.putShort((short) (getClientId() & 0x0000FFFF)); // CId / Client Id
buffer.put((byte) ((getType().getIndex() & 0x0F) | (getPacketFlags() & 0xF0))); // PT / Packet Type + Flags
// (payload) -- see writeBody impl in higher levels
}
@Override
protected void readHeader(ByteBuffer buffer) {
super.readHeader(buffer);
// https://github.com/ReSpeak/tsdeclarations/blob/master/ts3protocol.md
setClientId((buffer.getShort() & 0xFFFF));
byte packetTypeAndFlags = buffer.get();
setType(PacketType.fromId((packetTypeAndFlags & 0x0F))); // PT / Packet Type + Flags
setPacketFlags(packetTypeAndFlags & 0xF0);
// (payload) -- see writeBody impl in higher levels
}
@Override
public int getSize() {
return super.getSize() + 2 + 1;
}
}
@@ -0,0 +1,26 @@
package com.github.manevolent.ts3j.protocol.header;
public enum HeaderFlag {
NONE(0x0),
FRAGMENTED(0X10),
NEW_PROTOCOL(0x20),
COMPRESSED(0x40),
UNENCRYPTED(0x80);
private final int index;
HeaderFlag(int index) {
this.index = index;
}
public int getIndex() {
return index;
}
public static HeaderFlag fromId(int index) {
for (HeaderFlag value : values())
if (value.getIndex() == index) return value;
throw new IllegalArgumentException("invalid index: " + index);
}
}
@@ -0,0 +1,99 @@
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.ProtocolRole;
import java.nio.ByteBuffer;
public abstract class PacketHeader {
private final ProtocolRole role;
//private final byte[] DEFAULT_MAC = new byte[] {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0}; // 8x0's
private byte[] mac = new byte[8]; // 8 bytes
private int packetId = 0;
private PacketType type;
private int packetFlags;
protected PacketHeader(ProtocolRole role) {
this.role = role;
}
public int getPacketFlags() {
return packetFlags;
}
public void setPacketFlags(int packetFlags) {
this.packetFlags = packetFlags;
}
public boolean getPacketFlag(HeaderFlag flag) {
int idx = flag.getIndex();
return (packetFlags & idx) == idx;
}
public void setPacketFlag(HeaderFlag flag, boolean value) {
int idx = flag.getIndex();
if (value) {
packetFlags = packetFlags | idx;
} else {
packetFlags &= ~idx;
}
}
public PacketType getType() {
return type;
}
public void setType(PacketType type) {
this.type = type;
}
public int getPacketId() {
return packetId;
}
public void setPacketId(int packetId) {
this.packetId = packetId;
}
public byte[] getMac() {
return mac;
}
public void setMac(byte[] mac) {
Packet.assertArray("mac", mac, 8);
this.mac = mac;
}
public final void write(ByteBuffer buffer) {
writeHeader(buffer);
}
public final void read(ByteBuffer buffer) {
readHeader(buffer);
}
protected void writeHeader(ByteBuffer buffer) {
// https://github.com/ReSpeak/tsdeclarations/blob/master/ts3protocol.md
buffer.put(getMac(), 0, 8);
buffer.putShort((short) (getPacketId() & 0x0000FFFF)); // PiD / Packet ID
}
protected void readHeader(ByteBuffer buffer) {
// https://github.com/ReSpeak/tsdeclarations/blob/master/ts3protocol.md
buffer.get(mac);
setPacketId((int) buffer.getShort() & 0x0000FFFF);
}
public int getSize() {
return 8 + 2;
}
public ProtocolRole getRole() {
return role;
}
}
@@ -0,0 +1,49 @@
package com.github.manevolent.ts3j.protocol.header;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
import com.github.manevolent.ts3j.protocol.packet.PacketType;
import java.nio.ByteBuffer;
public class ServerPacketHeader extends PacketHeader {
private int packetFlags;
public ServerPacketHeader() {
super(ProtocolRole.SERVER);
}
public int getPacketFlags() {
return packetFlags;
}
public void setPacketFlags(int packetFlags) {
this.packetFlags = packetFlags;
}
@Override
protected void writeHeader(ByteBuffer buffer) {
super.writeHeader(buffer);
// https://github.com/ReSpeak/tsdeclarations/blob/master/ts3protocol.md
buffer.put((byte) ((getType().getIndex() & 0x0F) | (getPacketFlags() & 0xF0))); // PT / Packet Type + Flags
// (payload) -- see writeBody impl in higher levels
}
@Override
protected void readHeader(ByteBuffer buffer) {
super.readHeader(buffer);
// https://github.com/ReSpeak/tsdeclarations/blob/master/ts3protocol.md
byte packetTypeAndFlags = buffer.get();
setType(PacketType.fromId((packetTypeAndFlags & 0x0F))); // PT / Packet Type + Flags
setPacketFlags(packetTypeAndFlags & 0xF0);
// (payload) -- see writeBody impl in higher levels
}
@Override
public int getSize() {
return super.getSize() + 1;
}
}
@@ -0,0 +1,49 @@
package com.github.manevolent.ts3j.protocol.packet;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
import com.github.manevolent.ts3j.protocol.header.PacketHeader;
import java.nio.ByteBuffer;
public abstract class Packet {
private final PacketType type;
private final ProtocolRole role;
protected Packet(PacketType type, ProtocolRole role) {
this.type = type;
this.role = role;
}
public PacketType getType() {
return type;
}
public ProtocolRole getRole() {
return role;
}
public abstract void read(ByteBuffer buffer);
public abstract void write(ByteBuffer buffer);
public abstract int getSize();
public void setHeaderValues(PacketHeader header) {
// Do nothing
}
/**
* Asserts that an array length is correct
* @param field Field name being asserted
* @param bytes Bytes being set
* @param length Length of the byte array expected
* @throws IllegalArgumentException
*/
public static final void assertArray(String field, byte[] bytes, int length) throws IllegalArgumentException {
if (bytes == null)
throw new NullPointerException(field + " attempted set to null pointer");
if (bytes.length != length)
throw new IllegalArgumentException(field + " len " + bytes.length + " != " + length);
}
}
@@ -0,0 +1,434 @@
package com.github.manevolent.ts3j.protocol.packet;
import com.github.manevolent.ts3j.protocol.header.HeaderFlag;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
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;
public class Packet8Init1 extends Packet {
private static final byte[] LOW_LEVEL_MAC = new byte[] {
0x54, 0x53, 0x33, 0x49, 0x4E, 0x49, 0x54, 0x31
};
private byte[] version = new byte[4]; // 4 bytes
private Step step; // step instance
public Packet8Init1(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);
}
@Override
public void setHeaderValues(PacketHeader header) {
header.setMac(LOW_LEVEL_MAC);
header.setPacketFlag(HeaderFlag.UNENCRYPTED, true);
header.setPacketId((short)101);
}
public byte[] getVersion() {
return version;
}
public void setVersion(byte[] version) {
assertArray("version", version, 4);
this.version = version;
}
public Step getStep() {
return step;
}
public void setStep(Step step) {
if (step == null)
throw new NullPointerException("step");
if (step.getRole() != getRole())
throw new IllegalArgumentException(
"Init1 step mismatches expected role: " + step.getRole().name() +
" != " + getRole().name()
);
this.step = step;
}
@Override
public void write(ByteBuffer buffer) {
// only the client sends the version
if (getRole() == ProtocolRole.CLIENT)
buffer.put(getVersion());
buffer.put(getStep().getNumber());
step.write(buffer);
}
@Override
public void read(ByteBuffer buffer) {
// only the client sends a version
if (getRole() == ProtocolRole.CLIENT)
buffer.get(version);
byte stepNumber = buffer.get();
Step step;
switch (stepNumber) {
case 0:
step = new Step0();
break;
case 1:
step = new Step1();
break;
case 2:
step = new Step2();
break;
case 3:
step = new Step3();
break;
default:
throw new IllegalArgumentException("invalid Init1 step: " + stepNumber);
}
step.read(buffer);
setStep(step);
}
@Override
public int getSize() {
return 4 + 1 + (step == null ? 0 : step.getSize());
}
public static abstract class Step {
private final byte stepNumber;
private final ProtocolRole role;
protected Step(byte stepNumber, ProtocolRole role) {
this.stepNumber = stepNumber;
this.role = role;
}
public byte getNumber() {
return stepNumber;
}
public ProtocolRole getRole() {
return role;
}
public abstract void read(ByteBuffer byteBuffer);
public abstract void write(ByteBuffer byteBuffer);
public abstract int getSize();
}
public static class Step0 extends Step {
private byte[] timestamp = new byte[4]; // 4 bytes
private byte[] random = new byte[4]; // 4 bytes
private final byte[] reserved = new byte[8]; // 8 zeros, these are reserved for future use
public Step0() {
super((byte) 0, ProtocolRole.CLIENT);
}
public byte[] getTimestamp() {
return timestamp;
}
public void setTimestamp(int timestamp) {
ByteBuffer intBuffer = ByteBuffer.allocate(4);
intBuffer.order(ByteOrder.BIG_ENDIAN);
intBuffer.putInt(timestamp);
setTimestamp(intBuffer.array());
}
public void setTimestamp(byte[] timestamp) {
Packet.assertArray("timestamp", timestamp, 4);
this.timestamp = timestamp;
}
public byte[] getRandom() {
return random;
}
public void setRandom(byte[] random) {
Packet.assertArray("random", random, 4);
this.random = random;
}
@Override
public void read(ByteBuffer byteBuffer) {
byteBuffer.get(timestamp);
byteBuffer.get(random);
byteBuffer.get(reserved);
}
@Override
public void write(ByteBuffer byteBuffer) {
byteBuffer.put(timestamp);
byteBuffer.put(random);
byteBuffer.put(reserved);
}
@Override
public int getSize() {
return 4 + 4 + 8;
}
}
public static class Step1 extends Step {
private byte[] serverStuff = new byte[16]; // 16 bytes
private byte[] a0reversed = new byte[4]; // 4 bytes / The bytes from [A0] in reversed order
public Step1() {
super((byte) 1, ProtocolRole.SERVER);
}
public byte[] getA0reversed() {
return a0reversed;
}
public void setA0reversed(byte[] a0reversed) {
Packet.assertArray("a0reversed", a0reversed, 4);
this.a0reversed = a0reversed;
}
public byte[] getServerStuff() {
return serverStuff;
}
public void setServerStuff(byte[] serverStuff) {
Packet.assertArray("serverStuff", serverStuff, 16);
this.serverStuff = serverStuff;
}
@Override
public void read(ByteBuffer byteBuffer) {
byteBuffer.get(serverStuff);
byteBuffer.get(a0reversed);
}
@Override
public void write(ByteBuffer byteBuffer) {
byteBuffer.put(serverStuff);
byteBuffer.put(a0reversed);
}
@Override
public int getSize() {
return 16 + 4;
}
}
public static class Step2 extends Step {
private byte[] serverStuff = new byte[16]; // 16 bytes
private byte[] a0reversed = new byte[4]; // 4 bytes / The bytes from [A0] in reversed order
public Step2() {
super((byte) 2, ProtocolRole.CLIENT);
}
public byte[] getA0reversed() {
return a0reversed;
}
public void setA0reversed(byte[] a0reversed) {
Packet.assertArray("a0reversed", a0reversed, 4);
this.a0reversed = a0reversed;
}
public byte[] getServerStuff() {
return serverStuff;
}
public void setServerStuff(byte[] serverStuff) {
Packet.assertArray("serverStuff", serverStuff, 16);
this.serverStuff = serverStuff;
}
@Override
public void read(ByteBuffer byteBuffer) {
byteBuffer.get(serverStuff);
byteBuffer.get(a0reversed);
}
@Override
public void write(ByteBuffer byteBuffer) {
byteBuffer.put(serverStuff);
byteBuffer.put(a0reversed);
}
@Override
public int getSize() {
return 16 + 4;
}
}
public static class Step3 extends Step {
private byte[] x = new byte[64]; // x
private byte[] n = new byte[64]; // n
private int level;
private byte[] serverStuff = new byte[100]; // must be a lot of really important server stuff lol
public Step3() {
super((byte) 3, ProtocolRole.SERVER);
}
public byte[] getX() {
return x;
}
public void setX(byte[] x) {
Packet.assertArray("x", x, 64);
this.x = x;
}
public byte[] getN() {
return n;
}
public void setN(byte[] n) {
Packet.assertArray("n", n, 64);
this.n = n;
}
public byte[] getServerStuff() {
return serverStuff;
}
public void setServerStuff(byte[] serverStuff) {
Packet.assertArray("serverStuff", serverStuff, 100);
this.serverStuff = serverStuff;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
@Override
public void read(ByteBuffer byteBuffer) {
byteBuffer.get(x);
byteBuffer.get(n);
level = byteBuffer.getInt();
byteBuffer.get(serverStuff);
}
@Override
public void write(ByteBuffer byteBuffer) {
byteBuffer.put(x);
byteBuffer.put(n);
byteBuffer.putInt(level);
byteBuffer.put(serverStuff);
}
@Override
public int getSize() {
return 64 + 64 + 4 + 100;
}
}
public static class Step4 extends Step {
private byte[] x = new byte[64]; // x
private byte[] n = new byte[64]; // n
private int level;
private byte[] serverStuff = new byte[100]; // a2 echo from step 3
private byte[] y = new byte[64]; // y 0-paddded from the left
private byte[] clientivcommand = new byte[0]; // clientivcommand
public Step4() {
super((byte) 4, ProtocolRole.CLIENT);
}
public byte[] getX() {
return serverStuff;
}
public void setX(byte[] x) {
Packet.assertArray("x", x, 64);
this.x = x;
}
public byte[] getN() {
return serverStuff;
}
public void setN(byte[] n) {
Packet.assertArray("n", n, 64);
this.n = n;
}
public byte[] getServerStuff() {
return serverStuff;
}
public void setServerStuff(byte[] serverStuff) {
Packet.assertArray("serverStuff", serverStuff, 100);
this.serverStuff = serverStuff;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public byte[] getY() {
return y;
}
public void setY(byte[] y) {
Packet.assertArray("y", y, 64);
this.y = y;
}
public byte[] getClientIVCommand() {
return clientivcommand;
}
public void setClientIVcommand(byte[] clientivcommand) {
this.clientivcommand = clientivcommand;
}
@Override
public void read(ByteBuffer byteBuffer) {
byteBuffer.get(x);
byteBuffer.get(n);
level = byteBuffer.getInt();
byteBuffer.get(serverStuff);
byteBuffer.get(y);
clientivcommand = new byte[byteBuffer.remaining()];
byteBuffer.get(clientivcommand);
}
@Override
public void write(ByteBuffer byteBuffer) {
byteBuffer.put(x);
byteBuffer.put(n);
byteBuffer.putInt(level);
byteBuffer.put(serverStuff);
byteBuffer.put(y);
byteBuffer.put(clientivcommand);
}
@Override
public int getSize() {
return 64 + 64 + 4 + 100 + 64 + clientivcommand.length;
}
}
}
@@ -0,0 +1,37 @@
package com.github.manevolent.ts3j.protocol.packet;
public enum PacketType {
VOICE(0x0, null),
VOICE_WHISPER(0x1, null),
COMMAND(0x2, null),
COMMAND_LOW(0x3, null),
PING(0x4, null),
PONG(0x5, null),
ACK(0x6, null),
ACK_LOW(0x7, null),
INIT1(0x8, Packet8Init1.class)
;
private final int index;
private final Class<? extends Packet> packetClass;
PacketType(int index, Class<? extends Packet> packetClass) {
this.index = index;
this.packetClass = packetClass;
}
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;
}
}