I have limited ammount of experience creating packets and stuff when it come to networking. Somehow in multiplayer games everything works automagically.

My current worry is that i cant wrap around my head how to make this work. I know steamAPI has this all figured out, but i rather have my own way, in case we got another SNOY incident.

My goal would be to have lobby, that people can join and there they can see the available servers. Ofc, it would be easy if servers had just one port they broadcast on and then you sniff that port on all server would show up. However, sometimes that port is taken. And some peoples wants to host on another port.

So far ive been following though the tutorial by this fellow:

But he only does it on one port.
One port for broadcast and one for listen.

How to broadcast on multiple ports and let your server browser get all them servers?

server_browser.gd

extends Control
class_name ServerBrowser
signal found_server
signal server_removed
signal join_server

@onready var broadcast_timer: Timer = $BroadcastTimer

var room_info:RoomInfo =RoomInfo.new()

var broadcaster: PacketPeerUDP
var listener: PacketPeerUDP
@export var listen_port: int = 8911
@export var broadcast_port: int = 8912
@export var broadcast_address:String = "255.255.255.255"

@onready var server_list_container: VBoxContainer = $PanelContainer/MarginContainer/VBoxContainer/ServerListContainer

const SERVER_INFO_CONTAINER = preload("res://ui/server_info_container.tscn")


# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	set_up_listen()
	pass # Replace with function body.
func set_up_listen():
	listener = PacketPeerUDP.new()
	var ok = listener.bind(listen_port)
	if ok == OK:
		print("Bound to Listen Port " + str(listen_port) + " Successfully")
	else:
		print("Failed to bind to Listen Port: " + str(listen_port))
	
func set_up_broadcast(_name):
	room_info.room_name = _name
	room_info.player_count = GameManager.player_list.size()
	broadcaster = PacketPeerUDP.new()
	broadcaster.set_broadcast_enabled(true)
	broadcaster.set_dest_address(broadcast_address,listen_port)
	var ok = broadcaster.bind(broadcast_port)
	if ok == OK:
		print("Bound to Broadcast Port " + str(broadcast_port) + " Successfully")
	else:
		print("Failed to bind to Broadcast Port: " + str(broadcast_port))
	broadcast_timer.start()
	pass
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
	
	if listener.get_available_packet_count() >0:
		var server_ip = listener.get_packet_ip()
		var server_port = listener.get_packet_port()
		var bytes = listener.get_var()
		
		#var bytes = listener.get_packet()
		#var data = bytes.get_string_from_utf8()
		#var data_obj = JSON.parse_string(data)
		
		var data_obj:RoomInfo = RoomInfo.from_dict(bytes)
		
		print("Got packet: " + str(server_ip) + ":" + str(server_port) + " Room: " + str(data_obj.to_dict()))
		
		if not server_ip: return
		
		var server_info_node_name = str(server_ip) + "." + str(server_port)
		server_info_node_name = server_info_node_name.replace(".","_")
		
		
		if not server_list_container.has_node(str(server_info_node_name)):
			var new_server_info:ServerInfoContainer = SERVER_INFO_CONTAINER.instantiate()
			new_server_info.join_button_pressed.connect(join_button_pressed)
			new_server_info.server_ip = server_ip
			new_server_info.server_port = server_port
			new_server_info.server_name = data_obj.room_name
			new_server_info.player_count = str(data_obj.player_count)
			new_server_info.name = server_info_node_name
			server_list_container.add_child(new_server_info)
		else:
			var new_server_info:ServerInfoContainer = server_list_container.get_node(server_info_node_name)
			new_server_info.server_ip = server_ip
			new_server_info.server_port = server_port
			
			new_server_info.server_name = data_obj.room_name
			new_server_info.player_count = str(data_obj.player_count)
	pass


func _on_broadcast_timer_timeout() -> void:
	print("Broadcasting server!")
	room_info.player_count = GameManager.player_list.size()
	
	#var data = JSON.stringify(RoomInfo)
	#var packet = data.to_utf8_buffer()
	#broadcaster.put_packet(packet)

	var data = room_info.to_dict()
	
	broadcaster.put_var(data)
	
	pass # Replace with function body.
func join_button_pressed(server_info_container:ServerInfoContainer):
	join_server.emit(server_info_container)
	pass
	
