clockwork-city/addons/tube/tube_local_signaling_peer.gd

301 lines
6.3 KiB
GDScript3
Raw Permalink Normal View History

2026-04-13 11:34:00 -05:00
class_name TubeLocalSignalingPeer extends RefCounted
const BROADCAST_ADDRESS := "255.255.255.255"
const MIN_PORT := 49152
const MAX_PORT := 65535
const PORT_RANGE := MAX_PORT - MIN_PORT
signal received_signaling_data(data: Dictionary, address: String)
signal warning_raised(message: String)
signal data_sent(data: Dictionary, address: String, port: int)
signal received_data(data: Variant, address: String, port: int)
var udp_peer := PacketPeerUDP.new()
var port: int
static func is_local_signaling_available() -> bool:
return OS.get_name() != "Web"
func raise_warning(message: String):
push_warning(message)
warning_raised.emit(message)
func bind(
p_app_id: String,
p_session_id: String,
p_peer_id: int
) -> Error:
port = get_port(
p_app_id,
p_session_id,
p_peer_id
)
var error := udp_peer.bind(port)
if error:
raise_warning(
"Cannot set bind to port {port}: {error}".format({
"port": port,
"error": error_string(error)
}))
udp_peer.close()
return error
func is_bound() -> bool:
return udp_peer.is_bound()
func close():
udp_peer.close()
func get_port(
p_app_id: String,
p_session_id: String,
p_peer_id: int
) -> int:
return (p_app_id.hash() + p_session_id.hash() + p_peer_id)%PORT_RANGE + MIN_PORT
## Encodes tracker packet data as JSON string.
func encode_data(data: Dictionary) -> PackedByteArray:
var json := JSON.stringify(data)
return json.to_utf8_buffer()
## Decodes tracker packet data from a [PackedByteArray].
func decode_packet(p_packet: PackedByteArray) -> Variant:
var string := p_packet.get_string_from_utf8()
var data = JSON.parse_string(string)
if null == data:
return string
return data
func broadcast_signaling_data(
p_app_id: String,
p_session_id: String,
p_peer_id: int,
p_to_peer_id: int,
description: Dictionary,
ice_candidates: Array
) -> Error:
var data := {
"app_id": p_app_id,
"session_id": p_session_id,
"peer_id": p_peer_id,
"type": description.type,
"sdp": description.sdp,
"ice_candidates": ice_candidates,
}
var destination_port := get_port(
p_app_id,
p_session_id,
1, # Server peer_id
)
udp_peer.set_broadcast_enabled(true)
var error := udp_peer.set_dest_address(
BROADCAST_ADDRESS,
destination_port
)
if error:
raise_warning(
"Cannot set destination address to {address}: {error}".format({
"address": BROADCAST_ADDRESS,
"error": error_string(error)
}))
return error
error = udp_peer.put_packet(encode_data(data))
if error:
raise_warning(
"Cannot send packet to {address}: {error}".format({
"address": BROADCAST_ADDRESS,
"error": error_string(error)
}))
return error
data_sent.emit(
data,
BROADCAST_ADDRESS,
destination_port
)
return error
func send_signaling_data(
p_address: String,
p_app_id: String,
p_session_id: String,
p_peer_id: int,
p_to_peer_id: int,
description: Dictionary,
ice_candidates: Array
) -> Error:
var data := {
"app_id": p_app_id,
"session_id": p_session_id,
"peer_id": p_peer_id,
"type": description.type,
"sdp": description.sdp,
"ice_candidates": ice_candidates,
}
var destination_port := get_port(
p_app_id,
p_session_id,
p_to_peer_id,
)
var error := udp_peer.set_dest_address(
p_address,
destination_port,
)
if error:
raise_warning(
"Cannot set destination address to {address}: {error}".format({
"address": p_address,
"error": error_string(error)
}))
return error
error = udp_peer.put_packet(encode_data(data))
if error:
raise_warning(
"Cannot send packet to {address}: {error}".format({
"address": p_address,
"error": error_string(error)
}))
return error
data_sent.emit(
data,
p_address,
destination_port
)
return error
func _handle_signaling_data(p_data: Variant):
if not p_data is Dictionary:
raise_warning("signaling data invalid data type")
return
if not p_data.has("app_id"):
raise_warning("signaling data has no app_id")
return
if not p_data.app_id is String:
raise_warning("app_id invalid data type")
return
if not p_data.has("session_id"):
raise_warning("signaling data has no session_id")
return
if not p_data.session_id is String:
raise_warning("session_id invalid data type")
return
if not p_data.has("peer_id"):
raise_warning("signaling data has no peer_id")
return
if not p_data.peer_id is float:
raise_warning("peer_id invalid data type")
return
if not p_data.has("sdp"):
raise_warning("signaling data has no sdp")
return
if not p_data.sdp is String:
raise_warning("sdp invalid data type")
return
if not p_data.has("type"):
raise_warning("signaling data has no type")
return
if not p_data.type is String:
raise_warning("type invalid data type")
return
if not p_data.has("ice_candidates"):
raise_warning("signaling data has no ice_candidates")
return
if not p_data.ice_candidates is Array:
raise_warning("ice_candidates invalid data type")
return
received_signaling_data.emit(
p_data, udp_peer.get_packet_ip()
)
static func get_app_id_from_signaling_data(p_data: Dictionary) -> String:
return p_data.app_id
static func get_session_id_from_signaling_data(p_data: Dictionary) -> String:
return p_data.session_id
static func get_peer_id_from_signaling_data(p_data: Dictionary) -> int:
return int(p_data.peer_id)
static func get_type_from_signaling_data(p_data: Dictionary) -> String:
return p_data.type
static func get_sdp_from_signaling_data(p_data: Dictionary) -> String:
return p_data.sdp
static func get_ice_candidates_from_signaling_data(p_data: Dictionary) -> Array:
return p_data.ice_candidates
static func is_ice_candidate_data_valid(p_data: Variant) -> bool:
return TubeTracker.is_ice_candidate_data_valid(p_data)
static func get_media_from_ice_candidate_data(p_data: Dictionary) -> String:
return TubeTracker.get_media_from_ice_candidate_data(p_data)
static func get_index_from_ice_candidate_data(p_data: Dictionary) -> int:
return TubeTracker.get_index_from_ice_candidate_data(p_data)
static func get_sdp_from_ice_candidate_data(p_data: Dictionary) -> String:
return TubeTracker.get_sdp_from_ice_candidate_data(p_data)
func _process(delta):
while 0 < udp_peer.get_available_packet_count():
var data = decode_packet(udp_peer.get_packet())
received_data.emit(
data,
udp_peer.get_packet_ip(),
udp_peer.get_packet_port()
)
_handle_signaling_data(data)