Get host IP address from peers

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By magicalogic

Am trying to create an android LAN multiplayer game. I have used godots high level multiplayer API for this and everything works fine as long as I provide the hosts IP address to the peers by hard coding it.
If a player hosts the game they provide a bind ip address which is easy with IP.get_local_addresses()[0] but when I call the same function from other peers none of the addresses returned belongs to the host. Am using android hotspot for this and when I check the wifi settings from the connected devices, the gateway is actually the host IP address.

Now, I don’t want peers to type in the host IP address to connect but I don’t know any other way of doing it. Any suggestions are welcome.

I already looked into using qr codes but that needs camera access in android which isn’t implemented and there is no way to create or scan qr codes in godot unless you build it yourself including other necessary modules.

If you know of a better solution of getting the actual hosts IP address from other peers please share.

:bust_in_silhouette: Reply From: Wakatta

Confusion

IP.get_local_addresses() as the name suggests returns addresses available to the calling device (e.g wifi, hotspot, Bluetooth, data, USB, localhost) and not addresses available on the connected network.

Answer

Automating the process of finding a host is a really bad and error prone rabbit hole idea, but understand the need and will share a hint.

Hint

Once connected to the network your device will have the same IP scheme as the host

There are only 255 possible options minus the requesting device’s

It’s possible to ping an address and it will respond if it exists or poll for packets and respond to it’s sender

Code Snippet

# Server.gd
var udp_server = UDPServer.new()
var udp_port = 6969

func _ready():
    udp_server.listen(udp_port, "0.0.0.0")
    # do enetmultiplayer setup for host

func _process(delta):
    udp_server.poll()
    if udp_server.is_connection_available():
        var udp_peer : PacketPeerUDP = udp_server.take_connection()
        var packet = udp_peer.get_packet()
        print("Recieved : %s from %s:%s" %
                [
                    packet.get_string_from_ascii(),
                    udp_peer.get_packet_ip(),
                    udp_peer.get_packet_port(),
                ]
        )
        # Reply to valid udp_peer with server IP address example
        udp_peer.put_packet(IP.get_local_addresses()[0])
 
#Client.gd
var udp_client := PacketPeerUDP.new()
var udp_server_found = false
var udp_requests = 3
var delta_time = 0.0
var udp_port = 6969

func _ready():
    udp_client.set_broadcast_enabled(true)
    udp_client.set_destination_address("255.255.255.255", udp_port)

func _process(delta):
    delta_time += delta
    if delta_time >= 2.0: #every 2 seconds send request
        delta_time = 0.0
        if not udp_server_found: # Try to contact server
            udp_client.put_packet("Valid_Request".to_ascii())
            udp_requests -= 1
            if udp_requests == 0:
                #start as server or stop sending request
                pass
    if udp_client.get_available_packet_count() > 0:
        udp_server_found = true
        var server_address_to_connect_to = udp_client.get_packet()
        #connect to enetmultiplayer server using address

Notes

The above code example does weird things with ipv6 and you need to translate the recieved packet into a valid IP string on the client side before using.

The udp_port can be any port you desire that’s not the same as your enet multiplayer one

To make this truly intuitive start every peer as a client and search for a host if a host is not found within a certain time that client becomes the host instead, this way the faster of the two will always be the host and subsiquent peers will connect seemlessly

Thanks for your answer. I had already started looking into building android modules in android studio and exporting them as AAR for use in godot to enable qrcode creation and scanning in android phones but I will try your elimination method and use it if it is fast enough. I can use either as a fall back.

magicalogic | 2022-03-26 10:30

Nice one love that qrcode idea.

Another idea is to concatenate a string based on the user’s entry by getting the host’s address and sharing the last few digits

Example

# On host
var address = IP.get_local_addresses[0]
var code = address.right(address.find_last(".") + 1)

Then display the code for the other user to enter which will be up to 255 using a Label for example

# On client
var code = $Spinbox.get_value()
var address = IP.get_local_addresses[0]
address = address.left(address.find_last(".") + 1) + str(code)

Wakatta | 2022-03-26 14:02

That would definitely reduce the typing. Thanks.

magicalogic | 2022-03-27 19:29

So recently updated my Godot version and you should too if under v3.2.2 also updated my answer to reflect being able to achieve your initial goal.
Have not fully tested but you should be able to get exactly what you want from the samples provided

Wakatta | 2022-04-04 00:43