We can now connect to servers, get the welcome message, handle pings, properly decompress fragments, etc. I have also added CommandLow/AckLow support. We need to implement data objects now to recognize command objects, and then implement the voice protocol.
This commit is contained in:
@@ -29,6 +29,8 @@ public interface Command {
|
||||
*/
|
||||
default boolean willExpectResponse() { return false; }
|
||||
|
||||
void add(CommandParameter parameter);
|
||||
|
||||
/**
|
||||
* Builds the command to a netowrk-issueable string.
|
||||
* The implementation currently defaults to the basic command format for TS3:
|
||||
|
||||
@@ -1,11 +1,34 @@
|
||||
package com.github.manevolent.ts3j.command;
|
||||
|
||||
import com.github.manevolent.ts3j.command.client.ClientInitServerProcessor;
|
||||
import com.github.manevolent.ts3j.protocol.socket.client.AbstractTeamspeakClientSocket;
|
||||
import com.github.manevolent.ts3j.util.Ts3Debugging;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class CommandHandler implements CommandProcessor {
|
||||
private final Map<String, CommandProcessor> commandProcessors = new HashMap<>();
|
||||
|
||||
protected CommandHandler() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(AbstractTeamspeakClientSocket socket, ComplexCommand complexCommand)
|
||||
throws CommandProcessException {
|
||||
Ts3Debugging.debug("[COMMAND] " + complexCommand.build());
|
||||
|
||||
CommandProcessor processor = commandProcessors.get(complexCommand.getName().toLowerCase());
|
||||
if (processor != null)
|
||||
processor.process(socket, complexCommand);
|
||||
}
|
||||
|
||||
public static CommandHandler createLocalClient() {
|
||||
CommandHandler handler = new CommandHandler();
|
||||
|
||||
handler.commandProcessors.put("initserver", new ClientInitServerProcessor());
|
||||
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.github.manevolent.ts3j.command.client;
|
||||
|
||||
import com.github.manevolent.ts3j.command.CommandProcessException;
|
||||
import com.github.manevolent.ts3j.command.CommandProcessor;
|
||||
import com.github.manevolent.ts3j.command.ComplexCommand;
|
||||
import com.github.manevolent.ts3j.command.SimpleCommand;
|
||||
import com.github.manevolent.ts3j.protocol.client.ClientConnectionState;
|
||||
import com.github.manevolent.ts3j.protocol.socket.client.AbstractTeamspeakClientSocket;
|
||||
import com.github.manevolent.ts3j.util.Ts3Debugging;
|
||||
|
||||
public class ClientInitServerProcessor implements CommandProcessor {
|
||||
@Override
|
||||
public void process(AbstractTeamspeakClientSocket socket, ComplexCommand complexCommand) throws CommandProcessException {
|
||||
SimpleCommand command = complexCommand.simplify();
|
||||
|
||||
Ts3Debugging.debug(command.get("virtualserver_welcomemessage").getValue());
|
||||
|
||||
if (socket.getState() == ClientConnectionState.RETRIEVING_DATA)
|
||||
try {
|
||||
socket.setState(ClientConnectionState.CONNECTED);
|
||||
} catch (Exception e) {
|
||||
throw new CommandProcessException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,17 @@
|
||||
package com.github.manevolent.ts3j.command.response;
|
||||
|
||||
import com.github.manevolent.ts3j.command.Command;
|
||||
import com.github.manevolent.ts3j.command.ComplexCommand;
|
||||
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
public class CommandResponse {
|
||||
private final Object returnLock = new Object();
|
||||
private final Command command;
|
||||
private final int returnCode;
|
||||
|
||||
private long dispatchedTime;
|
||||
private ComplexCommand returnedCommand;
|
||||
|
||||
public CommandResponse(Command command, int returnCode) {
|
||||
this.command = command;
|
||||
@@ -21,20 +26,29 @@ public class CommandResponse {
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
public CommandResponse join() throws InterruptedException {
|
||||
public ComplexCommand getResponse()
|
||||
throws InterruptedException, TimeoutException {
|
||||
return getResponse(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
public ComplexCommand getResponse(int millis)
|
||||
throws InterruptedException, TimeoutException {
|
||||
if (returnCode < 0) return null;
|
||||
|
||||
synchronized (returnLock) {
|
||||
returnLock.wait();
|
||||
long waited, toWait;
|
||||
|
||||
while (getReturnedCommand() == null) {
|
||||
waited = System.currentTimeMillis() - getDispatchedTime();
|
||||
toWait = millis - waited;
|
||||
|
||||
if (toWait <= 0) throw new TimeoutException();
|
||||
|
||||
returnLock.wait(toWait);
|
||||
}
|
||||
|
||||
return this;
|
||||
return getReturnedCommand();
|
||||
}
|
||||
|
||||
public CommandResponse join(int millis) throws InterruptedException {
|
||||
synchronized (returnLock) {
|
||||
returnLock.wait(millis);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getDispatchedTime() {
|
||||
@@ -44,4 +58,19 @@ public class CommandResponse {
|
||||
public void setDispatchedTime(long dispatchedTime) {
|
||||
this.dispatchedTime = dispatchedTime;
|
||||
}
|
||||
|
||||
public ComplexCommand getReturnedCommand() {
|
||||
return returnedCommand;
|
||||
}
|
||||
|
||||
public void setReturnedCommand(ComplexCommand returnedCommand) {
|
||||
synchronized (returnLock) {
|
||||
if (this.returnCode < 0 ) throw new IllegalArgumentException("not expecting return command");
|
||||
if (this.returnedCommand != null) throw new IllegalArgumentException("already set return command");
|
||||
|
||||
this.returnedCommand = returnedCommand;
|
||||
|
||||
returnLock.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public abstract class PacketBody {
|
||||
public abstract int getSize();
|
||||
|
||||
public void setHeaderValues(PacketHeader header) {
|
||||
// Do nothing
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.github.manevolent.ts3j.protocol.packet;
|
||||
|
||||
import com.github.manevolent.ts3j.command.Command;
|
||||
import com.github.manevolent.ts3j.command.ComplexCommand;
|
||||
import com.github.manevolent.ts3j.protocol.ProtocolRole;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
public class PacketBody3CommandLow extends PacketBody {
|
||||
private String text;
|
||||
|
||||
public PacketBody3CommandLow(ProtocolRole role) {
|
||||
super(PacketBodyType.COMMAND_LOW, role);
|
||||
}
|
||||
|
||||
public PacketBody3CommandLow(ProtocolRole role, String command) {
|
||||
this(role);
|
||||
|
||||
this.text = command;
|
||||
}
|
||||
|
||||
public PacketBody3CommandLow(ProtocolRole role, Command command) {
|
||||
this(role, command.build());
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public ComplexCommand parse() {
|
||||
return ComplexCommand.parse(getRole(), getText());
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(ByteBuffer buffer) {
|
||||
text = Charset.forName("UTF8").decode(buffer).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuffer buffer) {
|
||||
buffer.put(text.getBytes(Charset.forName("UTF8")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return text.length();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.github.manevolent.ts3j.protocol.packet;
|
||||
|
||||
import com.github.manevolent.ts3j.protocol.ProtocolRole;
|
||||
import com.github.manevolent.ts3j.protocol.header.HeaderFlag;
|
||||
import com.github.manevolent.ts3j.protocol.header.PacketHeader;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class PacketBody4Ping extends PacketBody {
|
||||
public PacketBody4Ping(ProtocolRole role) {
|
||||
super(PacketBodyType.PING, role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeaderValues(PacketHeader header) {
|
||||
header.setPacketFlag(HeaderFlag.UNENCRYPTED, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(ByteBuffer buffer) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuffer buffer) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.github.manevolent.ts3j.protocol.packet;
|
||||
|
||||
import com.github.manevolent.ts3j.protocol.ProtocolRole;
|
||||
import com.github.manevolent.ts3j.protocol.header.HeaderFlag;
|
||||
import com.github.manevolent.ts3j.protocol.header.PacketHeader;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class PacketBody5Pong extends PacketBody {
|
||||
private int packetId;
|
||||
|
||||
public PacketBody5Pong(ProtocolRole role) {
|
||||
super(PacketBodyType.PONG, role);
|
||||
}
|
||||
|
||||
public PacketBody5Pong(ProtocolRole role, int packetId) {
|
||||
this(role);
|
||||
|
||||
setPacketId(packetId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeaderValues(PacketHeader header) {
|
||||
header.setPacketFlag(HeaderFlag.UNENCRYPTED, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(ByteBuffer buffer) {
|
||||
packetId = buffer.getShort() & 0x000000FFFFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuffer buffer) {
|
||||
buffer.putShort((short) (packetId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
public int getPacketId() {
|
||||
return packetId;
|
||||
}
|
||||
|
||||
public void setPacketId(int packetId) {
|
||||
if (packetId > 65535 || packetId < 0)
|
||||
throw new IllegalArgumentException("packetId out of bounds: " + packetId);
|
||||
|
||||
this.packetId = packetId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.github.manevolent.ts3j.protocol.packet;
|
||||
|
||||
import com.github.manevolent.ts3j.protocol.ProtocolRole;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class PacketBody7AckLow extends PacketBody {
|
||||
private int packetId;
|
||||
|
||||
public PacketBody7AckLow(ProtocolRole role) {
|
||||
super(PacketBodyType.ACK_LOW, role);
|
||||
}
|
||||
|
||||
public PacketBody7AckLow(ProtocolRole role, int packetId) {
|
||||
this(role);
|
||||
|
||||
setPacketId(packetId);
|
||||
}
|
||||
|
||||
public int getPacketId() {
|
||||
return packetId;
|
||||
}
|
||||
|
||||
public void setPacketId(int packetId) {
|
||||
this.packetId = packetId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(ByteBuffer buffer) {
|
||||
packetId = buffer.getShort() & 0x0000FFFF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuffer buffer) {
|
||||
buffer.putShort((short) (getPacketId() & 0xFFFFFF));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,12 @@ 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),
|
||||
PONG(0x5, PacketBody5Pong.class, null, false, false, false, false, false),
|
||||
PING(0x4, PacketBody4Ping.class, 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),
|
||||
ACK_LOW(0x7, PacketBody7AckLow.class, 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_LOW(0x3, PacketBody3CommandLow.class, ACK_LOW, true, true, true, true, true),
|
||||
COMMAND(0x2, PacketBody2Command.class, ACK, true, true, true, true, true);
|
||||
|
||||
private final int index;
|
||||
|
||||
-6
@@ -1,15 +1,9 @@
|
||||
package com.github.manevolent.ts3j.protocol.packet.handler.local;
|
||||
|
||||
import com.github.manevolent.ts3j.command.ComplexCommand;
|
||||
import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket;
|
||||
|
||||
public class LocalClientHandlerConnected extends LocalClientHandlerFull {
|
||||
public LocalClientHandlerConnected(LocalTeamspeakClientSocket client) {
|
||||
super(client);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleCommand(ComplexCommand command) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -1,5 +1,6 @@
|
||||
package com.github.manevolent.ts3j.protocol.packet.handler.local;
|
||||
|
||||
import com.github.manevolent.ts3j.command.CommandHandler;
|
||||
import com.github.manevolent.ts3j.command.SimpleCommand;
|
||||
import com.github.manevolent.ts3j.command.part.CommandSingleParameter;
|
||||
import com.github.manevolent.ts3j.protocol.Packet;
|
||||
@@ -228,6 +229,7 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
|
||||
)
|
||||
);
|
||||
|
||||
getClient().setCommandProcessor(CommandHandler.createLocalClient());
|
||||
getClient().setState(ClientConnectionState.RETRIEVING_DATA);
|
||||
} else if (command.getName().equals("error")) {
|
||||
getClient().setState(ClientConnectionState.DISCONNECTED);
|
||||
|
||||
+19
-2
@@ -5,6 +5,7 @@ import com.github.manevolent.ts3j.command.CommandProcessor;
|
||||
import com.github.manevolent.ts3j.command.ComplexCommand;
|
||||
import com.github.manevolent.ts3j.protocol.Packet;
|
||||
import com.github.manevolent.ts3j.protocol.packet.PacketBody2Command;
|
||||
import com.github.manevolent.ts3j.protocol.packet.PacketBody3CommandLow;
|
||||
import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -22,11 +23,27 @@ public abstract class LocalClientHandlerFull extends LocalClientHandler {
|
||||
|
||||
@Override
|
||||
public void handlePacket(Packet packet) throws IOException, TimeoutException {
|
||||
if (packet.getBody() instanceof PacketBody2Command)
|
||||
switch (packet.getBody().getType()) {
|
||||
case COMMAND:
|
||||
try {
|
||||
handleCommand(ComplexCommand.parse(packet.getRole(), ((PacketBody2Command) packet.getBody()).getText()));
|
||||
handleCommand(ComplexCommand.parse(
|
||||
packet.getRole(),
|
||||
((PacketBody2Command) packet.getBody()).getText()
|
||||
));
|
||||
} catch (CommandProcessException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
break;
|
||||
case COMMAND_LOW:
|
||||
try {
|
||||
handleCommand(ComplexCommand.parse(
|
||||
packet.getRole(),
|
||||
((PacketBody3CommandLow) packet.getBody()).getText()
|
||||
));
|
||||
} catch (CommandProcessException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-9
@@ -1,18 +1,9 @@
|
||||
package com.github.manevolent.ts3j.protocol.packet.handler.local;
|
||||
|
||||
import com.github.manevolent.ts3j.command.ComplexCommand;
|
||||
import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
public class LocalClientHandlerRetrievingData extends LocalClientHandlerFull {
|
||||
public LocalClientHandlerRetrievingData(LocalTeamspeakClientSocket client) {
|
||||
super(client);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleCommand(ComplexCommand command) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+97
-29
@@ -2,6 +2,7 @@ package com.github.manevolent.ts3j.protocol.socket.client;
|
||||
|
||||
import com.github.manevolent.ts3j.command.Command;
|
||||
import com.github.manevolent.ts3j.command.CommandProcessor;
|
||||
import com.github.manevolent.ts3j.command.part.CommandSingleParameter;
|
||||
import com.github.manevolent.ts3j.command.response.CommandResponse;
|
||||
import com.github.manevolent.ts3j.identity.Identity;
|
||||
import com.github.manevolent.ts3j.protocol.NetworkPacket;
|
||||
@@ -21,6 +22,7 @@ import com.github.manevolent.ts3j.protocol.socket.AbstractTeamspeakSocket;
|
||||
import com.github.manevolent.ts3j.util.QuickLZ;
|
||||
import com.github.manevolent.ts3j.util.Ts3Crypt;
|
||||
import com.github.manevolent.ts3j.util.Ts3Debugging;
|
||||
import javafx.util.Pair;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
@@ -41,6 +43,7 @@ public abstract class AbstractTeamspeakClientSocket
|
||||
private final Map<PacketBodyType, Reassembly> reassemblyQueue = new ConcurrentHashMap<>();
|
||||
private final Map<String, Object> clientOptions = new ConcurrentHashMap<>();
|
||||
private final Map<Integer, PacketResponse> sendQueue = new ConcurrentHashMap<>();
|
||||
private final Map<PacketBodyType, PacketCounter> sendCounter = new ConcurrentHashMap<>();
|
||||
|
||||
private CommandProcessor commandProcessor;
|
||||
|
||||
@@ -52,6 +55,7 @@ public abstract class AbstractTeamspeakClientSocket
|
||||
private int generationId = 0;
|
||||
private int clientId = 0;
|
||||
private int packetId = 0;
|
||||
private int commandReturnCode = 0;
|
||||
|
||||
private final NetworkReader networkReader = new NetworkReader();
|
||||
private final NetworkHandler networkHandler = new NetworkHandler();
|
||||
@@ -73,6 +77,13 @@ public abstract class AbstractTeamspeakClientSocket
|
||||
for (PacketBodyType bodyType : PacketBodyType.values())
|
||||
if (bodyType.isSplittable())
|
||||
reassemblyQueue.put(bodyType, new Reassembly());
|
||||
|
||||
// Initialize counters
|
||||
for (PacketBodyType bodyType : PacketBodyType.values())
|
||||
if (bodyType == PacketBodyType.INIT1)
|
||||
sendCounter.put(PacketBodyType.INIT1, new PacketCounterZero());
|
||||
else
|
||||
sendCounter.put(bodyType, new PacketCounterFull());
|
||||
}
|
||||
|
||||
protected void start() {
|
||||
@@ -169,14 +180,13 @@ public abstract class AbstractTeamspeakClientSocket
|
||||
CommandResponse response;
|
||||
|
||||
if (command.willExpectResponse()) {
|
||||
//command.appendParameter(new CommandSingleParameter("return_code", returnCodeIndex));
|
||||
response = new CommandResponse(command, 0); // TODO
|
||||
int returnCode = commandReturnCode++;
|
||||
response = new CommandResponse(command, returnCode++);
|
||||
command.add(new CommandSingleParameter("return_code", Integer.toString(returnCode)));
|
||||
} else {
|
||||
response = new CommandResponse(command, -1);
|
||||
}
|
||||
|
||||
String commandString = command.toString();
|
||||
|
||||
response.setDispatchedTime(System.currentTimeMillis());
|
||||
|
||||
writePacket(new PacketBody2Command(ProtocolRole.CLIENT, command));
|
||||
@@ -218,18 +228,12 @@ public abstract class AbstractTeamspeakClientSocket
|
||||
// Set header values
|
||||
packet.getBody().setHeaderValues(packet.getHeader());
|
||||
|
||||
if (packet.getHeader().getType() != PacketBodyType.INIT1 && packet.getHeader().getType() != PacketBodyType.ACK) {
|
||||
packetId++;
|
||||
packetId = packetId & 0x0000FFFF;
|
||||
|
||||
if (packetId == 0) generationId++;
|
||||
|
||||
Pair<Integer, Integer> counterResult = sendCounter.get(packet.getHeader().getType()).next();
|
||||
if (packet.getHeader() instanceof ClientPacketHeader)
|
||||
((ClientPacketHeader) packet.getHeader()).setClientId(clientId);
|
||||
|
||||
packet.getHeader().setGeneration(generationId);
|
||||
packet.getHeader().setPacketId(packetId);
|
||||
}
|
||||
packet.getHeader().setPacketId(counterResult.getKey());
|
||||
packet.getHeader().setGeneration(counterResult.getValue());
|
||||
|
||||
switch (packet.getHeader().getType()) {
|
||||
case INIT1:
|
||||
@@ -248,6 +252,7 @@ public abstract class AbstractTeamspeakClientSocket
|
||||
ByteBuffer outputBuffer = ByteBuffer.allocate(totalSize);
|
||||
packet.getBody().write(outputBuffer);
|
||||
|
||||
if (packet.getHeader().getType().isCompressible()) {
|
||||
byte[] compressed = QuickLZ.compress(outputBuffer.array(), 1);
|
||||
packet.getHeader().setPacketFlag(HeaderFlag.COMPRESSED, true);
|
||||
packet.setBody(new PacketBodyCompressed(packet.getHeader().getType(), packet.getRole(), compressed));
|
||||
@@ -256,9 +261,10 @@ public abstract class AbstractTeamspeakClientSocket
|
||||
writePacketIntl(packet);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!packet.getHeader().getType().isSplittable())
|
||||
throw new IllegalArgumentException("packet too large: " + packet.getSize());
|
||||
throw new IllegalArgumentException("packet too large: " + packet.getSize() + " (cannot split)");
|
||||
|
||||
for (int offs = 0; offs < totalSize;) {
|
||||
int flush = Math.min(500 - packet.getHeader().getSize(), totalSize - offs);
|
||||
@@ -367,9 +373,7 @@ public abstract class AbstractTeamspeakClientSocket
|
||||
}
|
||||
}
|
||||
|
||||
private Packet readPacketIntl() throws IOException {
|
||||
NetworkPacket networkPacket = readNetworkPacket();
|
||||
|
||||
private Packet readPacketIntl(NetworkPacket networkPacket) throws IOException {
|
||||
if (networkPacket.getHeader() == null) throw new NullPointerException("header");
|
||||
|
||||
Packet packet = new Packet(networkPacket.getHeader().getRole());
|
||||
@@ -386,7 +390,7 @@ public abstract class AbstractTeamspeakClientSocket
|
||||
networkPacket.getHeader().getType().canEncrypt();
|
||||
|
||||
if (networkPacket.getHeader().getType().mustEncrypt() && !encrypted)
|
||||
throw new IllegalArgumentException("packet is unencrypted, but must encrypt");
|
||||
throw new IllegalArgumentException("packet is unencrypted, but must be encrypted");
|
||||
|
||||
ByteBuffer packetBuffer =
|
||||
(ByteBuffer) networkPacket.getBuffer().position(networkPacket.getHeader().getSize());
|
||||
@@ -437,7 +441,7 @@ public abstract class AbstractTeamspeakClientSocket
|
||||
@Override
|
||||
public Packet readPacket() throws IOException, TimeoutException {
|
||||
while (isReading()) {
|
||||
Packet packet = readPacketIntl();
|
||||
Packet packet = readPacketIntl(readNetworkPacket());
|
||||
|
||||
// Find if the packet must be acknowledged
|
||||
PacketBodyType ackType = packet.getHeader().getType().getAcknolwedgedBy();
|
||||
@@ -446,6 +450,12 @@ public abstract class AbstractTeamspeakClientSocket
|
||||
case ACK:
|
||||
writePacket(new PacketBody6Ack(getRole().getOut(), packet.getHeader().getPacketId()));
|
||||
break;
|
||||
case ACK_LOW:
|
||||
writePacket(new PacketBody7AckLow(getRole().getOut(), packet.getHeader().getPacketId()));
|
||||
break;
|
||||
case PONG:
|
||||
writePacket(new PacketBody5Pong(getRole().getOut(), packet.getHeader().getPacketId()));
|
||||
continue; // Don't pass pings to the parent
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,17 +466,25 @@ public abstract class AbstractTeamspeakClientSocket
|
||||
}
|
||||
|
||||
// Find if the packet is itself an acknowledgement
|
||||
boolean handle;
|
||||
PacketResponse response;
|
||||
switch (packet.getHeader().getType()) {
|
||||
case ACK:
|
||||
int packetId = ((PacketBody6Ack)packet.getBody()).getPacketId();
|
||||
PacketResponse response = sendQueue.get(packetId);
|
||||
if (response != null) {
|
||||
response.acknowledge(packet);
|
||||
}
|
||||
response = sendQueue.get(((PacketBody6Ack)packet.getBody()).getPacketId());
|
||||
handle = false;
|
||||
break;
|
||||
case ACK_LOW:
|
||||
response = sendQueue.get(((PacketBody7AckLow)packet.getBody()).getPacketId());
|
||||
handle = false;
|
||||
break;
|
||||
default:
|
||||
handle = true;
|
||||
response = null;
|
||||
}
|
||||
|
||||
if (response != null) {
|
||||
response.acknowledge(packet);
|
||||
} else if (handle) {
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
@@ -672,6 +690,51 @@ public abstract class AbstractTeamspeakClientSocket
|
||||
}
|
||||
}
|
||||
|
||||
private interface PacketCounter {
|
||||
Pair<Integer, Integer> next();
|
||||
int getGenerationId();
|
||||
}
|
||||
|
||||
private class PacketCounterZero implements PacketCounter {
|
||||
@Override
|
||||
public Pair<Integer, Integer> next() {
|
||||
return new Pair<>(0,0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGenerationId() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private class PacketCounterFull implements PacketCounter {
|
||||
private final Object sendLock = new Object();
|
||||
private int packetId = 0;
|
||||
private int generationId = 0;
|
||||
|
||||
public int getPacketId() {
|
||||
return packetId;
|
||||
}
|
||||
|
||||
public int getGenerationId() {
|
||||
return generationId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<Integer, Integer> next() {
|
||||
synchronized (sendLock) {
|
||||
packetId++;
|
||||
|
||||
if (packetId >= 65536) {
|
||||
packetId = 0;
|
||||
generationId++;
|
||||
}
|
||||
|
||||
return new Pair<>(packetId, generationId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Reassembly {
|
||||
private final Map<Integer, Packet> queue = new LinkedHashMap<>();
|
||||
private boolean state = false;
|
||||
@@ -698,16 +761,21 @@ public abstract class AbstractTeamspeakClientSocket
|
||||
int totalLength = reassemblyList.stream().mapToInt(x -> x.getBody().getSize()).sum() + lastFragment.getBody().getSize();
|
||||
if (totalLength < 0) throw new IllegalArgumentException("reassembly too small: " + totalLength);
|
||||
|
||||
Packet firstPacket = reassemblyList.get(0);
|
||||
Packet reassembledPacket = new Packet(firstPacket.getRole());
|
||||
reassembledPacket.setHeader(firstPacket.getHeader());
|
||||
|
||||
ByteBuffer reassemblyBuffer = ByteBuffer.allocate(totalLength);
|
||||
|
||||
reassembledPacket.getHeader().setPacketFlag(HeaderFlag.FRAGMENTED, false);
|
||||
|
||||
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));
|
||||
if (firstPacket.getHeader().getPacketFlag(HeaderFlag.COMPRESSED))
|
||||
reassemblyBuffer = ByteBuffer.wrap(QuickLZ.decompress(reassemblyBuffer.array()));
|
||||
|
||||
reassembledPacket.readBody(reassemblyBuffer);
|
||||
|
||||
return reassembledPacket;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user