From da3495c297cde61bfdab7e295b2e5755c5a61db8 Mon Sep 17 00:00:00 2001 From: Manevolent Date: Fri, 17 Aug 2018 16:27:18 -0400 Subject: [PATCH] Whisper support --- .gitignore | 2 +- pom.xml | 2 +- .../ts3j/enums/GroupWhisperTarget.java | 30 +++ .../ts3j/enums/GroupWhisperType.java | 27 +++ .../packet/PacketBody1VoiceWhisper.java | 219 ++++++++++++++++-- .../client/LocalClientHandlerFull.java | 5 + .../client/AbstractTeamspeakClientSocket.java | 10 + 7 files changed, 269 insertions(+), 26 deletions(-) create mode 100644 src/main/java/com/github/manevolent/ts3j/enums/GroupWhisperTarget.java create mode 100644 src/main/java/com/github/manevolent/ts3j/enums/GroupWhisperType.java 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() {