From f50ada088889121aa7851323d8eb32900550ecca Mon Sep 17 00:00:00 2001 From: Manevolent Date: Wed, 1 Aug 2018 12:27:09 -0400 Subject: [PATCH] Working quickly to finish packet generation following. I've implemented my own counter now, using the same window system as the previous Java rewrite, using just an integer array and one array copy call. Hopefully this is good for performance. Also, there was a breaking issue with the computeParameters() call that the test did not account for. Once you reached packet ID 128, the encryption keys were all off. This was due to the packet ID byte shifting: it worked for the first 127 bytes. We can thank Java's lack of unsigned primitives, and a little bit of my own ignorance for that issue. At this point, I believe all we need now are the clientQuery commands. Several events must be added, and the responding commands must be typed out as well. More to come. Home stretch. --- LICENSE | 2 +- .../java/Punisher/NaCl/Internal/Sha512.java | 4 +- .../manevolent/ts3j/api/FileTransfer.java | 2 +- .../manevolent/ts3j/protocol/RingQueue.java | 164 --------- .../ts3j/protocol/header/PacketHeader.java | 1 + .../protocol/packet/PacketBody0Voice.java | 1 + .../ts3j/protocol/packet/PacketBodyType.java | 2 +- .../transformation/PacketTransformation.java | 4 +- .../client/AbstractTeamspeakClientSocket.java | 318 +++++++++++++++--- .../manevolent/ts3j/PacketCounterTest.java | 32 ++ .../manevolent/ts3j/ServerConnectionTest.java | 17 - 11 files changed, 317 insertions(+), 230 deletions(-) delete mode 100644 src/main/java/com/github/manevolent/ts3j/protocol/RingQueue.java create mode 100644 src/test/java/com/github/manevolent/ts3j/PacketCounterTest.java diff --git a/LICENSE b/LICENSE index 8b28719..0ccad99 100644 --- a/LICENSE +++ b/LICENSE @@ -127,7 +127,7 @@ Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system +(kernel, generationWindowSize system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. diff --git a/src/main/java/Punisher/NaCl/Internal/Sha512.java b/src/main/java/Punisher/NaCl/Internal/Sha512.java index de6d40f..18a3ac6 100644 --- a/src/main/java/Punisher/NaCl/Internal/Sha512.java +++ b/src/main/java/Punisher/NaCl/Internal/Sha512.java @@ -29,9 +29,9 @@ public class Sha512 if (offset < 0) throw new Exception("offset"); if (count < 0) - throw new Exception("count"); + throw new Exception("put"); if (data.length - offset < count) - throw new Exception("Requires offset + count <= data.Length"); + throw new Exception("Requires offset + put <= data.Length"); Array16 block = new Array16(); int bytesInBuffer = (int)_totalBytes & (BlockSize - 1); diff --git a/src/main/java/com/github/manevolent/ts3j/api/FileTransfer.java b/src/main/java/com/github/manevolent/ts3j/api/FileTransfer.java index 58f48a1..198d421 100644 --- a/src/main/java/com/github/manevolent/ts3j/api/FileTransfer.java +++ b/src/main/java/com/github/manevolent/ts3j/api/FileTransfer.java @@ -155,7 +155,7 @@ public class FileTransfer extends Wrapper { /** * Returns how many milliseconds have elapsed since the file transfer was first announced to - * the server. This value will count up even if the file transfer has not yet been started, + * the server. This value will put up even if the file transfer has not yet been started, * but will stop if the transfer has been paused or is complete. * * @return the transfer runtime in milliseconds diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/RingQueue.java b/src/main/java/com/github/manevolent/ts3j/protocol/RingQueue.java deleted file mode 100644 index 4b669e4..0000000 --- a/src/main/java/com/github/manevolent/ts3j/protocol/RingQueue.java +++ /dev/null @@ -1,164 +0,0 @@ -package com.github.manevolent.ts3j.protocol; - -public class RingQueue { - private static final int - NOT_SET = 0x00, - SET = 0x01, - OUT_OF_WINDOW = 0x00, - IN_WINDOW = 0x10; - - private boolean[] ringBufferSet; - - private final int initialBufferSize = 16; - private int currentStart, currentLength; - - private int mappedBaseOffset; - private final int mappedMod; - private int generation; - - private final int maxBufferSize; - - public RingQueue(int maxBufferSize, int mod) { - if (maxBufferSize == -1) - { - this.maxBufferSize = (mod / 2) - 1; - } - else - { - if (maxBufferSize >= mod) - throw new IllegalArgumentException("Modulo must be bigger than buffer size"); - - this.maxBufferSize = maxBufferSize; - } - - int setBufferSize = Math.min(initialBufferSize, maxBufferSize); - ringBufferSet = new boolean[setBufferSize]; - mappedMod = mod; - - clear(); - } - - private void bufferSet(int index) { - bufferExtend(index); - int local = indexToLocal(index); - int newLength = local - currentStart + 1 + (local >= currentStart ? 0 : ringBufferSet.length); - currentLength = Math.max(currentLength, newLength); - ringBufferSet[local] = true; - } - - private boolean stateGet(int index) { - bufferExtend(index); - int local = indexToLocal(index); - return ringBufferSet[local]; - } - - private void bufferPop() { - ringBufferSet[currentStart] = false; - - currentStart = (currentStart + 1) % ringBufferSet.length; - currentLength --; - } - - private void bufferExtend(int index) { - if (index < ringBufferSet.length) - return; - if (index >= maxBufferSize) - throw new IllegalArgumentException("the index does not fit into the maximal buffer size"); - int extendto = index < ringBufferSet.length * 2 - ? Math.min(ringBufferSet.length * 2, maxBufferSize) - : Math.min(index + ringBufferSet.length, maxBufferSize); - - Object[] extRingBuffer = new Object[extendto]; - boolean[] extRingBufferSet = new boolean[extendto]; - - System.arraycopy(ringBufferSet, currentStart, extRingBufferSet, 0, ringBufferSet.length - currentStart); - System.arraycopy(ringBufferSet, 0, extRingBufferSet, ringBufferSet.length - currentStart, currentStart); - - currentStart = 0; - ringBufferSet = extRingBufferSet; - } - - private int indexToLocal(int index) { - return (currentStart + index) % ringBufferSet.length; - } - - public void clear() - { - currentStart = 0; - currentLength = 0; - for (int i = 0; i < ringBufferSet.length; i ++) ringBufferSet[i] = false; - mappedBaseOffset = 0; - generation = 0; - } - - public void set(int mappedValue) { - int index = mappedToIndex(mappedValue); - - if (isSetIndex(index) != ItemSetStatus.IN_WINDOW_NOT_SET) - throw new IllegalStateException("object cannot be set"); - - bufferSet(index); - } - - private int mappedToIndex(int mappedValue) { - if (mappedValue >= mappedMod) - throw new ArrayIndexOutOfBoundsException("mappedValue"); - - if (isNextGen(mappedValue)) { - return (mappedValue + mappedMod) - mappedBaseOffset; - } else { - return mappedValue - mappedBaseOffset; - } - } - - public boolean isNextGen(int mappedValue) { - return mappedBaseOffset > mappedMod - maxBufferSize && mappedValue < maxBufferSize; - } - - public int getGeneration(int mappedValue) { - return generation + (isNextGen(mappedValue) ? 1 : 0); - } - - private ItemSetStatus isSetIndex(int index) { - if (index < 0) - return ItemSetStatus.OUT_OF_WINDOW_SET; - else if (index > currentLength && index < maxBufferSize) - return ItemSetStatus.IN_WINDOW_NOT_SET; - else if (index >= maxBufferSize) - return ItemSetStatus.OUT_OF_WINDOW_NOT_SET; - - return stateGet(index) ? ItemSetStatus.IN_WINDOW_SET : ItemSetStatus.IN_WINDOW_NOT_SET; - } - - public int getCount() { - return currentLength; - } - - public boolean isSet(int packetId) { - return isSetIndex(packetId) == ItemSetStatus.IN_WINDOW_SET; - } - - public enum ItemSetStatus { - OUT_OF_WINDOW_NOT_SET(0x00), - OUT_OF_WINDOW_SET(0x01), - IN_WINDOW_NOT_SET(0x10), - IN_WINDOW_SET(0x11); - - private final int index; - - ItemSetStatus(int index) { - this.index = index; - } - - public int getIndex() { - return index; - } - - public static ItemSetStatus fromId(int index) { - for (ItemSetStatus 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/header/PacketHeader.java b/src/main/java/com/github/manevolent/ts3j/protocol/header/PacketHeader.java index 77b04bf..b677b5f 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/header/PacketHeader.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/header/PacketHeader.java @@ -107,6 +107,7 @@ public abstract class PacketHeader { } public void setGeneration(int packetGeneration) { + if (packetGeneration < 0) throw new IllegalArgumentException("packetGeneration"); this.packetGeneration = packetGeneration; } } 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 12452d9..173241f 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,7 @@ 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.util.Ts3Debugging; import java.nio.ByteBuffer; diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBodyType.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBodyType.java index 96c9624..dec3c68 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBodyType.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBodyType.java @@ -1,7 +1,7 @@ package com.github.manevolent.ts3j.protocol.packet; public enum PacketBodyType { - VOICE(0x0, PacketBody0Voice.class, null, false, false, false, false, false), + VOICE(0x0, PacketBody0Voice.class, null, true, false, false, false, false), VOICE_WHISPER(0x1, PacketBody1VoiceWhisper.class, null, 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), diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/transformation/PacketTransformation.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/transformation/PacketTransformation.java index c0f6161..a1f7da2 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/transformation/PacketTransformation.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/transformation/PacketTransformation.java @@ -56,8 +56,8 @@ public class PacketTransformation { // ivStruct[0] = ivStruct[0] xor ((PId & 0xFF00) >> 8) // ivStruct[1] = ivStruct[1] xor ((PId & 0x00FF) >> 0) - key[0] = (byte) (key[0] ^ ((byte) (header.getPacketId() & 0xFF) >> 8) & 0xFF); - key[1] = (byte) (key[1] ^ ((byte) (header.getPacketId() & 0xFF) >> 0) & 0xFF); + key[0] = (byte) (key[0] ^ (header.getPacketId() >> 8) & 0xFF); + key[1] = (byte) (key[1] ^ (header.getPacketId() >> 0) & 0xFF); return new Pair<>(key, nonce); } 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 ea76bae..8403c22 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 @@ -4,7 +4,6 @@ import com.github.manevolent.ts3j.command.*; import com.github.manevolent.ts3j.identity.Identity; import com.github.manevolent.ts3j.protocol.NetworkPacket; import com.github.manevolent.ts3j.protocol.Packet; -import com.github.manevolent.ts3j.protocol.RingQueue; import com.github.manevolent.ts3j.protocol.SocketRole; import com.github.manevolent.ts3j.protocol.client.ClientConnectionState; import com.github.manevolent.ts3j.protocol.header.ClientPacketHeader; @@ -49,8 +48,8 @@ public abstract class AbstractTeamspeakClientSocket private final Map sendQueue = new ConcurrentHashMap<>(); private final Map sendQueueLow = new ConcurrentHashMap<>(); - private final Map localSendCounter = new ConcurrentHashMap<>(); - private final Map remoteQueue = new ConcurrentHashMap<>(); + private final Map localSendCounter = new ConcurrentHashMap<>(); + private final Map remoteSendCounter = new ConcurrentHashMap<>(); private CommandProcessor commandProcessor; @@ -89,12 +88,12 @@ public abstract class AbstractTeamspeakClientSocket // Initialize counters for (PacketBodyType bodyType : PacketBodyType.values()) { if (bodyType == PacketBodyType.INIT1) { - localSendCounter.put(bodyType, new PacketCounterZero()); + localSendCounter.put(bodyType, new LocalCounterZero()); + remoteSendCounter.put(bodyType, new RemoteCounterZero()); } else { - localSendCounter.put(bodyType, new PacketCounterFull(true)); + localSendCounter.put(bodyType, new LocalCounterFull(65536, true)); + remoteSendCounter.put(bodyType, new RemoteCounterFull(65536, 100)); } - - remoteQueue.put(bodyType, new RingQueue(100, 65536)); } } @@ -216,7 +215,7 @@ public abstract class AbstractTeamspeakClientSocket case ACK: counter = new Pair<>( ((PacketBody6Ack)packet.getBody()).getPacketId(), - remoteQueue.get(PacketBodyType.ACK).getGeneration( + remoteSendCounter.get(PacketBodyType.ACK).getGeneration( ((PacketBody6Ack) packet.getBody()).getPacketId() ) ); @@ -224,7 +223,7 @@ public abstract class AbstractTeamspeakClientSocket case ACK_LOW: counter = new Pair<>( ((PacketBody7AckLow)packet.getBody()).getPacketId(), - remoteQueue.get(PacketBodyType.ACK_LOW).getGeneration( + remoteSendCounter.get(PacketBodyType.ACK_LOW).getGeneration( ((PacketBody7AckLow) packet.getBody()).getPacketId() ) ); @@ -232,7 +231,7 @@ public abstract class AbstractTeamspeakClientSocket case PONG: counter = new Pair<>( ((PacketBody5Pong)packet.getBody()).getPacketId(), - remoteQueue.get(PacketBodyType.PONG).getGeneration( + remoteSendCounter.get(PacketBodyType.PONG).getGeneration( ((PacketBody5Pong) packet.getBody()).getPacketId() ) ); @@ -414,11 +413,6 @@ public abstract class AbstractTeamspeakClientSocket private Packet readPacketIntl(NetworkPacket networkPacket) throws IOException { if (networkPacket.getHeader() == null) throw new NullPointerException("header"); - RingQueue ring = remoteQueue.get(networkPacket.getHeader().getType()); - int currentGeneration = ring == null ? 0 : ring.getGeneration(networkPacket.getHeader().getPacketId()); - - networkPacket.getHeader().setGeneration(currentGeneration); - Packet packet = new Packet(networkPacket.getHeader().getRole()); packet.setHeader(networkPacket.getHeader()); @@ -432,8 +426,7 @@ public abstract class AbstractTeamspeakClientSocket "packet is fragment, but not splittable" ); - boolean encrypted = !networkPacket.getHeader().getPacketFlag(HeaderFlag.UNENCRYPTED) && - networkPacket.getHeader().getType().canEncrypt(); + boolean encrypted = !networkPacket.getHeader().getPacketFlag(HeaderFlag.UNENCRYPTED); if (networkPacket.getHeader().getType().mustEncrypt() && !encrypted) throw new IllegalArgumentException( @@ -446,7 +439,7 @@ public abstract class AbstractTeamspeakClientSocket // Decrypt if (encrypted) { Ts3Debugging.debug("[PROTOCOL] DECRYPT " + networkPacket.getHeader().getType().name() - + " generation=" + currentGeneration); + + " generation=" + networkPacket.getHeader().getGeneration()); try { packetBuffer = ByteBuffer.wrap( @@ -521,6 +514,15 @@ public abstract class AbstractTeamspeakClientSocket continue; } + // Get packet generation + RemoteCounter counter = remoteSendCounter.get(networkPacket.getHeader().getType()); + int generation = 0; + if (counter != null) { + generation = counter.getGeneration(networkPacket.getHeader().getPacketId()); + } + networkPacket.getHeader().setGeneration(generation); + + // Read packet (decrypt, decompress, etc) Packet packet = readPacketIntl(networkPacket); // Find if the packet must be acknowledged @@ -561,16 +563,14 @@ public abstract class AbstractTeamspeakClientSocket if (packet.getHeader().getType().canResend() && packet.getHeader().getType() != PacketBodyType.INIT1) { // Find if we already acknowledged this packet - RingQueue ring = remoteQueue.get(packet.getHeader().getType()); - if (ring.isSet(packet.getHeader().getPacketId())) { + if (!counter.put(packet.getHeader().getPacketId())) { Ts3Debugging.debug("[PROTOCOL] Ignoring handling resent packet: " + packet + " " + packet.getHeader().getType().name() + " id=" + - packet.getHeader().getPacketId()); + packet.getHeader().getPacketId() + " generation=" + + packet.getHeader().getGeneration()); continue; // Drop the packet - } else { - ring.set(packet.getHeader().getPacketId()); } } @@ -809,19 +809,249 @@ public abstract class AbstractTeamspeakClientSocket } } - private interface PacketCounter { - Pair next(); - int getPacketId(); - boolean setPacketId(int packetId); - int getGenerationId(); - void setGenerationId(int i); + public interface RemoteCounter { - default Pair current() { - return new Pair<>(getPacketId(), getGenerationId()); + /** + * Gets the generation for the specified packet ID. + * + * @param packetId packet ID + * @return expected generation + */ + int getGeneration(int packetId); + + /** + * Gets the current generation of the counter. + * + * @return current generation + */ + int getCurrentGeneration(); + + /** + * Counts the given packet by its packet ID and stores it in history. + * + * @param packetId packet to put + * @return true if the packet was placed, false otherwise + */ + boolean put(int packetId); + + } + + public static class RemoteCounterZero implements RemoteCounter { + + @Override + public int getGeneration(int packetId) { + return 0; + } + + @Override + public int getCurrentGeneration() { + return 0; + } + + @Override + public boolean put(int packetId) { + return true; + } + + } + + public static class RemoteCounterFull implements RemoteCounter { + private final Integer[] buffer; // history buffer + private final int bufferSize; // size of the packet history buffer, used for remembering received packets + private final int generationSize; // size of the physical window, typically 65536 for a 16-bit/ushort window field + + private Pair + bufferStart = new Pair<>(0,0), // position of the start of the buffer in the window + bufferEnd; // position of the end of the buffer in the window + + /** + * Constructs a new full counter + * + * @param windowSize size of the packet history buffer, used for remembering received packets + * @param generationSize length of any given generation, typically 65536 for a 16-bit/ushort window field + */ + public RemoteCounterFull(int generationSize, int windowSize) { // most often will be 65536, 100 + if (windowSize >= generationSize) throw new IllegalArgumentException("windowSize > generationSize"); + + this.buffer = new Integer[windowSize]; + this.bufferSize = windowSize; + this.generationSize = generationSize; + + this.bufferEnd = new Pair<>(0, windowSize - 1); + } + + @Override + public int getGeneration(int packetId) { + packetId %= generationSize; + + // find if right of the start and within the start's bounds + if (packetId >= bufferStart.getValue() + && packetId < bufferStart.getValue() + bufferSize + && packetId < generationSize) { + return bufferStart.getKey(); + } + + // find if left of end and within the end's bounds + if (packetId <= bufferEnd.getValue() + && packetId > 0 + && packetId > bufferEnd.getValue() - bufferSize + && packetId < generationSize) { + return bufferEnd.getKey(); + } + + // find if outside of buffer to the right + if (packetId > bufferEnd.getValue()) + return bufferEnd.getKey(); // ahead in current generation + + // find if outside of buffer to the left + if (packetId < bufferStart.getValue()) + return bufferEnd.getKey() + 1; // modulated wrap, ahead of current generation + + // should never arrive here. + throw new IllegalStateException(); + } + + @Override + public int getCurrentGeneration() { + return bufferEnd.getKey(); + } + + @Override + public boolean put(int packetId) { + // find if right of the start and within the start's bounds + if (packetId >= bufferStart.getValue() + && packetId < bufferStart.getValue() + bufferSize + && packetId < generationSize) { + return putRelative(packetId - bufferStart.getValue(), bufferStart.getKey()); + } + + // find if left of end and within the end's bounds + if (packetId <= bufferEnd.getValue() + && packetId >= 0 + && packetId > bufferEnd.getValue() - bufferSize + && packetId < generationSize) { + return putRelative(bufferSize - (bufferEnd.getValue() - packetId) - 1, bufferEnd.getKey()); + } + + // distance the start position must move right by + int amountMoved = 0; + + // find if outside of buffer to the right + if (packetId > bufferEnd.getValue()) { // slide buffer forward uniformly + amountMoved = packetId - bufferEnd.getValue(); + } + + // find if outside of buffer to the left + if (packetId < bufferStart.getValue()) { // start wrapping buffer or continue to do so + amountMoved = + (generationSize - bufferStart.getValue()) + + packetId + 1; + + amountMoved -= bufferSize; + } + + if (amountMoved > 0) { // should always be true + int toMove = Math.max(0, bufferSize - amountMoved); + Ts3Debugging.debug(Arrays.toString(buffer)); + if (toMove > 0 && toMove < bufferSize) + System.arraycopy(buffer, amountMoved, buffer, 0, toMove); + + int toNullify = Math.max(0, Math.min(bufferSize, amountMoved)); + for (int i = bufferSize - toNullify; i < bufferSize; i ++) + buffer[i] = null; + + Ts3Debugging.debug(Arrays.toString(buffer)); + int bufferStartGeneration = bufferStart.getKey(); + int bufferStartPosition = bufferStart.getValue(); + + bufferStartPosition += amountMoved; + if (bufferStartPosition >= generationSize) { + bufferStartPosition %= generationSize; + bufferStartGeneration ++; + } + + int bufferEndGeneration; + int bufferEndPosition = (bufferStartPosition + bufferSize - 1) % generationSize; + if (bufferEndPosition < bufferStartPosition) + bufferEndGeneration = bufferStartGeneration + 1; + else + bufferEndGeneration = bufferStartGeneration; + + this.bufferStart = new Pair<>(bufferStartGeneration, bufferStartPosition); + this.bufferEnd = new Pair<>(bufferEndGeneration, bufferEndPosition); + } else + throw new IllegalStateException(); + + // if we had to adjust the buffer size, recursively retry put + return put(packetId); + } + + private boolean putRelative(int index, int generation) { + Integer existing = buffer[index]; + Ts3Debugging.debug("putRelative: " + index); + if (existing == null || existing != generation) { + buffer[index] = generation; + return true; + } else { + return false; + } } } - private class PacketCounterZero implements PacketCounter { + + + public static class RemoteCounterFullModulated extends RemoteCounterFull { + private final int modulationSize; + private final int modulationsPerGeneration; + + /** + * Constructs a new full, modulated counter + * This counter is used to sub-modulate a full packet counter, in smaller increments, while still using + * the same code in the backend to modulate the physical window. + * + * @param generationSize length of any given generation, typically 65536 for a 16-bit/ushort window field + * @param windowSize size of the packet history buffer, used for remembering received packets + * @param modulationSize slots within the generation to extrapolated generations by + */ + public RemoteCounterFullModulated(int generationSize, int windowSize, int modulationSize) { + super(generationSize, windowSize); + + if (generationSize % modulationSize != 0) + throw new IllegalArgumentException("modulationSize invalid"); + + this.modulationSize = modulationSize; + this.modulationsPerGeneration = generationSize / modulationSize; + } + + private int calculateSubGeneration(int packetId) { + return packetId == 0 ? 0 : (int) Math.floor(packetId / modulationSize); + } + + @Override + public int getGeneration(int packetId) { + return (super.getGeneration(packetId) * modulationsPerGeneration) + calculateSubGeneration(packetId); + } + + @Override + public int getCurrentGeneration() { + // We can't exactly do this right now. This will require extra work. + throw new UnsupportedOperationException(); + } + } + + public interface LocalCounter { + Pair next(); + int getPacketId(); + boolean setPacketId(int packetId); + int getGeneration(); + void setGeneration(int i); + + default Pair current() { + return new Pair<>(getPacketId(), getGeneration()); + } + } + + public static class LocalCounterZero implements LocalCounter { @Override public Pair next() { return new Pair<>(0,0); @@ -838,27 +1068,31 @@ public abstract class AbstractTeamspeakClientSocket } @Override - public int getGenerationId() { + public int getGeneration() { return 0; } @Override - public void setGenerationId(int i) { + public void setGeneration(int i) { throw new UnsupportedOperationException(); } } - private class PacketCounterFull implements PacketCounter { + public static class LocalCounterFull implements LocalCounter { private final Object sendLock = new Object(); private final boolean counting; + private final int generationSize; + private int packetId = 0; private int generationId = 0; - public PacketCounterFull(boolean counting) { + public LocalCounterFull(int generationSize, boolean counting) { + this.generationSize = generationSize; this.counting = counting; } - public PacketCounterFull(int start, boolean counting) { + public LocalCounterFull(int generationSize, int start, boolean counting) { + this.generationSize = generationSize; this.counting = counting; setPacketId(start); @@ -873,9 +1107,9 @@ public abstract class AbstractTeamspeakClientSocket synchronized (sendLock) { if (this.packetId == packetId) return false; - if (packetId >= 65536) { + if (packetId >= generationSize) { this.packetId = 0; // wrap around - if (isCounting()) setGenerationId(getGenerationId() + 1); + if (isCounting()) setGeneration(getGeneration() + 1); } else { this.packetId = packetId; } @@ -884,14 +1118,14 @@ public abstract class AbstractTeamspeakClientSocket } } - public int getGenerationId() { + public int getGeneration() { return generationId; } @Override - public void setGenerationId(int i) { + public void setGeneration(int i) { synchronized (sendLock) { - Ts3Debugging.debug("setGenerationId: " + i); + Ts3Debugging.debug("setGeneration: " + i); this.generationId = i; } } diff --git a/src/test/java/com/github/manevolent/ts3j/PacketCounterTest.java b/src/test/java/com/github/manevolent/ts3j/PacketCounterTest.java new file mode 100644 index 0000000..919e07e --- /dev/null +++ b/src/test/java/com/github/manevolent/ts3j/PacketCounterTest.java @@ -0,0 +1,32 @@ +package com.github.manevolent.ts3j; + +import com.github.manevolent.ts3j.protocol.socket.client.AbstractTeamspeakClientSocket; +import com.github.manevolent.ts3j.util.Ts3Debugging; +import javafx.util.Pair; +import junit.framework.TestCase; + +public class PacketCounterTest extends TestCase { + public static void main(String[] args) throws Exception { + new PacketCounterTest().testParser(); + } + + public void testParser() throws Exception { + Ts3Debugging.setEnabled(true); + + AbstractTeamspeakClientSocket.LocalCounter localCounter = + new AbstractTeamspeakClientSocket.LocalCounterFull(65536, 0, true); + + AbstractTeamspeakClientSocket.RemoteCounter remoteCounter = + new AbstractTeamspeakClientSocket.RemoteCounterFull(65536, 100); + + for (int i = 0; i < 65580 / 5; i ++) { + Pair packet = null; + for (int n = 0; n < 5; n ++) + packet = localCounter.next(); + + assertEquals(true, remoteCounter.put(packet.getKey())); + assertEquals(packet.getValue().intValue(), remoteCounter.getCurrentGeneration()); + } + } + +} \ No newline at end of file diff --git a/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java b/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java index b9d584b..ee02934 100644 --- a/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java +++ b/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java @@ -41,23 +41,6 @@ public class ServerConnectionTest { 10000L ); - client.setMicrophone(new Microphone() { - @Override - public boolean isReady() { - return true; - } - - @Override - public CodecType getCodec() { - return CodecType.OPUS_MUSIC; - } - - @Override - public byte[] provide() { - return new byte[20]; - } - }); - assertEquals(client.getState(), ClientConnectionState.CONNECTED); while (true) {