func stop_broadcasting():
	if listener != null:
		listener.close()
	if broadcaster != null:
		broadcaster.close()
	broadcast_timer.stop()
	
func _exit_tree() -> void:
	stop_broadcasting()

Doing some more debugging. I cant seem to make it work over network.

I have 2 machines, well one machine but with sandbox.

Each OS instance has its own IP
I can ping from one to another and vice versa
I can join from one to another
But when im listening on UDP port, i cant see other server. But i can join the server entering the IP.

I thought this sets up listener on all IP addresses

func set_up_listen():
	listener = PacketPeerUDP.new()
	var ok = listener.bind(listen_port)
	if ok == OK:
		print("Bound to Listen Port " + str(listen_port) + " Successfully")
	else:
		print("Failed to bind to Listen Port: " + str(listen_port))

But somehow it does not work.. or am i doing it wrong?

This is my _process() when app launches it starts doing this.

func _ready() -> void:
	set_up_listen()
	pass

func _process(delta: float) -> void:
	
	if listener.get_available_packet_count() >0:
		var server_ip = listener.get_packet_ip()
		var server_port = listener.get_packet_port()
		var bytes = listener.get_var()
		
		
		var data_obj:RoomInfo = RoomInfo.from_dict(bytes)
		
		print("Got packet: " + str(server_ip) + ":" + str(server_port) + " Room: " + str(data_obj.to_dict()))
		
		if not server_ip: return
		
		var server_info_node_name = str(server_ip) + "." + str(server_port)
		server_info_node_name = server_info_node_name.replace(".","_")
		
		
		if not server_list_container.has_node(str(server_info_node_name)):
			var new_server_info:ServerInfoContainer = SERVER_INFO_CONTAINER.instantiate()
			new_server_info.join_button_pressed.connect(join_button_pressed)
			if data_obj.server_ip == "127.0.0.1":
				new_server_info.server_ip=  server_ip 
			else: 
				new_server_info.server_ip = data_obj.server_ip
			 
			new_server_info.server_port = data_obj.server_port
			new_server_info.server_name = data_obj.room_name
			new_server_info.player_count = str(data_obj.player_count)
			new_server_info.name = server_info_node_name
			server_list_container.add_child(new_server_info)
		else:
			var new_server_info:ServerInfoContainer = server_list_container.get_node(server_info_node_name)
			if data_obj.server_ip == "127.0.0.1":
				new_server_info.server_ip=  server_ip 
			else: 
				new_server_info.server_ip = data_obj.server_ip
			new_server_info.server_name = data_obj.room_name
			new_server_info.player_count = str(data_obj.player_count)

EDIT: Sorry for video compression, it was filmed between 2x 2k monitors so i guess it got automagically compressed when i uploaded it.
But in the video i jaunch 2x godot.exe instnaces and one joins the other, but when when i start hosting they should see the server in the list but it does not show up. Only the local (current machine) server shows up in the list.

It seems im not getting no packets when i set the IP to the other machine.

func set_up_listen():
	listener = PacketPeerUDP.new()
	var ok = listener.bind(listen_port,"*")
	if ok == OK:
		print("Bound to Listen Port " + str(listen_port) + " Successfully")
	else:
		print("Failed to bind to Listen Port: " + str(listen_port))

What could be the culprit?

Ok so diving into this, i have found out that this approach is garbage.

For server browser, most if not all games use some sort of masterlist instance, where the game-host sends the game info to and then game-client reads from that list and see all the possible servers.

Will try to implement something to that sort.

I have created the thing-ma-bob. It kinda works, now need to find limitations and debug more, once im done then ill update with solution.

Well my initial plan was to use dpmaster, it has proven to me that it works on low level networking jobs, not in what im doing in godot, using high level multiplayer API.

Now that client side of dpmaster is already done, i need to recreate the dpmaster server side.

My first thought was using some other framework.. maybe in python, go, or ecmascript, but now i have decided i will just use the same godot. Create a headless server "game" that will function just like dpmaster, only using godot high level networking API. Wont be as ubiquitous as dpmaster but will server my purpose.

Will post updates as they happen to work 🙂.

Source:
https://github.com/bad-mushroom/dpmaster

Compiled for windows:
https://icculus.org/twilight/darkplaces/files/dpmaster-2.2.zip

kuligs2 changed the title to Multiplayer server lobby/masterlist peer2peer p2p listen/broadcast ports .