diff --git a/.gitignore b/.gitignore
index c245070..4af064c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,7 +31,7 @@ out/
.userlibraries
# Maven
-target/
+targetId/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
diff --git a/pom.xml b/pom.xml
index 54bbc5e..7b4dc48 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,7 +10,7 @@
1.8
- 1.8
+ 1.8
diff --git a/src/main/java/com/github/manevolent/ts3j/enums/GroupWhisperTarget.java b/src/main/java/com/github/manevolent/ts3j/enums/GroupWhisperTarget.java
new file mode 100644
index 0000000..ec1aa97
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/enums/GroupWhisperTarget.java
@@ -0,0 +1,30 @@
+package com.github.manevolent.ts3j.enums;
+
+public enum GroupWhisperTarget {
+ ALL_CHANNELS(0),
+ CURRENT_CHANNEL(1),
+ PARENT_CHANNEL(2),
+ ALL_PARENT_CHANNEL(3),
+ CHANNEL_FAMILY(4),
+ COMPLETE_CHANNEL_FAMILY(5),
+ SUBCHANNELS(6)
+
+ ;
+
+ private final int index;
+
+ GroupWhisperTarget(int index) {
+ this.index = index;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public static GroupWhisperTarget fromId(int index) {
+ for (GroupWhisperTarget value : values())
+ if (value.getIndex() == index) return value;
+
+ throw new IllegalArgumentException("invalid index: " + index);
+ }
+}
diff --git a/src/main/java/com/github/manevolent/ts3j/enums/GroupWhisperType.java b/src/main/java/com/github/manevolent/ts3j/enums/GroupWhisperType.java
new file mode 100644
index 0000000..acadf20
--- /dev/null
+++ b/src/main/java/com/github/manevolent/ts3j/enums/GroupWhisperType.java
@@ -0,0 +1,27 @@
+package com.github.manevolent.ts3j.enums;
+
+public enum GroupWhisperType {
+ SERVER_GROUP(0),
+ CHANNEL_GROUP(1),
+ CHANNEL_COMMANDER(2),
+ ALL_CLIENTS(3)
+
+ ;
+
+ private final int index;
+
+ GroupWhisperType(int index) {
+ this.index = index;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public static GroupWhisperType fromId(int index) {
+ for (GroupWhisperType value : values())
+ if (value.getIndex() == index) return value;
+
+ throw new IllegalArgumentException("invalid index: " + index);
+ }
+}
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 84fa05d..2ae4cce 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
@@ -1,63 +1,63 @@
package com.github.manevolent.ts3j.protocol.packet;
import com.github.manevolent.ts3j.enums.CodecType;
+import com.github.manevolent.ts3j.enums.GroupWhisperTarget;
+import com.github.manevolent.ts3j.enums.GroupWhisperType;
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;
-import java.util.ArrayList;
-import java.util.List;
public class PacketBody1VoiceWhisper extends PacketBody {
private int packetId;
private int clientId;
private CodecType codecType;
+ private WhisperTarget target;
private byte[] codecData;
public PacketBody1VoiceWhisper(ProtocolRole role) {
super(PacketBodyType.VOICE_WHISPER, role);
}
+ @Override
+ public void setHeaderValues(PacketHeader header) {
+ if (target instanceof WhisperTargetGroup)
+ header.setPacketFlag(HeaderFlag.NEW_PROTOCOL, true);
+ else
+ header.setPacketFlag(HeaderFlag.NEW_PROTOCOL, false);
+ }
+
@Override
public void read(PacketHeader header, ByteBuffer buffer) {
- packetId = buffer.getShort() & 0x0000FFFF;
+ packetId = buffer.getShort() & 0xFFFF;
if (getRole() == ProtocolRole.SERVER)
- clientId = buffer.getShort() & 0x0000FFFF;
+ clientId = buffer.getShort() & 0xFFFF;
codecType = CodecType.fromId((int) (buffer.get() & 0xFF));
if (getRole() == ProtocolRole.CLIENT) {
if (header.getPacketFlag(HeaderFlag.NEW_PROTOCOL)) {
- int groupWhisperType = buffer.get() & 0xFF;
- int groupWhisperTarget = buffer.get() & 0xFF;
- long targetChannelIdorGroupId = buffer.getLong();
-
- // TODO
+ setTarget(new WhisperTargetGroup());
} else {
- int channelIdCount = buffer.get() & 0xFF;
- int clientIdCount = buffer.get() & 0xFF;
-
- List channelIds = new ArrayList<>(channelIdCount);
- for (int i = 0; i < channelIdCount; i ++) channelIds.add(buffer.getLong());
-
- List clientIds = new ArrayList<>(clientIdCount);
- for (int i = 0; i < clientIdCount; i ++) clientIds.add(buffer.getShort() & 0xFFFF);
-
- // TODO
+ setTarget(new WhisperTargetMultiple());
}
+ } else {
+ setTarget(new WhisperTargetServerToClient());
}
- codecData = new byte[buffer.remaining()];
+ target.read(buffer);
- buffer.get(codecData);
+ read(buffer);
}
+ // We implemented the above so this is unneccessary.
@Override
public void read(ByteBuffer buffer) {
- throw new UnsupportedOperationException();
+ codecData = new byte[buffer.remaining()];
+ buffer.get(codecData);
}
@Override
@@ -69,6 +69,9 @@ public class PacketBody1VoiceWhisper extends PacketBody {
buffer.put((byte)(codecType.getIndex() & 0xFF));
+ if (getRole() == ProtocolRole.CLIENT)
+ target.write(buffer);
+
buffer.put(codecData);
}
@@ -76,8 +79,7 @@ public class PacketBody1VoiceWhisper extends PacketBody {
public int getSize() {
switch (getRole()) {
case CLIENT:
- // TODO
- throw new UnsupportedOperationException();
+ return 2 + 1 + target.getSize() + codecData.length;
case SERVER:
return 2 + 2 + 1 + codecData.length;
default:
@@ -119,4 +121,173 @@ public class PacketBody1VoiceWhisper extends PacketBody {
this.clientId = clientId;
}
+
+ public WhisperTarget getTarget() {
+ return target;
+ }
+
+ public void setTarget(WhisperTarget target) {
+ this.target = target;
+ }
+
+ public static abstract class WhisperTarget {
+ public abstract void write(ByteBuffer buffer);
+ public abstract void read(ByteBuffer buffer);
+
+ public abstract int getSize();
+ }
+
+ public static class WhisperTargetServerToClient extends WhisperTarget {
+ @Override
+ public void write(ByteBuffer buffer) {
+ // Do nothing
+ }
+
+ @Override
+ public void read(ByteBuffer buffer) {
+ // Do nothing
+ }
+
+ @Override
+ public int getSize() {
+ return 0; // Empty
+ }
+ }
+
+ public static class WhisperTargetMultiple extends WhisperTarget {
+ private int[] channelIds;
+ private int[] clientIds;
+
+ public int[] getChannelIds() {
+ return channelIds;
+ }
+
+ public void setChannelIds(int[] channelIds) {
+ this.channelIds = channelIds;
+ }
+
+ public int[] getClientIds() {
+ return clientIds;
+ }
+
+ public void setClientIds(int[] clientIds) {
+ this.clientIds = clientIds;
+ }
+
+ @Override
+ public void write(ByteBuffer buffer) {
+ if (channelIds != null)
+ buffer.put((byte) (channelIds.length & 0xFF));
+ else
+ buffer.put((byte) 0x00);
+
+ if (clientIds != null)
+ buffer.put((byte) (clientIds.length & 0xFF));
+ else
+ buffer.put((byte) 0x00);
+
+ if (channelIds != null) {
+ for (int channelId : channelIds) {
+ buffer.putLong((long) channelId);
+ }
+ }
+
+ if (clientIds != null) {
+ for (int clientId : clientIds) {
+ buffer.putShort((short) (clientId & 0xFFFF));
+ }
+ }
+ }
+
+ @Override
+ public void read(ByteBuffer buffer) {
+ int channelIdCount = buffer.get() & 0xFF;
+ int clientIdCount = buffer.get() & 0xFF;
+
+ channelIds = new int[channelIdCount];
+ for (int n = 0; n < channelIdCount; n ++)
+ channelIds[n] = (int) (buffer.getLong());
+
+ clientIds = new int[clientIdCount];
+ for (int n = 0; n < channelIdCount; n ++)
+ clientIds[n] = (buffer.getShort() & 0xFFFF);
+ }
+
+ @Override
+ public int getSize() {
+ return 2 + 2 +
+ (channelIds == null ? 0 : channelIds.length * 8) +
+ (clientIds == null ? 0 : clientIds.length * 2);
+ }
+ }
+
+ public static class WhisperTargetGroup extends WhisperTarget {
+ private GroupWhisperType whisperType;
+ private GroupWhisperTarget whisperTarget;
+ private long targetId;
+
+ @Override
+ public void write(ByteBuffer buffer) {
+ buffer.put((byte) (whisperType.getIndex() & 0xFF));
+ buffer.put((byte) (whisperTarget.getIndex() & 0xFF));
+ buffer.putLong(targetId);
+ }
+
+ @Override
+ public void read(ByteBuffer buffer) {
+ whisperType = GroupWhisperType.fromId(buffer.get() & 0xFF);
+ whisperTarget = GroupWhisperTarget.fromId(buffer.get() & 0xFF);
+ targetId = buffer.getLong();
+ }
+
+ @Override
+ public int getSize() {
+ return 1 + 1 + 8;
+ }
+
+ public GroupWhisperType getWhisperType() {
+ return whisperType;
+ }
+
+ public void setWhisperType(GroupWhisperType whisperType) {
+ this.whisperType = whisperType;
+ }
+
+ public GroupWhisperTarget getWhisperTarget() {
+ return whisperTarget;
+ }
+
+ public void setWhisperTarget(GroupWhisperTarget whisperTarget) {
+ this.whisperTarget = whisperTarget;
+ }
+
+ public long getTargetId() {
+ return targetId;
+ }
+
+ public void setTargetId(long targetId) {
+ this.targetId = targetId;
+ }
+ }
+
+ public static WhisperTarget createChannel(int channelId) {
+ WhisperTargetMultiple multiple = new WhisperTargetMultiple();
+ multiple.setChannelIds(new int[] { channelId });
+ return multiple;
+ }
+
+ public static WhisperTarget createClient(int clientId) {
+ WhisperTargetMultiple multiple = new WhisperTargetMultiple();
+ multiple.setClientIds(new int[] { clientId });
+ return multiple;
+ }
+
+ public static WhisperTarget createServer() {
+ WhisperTargetGroup group = new WhisperTargetGroup();
+
+ group.setWhisperTarget(GroupWhisperTarget.ALL_CHANNELS);
+ group.setWhisperType(GroupWhisperType.ALL_CLIENTS);
+
+ return group;
+ }
}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerFull.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerFull.java
index f860999..5ccf035 100644
--- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerFull.java
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerFull.java
@@ -5,6 +5,7 @@ import com.github.manevolent.ts3j.command.CommandProcessor;
import com.github.manevolent.ts3j.command.MultiCommand;
import com.github.manevolent.ts3j.protocol.Packet;
import com.github.manevolent.ts3j.protocol.packet.PacketBody0Voice;
+import com.github.manevolent.ts3j.protocol.packet.PacketBody1VoiceWhisper;
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;
@@ -57,6 +58,10 @@ public abstract class LocalClientHandlerFull extends LocalClientHandler {
Consumer handler = getClient().getVoiceHandler();
if (handler != null) handler.accept((PacketBody0Voice) packet.getBody());
break;
+ case VOICE_WHISPER:
+ Consumer whisperConsumer = getClient().getWhisperHandler();
+ if (whisperConsumer != null) whisperConsumer.accept((PacketBody1VoiceWhisper) packet.getBody());
+ break;
}
}
}
diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/AbstractTeamspeakClientSocket.java b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/AbstractTeamspeakClientSocket.java
index 116d7d0..a17f60c 100644
--- a/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/AbstractTeamspeakClientSocket.java
+++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/AbstractTeamspeakClientSocket.java
@@ -70,6 +70,8 @@ public abstract class AbstractTeamspeakClientSocket
private CommandProcessor commandProcessor;
private Consumer voiceHandler;
+ private Consumer whisperHandler;
+
private Consumer exceptionHandler = Throwable::printStackTrace;
private Identity identity = null;
@@ -886,6 +888,14 @@ public abstract class AbstractTeamspeakClientSocket
this.voiceHandler = voiceHandler;
}
+ public Consumer getWhisperHandler() {
+ return whisperHandler;
+ }
+
+ public void setWhisperHandler(Consumer whisperHandler) {
+ this.whisperHandler = whisperHandler;
+ }
+
private class NetworkHandler implements Runnable {
@Override
public void run() {