Why can't a client from another machine receive packets sent from my machine?

Godot Version

4.6.2

Question

I’ve been doing some tests for my game, Monkanics, as the multiplayer hasn’t been working.

So, I broke it down to the bare minimum and I found the root of the problem: Packets sent get blocked in transit and don’t reach the other client’s machine.

My current test setup uses PacketPeerUDP to send a singular string ("Message Sent") that prints in the client’s debug menu after they connect to my IP and port.

At least, that’s how it should work in theory, but clearly it doesn’t.


The relevant code is the following (Minus my IP and port):

var Demetrius_Server_IP : String
var Demetrius_Port : int
var Server_Packet_Peer : PacketPeerUDP
var Client_Packet_Peer : PacketPeerUDP
func _ready() -> void:
	
	set_process(false)
	
	if OS.has_feature("dedicated_server"):
		
		Server_Packet_Peer = PacketPeerUDP.new()
	
	if not OS.has_feature("dedicated_server"):
		
		Client_Packet_Peer = PacketPeerUDP.new()
		Client_Packet_Peer.bind(Demetrius_Port)
	
	set_process(true)
func _process(delta: float) -> void:
	
	if OS.has_feature("dedicated_server"):
		
		send_packet()
	
	if not OS.has_feature("dedicated_server"):
		
		receive_packet()
func send_packet() -> void:
	
	if OS.has_feature("dedicated_server"):
		
		Server_Packet_Peer.set_dest_address(Demetrius_Server_IP, Demetrius_Port)
		Server_Packet_Peer.put_packet("Message Sent".to_utf8_buffer())

func receive_packet() -> void:
	
	if not OS.has_feature("dedicated_server"):
		
		if Client_Packet_Peer.get_available_packet_count() > 0:
			var array_bytes := Client_Packet_Peer.get_packet()
			var packet_string := array_bytes.get_string_from_ascii()
			print("Received message: ", packet_string)

This is the simplest setup I could make in Godot, so I know the issue is due to my lack of understanding. Most likely a fundamental networking concept.

From my testing with others on the Monkanics discord, I know the problem ISN’T the following:

  • The dedicated_server feature tag is applied properly on each export and in the code.
  • The server is successfully created and it sends packets.
  • The port is in the acceptable range of between 1024–49151.
  • The IP I used is my real public IPv4 and it doesn’t change.

I can also provide a project file if needed.

Any theories and help would be appreciated.

2 Likes

Is your server running behind a router? Have you configured the router to accept UDP traffic through that port?

3 Likes

I have not. How do I do this on Windows 10?

1 Like

I’m not on a Windows 10 machine so only I can recommend you is to google it. Used to be related to the Windows Defender Firewall. There you should find inbound and outbound rules where you configure protocol (UDP/TCP) and port.

3 Likes

That does make sense, as client connections should be accepted, as they initiated the request. But the may not connect because of MY router.

I’m looking into it now.

1 Like

Alright, I’m on this screen:

I could run another test on the Monkanics discord with my firewall off. To see what happens.

1 Like

Anyway just keep it off for the tests only, is not a good security measure to keep it like that. In case you have an Antivirus, check the configuration too.

1 Like

We just ran a test, and it didn’t work. So it isn’t the Windows Defender firewall.

1 Like

Are you trying to connect from a computer outside of your network?

If you are trying to connect locally, use your local machine’s IP address, not public IPv4, your public IPv4 address is your router’s IP address, not your computer’s.

If you are trying to connect externally, from a proxy or a friend, etc then you will need to port forward. You can try this with godot’s UPNP but many routers disable this feature so you may have to port forward manually.

3 Likes

Going back to my first replay, do you have a router? If you have a router (from your ISP) you will need to allow the ports too.

2 Likes

I’m trying to connect externally, and I haven’t tried port forwarding yet.

I’ll start research on that tomorrow.

1 Like

My apartment uses Spectrum internet. So I’m 99% sure there’s a router.

But after I looked it up, it seems I am unable to access the router’s settings because my parents control the Wi-Fi (AKA, I don’t pay for it). So I’ll have to find another way.

1 Like

If there is a router, by default only HTTP will be allowed, so, TCP 80 and 443. You will not be able to pass UDP without enabled that.

