using NetcodeIO.NET.Utils;
using NetcodeIO.NET.Utils.IO;
using NetcodeIO.NET.Internal;
namespace NetcodeIO.NET
{
///
/// Packet type code
///
internal enum NetcodePacketType
{
///
/// Connection request (sent from client to server)
///
ConnectionRequest = 0,
///
/// Connection denied (sent from server to client)
///
ConnectionDenied = 1,
///
/// Connection challenge (sent from server to client)
///
ConnectionChallenge = 2,
///
/// Challenge response (sent from client to server)
///
ChallengeResponse = 3,
///
/// Connection keep-alive (sent by both client and server)
///
ConnectionKeepAlive = 4,
///
/// Connection payload (sent by both client and server)
///
ConnectionPayload = 5,
///
/// Connection disconnect (sent by both client and server)
///
ConnectionDisconnect = 6,
///
/// Invalid packet
///
InvalidPacket = 7,
}
///
/// Header for a netcode.io packet
///
internal struct NetcodePacketHeader
{
public NetcodePacketType PacketType;
public ulong SequenceNumber;
public byte ReadSequenceByte;
///
/// Create the prefix byte for this packet header
///
public byte GetPrefixByte()
{
if (this.PacketType == NetcodePacketType.ConnectionRequest)
{
return 0;
}
else
{
byte prefixByte = 0;
prefixByte |= (byte)this.PacketType;
// check how many bytes are required to write sequence number
int sequenceBytes = 0;
ulong tempSequenceNumber = this.SequenceNumber;
while (tempSequenceNumber > 0)
{
sequenceBytes++;
tempSequenceNumber >>= 8;
}
if (sequenceBytes == 0)
sequenceBytes = 1;
prefixByte |= (byte)(sequenceBytes << 4);
return prefixByte;
}
}
///
/// Reads a packet from the stream.
/// If packet is a connection request packet, stream read position lies at version info
/// Otherwise, stream read position lies at packet-specific data
///
public void Read(ByteArrayReaderWriter stream)
{
byte prefixByte = stream.ReadByte();
this.ReadSequenceByte = prefixByte;
// read in packet type
int packetTypeNibble = prefixByte & 0x0F;
if (packetTypeNibble >= 7)
{
this.PacketType = NetcodePacketType.InvalidPacket;
return;
}
else
{
this.PacketType = (NetcodePacketType)packetTypeNibble;
}
// read in the sequence number
// high 4 bits of prefix byte are number of bytes used to encode sequence number
if (this.PacketType != NetcodePacketType.ConnectionRequest)
{
int numSequenceBytes = (prefixByte >> 4);
// num sequence bytes is between 1 and 8.
// if it is outside this range, we have an invalid packet
if (numSequenceBytes < 1 || numSequenceBytes > 8)
{
this.PacketType = NetcodePacketType.InvalidPacket;
return;
}
ulong sequenceNumber = 0;
for (int i = 0; i < numSequenceBytes; i++)
{
sequenceNumber |= ((ulong)stream.ReadByte() << (i * 8));
}
this.SequenceNumber = sequenceNumber;
}
}
///
/// Writes packet header to the stream
///
public void Write(ByteArrayReaderWriter stream)
{
if (this.PacketType == NetcodePacketType.ConnectionRequest)
{
stream.Write((byte)0);
}
else
{
byte prefixByte = this.GetPrefixByte();
// now write prefix byte and sequence number bytes
stream.Write(prefixByte);
int sequenceBytes = prefixByte >> 4;
ulong tempSequenceNumber = this.SequenceNumber;
for (int i = 0; i < sequenceBytes; i++)
{
stream.Write((byte)(tempSequenceNumber & 0xFF));
tempSequenceNumber >>= 8;
}
}
}
}
internal struct NetcodeKeepAlivePacket
{
public NetcodePacketHeader Header;
public uint ClientIndex;
public uint MaxSlots;
public bool Read(ByteArrayReaderWriter stream, int length, byte[] key, ulong protocolID)
{
if (length != 8 + Defines.MAC_SIZE)
return false;
byte[] tempBuffer = BufferPool.GetBuffer(length);
try
{
PacketIO.ReadPacketData(Header, stream, length, protocolID, key, tempBuffer);
}
catch
{
BufferPool.ReturnBuffer(tempBuffer);
return false;
}
using (var dataReader = ByteArrayReaderWriter.Get(tempBuffer))
{
ClientIndex = dataReader.ReadUInt32();
MaxSlots = dataReader.ReadUInt32();
}
return true;
}
public void Write(ByteArrayReaderWriter stream)
{
stream.Write(ClientIndex);
stream.Write(MaxSlots);
}
}
internal struct NetcodePublicConnectToken
{
public ulong ProtocolID;
public ulong CreateTimestamp;
public ulong ExpireTimestamp;
public ulong ConnectTokenSequence;
public byte[] PrivateConnectTokenBytes;
public ConnectTokenServerEntry[] ConnectServers;
public byte[] ClientToServerKey;
public byte[] ServerToClientKey;
public uint TimeoutSeconds;
public bool Read(ByteArrayReaderWriter reader)
{
char[] vInfo = new char[13];
reader.ReadASCIICharsIntoBuffer(vInfo, 13);
if (!MiscUtils.MatchChars(vInfo, Defines.NETCODE_VERSION_INFO_STR))
return false;
ProtocolID = reader.ReadUInt64();
CreateTimestamp = reader.ReadUInt64();
ExpireTimestamp = reader.ReadUInt64();
ConnectTokenSequence = reader.ReadUInt64();
PrivateConnectTokenBytes = reader.ReadBytes(Defines.NETCODE_CONNECT_TOKEN_PRIVATE_BYTES);
int numServers = (int)reader.ReadUInt32();
if (numServers < 1 || numServers > Defines.MAX_SERVER_ADDRESSES)
return false;
this.ConnectServers = new ConnectTokenServerEntry[numServers];
for (int i = 0; i < numServers; i++)
{
this.ConnectServers[i] = new ConnectTokenServerEntry();
this.ConnectServers[i].ReadData(reader);
}
ClientToServerKey = reader.ReadBytes(32);
ServerToClientKey = reader.ReadBytes(32);
TimeoutSeconds = reader.ReadUInt32();
return true;
}
public void Write(ByteArrayReaderWriter writer)
{
writer.Write(CreateTimestamp);
writer.Write(ExpireTimestamp);
writer.Write(ConnectTokenSequence);
writer.Write(PrivateConnectTokenBytes);
writer.Write((uint)ConnectServers.Length);
for (int i = 0; i < ConnectServers.Length; i++)
ConnectServers[i].WriteData(writer);
writer.Write(ClientToServerKey);
writer.Write(ServerToClientKey);
writer.Write(TimeoutSeconds);
}
}
internal struct NetcodePrivateConnectToken
{
public ulong ClientID;
public ConnectTokenServerEntry[] ConnectServers;
public byte[] ClientToServerKey;
public byte[] ServerToClientKey;
public byte[] UserData;
public bool Read(byte[] token, byte[] key, ulong protocolID, ulong expiration, ulong sequence)
{
byte[] tokenBuffer = BufferPool.GetBuffer(Defines.NETCODE_CONNECT_TOKEN_PRIVATE_BYTES);
int tokenLen = 0;
try
{
tokenLen = PacketIO.DecryptPrivateConnectToken(token, protocolID, expiration, sequence, key, tokenBuffer);
}
catch
{
BufferPool.ReturnBuffer(tokenBuffer);
return false;
}
try
{
using (var reader = ByteArrayReaderWriter.Get(tokenBuffer))
{
this.ClientID = reader.ReadUInt64();
uint numServerAddresses = reader.ReadUInt32();
if (numServerAddresses == 0 || numServerAddresses > Defines.MAX_SERVER_ADDRESSES)
{
BufferPool.ReturnBuffer(tokenBuffer);
return false;
}
this.ConnectServers = new ConnectTokenServerEntry[numServerAddresses];
for (int i = 0; i < numServerAddresses; i++)
{
this.ConnectServers[i] = new ConnectTokenServerEntry();
this.ConnectServers[i].ReadData(reader);
}
ClientToServerKey = new byte[32];
ServerToClientKey = new byte[32];
UserData = new byte[256];
reader.ReadBytesIntoBuffer(ClientToServerKey, 32);
reader.ReadBytesIntoBuffer(ServerToClientKey, 32);
reader.ReadBytesIntoBuffer(UserData, 256);
}
}
catch
{
BufferPool.ReturnBuffer(tokenBuffer);
return false;
}
return true;
}
public void Write(ByteArrayReaderWriter stream)
{
stream.Write(ClientID);
stream.Write((uint)ConnectServers.Length);
foreach (var server in ConnectServers)
server.WriteData(stream);
stream.Write(ClientToServerKey);
stream.Write(ServerToClientKey);
stream.Write(UserData);
}
}
internal struct NetcodeChallengeToken
{
public ulong ClientID;
public byte[] UserData;
public bool Read(byte[] token, ulong sequenceNum, byte[] key)
{
byte[] tokenBuffer = BufferPool.GetBuffer(300);
int tokenLen = 0;
try
{
tokenLen = PacketIO.DecryptChallengeToken(sequenceNum, token, key, tokenBuffer);
}
catch
{
BufferPool.ReturnBuffer(tokenBuffer);
return false;
}
using (var reader = ByteArrayReaderWriter.Get(tokenBuffer))
{
ClientID = reader.ReadUInt64();
UserData = reader.ReadBytes(256);
}
return true;
}
public void Write(ByteArrayReaderWriter stream)
{
stream.Write(ClientID);
stream.Write(UserData);
}
}
internal struct NetcodeConnectionRequestPacket
{
public ulong Expiration;
public ulong TokenSequenceNum;
public byte[] ConnectTokenBytes;
public bool Read(ByteArrayReaderWriter stream, int length, ulong protocolID)
{
if (length != 13 + 8 + 8 + 8 + Defines.NETCODE_CONNECT_TOKEN_PRIVATE_BYTES)
return false;
char[] vInfo = new char[Defines.NETCODE_VERSION_INFO_BYTES];
stream.ReadASCIICharsIntoBuffer(vInfo, Defines.NETCODE_VERSION_INFO_BYTES);
if (!MiscUtils.MatchChars(vInfo, Defines.NETCODE_VERSION_INFO_STR))
{
return false;
}
if (stream.ReadUInt64() != protocolID)
{
return false;
}
this.Expiration = stream.ReadUInt64();
this.TokenSequenceNum = stream.ReadUInt64();
this.ConnectTokenBytes = BufferPool.GetBuffer(Defines.NETCODE_CONNECT_TOKEN_PRIVATE_BYTES);
stream.ReadBytesIntoBuffer(this.ConnectTokenBytes, Defines.NETCODE_CONNECT_TOKEN_PRIVATE_BYTES);
return true;
}
public void Release()
{
BufferPool.ReturnBuffer(this.ConnectTokenBytes);
}
}
internal struct NetcodeConnectionChallengeResponsePacket
{
public NetcodePacketHeader Header;
public ulong ChallengeTokenSequence;
public byte[] ChallengeTokenBytes;
public bool Read(ByteArrayReaderWriter stream, int length, byte[] key, ulong protocolID)
{
byte[] packetBuffer = BufferPool.GetBuffer(8 + 300 + Defines.MAC_SIZE);
int packetLen = 0;
try
{
packetLen = PacketIO.ReadPacketData(Header, stream, length, protocolID, key, packetBuffer);
}
catch(System.Exception e)
{
BufferPool.ReturnBuffer(packetBuffer);
return false;
}
if (packetLen != 308)
{
BufferPool.ReturnBuffer(packetBuffer);
return false;
}
ChallengeTokenBytes = BufferPool.GetBuffer(300);
using (var reader = ByteArrayReaderWriter.Get(packetBuffer))
{
ChallengeTokenSequence = reader.ReadUInt64();
reader.ReadBytesIntoBuffer(ChallengeTokenBytes, 300);
}
BufferPool.ReturnBuffer(packetBuffer);
return true;
}
public void Write(ByteArrayReaderWriter stream)
{
stream.Write(ChallengeTokenSequence);
stream.Write(ChallengeTokenBytes);
}
public void Release()
{
BufferPool.ReturnBuffer(ChallengeTokenBytes);
}
}
internal struct NetcodePayloadPacket
{
public NetcodePacketHeader Header;
public byte[] Payload;
public int Length;
public bool Read(ByteArrayReaderWriter stream, int length, byte[] key, ulong protocolID)
{
Payload = BufferPool.GetBuffer(2048);
Length = 0;
try
{
Length = PacketIO.ReadPacketData(Header, stream, length, protocolID, key, Payload);
}
catch
{
BufferPool.ReturnBuffer(Payload);
return false;
}
if (Length < 1 || Length > Defines.MAX_PAYLOAD_SIZE)
{
BufferPool.ReturnBuffer(Payload);
return false;
}
return true;
}
public void Release()
{
BufferPool.ReturnBuffer(Payload);
Payload = null;
Length = 0;
}
}
internal struct NetcodeDenyConnectionPacket
{
public NetcodePacketHeader Header;
public bool Read(ByteArrayReaderWriter stream, int length, byte[] key, ulong protocolID)
{
if (length != Defines.MAC_SIZE)
return false;
byte[] tempBuffer = BufferPool.GetBuffer(0);
try
{
PacketIO.ReadPacketData(Header, stream, length, protocolID, key, tempBuffer);
}
catch
{
BufferPool.ReturnBuffer(tempBuffer);
return false;
}
return true;
}
}
internal struct NetcodeDisconnectPacket
{
public NetcodePacketHeader Header;
public bool Read(ByteArrayReaderWriter stream, int length, byte[] key, ulong protocolID)
{
if (length != Defines.MAC_SIZE)
return false;
byte[] tempBuffer = BufferPool.GetBuffer(0);
try
{
PacketIO.ReadPacketData(Header, stream, length, protocolID, key, tempBuffer);
}
catch
{
BufferPool.ReturnBuffer(tempBuffer);
return false;
}
return true;
}
}
}