Adding missing files
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user