Skip to content

raknet #

raknet

raknet is a RakNet implementation for V, focused on the classic RakNet protocol used by Minecraft: Bedrock Edition.

The API is plain-structed and inspired by Sandertv's go-raknet.

Basic Server

import raknet

mut listener := raknet.listen('0.0.0.0:19132')!
defer {
    listener.close() or {}
}

listener.set_pong_data('raknet server'.bytes())!

mut conn := listener.accept()!
mut buf := []u8{len: 4096}
n := conn.read(mut buf)!
conn.write(buf[..n])!

Basic Client

import raknet

mut conn := raknet.dial('127.0.0.1:19132')!
defer {
    conn.close() or {}
}

conn.write('ping'.bytes())!
packet := conn.read_packet()!
println(packet.bytestr())

Basic Ping

import raknet

data := raknet.ping('127.0.0.1:19132')!
println(data.bytestr())

IPv6 addresses use bracket notation:

mut conn := raknet.dial('[::1]:19133')!

Configuration

import time
import raknet

mut listener := raknet.ListenConfig{
    max_mtu: 1200
    disable_cookies: false
    handshake_timeout: 10 * time.second
}.listen('0.0.0.0:19132')!

dialer := raknet.Dialer{
    max_mtu: 1200
    timeout: 2 * time.second
}
mut conn := dialer.dial(listener.addr())!

Logging

ListenConfig and Dialer both accept an optional error_log hook for diagnostic events (blocked reads, transient UDP errors, close-drain progress...) that would otherwise be silently dropped:

fn on_event(msg string, fields map[string]string) {
    eprintln('${msg}: ${fields}')
}

mut listener := raknet.ListenConfig{
    error_log: on_event
}.listen('0.0.0.0:19132')!

Tests

v test .

Constants #

const protocol_version = u8(11)
const min_mtu_size = u16(400)
const max_mtu_size = u16(1492)
const err_code_connection_closed = 110001
const err_code_listener_closed = 110002
const err_code_read_deadline_exceeded = 110003
const err_code_write_deadline_exceeded = 110004
const err_code_connection_timed_out = 110005
const err_code_not_supported = 110006
const err_code_protocol_mismatch = 110007
const err_code_invalid_packet = 110008
const err_code_pong_data_too_large = 110009
const err_connection_closed = error_with_code('connection closed', err_code_connection_closed)
const err_listener_closed = error_with_code('listener closed', err_code_listener_closed)
const err_read_deadline_exceeded = error_with_code('read deadline exceeded',
	err_code_read_deadline_exceeded)
const err_write_deadline_exceeded = error_with_code('write deadline exceeded',
	err_code_write_deadline_exceeded)
const err_connection_timed_out = error_with_code('connection timed out',
	err_code_connection_timed_out)
const err_not_supported = error_with_code('not supported', err_code_not_supported)
const err_invalid_packet = error_with_code('invalid packet', err_code_invalid_packet)
const err_pong_data_too_large = error_with_code('pong data must be no longer than ${max_i16} bytes',
	err_code_pong_data_too_large)

fn dial #

fn dial(address string) !&Conn

fn dial_timeout #

fn dial_timeout(address string, timeout time.Duration) !&Conn

fn listen #

fn listen(address string) !&Listener

fn load_uint24 #

fn load_uint24(b []u8) Uint24

fn ping #

fn ping(address string) ![]u8

fn ping_timeout #

fn ping_timeout(address string, timeout time.Duration) ![]u8

fn safe_load_uint24 #

fn safe_load_uint24(b []u8) !Uint24

fn write_uint24 #

fn write_uint24(mut b []u8, v Uint24)

fn Reliability.from #

fn Reliability.from[W](input W) !Reliability

type LogFunc #

type LogFunc = fn (msg string, fields map[string]string)

LogFunc is an optional diagnostic hook. msg is a short, stable event name (e.g. "read from", "blocked read", "transient udp error", "close drain timeout") and fields carries event-specific context such as the remote address or underlying error.

type PongDataFunc #

type PongDataFunc = fn (net.Addr) []u8

type Uint24 #

type Uint24 = u32

fn (Uint24) inc #

fn (mut u Uint24) inc() Uint24

In V, a mutable receiver for an alias type behaves pointer-like here, so copy the underlying value explicitly before incrementing.

struct Conn #

@[heap]
struct Conn {
mut:
	udp                 &net.UdpConn = unsafe { nil }
	remote              net.Addr
	remote_key          string
	mtu                 u16
	seq                 Uint24
	message_idx         Uint24
	sequence_idx        Uint24
	order_idx           Uint24
	split_id            u32
	packets             PacketChan = new_packet_chan(packet_chan_initial_cap, packet_chan_max_cap,
		packet_chan_max_bytes)
	connected           chan bool
	splits              map[u16][][]u8
	resend              ResendMap
	win                 DatagramWindow
	packet_queue        PacketQueue
	pending_ack         []Uint24
	pending_nack        []Uint24
	sent_raw            [][]u8
	rtt                 time.Duration
	last_activity       time.Time
	idle_timeout        time.Duration
	keepalive_interval  time.Duration
	read_deadline       time.Time
	write_deadline      time.Time
	read_timeout        time.Duration
	is_server           bool
	listener            &Listener   = unsafe { nil }
	mutex               &sync.Mutex = sync.new_mutex()
	ack_mutex           &sync.Mutex = sync.new_mutex()
	lifecycle_mutex     &sync.Mutex = sync.new_mutex()
	closed_chan         chan bool   = chan bool{cap: 1}
	connected_confirmed bool
	closing_deadline    time.Time
	closing             bool
	closed              bool
	error_log           LogFunc = unsafe { nil }
}

