extends Node const DISPLAY_NAME_FILE = "user://display_name.txt" const DEFAULT_DISPLAY_NAME = "UnnamedPlayer" const DEFAULT_SIGNALLER_URL = "ws://localhost:8888" # const DEFAULT_SIGNALLER_URL = "wss://webrtc-signaller.deno.dev:443" const DEFAULT_ICE_SERVERS = [ { "urls": ["stun:[::1]:3478", "stun:stun.l.google.com:19302"] }, # { "urls": ["stun:stun.l.google.com:19302"] }, # just google # { "urls": ["stun:[::1]:3478"] }, # just localhost ] signal lobby_data(lobbies) signal lobby_joined(lobby_and_peer) signal lobby_left(id) signal lobby_delete(id) signal peer_data(peers) signal peer_left(id) signal peer_init(id) signal candidate_received(cand) signal offer_received(data) signal answer_received(data) # webrtc signal webrtc_peer_connected(id) signal webrtc_peer_disconnected(id) signal webrtc_connection_succeeded() # websocket signal signaller_connected signal signaller_connection_failure signal signaller_disconnected onready var webrtc = WebRTCMultiplayer.new() onready var websocket = WebSocketClient.new() onready var display_name = DEFAULT_DISPLAY_NAME setget set_display_name onready var current_lobby = null onready var lobbies = {} onready var peers = {} onready var signaller_url = DEFAULT_SIGNALLER_URL func _init(): pass func _ready(): display_name = _load_display_name() webrtc.connect("connection_succeeded", self, "_webrtc_connection_succeeded") webrtc.connect("peer_connected", self, "_webrtc_peer_connected") webrtc.connect("peer_disconnected", self, "_webrtc_peer_disconnected") websocket.connect("connection_established", self, "_signaller_connected") websocket.connect("data_received", self, "_signaller_data") websocket.connect("server_close_request", self, "_signaller_close_request") websocket.connect("connection_closed", self, "_signaller_closed") websocket.connect("connection_error", self, "_signaller_error", [false]) websocket.connect("connection_failed", self, "_signaller_error", [false]) func connect_to_signaller(url = signaller_url): signaller_url = url close() print("Attempting to connect to WebSocket signalling server at %s" % signaller_url) var result = websocket.connect_to_url(signaller_url) if result != OK: print("Failed to connect to WebSocket signalling server at %s: %s" % [signaller_url, result]) func close(): webrtc.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 get_lobby_name(): return current_lobby.name func create_lobby(): return _send("lobby_create") func is_host(): return get_tree().get_network_unique_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(id, mid, index, sdp) -> int: return _send_json({"id": id, "mid": mid, "index": index, "sdp": sdp}, "candidate") func send_offer(id, offer) -> int: return _send_json({"id": id, "offer": offer }, "offer") func send_answer(id, answer) -> int: return _send_json({"id": id, "answer": answer }, "answer") func _webrtc_peer_connected(id): emit_signal("webrtc_peer_connected", id) func _webrtc_peer_disconnected(id): emit_signal("webrtc_peer_disconnected", id) func _webrtc_connection_succeeded(): emit_signal("webrtc_connection_succeeded") func _signaller_error(err): print("WebSocket error: %s" % err) emit_signal("signaller_connection_failure") _signaller_closed(err) func _signaller_closed(code): print("WebSocket closed: %s: " % code) emit_signal("signaller_disconnected") func _signaller_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 _signaller_connected(protocol = ""): print("Signaller connected via WebSocket using protocol %s" % protocol) websocket.get_peer(1).set_write_mode(WebSocketPeer.WRITE_MODE_TEXT) emit_signal("signaller_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 _signaller_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_signaller_message(data.result) var _result = websocket.get_peer(1).put_packet("".to_utf8()) else: var d = msg.split(":", false, 2) _process_signaller_message({type = d[0], data = d[1]}) func _process_signaller_message(data: Dictionary): match data["type"]: # "init": "init_peer": webrtc.initialize(int(data.data), true) get_tree().network_peer = webrtc "lobby_data": emit_signal("lobby_data", data.data) "lobby_delete": emit_signal("lobby_delete", data.id) "lobby_joined": current_lobby = data emit_signal("lobby_joined", data) "lobby_left": emit_signal("lobby_left", data.id) "peer_data": emit_signal("peer_data", [data] if data.has("id") else data.data) "peer_left": emit_signal("peer_left", data.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))