extends Node enum Message {JOIN, ID, PEER_CONNECT, PEER_DISCONNECT, OFFER, ANSWER, CANDIDATE, SEAL, GET_PUBLIC} @export var autojoin := true @export var lobby := "" # Will create a new lobby if empty. @export var mesh := true # Will use the lobby host as relay otherwise. var ws: WebSocketPeer = WebSocketPeer.new() var code = 1000 var reason = "Unknown" signal lobby_joined(lobby) signal connected(id, use_mesh) signal disconnected() signal peer_connected(id, host_id) signal peer_disconnected(id) signal offer_received(id, offer) signal answer_received(id, answer) signal candidate_received(id, mid, index, sdp) signal lobby_sealed() signal public_lobbies_recived(id, list) func connect_to_url(url): close() code = 1000 reason = "Unknown" ws.connect_to_url(url) func close(): ws.close() func _process(delta): var old_state: int = ws.get_ready_state() if old_state == WebSocketPeer.STATE_CLOSED: return ws.poll() var state = ws.get_ready_state() if state != old_state and state == WebSocketPeer.STATE_OPEN and autojoin: join_lobby(lobby) while state == WebSocketPeer.STATE_OPEN and ws.get_available_packet_count(): if not _parse_msg(): print("Error parsing message from server.") if state == WebSocketPeer.STATE_CLOSED: code = ws.get_close_code() reason = ws.get_close_reason() disconnected.emit() func _parse_msg(): var parsed = JSON.parse_string(ws.get_packet().get_string_from_utf8()) if typeof(parsed) != TYPE_DICTIONARY or not parsed.has("type") or not parsed.has("id") or \ typeof(parsed.get("data")) != TYPE_STRING: return false var msg := parsed as Dictionary if not str(msg.type).is_valid_int() or not str(msg.id).is_valid_int(): return false var type := str(msg.type).to_int() var src_id := str(msg.id).to_int() if type == Message.ID: connected.emit(src_id, msg.data) elif type == Message.JOIN: lobby_joined.emit(msg.data) elif type == Message.SEAL: lobby_sealed.emit() elif type == Message.PEER_CONNECT: # Client connected peer_connected.emit(src_id, str(msg.data).to_int()) elif type == Message.PEER_DISCONNECT: # Client connected peer_disconnected.emit(src_id) elif type == Message.OFFER: # Offer received offer_received.emit(src_id, msg.data) elif type == Message.ANSWER: # Answer received answer_received.emit(src_id, msg.data) elif type == Message.CANDIDATE: # Candidate received var candidate: PackedStringArray = msg.data.split("\n", false) if candidate.size() != 3: return false if not candidate[1].is_valid_int(): return false candidate_received.emit(src_id, candidate[0], candidate[1].to_int(), candidate[2]) elif type == Message.GET_PUBLIC: public_lobbies_recived.emit(src_id, JSON.parse_string(msg.data)) else: return false return true # Parsed func join_lobby(lobby: String, single_host:=false, public:=false): var data = {"lobby":lobby,"single_host":single_host,"public":public} return _send_msg(Message.JOIN, 0 if mesh else 1, JSON.stringify(data)) func seal_lobby(): return _send_msg(Message.SEAL, 0) func send_candidate(id, mid, index, sdp) -> int: return _send_msg(Message.CANDIDATE, id, "\n%s\n%d\n%s" % [mid, index, sdp]) func send_offer(id, offer) -> int: return _send_msg(Message.OFFER, id, offer) func send_answer(id, answer) -> int: return _send_msg(Message.ANSWER, id, answer) func ask_for_public_lobbies(id) -> int: return _send_msg(Message.GET_PUBLIC, id, "") func _send_msg(type: int, id: int, data:="") -> int: return ws.send_text(JSON.stringify({ "type": type, "id": id, "data": data }))