79 lines
3.1 KiB
GDScript
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()
|