Files

79 lines
3.1 KiB
GDScript

class_name LanDiscovery
extends Node
const DISCOVERY_PORT := 27841
var game: Node3D
var listener := PacketPeerUDP.new()
var sender := PacketPeerUDP.new()
var servers := {}
var elapsed := 0.0
var available := false
var probes := {}
func _ready() -> void:
# A dedicated server only advertises; leave the receive port to local clients.
available = not game.dedicated and listener.bind(DISCOVERY_PORT, "0.0.0.0") == OK
sender.set_broadcast_enabled(true)
sender.set_dest_address("255.255.255.255", DISCOVERY_PORT)
func _process(dt: float) -> void:
elapsed += dt
if elapsed > 1:
elapsed = 0
if game.active and game.is_host and game.config.get("discovery", true):
var humans := 0
for p: Fighter in game.players.values():
if not p.bot:
humans += 1
var info := {"protocol": "BLOCKLINE_1", "name": game.config.server_name, "players": humans,
"map": game.config.get("map", "quarry"), "max": game.config.max_players, "mode": game.config.mode, "port": game.port}
sender.set_dest_address("255.255.255.255", DISCOVERY_PORT)
sender.put_packet(JSON.stringify(info).to_utf8_buffer())
for address in servers.keys():
if Time.get_ticks_msec() - servers[address].seen > 3500:
servers.erase(address)
while available and listener.get_available_packet_count() > 0:
var bytes := listener.get_packet()
var address := listener.get_packet_ip()
var reply_port := listener.get_packet_port()
if bytes.size() > 1024:
continue
var info: Variant = JSON.parse_string(bytes.get_string_from_utf8())
if info is Dictionary and info.get("protocol", "") == "BLOCKLINE_1":
if not info.get("port") is float and not info.get("port") is int:
continue
if int(info.port) < 1024 or int(info.port) > 65535:
continue
var key := address + ":" + str(int(info.port))
info["ping"] = servers.get(key, {}).get("ping", -1)
info["seen"] = Time.get_ticks_msec()
info["ip"] = address
servers[key] = info
var nonce := str(Time.get_ticks_usec())
probes[nonce] = {"key": key, "ip": address, "port": reply_port, "sent": Time.get_ticks_msec()}
sender.set_dest_address(address, reply_port)
sender.put_packet(JSON.stringify({"kind": "ping", "nonce": nonce}).to_utf8_buffer())
for nonce in probes.keys():
if Time.get_ticks_msec() - probes[nonce].sent > 3000:
probes.erase(nonce)
while sender.get_available_packet_count() > 0:
var bytes := sender.get_packet()
var ip := sender.get_packet_ip()
var reply_port := sender.get_packet_port()
if bytes.size() > 256: continue
var packet: Variant = JSON.parse_string(bytes.get_string_from_utf8())
if not packet is Dictionary: continue
var nonce := str(packet.get("nonce", "")).left(32)
if packet.get("kind") == "ping" and game.active and game.is_host:
sender.set_dest_address(ip, reply_port)
sender.put_packet(JSON.stringify({"kind": "pong", "nonce": nonce}).to_utf8_buffer())
elif packet.get("kind") == "pong" and probes.has(nonce):
var probe: Dictionary = probes[nonce]
if ip == probe.ip and reply_port == probe.port and servers.has(probe.key):
servers[probe.key].ping = Time.get_ticks_msec() - probe.sent
probes.erase(nonce)
func _exit_tree() -> void:
listener.close()
sender.close()