Description
Describe the project you are working on
A peer to peer mesh networking system for communication between many nodes in a network
Describe the problem or limitation you are having in your project
In my project, I’m using Godot's UDPServer to receive stateless UDP messages, such as device discovery packets or one-shot control messages, from many remote IP/port pairs.
However, UDPServer::poll() automatically creates a PacketPeerUDP for each new sender and stores it in the pending connections list. This results in unnecessary memory usage and resource retention, even when I only need to receive a single packet from a sender and have no intention of maintaining a connection.
Additionally, I’m required to store a reference to every PacketPeerUDP and manually check each one for new packets — even for senders that only send one message and then exit. Since UDP is connectionless, there's no reliable way to detect when a remote sender has gone offline, which means these PacketPeerUDP instances remain active and are checked every frame, even if they’re no longer useful.
Currently, there’s no way to simply receive a raw UDP packet along with the sender’s IP and port without triggering this overhead. This makes UDPServer inefficient and cumbersome to use.
Describe the feature / enhancement and how it helps to overcome the problem or limitation
I propose adding a pair of new methods to the UDPServer class for lightweight, stateless access to incoming packets:
UDPServer.is_data_available() -> bool
UDPServer.get_packet_peer() -> PacketPeerUDP
-
is_data_available() returns true if a new UDP packet is waiting to be read.
-
get_packet_peer() returns a temporary PacketPeerUDP instance populated with the received data and the sender's IP and port.
-
This peer is not added to the pending list, and the server does not retain any reference to it — the user handles it manually.
This enhancement addresses the limitations of the current system by allowing efficient processing of stateless UDP messages (e.g. discovery, one-shot control packets) without triggering persistent memory allocations or tracking peer state.
Importantly, this proposal does not break compatibility with the existing poll() and PacketPeerUDP-based workflow. That system remains useful and appropriate for use cases where:
- Stateful communication is desired.
- Packet queuing and per-peer abstraction is
- Reliable long-lived UDP peer tracking is needed.
This addition simply provides an alternative pathway better suited to stateless or high-throughput networking patterns, while preserving the strengths of the current design.
Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams
var udp_server = UDPServer.new()
udp_server.listen(12345)
func _process(delta):
while udp_server.is_data_available():
var packet_peer = udp_server.get_packet_peer()
# Read the raw packet data as a PackedByteArray
var data = packet_peer.get_packet()
# Get sender info
var sender_ip = packet_peer.get_packet_ip()
var sender_port = packet_peer.get_packet_port()
print("Received packet from: ", sender_ip, ":", sender_port, " size: ", data.size(), " bytes")
# Process the packet however you want here, or discard it
_handle_packet(data, sender_ip, sender_port)
If this enhancement will not be used often, can it be worked around with a few lines of script?
Not with out causing the same issue this proposal aims to resolve
Is there a reason why this should be core and not an add-on in the asset library?
Without writing your own custom UDP server in another language, this can't be worked around with an add-on