interface
FreeBodyEngine.net.interface
#
BYTE_ORDER = 'big'
module-attribute
#
LOCAL = '127.0.0.1'
module-attribute
#
NetworkAddress = tuple[str, int]
module-attribute
#
Contains the host and port.
ORDERED_BIT = 1 << 1
module-attribute
#
RELIABLE_BIT = 1 << 0
module-attribute
#
ACKPacket(channel, seq)
#
DataPacket(channel, flags, seq, length, payload, address)
#
Bases: Packet
A decoded DATA packet carrying an application payload.
Args:
channel: The logical channel this packet was sent/received on.
flags: Bitfield of RELIABLE_BIT/ORDERED_BIT.
seq: This packet's sequence number.
length: Length of payload, in bytes.
payload: The raw application payload.
address: The (host, port) this packet came from/is going to.
NetworkInterface(port=7433, host=LOCAL, max_packet_size=4096, reliable_resend_threshold=0.3, sequence_duplicate_threshold=64)
#
A basic UDP networking interface that inmplements TCP features to allow for both speed and reliability
Binds a non-blocking UDP socket at (host, port).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
port
|
int
|
Local UDP port to bind. |
7433
|
host
|
str
|
Local address to bind. |
LOCAL
|
max_packet_size
|
int
|
Max bytes read per incoming packet in |
4096
|
reliable_resend_threshold
|
float
|
Seconds to wait for an ACK before resending a reliable packet. |
0.3
|
sequence_duplicate_threshold
|
int
|
Sliding-window size passed to each
peer's |
64
|
address = (self.host, self.port)
instance-attribute
#
host = host
instance-attribute
#
last_sequences = {}
instance-attribute
#
max_packet_size = max_packet_size
instance-attribute
#
ordered_buffers = {}
instance-attribute
#
packets = []
instance-attribute
#
pending_reliable = {}
instance-attribute
#
port = port
instance-attribute
#
reliable_resend_threshold = reliable_resend_threshold
instance-attribute
#
seq_counter = 0
instance-attribute
#
sequence_duplicate_threshold = sequence_duplicate_threshold
instance-attribute
#
socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
instance-attribute
#
check_ordered_packet(packet, address)
#
Runs packet through address's OrderedBuffer, creating one if
this is the first ordered packet seen from address.
is_packet_duplicate(seq, address)
#
Whether seq from address is a duplicate, creating a fresh
SequenceHandler for address the first time it's seen.
poll_data()
#
Drains every packet currently waiting on the socket, decoding each
one and appending it to self.packets.
Duplicate reliable packets are still ACKed (the peer may not have
gotten the first ACK) but not re-appended. Ordered packets are only
appended once released in sequence by check_ordered_packet, so an
early arrival can sit buffered rather than showing up in self.packets
right away. A received ACK instead clears the matching entry from
pending_reliable so it stops being resent.
process_reliable_packets()
#
Resends any packet in pending_reliable that's been waiting longer
than reliable_resend_threshold for its ACK.
recieve_packet()
#
Reads one raw packet off the socket, returning (data, address),
or (None, None) if the peer is unreachable (see send_packet).
send_ack_packet(channel, address, seq)
#
Encodes and sends an ACK packet for sequence number seq to address.
send_data_packet(channel, address, payload, reliable=False, ordered=False)
#
Encodes and sends a DATA packet to address, assigning it the
next sequence number.
If reliable is set, the packet is also stashed in
pending_reliable so process_reliable_packets will keep resending
it until an ACK for its sequence number arrives.
Returns:
| Type | Description |
|---|---|
int
|
The sequence number assigned to this packet. |
send_packet(packet, address)
#
Sends an already-encoded raw packet to address, silently
dropping it if the peer is unreachable (see the ConnectionError
comment below - UDP has no real "connection" to fail, so this can
only mean a previous packet bounced).
update()
#
Per-frame pump: reads and decodes any waiting packets, then resends any reliable packets that timed out waiting for an ACK.
OrderedBuffer()
#
Per-peer reordering buffer for packets sent with ORDERED_BIT: holds
packets that arrived out of order until the ones before them (by
sequence number) show up, so check_packet releases them strictly in
sequence.
Starts an empty buffer expecting sequence number 0 next.
next_seq = 0
instance-attribute
#
packets = {}
instance-attribute
#
check_packet(packet)
#
Buffers packet and returns every packet (including packet
itself, if it was next) now releasable in sequence order.
get_releasable_packets()
#
Pops and returns every buffered packet starting at next_seq that
forms an unbroken run, advancing next_seq (wrapping at 16 bits) past them.
Packet(channel, type)
#
PacketType
#
SequenceHandler(threshold=64)
#
Per-peer duplicate detection over a sliding window of recent sequence numbers, so a resent reliable packet (the sender never got the ACK) isn't processed twice.
threshold: How far behind the highest sequence number seen a sequence number can still be and be tracked/considered - both bounds the memory used and means anything older is just assumed to be a duplicate.
current_seq = -1
instance-attribute
#
sequences = set()
instance-attribute
#
threshold = threshold
instance-attribute
#
is_duplicate(sequence)
#
Whether sequence has already been seen, recording it if not.
A new highest sequence number is never a duplicate, and also prunes
sequences of anything now older than threshold behind it. A
sequence number older than threshold behind the current highest is
always treated as a duplicate (it's outside the tracked window, so
there's no way to tell) - otherwise it's a duplicate only if already
in sequences.