Removing previous iteration

This commit is contained in:
Collin Smith 2020-06-23 18:40:17 -07:00
parent b19d61b8f2
commit 3a79adcaae
2 changed files with 224 additions and 219 deletions

View File

@ -3,11 +3,14 @@ package com.riiablo.net.reliable;
import io.netty.buffer.ByteBuf;
import org.apache.commons.lang3.builder.ToStringBuilder;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.utils.Pool;
import com.badlogic.gdx.utils.Pools;
public abstract class Packet {
public class Packet {
private static final String TAG = "Packet";
public static final int USHORT_MAX_VALUE = 0xFFFF;
static final int MAX_PACKET_HEADER_SIZE = 10;
static final int FRAGMENT_HEADER_SIZE = 6;
@ -27,11 +30,24 @@ public abstract class Packet {
static final int ACK_BYTE2_MASK = 0x00FF0000;
static final int ACK_BYTE3_MASK = 0xFF000000;
public static boolean isSinglePacket(int prefixByte) {
return (prefixByte & TYPE_MASK) == SINGLE;
private static final int FLAGS_OFFSET = 0;
private static final int CHANNEL_OFFSET = 1;
public static byte getFlags(ByteBuf bb) {
return bb.getByte(FLAGS_OFFSET);
}
static int getAckBitByteFlags(int ackBits, int prefixByte) {
public static int getChannelId(ByteBuf bb) {
return bb.getUnsignedByte(CHANNEL_OFFSET);
}
private Packet() {}
public static boolean isFragmented(byte flags) {
return (flags & TYPE_MASK) == FRAGMENTED;
}
private static int getAckBitsFlags(int ackBits, int prefixByte) {
if ((ackBits & ACK_BYTE0_MASK) != ACK_BYTE0_MASK) prefixByte |= ACK_BYTE0;
if ((ackBits & ACK_BYTE1_MASK) != ACK_BYTE1_MASK) prefixByte |= ACK_BYTE1;
if ((ackBits & ACK_BYTE2_MASK) != ACK_BYTE2_MASK) prefixByte |= ACK_BYTE2;
@ -39,54 +55,202 @@ public abstract class Packet {
return prefixByte;
}
static void writeAckBitByteFlags(ByteBuf bb, int ackBits) {
private static void writeAckBitsFlags(ByteBuf bb, int ackBits) {
if ((ackBits & ACK_BYTE0_MASK) != ACK_BYTE0_MASK) bb.writeByte(ackBits);
if ((ackBits & ACK_BYTE1_MASK) != ACK_BYTE1_MASK) bb.writeByte(ackBits >> 8);
if ((ackBits & ACK_BYTE2_MASK) != ACK_BYTE2_MASK) bb.writeByte(ackBits >> 16);
if ((ackBits & ACK_BYTE3_MASK) != ACK_BYTE3_MASK) bb.writeByte(ackBits >> 24);
}
static void logError(String format, Object... args) {
logError(String.format(format, args));
public static int writeAck(ByteBuf bb, int channelId, int ack, int ackBits) {
int startIndex = bb.writerIndex();
final int flags = getAckBitsFlags(ackBits, ACK);
bb.writeByte(flags);
bb.writeByte(channelId);
bb.writeShortLE(ack);
writeAckBitsFlags(bb, ackBits);
return bb.writerIndex() - startIndex;
}
static void logError(String format) {
Gdx.app.error(TAG, format);
}
public static int writePacketHeader(ByteBuf bb, int channelId, int sequence, int ack, int ackBits) {
int startIndex = bb.writerIndex();
int flags = getAckBitsFlags(ackBits, SINGLE);
// Used for debugging purposes
@Deprecated
public static Packet readHeader(ReliableConfig config, ByteBuf bb) {
if (bb.readableBytes() < 1) {
logError("buffer too small for packet header");
return null;
}
int sequenceDiff = sequence - ack;
if (sequenceDiff < 0) sequenceDiff += USHORT_MAX_VALUE;
if (sequenceDiff <= 0xFF) flags |= SEQ_DIFF;
byte prefixByte = bb.readByte();
Packet packet;
if (isSinglePacket(prefixByte)) {
packet = new SinglePacket();
bb.writeByte(flags);
bb.writeByte(channelId);
bb.writeShortLE(sequence);
if (sequenceDiff <= 0xFF) {
bb.writeByte(sequenceDiff);
} else {
packet = new FragmentedPacket();
bb.writeShortLE(ack);
}
packet.readHeader(config, bb, prefixByte);
return packet;
writeAckBitsFlags(bb, ackBits);
return bb.writerIndex() - startIndex;
}
abstract int readHeader(ReliableConfig config, ByteBuf bb, final byte prefixByte);
public static int readPacketHeader(ReliableConfiguration config, ByteBuf bb, HeaderData out) {
assert out != null;
int startIndex = bb.readerIndex();
if (bb.readableBytes() < 4) {
Log.error(TAG, "buffer too small for packet header (1)");
return out.headerSize = -1;
}
static class SinglePacket extends Packet {
public int channelId;
public int sequence;
public int ack;
public int ackBits;
final byte flags = out.flags = bb.readByte();
if ((flags & TYPE_MASK) != SINGLE) {
Log.error(TAG, "packet header not flagged as single packet");
return out.headerSize = -1;
}
int headerSize;
out.channelId = bb.readUnsignedByte();
out.sequence = (flags & ACK) == ACK ? 0 : bb.readUnsignedShortLE(); // ACK doesn't have sequence
if ((flags & SEQ_DIFF) == SEQ_DIFF) {
if (bb.readableBytes() < 1) {
Log.error(TAG, "buffer too small for packet header (2)");
return out.headerSize = -1;
}
int sequenceDiff = bb.readUnsignedByte();
out.ack = (out.sequence - sequenceDiff) & USHORT_MAX_VALUE;
} else {
if (bb.readableBytes() < 2) {
Log.error(TAG, "buffer too small for packet header (3)");
return out.headerSize = -1;
}
out.ack = bb.readUnsignedShortLE();
}
int expectedBytes = 0;
for (int i = ACK_BYTE0; i <= ACK_BYTE3; i <<= 1) {
if ((flags & i) == i) expectedBytes++;
}
if (bb.readableBytes() < expectedBytes) {
Log.error(TAG, "buffer too small for packet header (4)");
return out.headerSize = -1;
}
int ackBits = 0xFFFFFFFF;
if ((flags & ACK_BYTE0) == ACK_BYTE0) {
ackBits &= ~ACK_BYTE0_MASK;
ackBits |= bb.readByte();
}
if ((flags & ACK_BYTE1) == ACK_BYTE1) {
ackBits &= ~ACK_BYTE1_MASK;
ackBits |= (bb.readByte() << 8);
}
if ((flags & ACK_BYTE2) == ACK_BYTE2) {
ackBits &= ~ACK_BYTE2_MASK;
ackBits |= (bb.readByte() << 16);
}
if ((flags & ACK_BYTE3) == ACK_BYTE3) {
ackBits &= ~ACK_BYTE3_MASK;
ackBits |= (bb.readByte() << 24);
}
out.ackBits = ackBits;
return out.headerSize = bb.readerIndex() - startIndex;
}
public static int readFragmentHeader(ReliableConfiguration config, ByteBuf bb, FragmentedHeaderData out) {
assert out != null;
int startIndex = bb.readerIndex();
if (bb.readableBytes() < FRAGMENT_HEADER_SIZE) {
Log.error(TAG, "buffer too small for fragment header");
return out.headerSize = -1;
}
final byte flags = out.flags = bb.readByte();
if ((flags & TYPE_MASK) != FRAGMENTED) {
Log.error(TAG, "packet header not flagged as fragmented packet");
return out.headerSize = -1;
}
final int channelId = out.channelId = bb.readUnsignedByte();
final int sequence = out.sequence = bb.readUnsignedShortLE();
final int fragmentId = out.fragmentId = bb.readUnsignedByte();
final int numFragments = out.numFragments = bb.readUnsignedByte();
if (numFragments > config.maxFragments) {
Log.error(TAG, "num fragments %d outside of range of max fragments %d", numFragments, config.maxFragments);
return out.headerSize = -1;
}
if (fragmentId >= numFragments) {
Log.error(TAG, "fragment id %d outside of range of num fragments %d", fragmentId, numFragments);
return out.headerSize = -1;
}
if (fragmentId == 0) {
if (bb.readableBytes() < 1) {
Log.error(TAG, "buffer too small for packet header");
return out.headerSize = -1;
}
final HeaderData packetHeader = out.header;
int headerSize = readPacketHeader(config, bb, packetHeader);
if (headerSize < 0) {
Log.error(TAG, "bad packet header in fragment");
return out.headerSize = -1;
}
if (packetHeader.sequence != sequence) {
Log.error(TAG, "bad packet sequence in fragment. expected %d, got %d", sequence, packetHeader.sequence);
return out.headerSize = -1;
}
out.ack = packetHeader.ack;
out.ackBits = packetHeader.ackBits;
} else {
out.ack = 0;
out.ackBits = 0;
}
final int fragmentSize = out.fragmentSize = bb.readableBytes();
if (fragmentSize > config.fragmentSize) {
Log.error(TAG, "fragment bytes %d > fragment size %d", fragmentSize, config.fragmentSize);
return out.headerSize = -1;
}
if (fragmentId != numFragments - 1 && fragmentSize != config.fragmentSize) {
Log.error(TAG, "fragment %d is %d bytes, which is not the expected fragment size %d bytes", fragmentId, fragmentSize, config.fragmentSize);
return out.headerSize = -1;
}
return out.headerSize = bb.readerIndex() - startIndex;
}
private static final Pool<HeaderData> HEADER_DATA_POOL = Pools.get(HeaderData.class);
public static HeaderData obtainData() {
return HEADER_DATA_POOL.obtain();
}
public static class HeaderData {
public byte flags;
public int channelId;
public int sequence;
public int ack;
public int ackBits;
public int headerSize;
public void free() {
HEADER_DATA_POOL.free(this);
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("flags", String.format("%02x", flags))
.append("channelId", channelId)
.append("sequence", sequence)
.append("ack", ack)
@ -94,190 +258,48 @@ public abstract class Packet {
.append("headerSize", headerSize)
.build();
}
@Override
int readHeader(ReliableConfig config, ByteBuf bb, byte prefixByte) {
if ((prefixByte & TYPE_MASK) != SINGLE) {
logError("packet header not flagged as single packet");
return -1;
}
int startIndex = bb.readerIndex();
if (bb.readableBytes() < 3) {
logError("buffer too small for packet header (1)");
return -1;
}
channelId = bb.readUnsignedByte();
// ack packets don't have sequence numbers
sequence = (prefixByte & ACK) == ACK ? 0 : bb.readUnsignedShortLE();
if ((prefixByte & SEQ_DIFF) == SEQ_DIFF) {
if (bb.readableBytes() < 1) {
logError("buffer too small for packet header (2)");
return -1;
}
int sequenceDiff = bb.readUnsignedByte();
ack = (sequence - sequenceDiff) & 0xFFFF;
} else {
if (bb.readableBytes() < 2) {
logError("buffer too small for packet header (3)");
return -1;
}
ack = bb.readUnsignedShortLE();
}
int expectedBytes = 0;
for (int i = ACK_BYTE0; i <= ACK_BYTE3; i <<= 1) {
if ((prefixByte & i) == i) expectedBytes++;
}
if (bb.readableBytes() < expectedBytes) {
logError("buffer too small for packet header (4)");
return -1;
}
ackBits = 0xFFFFFFFF;
if ((prefixByte & ACK_BYTE0) == ACK_BYTE0) {
ackBits &= ~ACK_BYTE0_MASK;
ackBits |= bb.readByte();
}
if ((prefixByte & ACK_BYTE1) == ACK_BYTE1) {
ackBits &= ~ACK_BYTE1_MASK;
ackBits |= (bb.readByte() << 8);
}
if ((prefixByte & ACK_BYTE2) == ACK_BYTE2) {
ackBits &= ~ACK_BYTE2_MASK;
ackBits |= (bb.readByte() << 16);
}
if ((prefixByte & ACK_BYTE3) == ACK_BYTE3) {
ackBits &= ~ACK_BYTE3_MASK;
ackBits |= (bb.readByte() << 24);
}
headerSize = bb.readerIndex() - startIndex + 1; // include prefixByte
return headerSize;
}
static void writeHeader(ByteBuf bb, int channelId, int sequence, int ack, int ackBits) {
int prefixByte = getAckBitByteFlags(ackBits, 0);
int sequenceDiff = sequence - ack;
if (sequenceDiff < 0) sequenceDiff += 0xFFFF;
if (sequenceDiff <= 0xFF) prefixByte |= SEQ_DIFF;
bb.writeByte(prefixByte);
bb.writeByte(channelId);
bb.writeShortLE(sequence);
if (sequenceDiff <= 0xFF) {
bb.writeByte(sequenceDiff);
} else {
bb.writeShortLE(ack);
}
writeAckBitByteFlags(bb, ackBits);
}
static void writeAck(ByteBuf bb, int channelId, int ack, int ackBits) {
int prefixByte = getAckBitByteFlags(ackBits, ACK);
bb.writeByte(prefixByte);
bb.writeByte(channelId);
bb.writeShortLE(ack);
writeAckBitByteFlags(bb, ackBits);
}
}
static class FragmentedPacket extends Packet {
public int channelId;
public int sequence;
public int ack;
public int ackBits;
private static final Pool<FragmentedHeaderData> FRAGMENTED_HEADER_DATA_POOL = Pools.get(FragmentedHeaderData.class);
public int fragmentId;
public int numFragments;
public static FragmentedHeaderData obtainFragmentedData() {
return FRAGMENTED_HEADER_DATA_POOL.obtain();
}
int headerSize;
int fragmentSize;
public static class FragmentedHeaderData {
public byte flags;
public int channelId;
public int sequence;
public int ack;
public int ackBits;
public int headerSize;
public int fragmentId;
public int numFragments;
public int fragmentSize;
public final HeaderData header = new HeaderData();
public void free() {
FRAGMENTED_HEADER_DATA_POOL.free(this);
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("flags", String.format("%02x", flags))
.append("channelId", channelId)
.append("sequence", sequence)
.append("ack", ack)
.append("ackBits", String.format("%08x", ackBits))
.append("headerSize", headerSize)
.append("fragmentId", fragmentId)
.append("numFragments", numFragments)
.append("fragmentSize", fragmentSize)
.append("header", header)
.build();
}
@Override
int readHeader(ReliableConfig config, ByteBuf bb, final byte prefixByte) {
if ((prefixByte & TYPE_MASK) != FRAGMENTED) {
logError("packet header not flagged as fragmented packet");
return -1;
}
int startIndex = bb.readerIndex();
if (bb.readableBytes() < FRAGMENT_HEADER_SIZE) {
logError("buffer too small for fragment header");
return -1;
}
channelId = bb.readUnsignedByte();
sequence = bb.readUnsignedShortLE();
fragmentId = bb.readUnsignedByte();
numFragments = bb.readUnsignedByte() + 1;
if (numFragments > config.maxFragments) {
logError("num fragments %d outside of range of max fragments %d", numFragments, config.maxFragments);
return -1;
}
if (fragmentId >= numFragments) {
logError("fragment id %d outside of range of num fragments %d", fragmentId, numFragments);
return -1;
}
if (fragmentId == 0) {
if (bb.readableBytes() < 1) {
logError("buffer too small for packet header");
return -1;
}
SinglePacket packetHeader = new SinglePacket();
int packetHeaderSize = packetHeader.readHeader(config, bb, bb.readByte());
if (packetHeaderSize < 0) {
logError("bad packet header in fragment");
return -1;
}
if (packetHeader.sequence != sequence) {
logError("bad packet sequence in fragment. expected %d, got %d", sequence, packetHeader.sequence);
return -1;
}
ack = packetHeader.ack;
ackBits = packetHeader.ackBits;
fragmentSize = bb.readableBytes();
} else {
ack = 0;
ackBits = 0;
fragmentSize = bb.readableBytes();
}
if (fragmentSize > config.fragmentSize) {
logError("fragment bytes %d > fragment size %d", fragmentSize, config.fragmentSize);
return -1;
}
if (fragmentId != numFragments - 1 && fragmentSize != config.fragmentSize) {
logError("fragment %d is %d bytes, which is not the expected fragment size %d bytes", fragmentId, fragmentSize, config.fragmentSize);
return -1;
}
headerSize = bb.readerIndex() - startIndex + 1; // include prefixByte
return headerSize;
}
}
}

View File

@ -1,17 +0,0 @@
package com.riiablo.net.reliable;
public class ReliableConfig {
public static final ReliableConfig DEFAULT_CONFIG = new ReliableConfig();
public int maxPacketSize = 16384;
public int fragmentThreshold = 1024;
public int maxFragments = 16;
public int fragmentSize = 1024;
public int sentPacketBufferSize = 256;
public int receivedPacketBufferSize = 256;
public int fragmentReassemblyBufferSize = 64;
public float rttSmoothingFactor = 0.25f;
public float packetLossSmoothingFactor = 0.1f;
public float bandwidthSmoothingFactor = 0.1f;
public int packetHeaderSize = 28;
}