extends Node """ This module is responsible for making a WebSocket connection to the signaller in order to enable establishish WebRTC P2P connections. Another module is expected to fully setup the peer connections. """ const DISPLAY_NAME_FILE = "user://display_name.txt" const DEFAULT_DISPLAY_NAME = "UnnamedPlayer" # lobby data signal lobby_data(lobbies) signal lobby_joined(lobby_and_peer) signal lobby_left(id) signal lobby_delete(id) # peer data signal peer_data(peers) signal peer_left(id) signal peer_init(peer_id) # WebRTC negotiations signal candidate_received(cand) signal offer_received(data) signal answer_received(data) # underlying websocket signal received_message signal connected signal connection_failure signal disconnected onready var websocket: WebSocketClient = WebSocketClient.new() # TODO: setget these? onready var client_id = null onready var display_name = DEFAULT_DISPLAY_NAME setget set_display_name onready var peer_id = null onready var lobby_data = null var websocket_url = null func _init(url): websocket_url = url func _ready(): display_name = _load_display_name() websocket.connect("data_received", self, "_handle_data") websocket.connect("connection_established", self, "_connected") # websocket.connect("connection_succeeded", self, "_connection_succeeded") websocket.connect("connection_closed", self, "_closed") websocket.connect("connection_error", self, "_connection_error", [false]) websocket.connect("connection_failed", self, "_connection_error", [false]) websocket.connect("server_close_request", self, "_close_request") func connect_websocket(): close() print("Attempting to connect to WebSocket signalling server at %s" % websocket_url) var result = websocket.connect_to_url(websocket_url) if result != OK: print("Failed to connect to WebSocket signalling server at %s: %s" % [websocket_url, result]) func close(): websocket.disconnect_from_host() func set_display_name(new_display_name: String): display_name = new_display_name _send("update_display_name:%s" % display_name) func request_lobby_list(): return _send("request_lobby_list") func join_lobby(id: String): return _send("lobby_join:%s" % id) func create_lobby(): return _send("lobby_create") func is_host(): return peer_id == 1 func set_lobby_name(s: String): if is_host(): _send_json({"name": s}, "update_lobby") func lock_lobby(): if is_host(): _send_json({"locked": true}, "update_lobby") func set_lobby_max_players(n: int): if is_host(): _send_json({"maxPlayers": n}, "update_lobby") func request_peer_list(): _send("request_peer_list") func send_candidate(peerId, mid, index, sdp) -> int: return _send_json({"peerId": peerId, "mid": mid, "index": index, "sdp": sdp}, "candidate") func send_offer(peerId, offer) -> int: return _send_json({"peerId": peerId, "offer": offer }, "offer") func send_answer(peerId, answer) -> int: return _send_json({"peerId": peerId, "answer": answer }, "answer") func get_lobby_name(): return lobby_data.name func _closed(code): print("WebSocket closed: %s: " % code) emit_signal("disconnected") func _close_request(code: int, reason: String): print("Received WebSocket close request from signalling server - Code: %s, Reason: %s" % [code, reason]) # TODO: does this fire _closed? func _connected(protocol = ""): print("Signaller connected via WebSocket using protocol %s" % protocol) websocket.get_peer(1).set_write_mode(WebSocketPeer.WRITE_MODE_TEXT) emit_signal("connected") _send_json({name = display_name}, "init") func _process(_delta: float): var status: int = websocket.get_connection_status() if status == WebSocketClient.CONNECTION_CONNECTED or status == WebSocketClient.CONNECTION_CONNECTING: websocket.poll() func _handle_data(): var msg: String = websocket.get_peer(1).get_packet().get_string_from_utf8() if msg.begins_with("json:"): var data = JSON.parse(msg.substr(5)) if data.error == OK: _process_message(data.result) var _result = websocket.get_peer(1).put_packet("".to_utf8()) else: var d = msg.split(":", false, 2) _process_message({type = d[0], data = d[1]}) func _process_message(data: Dictionary): match data["type"]: "init": client_id = data.id print("Signaller Received init response: %s" % data) "init_peer": print("init_peer: %s" % data) peer_id = int(data.data) emit_signal("peer_init", peer_id) "lobby_data": emit_signal("lobby_data", data.data) "lobby_delete": emit_signal("lobby_delete", data.id) "lobby_joined": print("lobby_joined: %s" % data) lobby_data = data emit_signal("lobby_joined", data) "lobby_left": peer_id = null emit_signal("lobby_left", data["id"]) "peer_data": emit_signal("peer_data", [data] if data.has("id") else data["data"]) "peer_left": print("Peer Left: %s" % data) emit_signal("peer_left", data) "candidate": print("Candidate received - Data: %s" % JSON.print(data)) emit_signal("candidate_received", data) "offer": print("Offer received - Data: %s" % JSON.print(data)) emit_signal("offer_received", data) "answer": print("Answer received - Data: %s" % JSON.print(data)) emit_signal("answer_received", data) "ping": _send("pong") _: print("Unhandled Message - Data: %s" % JSON.print(data)) func _load_display_name(): var _display_name = DEFAULT_DISPLAY_NAME var display_name_file = File.new() if display_name_file.open(DISPLAY_NAME_FILE, File.READ) == OK: _display_name = display_name_file.get_as_text() else: print("Failed to open %s for reading display_name, creating default" % DISPLAY_NAME_FILE) _store_display_name() display_name_file.close() return _display_name func _store_display_name(): var display_name_file = File.new() if display_name_file.open(DISPLAY_NAME_FILE, File.WRITE) == OK: display_name_file.store_string(display_name) else: print("Failed to open %s for writing display_name" % DISPLAY_NAME_FILE) display_name_file.close() func _send(s: String): return websocket.get_peer(1).put_packet(s.to_utf8()) func _send_json(data: Dictionary, type=null): if type != null: data["type"] = type _send("json:%s" % JSON.print(data))