Merge remote-tracking branch 'origin/master'

# Conflicts:
#	src/main/java/com/github/manevolent/ts3j/protocol/socket/client/LocalTeamspeakClientSocket.java
This commit is contained in:
Manevolent
2018-10-22 13:42:29 -04:00
6 changed files with 113 additions and 23 deletions
@@ -2,6 +2,8 @@ package com.github.manevolent.ts3j.protocol.packet;
import com.github.manevolent.ts3j.enums.CodecType; import com.github.manevolent.ts3j.enums.CodecType;
import com.github.manevolent.ts3j.protocol.ProtocolRole; import com.github.manevolent.ts3j.protocol.ProtocolRole;
import com.github.manevolent.ts3j.protocol.header.HeaderFlag;
import com.github.manevolent.ts3j.protocol.header.PacketHeader;
import com.github.manevolent.ts3j.util.Ts3Debugging; import com.github.manevolent.ts3j.util.Ts3Debugging;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@@ -12,6 +14,13 @@ public class PacketBody0Voice extends PacketBody {
private CodecType codecType; private CodecType codecType;
private byte[] codecData; private byte[] codecData;
// Unknown at this time (have seen: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07)
// Set for the first 5 packets of a new voice session
// I believe this is related to tracking opening a new decoder
// Not all new voice sessions will change this flag
// Best to ignore it for now, but it's here if you need it or figure it out
private Byte serverFlag0;
public PacketBody0Voice(ProtocolRole role) { public PacketBody0Voice(ProtocolRole role) {
super(PacketBodyType.VOICE, role); super(PacketBodyType.VOICE, role);
} }
@@ -44,6 +53,12 @@ public class PacketBody0Voice extends PacketBody {
return clientId; return clientId;
} }
public Byte getServerFlag0() { return serverFlag0; }
public void setServerFlag0(Byte flag) {
this.serverFlag0 = flag;
}
public void setClientId(int clientId) { public void setClientId(int clientId) {
if (getRole() != ProtocolRole.SERVER) if (getRole() != ProtocolRole.SERVER)
throw new IllegalStateException("cannot set client ID field on non-server packet"); throw new IllegalStateException("cannot set client ID field on non-server packet");
@@ -51,6 +66,32 @@ public class PacketBody0Voice extends PacketBody {
this.clientId = clientId; this.clientId = clientId;
} }
@Override
public void setHeaderValues(PacketHeader header) {
if (serverFlag0 != null) // Ensure padding is signaled
header.setPacketFlag(HeaderFlag.COMPRESSED, true);
}
@Override
public void read(PacketHeader header, ByteBuffer buffer) {
super.read(header, buffer);
int padding = 0;
boolean compressed = header.getPacketFlag(HeaderFlag.COMPRESSED);
if (compressed)
padding ++;
codecData = new byte[buffer.remaining() - padding];
buffer.get(codecData);
// Handle trailing data
if (padding > 0) {
if (compressed)
serverFlag0 = buffer.get();
}
}
@Override @Override
public void read(ByteBuffer buffer) { public void read(ByteBuffer buffer) {
packetId = buffer.getShort() & 0x0000FFFF; packetId = buffer.getShort() & 0x0000FFFF;
@@ -59,10 +100,6 @@ public class PacketBody0Voice extends PacketBody {
clientId = buffer.getShort() & 0x0000FFFF; clientId = buffer.getShort() & 0x0000FFFF;
codecType = CodecType.fromId(buffer.get() & 0xFF); codecType = CodecType.fromId(buffer.get() & 0xFF);
codecData = new byte[buffer.remaining()];
buffer.get(codecData);
} }
@Override @Override
@@ -75,10 +112,17 @@ public class PacketBody0Voice extends PacketBody {
buffer.put((byte)(codecType.getIndex() & 0xFF)); buffer.put((byte)(codecType.getIndex() & 0xFF));
buffer.put(codecData); buffer.put(codecData);
if (serverFlag0 != null)
buffer.put(serverFlag0);
} }
@Override @Override
public int getSize() { public int getSize() {
return 2 + (getRole() == ProtocolRole.SERVER ? 2 : 0) + 1 + codecData.length; return 2 +
(getRole() == ProtocolRole.SERVER ? 2 : 0) +
1 +
codecData.length +
(serverFlag0 != null ? 1 : 0);
} }
} }
@@ -16,20 +16,42 @@ public class PacketBody1VoiceWhisper extends PacketBody {
private WhisperTarget target; private WhisperTarget target;
private byte[] codecData; private byte[] codecData;
// Unknown at this time (have seen: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07)
// Set for the first 5 packets of a new voice session
// I believe this is related to tracking opening a new decoder
// Not all new voice sessions will change this flag
// Best to ignore it for now, but it's here if you need it or figure it out
private Byte serverFlag0;
public PacketBody1VoiceWhisper(ProtocolRole role) { public PacketBody1VoiceWhisper(ProtocolRole role) {
super(PacketBodyType.VOICE_WHISPER, role); super(PacketBodyType.VOICE_WHISPER, role);
} }
public Byte getServerFlag0() { return serverFlag0; }
public void setServerFlag0(Byte flag) {
this.serverFlag0 = flag;
}
@Override @Override
public void setHeaderValues(PacketHeader header) { public void setHeaderValues(PacketHeader header) {
if (target instanceof WhisperTargetGroup) if (target instanceof WhisperTargetGroup)
header.setPacketFlag(HeaderFlag.NEW_PROTOCOL, true); header.setPacketFlag(HeaderFlag.NEW_PROTOCOL, true);
else else
header.setPacketFlag(HeaderFlag.NEW_PROTOCOL, false); header.setPacketFlag(HeaderFlag.NEW_PROTOCOL, false);
if (serverFlag0 != null) // Ensure padding is signaled
header.setPacketFlag(HeaderFlag.COMPRESSED, true);
} }
@Override @Override
public void read(PacketHeader header, ByteBuffer buffer) { public void read(PacketHeader header, ByteBuffer buffer) {
int padding = 0;
boolean compressed = header.getPacketFlag(HeaderFlag.COMPRESSED);
if (compressed)
padding ++;
packetId = buffer.getShort() & 0xFFFF; packetId = buffer.getShort() & 0xFFFF;
if (getRole() == ProtocolRole.SERVER) if (getRole() == ProtocolRole.SERVER)
@@ -37,7 +59,6 @@ public class PacketBody1VoiceWhisper extends PacketBody {
codecType = CodecType.fromId((int) (buffer.get() & 0xFF)); codecType = CodecType.fromId((int) (buffer.get() & 0xFF));
if (getRole() == ProtocolRole.CLIENT) { if (getRole() == ProtocolRole.CLIENT) {
if (header.getPacketFlag(HeaderFlag.NEW_PROTOCOL)) { if (header.getPacketFlag(HeaderFlag.NEW_PROTOCOL)) {
setTarget(new WhisperTargetGroup()); setTarget(new WhisperTargetGroup());
@@ -50,14 +71,20 @@ public class PacketBody1VoiceWhisper extends PacketBody {
target.read(buffer); target.read(buffer);
read(buffer); codecData = new byte[buffer.remaining() - padding];
buffer.get(codecData);
// Handle trailing data
if (padding > 0) {
if (compressed)
serverFlag0 = buffer.get();
}
} }
// We implemented the above so this is unneccessary. // We implemented the above so this is unneccessary.
@Override @Override
public void read(ByteBuffer buffer) { public void read(ByteBuffer buffer) {
codecData = new byte[buffer.remaining()]; throw new UnsupportedOperationException();
buffer.get(codecData);
} }
@Override @Override
@@ -73,15 +100,18 @@ public class PacketBody1VoiceWhisper extends PacketBody {
target.write(buffer); target.write(buffer);
buffer.put(codecData); buffer.put(codecData);
if (serverFlag0 != null)
buffer.put(serverFlag0);
} }
@Override @Override
public int getSize() { public int getSize() {
switch (getRole()) { switch (getRole()) {
case CLIENT: case CLIENT:
return 2 + 1 + target.getSize() + codecData.length; return 2 + 1 + target.getSize() + codecData.length + (serverFlag0 != null ? 1 : 0);
case SERVER: case SERVER:
return 2 + 2 + 1 + codecData.length; return 2 + 2 + 1 + codecData.length + (serverFlag0 != null ? 1 : 0);
default: default:
throw new IllegalArgumentException("unknown role"); throw new IllegalArgumentException("unknown role");
} }
@@ -54,13 +54,14 @@ public abstract class LocalClientHandlerFull extends LocalClientHandler {
} }
break; break;
case VOICE: case VOICE:
Consumer<PacketBody0Voice> handler = getClient().getVoiceHandler(); Consumer<PacketBody0Voice> handler = getClient().getVoiceHandler();
if (handler != null) handler.accept((PacketBody0Voice) packet.getBody()); if (handler != null) handler.accept((PacketBody0Voice)packet.getBody());
break; break;
case VOICE_WHISPER: case VOICE_WHISPER:
Consumer<PacketBody1VoiceWhisper> whisperConsumer = getClient().getWhisperHandler(); Consumer<PacketBody1VoiceWhisper> whisperConsumer = getClient().getWhisperHandler();
if (whisperConsumer != null) whisperConsumer.accept((PacketBody1VoiceWhisper) packet.getBody()); if (whisperConsumer != null) whisperConsumer.accept((PacketBody1VoiceWhisper)packet.getBody());
break; break;
} }
} }
@@ -515,6 +515,8 @@ public class LocalTeamspeakClientSocket
private class MicrophoneTask implements Runnable { private class MicrophoneTask implements Runnable {
private boolean wasSendingAudio = false; private boolean wasSendingAudio = false;
private byte sessionId = 1;
private int flaggedPackets = 5;
@Override @Override
public void run() { public void run() {
@@ -531,14 +533,19 @@ public class LocalTeamspeakClientSocket
// TODO // TODO
// Set a state and send a muted event // Set a state and send a muted event
} else if (microphone.isReady()) { } else if (microphone.isReady()) {
byte[] data = microphone.provide();
if (data.length <= 0) return;
PacketBody0Voice voice = new PacketBody0Voice(getRole().getOut()); PacketBody0Voice voice = new PacketBody0Voice(getRole().getOut());
voice.setCodecType(microphone.getCodec()); voice.setCodecType(microphone.getCodec());
voice.setCodecData(microphone.provide()); voice.setCodecData(data);
if (flaggedPackets > 0) voice.setServerFlag0(sessionId);
try { try {
writePacket(voice); writePacket(voice);
flaggedPackets --;
wasSendingAudio = true; wasSendingAudio = true;
} catch (Exception e) { } catch (Exception e) {
// All we are really concerned about here is a disconnection event, in which case the loop // All we are really concerned about here is a disconnection event, in which case the loop
@@ -554,6 +561,13 @@ public class LocalTeamspeakClientSocket
try { try {
writePacket(voice); writePacket(voice);
// reset decoder flag
flaggedPackets = 5;
sessionId ++;
if (sessionId >= 8)
sessionId = 1;
wasSendingAudio = false; wasSendingAudio = false;
} catch (Exception e) { } catch (Exception e) {
// All we are really concerned about here is a disconnection event, in which case the loop // All we are really concerned about here is a disconnection event, in which case the loop
@@ -697,12 +711,11 @@ public class LocalTeamspeakClientSocket
switch (getState()) { switch (getState()) {
case RETRIEVING_DATA: case RETRIEVING_DATA:
case CONNECTED: case CONNECTED:
Command banCommand = new SingleCommand("clientupdate", ProtocolRole.CLIENT); Command command = new SingleCommand("clientupdate", ProtocolRole.CLIENT);
command.add(new CommandSingleParameter("client_nickname", nickname));
banCommand.add(new CommandSingleParameter("client_nickname", nickname));
try { try {
executeCommand(banCommand).complete(); executeCommand(command).complete();
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -5,10 +5,8 @@ import Punisher.NaCl.Internal.Ed25519Ref10.GroupElementP3;
import Punisher.NaCl.Internal.Ed25519Ref10.GroupOperations; import Punisher.NaCl.Internal.Ed25519Ref10.GroupOperations;
import Punisher.NaCl.Internal.Ed25519Ref10.ScalarOperations; import Punisher.NaCl.Internal.Ed25519Ref10.ScalarOperations;
import Punisher.NaCl.Internal.Sha512; import Punisher.NaCl.Internal.Sha512;
import com.github.manevolent.ts3j.api.Message;
import com.github.manevolent.ts3j.identity.*; import com.github.manevolent.ts3j.identity.*;
import com.github.manevolent.ts3j.license.License; import com.github.manevolent.ts3j.license.License;
import com.github.manevolent.ts3j.util.Pair;
import org.bouncycastle.asn1.*; import org.bouncycastle.asn1.*;
import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECDomainParameters;
@@ -25,7 +23,6 @@ import java.io.IOException;
import java.math.BigInteger; import java.math.BigInteger;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.security.*; import java.security.*;
import java.util.Base64;
import java.util.List; import java.util.List;
public final class Ts3Crypt { public final class Ts3Crypt {
@@ -5,6 +5,7 @@ import com.github.manevolent.ts3j.audio.Microphone;
import com.github.manevolent.ts3j.enums.CodecType; import com.github.manevolent.ts3j.enums.CodecType;
import com.github.manevolent.ts3j.event.*; import com.github.manevolent.ts3j.event.*;
import com.github.manevolent.ts3j.identity.LocalIdentity; import com.github.manevolent.ts3j.identity.LocalIdentity;
import com.github.manevolent.ts3j.protocol.Packet;
import com.github.manevolent.ts3j.protocol.client.ClientConnectionState; import com.github.manevolent.ts3j.protocol.client.ClientConnectionState;
import com.github.manevolent.ts3j.protocol.packet.PacketBody0Voice; import com.github.manevolent.ts3j.protocol.packet.PacketBody0Voice;
import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket; import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket;
@@ -22,14 +23,18 @@ public class ServerConnectionTest {
LocalTeamspeakClientSocket client = new LocalTeamspeakClientSocket(); LocalTeamspeakClientSocket client = new LocalTeamspeakClientSocket();
Ts3Debugging.setEnabled(true); Ts3Debugging.setEnabled(false);
LocalIdentity identity = LocalIdentity.generateNew(9); LocalIdentity identity = LocalIdentity.generateNew(10);
client.setIdentity(identity); client.setIdentity(identity);
client.setNickname("ts3j issue 2"); client.setNickname("ts3j issue 2");
client.setHWID("TestTestTest"); client.setHWID("TestTestTest");
client.setVoiceHandler(packet ->
System.out.println(packet.getServerFlag0())
);
while (true) { while (true) {
try { try {
client.connect( client.connect(