3 Likes

Here’s a global script I made to handle UPNP, currently it only handles UDP and I’m unsure if it handles errors to my liking. But you can use it like so

func _on_upnp_setup_pressed() -> void:
	UpnpManager.open_upnp_port(my_port)
	var error_msg: String = await UpnpManger.upnp_finished
	if !error_msg.is_empty():
		push_warning(error_msg)

Of course read more about Godot’s UPNP implementation in the docs:

Global UPnPManager.gd

extends Node

var _open_udp_ports: Array[int] = []
#var _open_tcp_ports: Array[int] = []

var _upnp: UPNP
var _upnp_thread: Thread = null
signal upnp_finished(error_msg: String)

func open_upnp_port(port: int) -> void:
	_check_thread()

	_upnp_thread = Thread.new()
	_upnp_thread.start(_setup_upnp.bind(port), Thread.PRIORITY_LOW)
	set_process(true)


func close_upnp_port(port: int) -> void:
	_check_thread()

	_upnp_thread = Thread.new()
	_upnp_thread.start(_teardown_upnp.bind(port), Thread.PRIORITY_LOW)
	set_process(true)


func _setup_upnp(port: int) -> String:
	if _upnp == null:
		_upnp = UPNP.new()

	if _upnp.get_device_count() == 0:
		# The slowest function is discovery
		var discover_error := _upnp.discover()
		if discover_error != UPNP.UPNP_RESULT_SUCCESS:
			return "Discovery failed"

	if port in _open_udp_ports:
		return ""

	var gateway := _upnp.get_gateway()
	if gateway and gateway.is_valid_gateway():
		var mapping_error := _upnp.add_port_mapping(port, port, "Godot - UPNP")
		if mapping_error != UPNP.UPNP_RESULT_SUCCESS:
			push_warning("Failed to map UPnP port: ", mapping_error)
			return "Mapping failed"
		_open_udp_ports.append(port)
		return ""
	else:
		return "No gateway"


func _teardown_upnp(port: int) -> String:
	if not port in _open_udp_ports:
		return "Port not open"

	var gateway := _upnp.get_gateway()
	if gateway and gateway.is_valid_gateway():
		var mapping_error := _upnp.delete_port_mapping(port)
		if mapping_error != UPNP.UPNP_RESULT_SUCCESS:
			return "Failed to remove port"
		_open_udp_ports.erase(port)
		return ""
	else:
		return "No gateway to remove from"


func _exit_tree() -> void:
	_check_thread()
	for open_port in _open_udp_ports:
		_upnp.delete_port_mapping(open_port)


func _check_thread() -> void:
	if _upnp_thread and _upnp_thread.is_started():
		_upnp_thread.wait_to_finish()


func _ready() -> void:
	set_process(false)


func _process(_delta: float) -> void:
	if _upnp_thread and _upnp_thread.is_started():
		if _upnp_thread.is_alive():
			return

		var err_msg: String = _upnp_thread.wait_to_finish()
		upnp_finished.emit(err_msg)

	set_process(false)
5 Likes

You have to do a similar thing on the admin page of your router. You should change the admin login and password from the shipped default if you haven’t already as it’s the same as having unencrypted Wi-Fi and no firewall for any half decent cracker.

It’s the hard part of peer to peer with NAT and having to punch through a firewall, as you have to deal with complex combinations of network stack and hardware configurations.

2 Likes

If you need want to avoid problems configuring routers and firewalls, try websockets on port 80, maybe crete a very small app and test it with some friend.

3 Likes

You can check if your ports are open with an online tool while your service is running, 80 and 443 are not usually open by default.

4 Likes

I just checked about 30 ports and they said they’re all closed unfortunately:

1 Like

You will need a service running with that port bound for the tester to work. Good that random ports aren’t open on their own, that would be a huge security issue. You will need to use UPnP, port forwarding, or hole punching (of which I’ve had the least success).

2 Likes

That makes sense. I have to open/forward the port to make it public. Maybe that’s why the client can’t connect.

Side question, what do you think of the Netfox/Noray plugin(s). I’m really leaning towards using them, because man, all this is a doozy.

1 Like