WIP unification of multiplayer crap
This commit is contained in:
parent
438ba9682f
commit
68a35b25ac
|
@ -1,19 +1,8 @@
|
||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
onready var signaller_url = "ws://localhost:8888"
|
const SignalledWebRTCMultiplayer = preload("./signalled_webrtc_multiplayer.gd")
|
||||||
# onready var signaller_url = "wss://webrtc-signaller.deno.dev:443"
|
|
||||||
|
|
||||||
onready var ice_servers = [
|
onready var client = SignalledWebRTCMultiplayer.new()
|
||||||
{ "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
|
|
||||||
]
|
|
||||||
|
|
||||||
const SignallerClient = preload("signaller_client.gd")
|
|
||||||
const WebRTCNegotiator = preload("webrtc_negotiator.gd")
|
|
||||||
|
|
||||||
onready var signaller_client = SignallerClient.new(signaller_url)
|
|
||||||
onready var negotiator = WebRTCNegotiator.new(ice_servers, signaller_client)
|
|
||||||
|
|
||||||
onready var onetime_cmd_flags = {
|
onready var onetime_cmd_flags = {
|
||||||
"--multiplayer": false,
|
"--multiplayer": false,
|
||||||
|
@ -21,39 +10,28 @@ onready var onetime_cmd_flags = {
|
||||||
"--join-first-available-lobby": false,
|
"--join-first-available-lobby": false,
|
||||||
}
|
}
|
||||||
|
|
||||||
func check_onetime_flag(flag):
|
|
||||||
var result = onetime_cmd_flags["--%s" % flag]
|
|
||||||
onetime_cmd_flags["--%s" % flag] = false
|
|
||||||
return result
|
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
signaller_client.connect("connected", self, "_signaller_connected")
|
client.connect("signaller_connected", self, "_signaller_connected")
|
||||||
negotiator.signaller_client = signaller_client
|
add_child(client)
|
||||||
negotiator.ice_servers = ice_servers
|
|
||||||
add_child(negotiator)
|
|
||||||
|
|
||||||
for flag in onetime_cmd_flags.keys():
|
for flag in onetime_cmd_flags.keys():
|
||||||
if flag in OS.get_cmdline_args():
|
if flag in OS.get_cmdline_args():
|
||||||
onetime_cmd_flags[flag] = true
|
onetime_cmd_flags[flag] = true
|
||||||
|
|
||||||
|
func check_onetime_flag(flag):
|
||||||
|
var result = onetime_cmd_flags["--%s" % flag]
|
||||||
|
onetime_cmd_flags["--%s" % flag] = false
|
||||||
|
return result
|
||||||
|
|
||||||
func goto_scene(scene_resource_name):
|
func goto_scene(scene_resource_name):
|
||||||
var _result = get_tree().change_scene("res://screens/%s.tscn" % scene_resource_name)
|
var _result = get_tree().change_scene("res://screens/%s.tscn" % scene_resource_name)
|
||||||
|
|
||||||
func main_menu():
|
func main_menu():
|
||||||
negotiator.close()
|
client.close()
|
||||||
goto_scene("main_menu")
|
goto_scene("main_menu")
|
||||||
|
|
||||||
func fake_singleplayer():
|
|
||||||
print("Faking network peer for singleplayer...")
|
|
||||||
negotiator.multiplayer.initialize(1, true)
|
|
||||||
get_tree().network_peer = negotiator.multiplayer
|
|
||||||
var peer: WebRTCPeerConnection = WebRTCPeerConnection.new()
|
|
||||||
peer.initialize()
|
|
||||||
negotiator.multiplayer.add_peer(peer, 1)
|
|
||||||
|
|
||||||
func start_singleplayer_game():
|
func start_singleplayer_game():
|
||||||
fake_singleplayer()
|
client.close()
|
||||||
negotiator.close()
|
|
||||||
goto_game()
|
goto_game()
|
||||||
|
|
||||||
func goto_game():
|
func goto_game():
|
||||||
|
@ -63,14 +41,14 @@ func _signaller_connected():
|
||||||
goto_scene("lobby_browser")
|
goto_scene("lobby_browser")
|
||||||
|
|
||||||
func lobby_browser():
|
func lobby_browser():
|
||||||
negotiator.close()
|
client.close()
|
||||||
negotiator.connect_to_signaller()
|
client.connect_to_signaller()
|
||||||
|
|
||||||
func lobby():
|
func lobby():
|
||||||
goto_scene("multiplayer_lobby")
|
goto_scene("multiplayer_lobby")
|
||||||
|
|
||||||
func quit():
|
func quit():
|
||||||
negotiator.close()
|
client.close()
|
||||||
get_tree().quit()
|
get_tree().quit()
|
||||||
|
|
||||||
func key_shortcut(code):
|
func key_shortcut(code):
|
||||||
|
|
214
scripts/global/signalled_webrtc_multiplayer.gd
Normal file
214
scripts/global/signalled_webrtc_multiplayer.gd
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
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(peer_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])
|
||||||
|
|
||||||
|
print(self)
|
||||||
|
print(websocket)
|
||||||
|
print(webrtc)
|
||||||
|
add_child(websocket)
|
||||||
|
add_child(webrtc)
|
||||||
|
|
||||||
|
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 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))
|
|
@ -28,7 +28,6 @@ func _ready():
|
||||||
signaller_client.connect("answer_received", self, "_answer_received")
|
signaller_client.connect("answer_received", self, "_answer_received")
|
||||||
signaller_client.connect("candidate_received", self, "_candidate_received")
|
signaller_client.connect("candidate_received", self, "_candidate_received")
|
||||||
|
|
||||||
signaller_client.connect("peer_init", self, "_peer_id_set")
|
|
||||||
signaller_client.connect("peer_data", self, "_peer_data")
|
signaller_client.connect("peer_data", self, "_peer_data")
|
||||||
|
|
||||||
webrtc.connect("connection_succeeded", self, "_connection_succeeded")
|
webrtc.connect("connection_succeeded", self, "_connection_succeeded")
|
||||||
|
|
|
@ -12,7 +12,7 @@ onready var lobbies = {}
|
||||||
onready var lobby = preload("res://objects/lobby.tscn")
|
onready var lobby = preload("res://objects/lobby.tscn")
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
display_name_edit.text = Global.signaller_client.display_name
|
display_name_edit.text = Global.client.display_name
|
||||||
Global.signaller_client.connect("lobby_data", self, "_lobby_data")
|
Global.signaller_client.connect("lobby_data", self, "_lobby_data")
|
||||||
Global.signaller_client.connect("lobby_delete", self, "_lobby_delete")
|
Global.signaller_client.connect("lobby_delete", self, "_lobby_delete")
|
||||||
Global.signaller_client.connect("lobby_joined", self, "_lobby_joined")
|
Global.signaller_client.connect("lobby_joined", self, "_lobby_joined")
|
||||||
|
|
101
server.ts
101
server.ts
|
@ -3,11 +3,12 @@
|
||||||
const SERVER_VERSION = "0.2.0";
|
const SERVER_VERSION = "0.2.0";
|
||||||
// TODO: version comparison
|
// TODO: version comparison
|
||||||
|
|
||||||
type ID = string;
|
type UUID = string;
|
||||||
|
type ID = number;
|
||||||
|
|
||||||
// app state
|
// app state
|
||||||
const allLobbies = new Map<ID, Lobby>();
|
const allLobbies = new Map<UUID, Lobby>();
|
||||||
const allClients = new Map<ID, Client>();
|
const allClients = new Map<UUID, Client>();
|
||||||
// TODO: client index by id?
|
// TODO: client index by id?
|
||||||
|
|
||||||
interface DataMessage {
|
interface DataMessage {
|
||||||
|
@ -18,7 +19,12 @@ type ServerDataObject = Record<
|
||||||
"peerId" | string,
|
"peerId" | string,
|
||||||
string | number | symbol | null | boolean
|
string | number | symbol | null | boolean
|
||||||
>;
|
>;
|
||||||
type ServerData = ServerDataObject[] | ServerDataObject | string | boolean;
|
type ServerData =
|
||||||
|
| ServerDataObject[]
|
||||||
|
| ServerDataObject
|
||||||
|
| string
|
||||||
|
| boolean
|
||||||
|
| number;
|
||||||
type Message = string | DataMessage;
|
type Message = string | DataMessage;
|
||||||
|
|
||||||
const buildMessage = (
|
const buildMessage = (
|
||||||
|
@ -31,20 +37,19 @@ const buildMessage = (
|
||||||
};
|
};
|
||||||
|
|
||||||
class Client {
|
class Client {
|
||||||
id: ID;
|
id: number | null;
|
||||||
peerId: number | null;
|
uuid: string;
|
||||||
name: string;
|
name: string;
|
||||||
socket: WebSocket;
|
socket: WebSocket;
|
||||||
lobby: Lobby | null;
|
lobby: Lobby | null;
|
||||||
|
|
||||||
constructor(socket: WebSocket) {
|
constructor(socket: WebSocket) {
|
||||||
this.id = crypto.randomUUID();
|
this.id = null;
|
||||||
|
this.uuid = crypto.randomUUID();
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
this.peerId = null;
|
|
||||||
this.name = "Client";
|
this.name = "Client";
|
||||||
this.lobby = null;
|
this.lobby = null;
|
||||||
|
allClients.set(this.uuid, this);
|
||||||
allClients.set(this.id, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isConnected() {
|
isConnected() {
|
||||||
|
@ -56,7 +61,7 @@ class Client {
|
||||||
if (this.isConnected()) {
|
if (this.isConnected()) {
|
||||||
this.socket.close();
|
this.socket.close();
|
||||||
}
|
}
|
||||||
allClients.delete(this.id);
|
allClients.delete(this.uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
send(message: Message) {
|
send(message: Message) {
|
||||||
|
@ -95,18 +100,17 @@ class Client {
|
||||||
const peers: {
|
const peers: {
|
||||||
id: ID;
|
id: ID;
|
||||||
name: string;
|
name: string;
|
||||||
peerId: number | null;
|
|
||||||
}[] = [];
|
}[] = [];
|
||||||
this.lobby.clients.forEach(({ id, name, peerId }) =>
|
this.lobby.clients.forEach(({ id, name }) => {
|
||||||
peers.push({ id, name, peerId })
|
if (typeof id === "number") peers.push({ id, name });
|
||||||
);
|
});
|
||||||
this.send(buildMessage("peer_data", peers));
|
this.send(buildMessage("peer_data", peers));
|
||||||
// TODO: chunk async?
|
// TODO: chunk async?
|
||||||
}
|
}
|
||||||
|
|
||||||
lobbyList() {
|
lobbyList() {
|
||||||
const netLobbies: {
|
const netLobbies: {
|
||||||
id: ID;
|
uuid: UUID;
|
||||||
name: string;
|
name: string;
|
||||||
maxPlayers: number;
|
maxPlayers: number;
|
||||||
locked: boolean;
|
locked: boolean;
|
||||||
|
@ -124,9 +128,9 @@ class Client {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
lobbyDelete({ id }: Lobby) {
|
lobbyDelete({ uuid }: Lobby) {
|
||||||
if (this.lobby) return;
|
if (this.lobby) return;
|
||||||
this.send(buildMessage("lobby_delete", { id }));
|
this.send(buildMessage("lobby_delete", { uuid }));
|
||||||
}
|
}
|
||||||
|
|
||||||
lobbyCreate(opts?: Partial<Lobby>) {
|
lobbyCreate(opts?: Partial<Lobby>) {
|
||||||
|
@ -134,7 +138,7 @@ class Client {
|
||||||
this.send(
|
this.send(
|
||||||
buildMessage(
|
buildMessage(
|
||||||
"info",
|
"info",
|
||||||
`cannot create lobby: already in lobby ${this.lobby.id}`,
|
`cannot create lobby: already in lobby ${this.lobby.uuid}`,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
@ -147,7 +151,7 @@ class Client {
|
||||||
this.send(
|
this.send(
|
||||||
buildMessage(
|
buildMessage(
|
||||||
`info`,
|
`info`,
|
||||||
`cannot join lobby: already in lobby ${this.lobby.id}`,
|
`cannot join lobby: already in lobby ${this.lobby.uuid}`,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
@ -157,7 +161,7 @@ class Client {
|
||||||
this.send(
|
this.send(
|
||||||
buildMessage("lobby_joined", {
|
buildMessage("lobby_joined", {
|
||||||
...this.lobby.toData(),
|
...this.lobby.toData(),
|
||||||
peerId: this.peerId,
|
id: this.id,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -168,44 +172,44 @@ class Client {
|
||||||
this.send(`[info] cannot leave lobby (not in a lobby)`);
|
this.send(`[info] cannot leave lobby (not in a lobby)`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.peerId = null;
|
this.id = null;
|
||||||
this.lobby = null;
|
this.lobby = null;
|
||||||
if (this.isConnected()) {
|
if (this.isConnected()) {
|
||||||
this.send(buildMessage("lobby_left", { id: leavingLobby.id }));
|
this.send(buildMessage("lobby_left", { id: leavingLobby.uuid }));
|
||||||
}
|
}
|
||||||
leavingLobby.removeClient(this);
|
leavingLobby.removeClient(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Lobby {
|
class Lobby {
|
||||||
id: ID;
|
uuid: UUID;
|
||||||
name: string;
|
name: string;
|
||||||
clients: Map<ID, Client>;
|
clients: Map<ID, Client>;
|
||||||
hostClientId: ID;
|
hostClientUuid: UUID;
|
||||||
maxPlayers: number;
|
maxPlayers: number;
|
||||||
locked: boolean;
|
locked: boolean;
|
||||||
|
|
||||||
_shouldNotify: boolean;
|
_shouldNotify: boolean;
|
||||||
|
|
||||||
constructor(host: Client, name?: string, maxPlayers = 20) {
|
constructor(host: Client, name?: string, maxPlayers = 20) {
|
||||||
this.id = crypto.randomUUID();
|
this.uuid = crypto.randomUUID();
|
||||||
this.hostClientId = host.id;
|
this.hostClientUuid = host.uuid;
|
||||||
this.clients = new Map<ID, Client>();
|
this.clients = new Map<ID, Client>();
|
||||||
this.name = name || this.id;
|
this.name = name || this.uuid;
|
||||||
this.maxPlayers = maxPlayers;
|
this.maxPlayers = maxPlayers;
|
||||||
this.locked = false;
|
this.locked = false;
|
||||||
|
|
||||||
this._shouldNotify = true;
|
this._shouldNotify = true;
|
||||||
|
|
||||||
allLobbies.set(this.id, this);
|
allLobbies.set(this.uuid, this);
|
||||||
host.peerId = 1;
|
host.id = 1;
|
||||||
host.lobbyJoin(this);
|
host.lobbyJoin(this);
|
||||||
this.notify();
|
this.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
toData() {
|
toData() {
|
||||||
return {
|
return {
|
||||||
id: this.id,
|
uuid: this.uuid,
|
||||||
name: this.name,
|
name: this.name,
|
||||||
maxPlayers: this.maxPlayers,
|
maxPlayers: this.maxPlayers,
|
||||||
locked: this.locked,
|
locked: this.locked,
|
||||||
|
@ -217,7 +221,7 @@ class Lobby {
|
||||||
requestor: Client,
|
requestor: Client,
|
||||||
newValues: { name?: string; maxPlayers?: number; locked?: boolean },
|
newValues: { name?: string; maxPlayers?: number; locked?: boolean },
|
||||||
) {
|
) {
|
||||||
if (requestor.peerId === 1) {
|
if (requestor.id === 1) {
|
||||||
for (const k in newValues) {
|
for (const k in newValues) {
|
||||||
switch (k) {
|
switch (k) {
|
||||||
case "name":
|
case "name":
|
||||||
|
@ -250,7 +254,7 @@ class Lobby {
|
||||||
|
|
||||||
remove() {
|
remove() {
|
||||||
this._shouldNotify = false;
|
this._shouldNotify = false;
|
||||||
allLobbies.delete(this.id);
|
allLobbies.delete(this.uuid);
|
||||||
allClients.forEach((client) => client.lobbyDelete(this));
|
allClients.forEach((client) => client.lobbyDelete(this));
|
||||||
this.clients.forEach((client) => {
|
this.clients.forEach((client) => {
|
||||||
client.lobbyLeave();
|
client.lobbyLeave();
|
||||||
|
@ -262,24 +266,23 @@ class Lobby {
|
||||||
}
|
}
|
||||||
|
|
||||||
addClient(client: Client) {
|
addClient(client: Client) {
|
||||||
if (!client.peerId) {
|
if (!client.id) {
|
||||||
const arr = new Int32Array(1);
|
const arr = new Int32Array(1);
|
||||||
crypto.getRandomValues(arr);
|
crypto.getRandomValues(arr);
|
||||||
client.peerId = Math.abs(arr[0]);
|
client.id = Math.abs(arr[0]);
|
||||||
}
|
}
|
||||||
client.send(buildMessage("init_peer", client.peerId.toString()));
|
client.send(buildMessage("init_peer", client.id));
|
||||||
this.broadcast(
|
this.broadcast(
|
||||||
buildMessage("peer_data", [{
|
buildMessage("peer_data", [{
|
||||||
id: client.id,
|
id: client.id,
|
||||||
name: client.name,
|
name: client.name,
|
||||||
peerId: client.peerId,
|
|
||||||
}]),
|
}]),
|
||||||
);
|
);
|
||||||
client.send(
|
client.send(
|
||||||
buildMessage(
|
buildMessage(
|
||||||
"peer_data",
|
"peer_data",
|
||||||
Array.from(this.clients.values()).map(
|
Array.from(this.clients.values()).map(
|
||||||
({ id, name, peerId }) => ({ id, name, peerId }),
|
({ id, name }) => ({ id, name }),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -287,19 +290,20 @@ class Lobby {
|
||||||
this.notify();
|
this.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
removeClient({ id }: Client) {
|
removeClient({ id, uuid }: Client) {
|
||||||
|
if (typeof id !== "number") return;
|
||||||
this.clients.delete(id);
|
this.clients.delete(id);
|
||||||
this.broadcast(buildMessage("peer_left", [{ id }]));
|
this.broadcast(buildMessage("peer_left", [{ id }]));
|
||||||
if (id === this.hostClientId) {
|
if (uuid === this.hostClientUuid) {
|
||||||
console.warn("Host left!");
|
console.warn("Host left!");
|
||||||
this.remove();
|
this.remove();
|
||||||
}
|
}
|
||||||
this.notify();
|
this.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
getPeer(peerId: number): Client | null {
|
getPeer(id: number): Client | null {
|
||||||
for (const [_id, client] of this.clients) {
|
for (const [_id, client] of this.clients) {
|
||||||
if (client.peerId == peerId) return client;
|
if (client.id == id) return client;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -347,7 +351,6 @@ function onMessage(client: Client, ev: MessageEvent) {
|
||||||
}
|
}
|
||||||
client.send(buildMessage("init", {
|
client.send(buildMessage("init", {
|
||||||
id: client.id,
|
id: client.id,
|
||||||
peerId: client.peerId,
|
|
||||||
name: client.name,
|
name: client.name,
|
||||||
serverVersion: SERVER_VERSION,
|
serverVersion: SERVER_VERSION,
|
||||||
}));
|
}));
|
||||||
|
@ -398,16 +401,16 @@ function onMessage(client: Client, ev: MessageEvent) {
|
||||||
case "offer": {
|
case "offer": {
|
||||||
if (!client.lobby) return;
|
if (!client.lobby) return;
|
||||||
if (typeof msg.data !== "object") return;
|
if (typeof msg.data !== "object") return;
|
||||||
const webrtcMessage = (msg.data as { peerId?: number });
|
const webrtcMessage = (msg.data as { id?: number });
|
||||||
if (!webrtcMessage.peerId || typeof webrtcMessage.peerId !== "number") {
|
if (!webrtcMessage.id || typeof webrtcMessage.id !== "number") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(
|
console.log(
|
||||||
`Received WebRTC Negotiation Message (type: ${msg.type}, from: ${client.peerId}, to: ${webrtcMessage.peerId})...`,
|
`Received WebRTC Negotiation Message (type: ${msg.type}, from: ${client.id}, to: ${webrtcMessage.id})...`,
|
||||||
);
|
);
|
||||||
const destClient = client.lobby.getPeer(webrtcMessage.peerId);
|
const destClient = client.lobby.getPeer(webrtcMessage.id);
|
||||||
if (!destClient || !destClient.peerId) return;
|
if (!destClient || !destClient.id) return;
|
||||||
webrtcMessage.peerId = client.peerId as number;
|
webrtcMessage.id = client.id as number;
|
||||||
destClient.send(buildMessage(msg.type, webrtcMessage));
|
destClient.send(buildMessage(msg.type, webrtcMessage));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue