Skip to content

Commit

Permalink
big refactor of serialization and more (#32)
Browse files Browse the repository at this point in the history
* add field num_server_addresses to ConnectTokenClient

* rename ConnectTokenClient to ConnectTokenPacket

* add abstract type AbstractPacket

* add compact get_serialized_size method for AbstractPacket

* add a generic write method for AbstractPacket

* remove older write method for ConnectionRequestPacket

* create and write a ConnectTokenPacket as the response

* add function write_fields

* add function get_serialized_size_fields

* add function get_padding_size

* remove struct PaddedConnectToken

* remove unused commented code

* remove struct PaddedPrivateConnectToken

* remove get_serialized_size and write methods for ConnectToken

* store explicit fields in PrivateConnectToken

* store explicit fields in PrivateConnectTokenAssociatedData

* clean up associated data writing

* remove struct EncryptedPrivateConnectToken

* rename ConnectToken to ConnectTokenInfo and connect_token to connect_token_info

* add convenient constructors for PrivateConnectToken and
PrivateConnectTokenAssociatedData

* clean up some stuff in ConnectTokenPacket constructor

* add function encrypt

* rename connect_token_client to connect_token_packet

* just do a while !eof(io) instead of getting padding size and so on

* remove get_padding_size function

* update get_serialized_size method for PrivateConnectToken

* no need of value in get_serialized_size method for ConnectTokenPacket

* add functions write_fields_and_padding and get_serialize_data

* add protocol_constants.jl and types.jl

* rename SIZE_OF_PADDED_CONNECT_TOKEN to SIZE_OF_CONNECT_TOKEN_PACKET

* rename PACKET_TYPE_CONNECTION_REQUEST to PACKET_TYPE_CONNECTION_REQUEST_PACKET

* add SIZE_OF_KEY instead of SIZE_OF_CLIENT_TO_SERVER_KEY,
SIZE_OF_SERVER_TO_CLIENT_KEY, AND SIZE_OF_SERVER_SIDE_SHARED_KEY

* remove bunch of unused constants

* rename MAX_GAME_SERVERS to MAX_NUM_SERVER_ADDRESSES

* add serialization.jl

* move try_read methods to serialization.jl

* move a bunch of constructors and other methods to types.jl
  • Loading branch information
Sid-Bhatia-0 authored Mar 22, 2024
1 parent 5982fa0 commit ae357ea
Show file tree
Hide file tree
Showing 4 changed files with 452 additions and 604 deletions.
43 changes: 43 additions & 0 deletions netcode/protocol_constants.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import Sockets

const NETCODE_VERSION_INFO = Vector{UInt8}("NETCODE 1.02\0")
const SIZE_OF_NETCODE_VERSION_INFO = length(NETCODE_VERSION_INFO)

const TYPE_OF_PROTOCOL_ID = UInt64

const TYPE_OF_TIMESTAMP = UInt64

const TYPE_OF_TIMEOUT_SECONDS = UInt32

const TYPE_OF_CLIENT_ID = UInt64

const SIZE_OF_USER_DATA = 32

const SIZE_OF_NONCE = 24

const SIZE_OF_KEY = 32

const SIZE_OF_HMAC = 16

const SIZE_OF_ENCRYPTED_PRIVATE_CONNECT_TOKEN_DATA = 1024

const TYPE_OF_ADDRESS_TYPE = UInt8
const SIZE_OF_ADDRESS_TYPE = sizeof(TYPE_OF_ADDRESS_TYPE)

const ADDRESS_TYPE_IPV4 = TYPE_OF_ADDRESS_TYPE(1)
const TYPE_OF_IPV4_HOST = fieldtype(Sockets.IPv4, :host)
const TYPE_OF_IPV4_PORT = fieldtype(Sockets.InetAddr{Sockets.IPv4}, :port)

const ADDRESS_TYPE_IPV6 = TYPE_OF_ADDRESS_TYPE(2)
const TYPE_OF_IPV6_HOST = fieldtype(Sockets.IPv6, :host)
const TYPE_OF_IPV6_PORT = fieldtype(Sockets.InetAddr{Sockets.IPv6}, :port)

const TYPE_OF_NUM_SERVER_ADDRESSES = UInt32
const MAX_NUM_SERVER_ADDRESSES = 32

const TYPE_OF_PACKET_TYPE = UInt8

const SIZE_OF_CONNECT_TOKEN_PACKET = 2048

const PACKET_TYPE_CONNECTION_REQUEST_PACKET = TYPE_OF_PACKET_TYPE(0)
const SIZE_OF_CONNECTION_REQUEST_PACKET = 1078
223 changes: 223 additions & 0 deletions netcode/serialization.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
function get_serialized_size(value::Integer)
if !isbits(value)
error("Currently only isbits Integer values are supported for serialization")
else
return sizeof(value)
end
end

get_serialized_size(value::Vector{UInt8}) = length(value)

get_serialized_size(value::Union{Sockets.IPv4, Sockets.IPv6}) = get_serialized_size(value.host)

get_serialized_size(value::Union{Sockets.InetAddr{Sockets.IPv4}, Sockets.InetAddr{Sockets.IPv6}}) = get_serialized_size(value.host) + sizeof(value.port)

get_serialized_size(value::NetcodeInetAddr) = SIZE_OF_ADDRESS_TYPE + get_serialized_size(value.address)

get_serialized_size(value::Vector{NetcodeInetAddr}) = sum(get_serialized_size, value)

get_serialized_size_fields(value) = sum(get_serialized_size(getfield(value, i)) for i in 1:fieldcount(typeof(value)))

get_serialized_size(::PrivateConnectToken) = SIZE_OF_ENCRYPTED_PRIVATE_CONNECT_TOKEN_DATA - SIZE_OF_HMAC

get_serialized_size(value::PrivateConnectTokenAssociatedData) = get_serialized_size_fields(value)

get_serialized_size(packet::AbstractPacket) = get_serialized_size_fields(packet)

get_serialized_size(::ConnectTokenPacket) = SIZE_OF_CONNECT_TOKEN_PACKET

function get_serialized_data(value)
data = zeros(UInt8, get_serialized_size(value))

io = IOBuffer(data, write = true, maxsize = length(data))

num_bytes_written = write(io, value)

@assert num_bytes_written == length(data) "$(num_bytes_written), $(length(data))"

return data
end

function Base.write(io::IO, netcode_inetaddr::NetcodeInetAddr)
n = 0

n += write(io, get_address_type(netcode_inetaddr))
n += write(io, netcode_inetaddr.address.host.host)
n += write(io, netcode_inetaddr.address.port)

return n
end

function Base.write(io::IO, netcode_addresses::Vector{NetcodeInetAddr})
n = 0

for netcode_address in netcode_addresses
n += write(io, netcode_address)
end

return n
end

function write_fields(io::IO, value)
n = 0

for i in 1:fieldcount(typeof(value))
n += write(io, getfield(value, i))
end

return n
end

function write_fields_and_padding(io::IO, value)
n = write_fields(io, value)

serialized_size = get_serialized_size(value)

padding_size = serialized_size - n

for i in 1 : padding_size
n += write(io, UInt8(0))
end

return n
end

Base.write(io::IO, private_connect_token::PrivateConnectToken) = write_fields_and_padding(io, private_connect_token)

Base.write(io::IO, private_connect_token_associated_data::PrivateConnectTokenAssociatedData) = write_fields(io, private_connect_token_associated_data)

Base.write(io::IO, packet::AbstractPacket) = write_fields(io, packet)

Base.write(io::IO, packet::ConnectTokenPacket) = write_fields_and_padding(io, packet)

function try_read(io::IO, ::Type{NetcodeInetAddr})
address_type = read(io, TYPE_OF_ADDRESS_TYPE)

if address_type == ADDRESS_TYPE_IPV4
host = Sockets.IPv4(read(io, TYPE_OF_IPV4_HOST))
port = read(io, TYPE_OF_IPV4_PORT)
elseif address_type == ADDRESS_TYPE_IPV6
host = Sockets.IPv6(read(io, TYPE_OF_IPV6_HOST))
port = read(io, TYPE_OF_IPV6_PORT)
else
return nothing
end

return NetcodeInetAddr(Sockets.InetAddr(host, port))
end

function try_read(data::Vector{UInt8}, ::Type{ConnectTokenPacket})
if length(data) != SIZE_OF_CONNECT_TOKEN_PACKET
return nothing
end

io = IOBuffer(data)

netcode_version_info = read(io, SIZE_OF_NETCODE_VERSION_INFO)
if netcode_version_info != NETCODE_VERSION_INFO
return nothing
end

protocol_id = read(io, TYPE_OF_PROTOCOL_ID)
if protocol_id != PROTOCOL_ID
return nothing
end

create_timestamp = read(io, TYPE_OF_TIMESTAMP)
expire_timestamp = read(io, TYPE_OF_TIMESTAMP)
if expire_timestamp < create_timestamp
return nothing
end

nonce = read(io, SIZE_OF_NONCE)

encrypted_private_connect_token_data = read(io, SIZE_OF_ENCRYPTED_PRIVATE_CONNECT_TOKEN_DATA)

timeout_seconds = read(io, TYPE_OF_TIMEOUT_SECONDS)

num_server_addresses = read(io, TYPE_OF_NUM_SERVER_ADDRESSES)
if !(1 <= num_server_addresses <= MAX_NUM_SERVER_ADDRESSES)
return nothing
end

netcode_addresses = NetcodeInetAddr[]

for i in 1:num_server_addresses
netcode_address = try_read(io, NetcodeInetAddr)
if !isnothing(netcode_address)
push!(netcode_addresses, netcode_address)
else
return nothing
end
end

client_to_server_key = read(io, SIZE_OF_KEY)

server_to_client_key = read(io, SIZE_OF_KEY)

while !eof(io)
x = read(io, UInt8)
if x != 0
return nothing
end
end

connect_token_packet = ConnectTokenPacket(
netcode_version_info,
protocol_id,
create_timestamp,
expire_timestamp,
nonce,
encrypted_private_connect_token_data,
timeout_seconds,
num_server_addresses,
netcode_addresses,
client_to_server_key,
server_to_client_key,
)

return connect_token_packet
end

function try_read(data::Vector{UInt8}, ::Type{ConnectionRequestPacket})
if length(data) != SIZE_OF_CONNECTION_REQUEST_PACKET
return nothing
end

io = IOBuffer(data)

packet_type = read(io, TYPE_OF_PACKET_TYPE)
if packet_type != PACKET_TYPE_CONNECTION_REQUEST_PACKET
return nothing
end

netcode_version_info = read(io, SIZE_OF_NETCODE_VERSION_INFO)
if netcode_version_info != NETCODE_VERSION_INFO
return nothing
end

protocol_id = read(io, TYPE_OF_PROTOCOL_ID)
if protocol_id != PROTOCOL_ID
return nothing
end

expire_timestamp = read(io, TYPE_OF_TIMESTAMP)
if expire_timestamp <= time_ns()
return nothing
end

nonce = read(io, SIZE_OF_NONCE)

encrypted_private_connect_token_data = read(io, SIZE_OF_ENCRYPTED_PRIVATE_CONNECT_TOKEN_DATA)

connection_request_packet = ConnectionRequestPacket(
packet_type,
netcode_version_info,
protocol_id,
expire_timestamp,
nonce,
encrypted_private_connect_token_data,
)

return connection_request_packet
end
Loading

0 comments on commit ae357ea

Please sign in to comment.