diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody0Voice.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody0Voice.java index 173241f..5524795 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody0Voice.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody0Voice.java @@ -2,6 +2,8 @@ package com.github.manevolent.ts3j.protocol.packet; import com.github.manevolent.ts3j.enums.CodecType; 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 java.nio.ByteBuffer; @@ -12,6 +14,13 @@ public class PacketBody0Voice extends PacketBody { private CodecType codecType; 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) { super(PacketBodyType.VOICE, role); } @@ -44,6 +53,12 @@ public class PacketBody0Voice extends PacketBody { return clientId; } + public Byte getServerFlag0() { return serverFlag0; } + + public void setServerFlag0(Byte flag) { + this.serverFlag0 = flag; + } + public void setClientId(int clientId) { if (getRole() != ProtocolRole.SERVER) throw new IllegalStateException("cannot set client ID field on non-server packet"); @@ -51,6 +66,32 @@ public class PacketBody0Voice extends PacketBody { 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 public void read(ByteBuffer buffer) { packetId = buffer.getShort() & 0x0000FFFF; @@ -59,10 +100,6 @@ public class PacketBody0Voice extends PacketBody { clientId = buffer.getShort() & 0x0000FFFF; codecType = CodecType.fromId(buffer.get() & 0xFF); - - codecData = new byte[buffer.remaining()]; - - buffer.get(codecData); } @Override @@ -75,10 +112,17 @@ public class PacketBody0Voice extends PacketBody { buffer.put((byte)(codecType.getIndex() & 0xFF)); buffer.put(codecData); + + if (serverFlag0 != null) + buffer.put(serverFlag0); } @Override 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); } } diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody1VoiceWhisper.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody1VoiceWhisper.java index 2ae4cce..5e70989 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody1VoiceWhisper.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody1VoiceWhisper.java @@ -16,20 +16,42 @@ public class PacketBody1VoiceWhisper extends PacketBody { private WhisperTarget target; 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) { super(PacketBodyType.VOICE_WHISPER, role); } + public Byte getServerFlag0() { return serverFlag0; } + + public void setServerFlag0(Byte flag) { + this.serverFlag0 = flag; + } + @Override public void setHeaderValues(PacketHeader header) { if (target instanceof WhisperTargetGroup) header.setPacketFlag(HeaderFlag.NEW_PROTOCOL, true); else header.setPacketFlag(HeaderFlag.NEW_PROTOCOL, false); + + if (serverFlag0 != null) // Ensure padding is signaled + header.setPacketFlag(HeaderFlag.COMPRESSED, true); } @Override public void read(PacketHeader header, ByteBuffer buffer) { + int padding = 0; + boolean compressed = header.getPacketFlag(HeaderFlag.COMPRESSED); + + if (compressed) + padding ++; + packetId = buffer.getShort() & 0xFFFF; if (getRole() == ProtocolRole.SERVER) @@ -37,7 +59,6 @@ public class PacketBody1VoiceWhisper extends PacketBody { codecType = CodecType.fromId((int) (buffer.get() & 0xFF)); - if (getRole() == ProtocolRole.CLIENT) { if (header.getPacketFlag(HeaderFlag.NEW_PROTOCOL)) { setTarget(new WhisperTargetGroup()); @@ -50,14 +71,20 @@ public class PacketBody1VoiceWhisper extends PacketBody { 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. @Override public void read(ByteBuffer buffer) { - codecData = new byte[buffer.remaining()]; - buffer.get(codecData); + throw new UnsupportedOperationException(); } @Override @@ -73,15 +100,18 @@ public class PacketBody1VoiceWhisper extends PacketBody { target.write(buffer); buffer.put(codecData); + + if (serverFlag0 != null) + buffer.put(serverFlag0); } @Override public int getSize() { switch (getRole()) { case CLIENT: - return 2 + 1 + target.getSize() + codecData.length; + return 2 + 1 + target.getSize() + codecData.length + (serverFlag0 != null ? 1 : 0); case SERVER: - return 2 + 2 + 1 + codecData.length; + return 2 + 2 + 1 + codecData.length + (serverFlag0 != null ? 1 : 0); default: throw new IllegalArgumentException("unknown role"); } diff --git a/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java b/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java index 752950c..2a2cd4c 100644 --- a/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java +++ b/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java @@ -5,6 +5,7 @@ import com.github.manevolent.ts3j.audio.Microphone; import com.github.manevolent.ts3j.enums.CodecType; import com.github.manevolent.ts3j.event.*; 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.packet.PacketBody0Voice; import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket; @@ -22,7 +23,7 @@ public class ServerConnectionTest { LocalTeamspeakClientSocket client = new LocalTeamspeakClientSocket(); - Ts3Debugging.setEnabled(true); + Ts3Debugging.setEnabled(false); LocalIdentity identity = LocalIdentity.generateNew(10); @@ -30,6 +31,10 @@ public class ServerConnectionTest { client.setNickname("ts3j issue 2"); client.setHWID("TestTestTest"); + client.setVoiceHandler(packet -> + System.out.println(((PacketBody0Voice)packet.getBody()).getServerFlag0()) + ); + while (true) { try { client.connect(