godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
90.18k stars 21.19k forks source link

Packet Loss in unreliable UDP localhost #98143

Open TheYellowArchitect opened 1 day ago

TheYellowArchitect commented 1 day ago

Tested versions

System information

Godot v4.4.dev (0a9ad8f9d) - Artix Linux #1 SMP PREEMPT_DYNAMIC on Tty - X11 display driver, Multi-window, 1 monitor - OpenGL 3 (Compatibility) - NVIDIA GeForce GTX 1050 Ti (nvidia; 560.35.03) - AMD Ryzen 5 2600 Six-Core Processor (12 threads)

Issue description

I use an RPC with "unreliable" or "unreliable_ordered" mode. The channel doesn't matter. The bug is that sometimes, some RPCs aren't received (aka packets are lost) Now, that is normal because of UDP, but I am uncertain if this is the case because this is localhost. As in, I have no LAN or wifi connected, and send just integers. I searched if UDP packets can drop at localhost and https://stackoverflow.com/questions/7968566/what-would-cause-udp-packets-to-be-dropped-when-being-sent-to-localhost#7968907 users claim yes at MB/s to overflow the buffer, but this isn't even 1 KB/s

Granted, packets dropping at localhost are rare, but it got me to wonder and at least want to report it in case there is some ENET initialization bug or something, given it often happens near the start of joining. So feel free to close this issue without warning, as it is very likely a false flag.

As for my testing, this is a tldr video

https://github.com/user-attachments/assets/778794fc-a8b3-4dfe-870b-8ea6d13be1d8

of the MRP. And I have to admit it takes like 5+ boots to get it to happen.

Steps to reproduce

  1. Open the MRP
  2. Press F5/F6
  3. Host as server
  4. Join as client
  5. Await a few seconds, if no errors, F8, repeat step 2 until you get an error.

Minimal reproduction project (MRP)

(The MRP is a tweak of a very old MRP made by @Mathis-Z)

mrp.zip

Its code is just 1 class with this code, posted below by filtering away the host/join code:


extends Node

var peer: ENetMultiplayerPeer = null

var last_tick: int = 0

func _process(delta: float):
    if peer and not multiplayer.is_server() and peer.get_connection_status() == ENetMultiplayerPeer.CONNECTION_CONNECTED:
        last_tick += 1
        set_num.rpc(last_tick)

@rpc("any_peer", "call_remote", "unreliable")
func set_num(tick: int):
    if tick != last_tick + 1:
        if tick < last_tick:
            push_error("SHUFFLED! Received tick %s while last tick is %s" % [tick, last_tick])
        else:
            push_error("Received tick %s while last tick is %s" % [tick, last_tick])
    last_tick = tick
    if multiplayer.is_server():
        print(tick)
timothyqiu commented 1 day ago

I'm not an expert in this area. But aren't unreliable and unreliable_ordered by definition "unreliable"? They are not "unreliable-if-not-localhost" :stuck_out_tongue:

Packets are not acknowledged, no resend attempts are made for lost packets. ^1

I don't think it makes sense to explore the conditions required to meet the no-packet-loss case in unreliable mode. The documentation should prevail. If you do want to avoid packet loss, reliable mode should be used instead.

TheYellowArchitect commented 1 day ago

I know but I want a confirmation because these packets drop near the start of the launch/boot and it seems weird for localhost to drop packets, especially because its just an integer spam being ~1KB/s, so it could be a bug