fn (Conn) remote_addr #

fn (mut c Conn) remote_addr() string

fn (Conn) local_addr #

fn (mut c Conn) local_addr() string

fn (Conn) latency #

fn (mut c Conn) latency() time.Duration

fn (Conn) set_read_deadline #

fn (mut c Conn) set_read_deadline(deadline time.Time)

fn (Conn) set_write_deadline #

fn (mut c Conn) set_write_deadline(deadline time.Time)

fn (Conn) set_deadline #

fn (mut c Conn) set_deadline(deadline time.Time)

fn (Conn) set_read_timeout #

fn (mut c Conn) set_read_timeout(timeout time.Duration)

fn (Conn) set_write_timeout #

fn (mut c Conn) set_write_timeout(timeout time.Duration)

fn (Conn) set_idle_timeout #

fn (mut c Conn) set_idle_timeout(timeout time.Duration)

fn (Conn) set_keepalive_interval #

fn (mut c Conn) set_keepalive_interval(interval time.Duration)

fn (Conn) write #

fn (mut c Conn) write(data []u8) !int

fn (Conn) write_reliable_ordered #

fn (mut c Conn) write_reliable_ordered(data []u8) !int

fn (Conn) write_reliable #

fn (mut c Conn) write_reliable(data []u8) !int

fn (Conn) write_unreliable #

fn (mut c Conn) write_unreliable(data []u8) !int

fn (Conn) write_unreliable_sequenced #

fn (mut c Conn) write_unreliable_sequenced(data []u8) !int

fn (Conn) write_reliable_sequenced #

fn (mut c Conn) write_reliable_sequenced(data []u8) !int

fn (Conn) read #

fn (mut c Conn) read(mut buf []u8) !int

fn (Conn) read_packet #

fn (mut c Conn) read_packet() ![]u8

fn (Conn) close #

fn (mut c Conn) close() !

struct Dialer #

struct Dialer {
pub mut:
	max_mtu              u16
	timeout              time.Duration
	max_transient_errors int
	error_log            LogFunc = unsafe { nil }
}

fn (Dialer) ping #

fn (dialer Dialer) ping(address string) ![]u8

fn (Dialer) ping_timeout #

fn (dialer Dialer) ping_timeout(address string, timeout time.Duration) ![]u8

fn (Dialer) dial #

fn (dialer Dialer) dial(address string) !&Conn

struct ListenConfig #

struct ListenConfig {
pub mut:
	disable_cookies   bool
	max_mtu           u16
	block_duration    time.Duration
	capture_packets   bool
	handshake_timeout time.Duration
	error_log         LogFunc = unsafe { nil }
}

fn (ListenConfig) listen #

fn (conf ListenConfig) listen(address string) !&Listener

struct Listener #

@[heap]
struct Listener {
mut:
	udp                  &net.UdpConn = unsafe { nil }
	incoming             chan &Conn
	connections          map[string]&Conn
	connections_mutex    &sync.Mutex = sync.new_mutex()
	id                   i64
	closed               bool
	lifecycle_mutex      &sync.Mutex = sync.new_mutex()
	pong_data            []u8
	pong_data_func       PongDataFunc = unsafe { nil }
	pong_data_mutex      &sync.Mutex  = sync.new_mutex()
	max_mtu              u16
	disable_cookies      bool
	cookie_salt          u64
	previous_cookie_salt u64
	block_duration       time.Duration
	blocks               map[string]time.Time
	security_mutex       &sync.Mutex = sync.new_mutex()
	capture_packets      bool
	captured             [][]u8
	capture_mutex        &sync.Mutex = sync.new_mutex()
	handshake_timeout    time.Duration
	error_log            LogFunc = unsafe { nil }
}

fn (Listener) addr #

fn (l &Listener) addr() string

fn (Listener) accept #

fn (l &Listener) accept() !&Conn

fn (Listener) accept_timeout #

fn (l &Listener) accept_timeout(timeout time.Duration) !&Conn

fn (Listener) close #

fn (mut l Listener) close() !

fn (Listener) set_pong_data #

fn (mut l Listener) set_pong_data(data []u8) !

fn (Listener) set_pong_data_func #

fn (mut l Listener) set_pong_data_func(f PongDataFunc)

fn (Listener) clear_pong_data_func #

fn (mut l Listener) clear_pong_data_func()

fn (Listener) block #

fn (mut l Listener) block(addr net.Addr)

fn (Listener) block_for #

fn (mut l Listener) block_for(addr net.Addr, duration time.Duration)

fn (Listener) captured_packets #

fn (mut l Listener) captured_packets() [][]u8