Skip to content

Instantly share code, notes, and snippets.

@Mr00Anderson
Created November 24, 2020 13:51
Show Gist options
  • Save Mr00Anderson/2a44fc7ef5c06573704bd4f991cc111a to your computer and use it in GitHub Desktop.
Save Mr00Anderson/2a44fc7ef5c06573704bd4f991cc111a to your computer and use it in GitHub Desktop.
package app.virtualhex.network;
import app.virtualhex.network.internal.api.SessionLookup;
import com.virtual_hex.networking.datatypes.VLQDecoder;
import com.virtual_hex.types.BitSlotInt;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.zip.Adler32;
/**
* The client only sends and receives from this pipeline, while the servers has many received on
* its pipe line and will have to have special handling
*/
public class StdDatagramPacketDecoder extends SimpleChannelInboundHandler<DatagramPacket> {
/**
* Simply a logger reference
*/
private static final Logger L = LoggerFactory.getLogger(StdDatagramPacketDecoder.class);
private SessionLookup<Session> sessionLookup;
// private SessionLookup<> pendingSessionLookup;
private ConnectionData connectionData;
public StdDatagramPacketDecoder() {
super(false);
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
this.sessionLookup = ctx.channel().attr(AttributeKeys.SESSION_LOOKUP).get();
// this.connectionData = ctx.channel().attr(AttributeKeys.CONNECTION_NAME).get().connectionNameConnectionData;
super.channelRegistered(ctx);
}
@Override
protected void channelRead0(final ChannelHandlerContext ctx, final DatagramPacket msg) throws Exception {
// Check Addresses TODO Needs to be super fast for fast fail
InetSocketAddress sender = msg.sender();
String hostString = sender.getHostString();
int port = sender.getPort();
L.debug("{} packet received by {} on {} port", this, hostString, port);
// Get the data
ByteBuf in = msg.content();
int readableBytes = in.readableBytes();
// Read CRC32
long crc32Packet = in.readLong();
// Lets check and reply for reliability ASAP
int reliabilityOffset = in.readByte();
boolean reliable = BitSlotInt.isBitSet(reliabilityOffset, BitSlotInt._8);
ByteBuffer payloadBuffer = in.nioBuffer(8, in.readableBytes());
// Lets get the session number and look our session up
int sessionNumber = VLQDecoder.decodeInteger(in);
Session session = sessionLookup.get(sessionNumber);
if(session == null){
L.debug("{} received a packet for a session with number {} but no session was found.", this, sessionNumber);
msg.release();
return;
}
SessionToken sessionToken = session.sessionToken;
// checksum time
Adler32 crc32 = new Adler32();// Todo object creation potential
boolean saltFirst = sessionToken.dataIntegrity.isSaltFirst();
if(saltFirst){
crc32.update(sessionToken.dataIntegrity.salt, 0 , sessionToken.dataIntegrity.salt.length);
crc32.update(payloadBuffer);
} else {
crc32.update(payloadBuffer);
crc32.update(sessionToken.dataIntegrity.salt,0 , sessionToken.dataIntegrity.salt.length);
}
long crc32Value = crc32.getValue();
boolean dataIntactUnmodified = crc32Value == crc32Packet;
// Lets check in the sequence number so we can track packet loss
int sequenceNumber = in.readUnsignedByte();
// Lets check and reply for reliability ASAP
if(!dataIntactUnmodified){
session.recordBadChecksum(sequenceNumber);
msg.release();
return;
}
boolean alreadyExistedRecently = session.receivedSequenceNumber(sequenceNumber);
if(alreadyExistedRecently){
session.recordDuplicatePacket(sequenceNumber);
msg.release();
return;
}
if(reliable){
session.writeAcknowledgement(sequenceNumber);
}
int bytesLeft = in.readableBytes();
while (in.isReadable() && bytesLeft < in.readableBytes()){// Stop loop in case packet does not read
// We will always assume UDP is a multi-packet
byte packetId = in.readByte();
Packet packet = session.packetManager.getPacketById(packetId);
packet.read(msg, in);
}
// Record Stats
session.updateUdpReadBytes(readableBytes);
msg.release();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//TODO Resource clean up
L.warn("{} - Channel exception", "todo parent", cause);
cause.printStackTrace();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment