This commit is contained in:
Daniel Flanagan 2021-12-02 15:13:12 -06:00
parent 9c5b376852
commit 292bf5ba5c
Signed by: lytedev
GPG key ID: 5B2020A0F9921EF4
31 changed files with 209 additions and 164 deletions

1
.gitattributes vendored Normal file
View file

@ -0,0 +1 @@
*.ttf filter=lfs diff=lfs merge=lfs -text

BIN
assets/fonts/iosevkalyte/iosevkalyte-black.ttf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/fonts/iosevkalyte/iosevkalyte-blackitalic.ttf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/fonts/iosevkalyte/iosevkalyte-blackoblique.ttf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/fonts/iosevkalyte/iosevkalyte-bold.ttf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/fonts/iosevkalyte/iosevkalyte-bolditalic.ttf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/fonts/iosevkalyte/iosevkalyte-boldoblique.ttf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/fonts/iosevkalyte/iosevkalyte-book.ttf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/fonts/iosevkalyte/iosevkalyte-bookitalic.ttf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/fonts/iosevkalyte/iosevkalyte-bookoblique.ttf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/fonts/iosevkalyte/iosevkalyte-extended.ttf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/fonts/iosevkalyte/iosevkalyte-extendedblack.ttf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/fonts/iosevkalyte/iosevkalyte-extendedblackitalic.ttf (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

BIN
assets/fonts/iosevkalyte/iosevkalyte-extendedbold.ttf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/fonts/iosevkalyte/iosevkalyte-extendedbolditalic.ttf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/fonts/iosevkalyte/iosevkalyte-extendedboldoblique.ttf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/fonts/iosevkalyte/iosevkalyte-extendedbook.ttf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/fonts/iosevkalyte/iosevkalyte-extendedbookitalic.ttf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/fonts/iosevkalyte/iosevkalyte-extendedbookoblique.ttf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/fonts/iosevkalyte/iosevkalyte-extendeditalic.ttf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/fonts/iosevkalyte/iosevkalyte-extendedoblique.ttf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/fonts/iosevkalyte/iosevkalyte-italic.ttf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/fonts/iosevkalyte/iosevkalyte-oblique.ttf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/fonts/iosevkalyte/iosevkalyte-regular.ttf (Stored with Git LFS) Normal file

Binary file not shown.

6
iosevkalyte.tres Normal file
View file

@ -0,0 +1,6 @@
[gd_resource type="DynamicFont" load_steps=2 format=2]
[ext_resource path="res://assets/fonts/iosevkalyte/iosevkalyte-regular.ttf" type="DynamicFontData" id=1]
[resource]
font_data = ExtResource( 1 )

View file

@ -1,30 +1,40 @@
extends Node2D
onready var peers = $MarginContainer/peers
onready var cursors = {}
func _ready():
Global.client.signaller.connect("peer_joined", self, "_peer_joined")
Global.client.signaller.connect("peer_left", self, "_peer_left")
Global.client.signaller.connect("lobby_left", self, "_lobby_left")
$MarginContainer/Label.text = Global.client.signaller.lobby_id
$MarginContainer/Label.text = "%s (%s)" % [Global.client.signaller.lobby_id, get_tree().get_network_unique_id()]
Global.client.signaller.connect("websocket_disconnected", self, "_signaller_disconnected")
Global.client.signaller.request_peer_list()
_peer_joined([{
"id": Global.client.signaller.client_id,
"peerId": Global.client.signaller.peer_id,
"name": "You!",
}])
func _signaller_disconnected():
Global.main_menu()
func _draw():
pass
func _process(_delta):
pass
$text.text = JSON.print(Global.client.signaller)
func _peer_joined(joined_peers):
for i in range(len(joined_peers)):
var id = joined_peers[i]["id"]
var exists = false
for j in range(peers.get_item_count()):
if id == peers.get_item_metadata(j)["id"]:
exists = true
break
if !exists:
var peerId = joined_peers[i]["peerId"]
var name = joined_peers[i]["name"]
print("New Lobby Peer ", id, name)
peers.add_item("%s" % name)
print("New Lobby Peer ID: %s, Name: %s, PeerID: %s" % [id, name, peerId])
peers.add_item("%s (%s)" % [name, peerId])
peers.set_item_metadata(peers.get_item_count() - 1, { "id": id })
func _peer_left(id):
@ -34,7 +44,15 @@ func _peer_left(id):
return
func _on_Button_pressed():
Global.lobby_browser()
rpc("shift_cursor")
# move cursor a bit
remotesync func shift_cursor():
print("Shifting cursor...")
$shifter.rect_position.x += 10
func _lobby_left(_id):
Global.lobby_browser()
func _on_leave_button_pressed():
Global.lobby_browser()

View file

@ -1,10 +1,41 @@
[gd_scene load_steps=2 format=2]
[gd_scene load_steps=3 format=2]
[ext_resource path="res://lobby.gd" type="Script" id=1]
[ext_resource path="res://iosevkalyte.tres" type="DynamicFont" id=2]
[node name="Node2D" type="Node2D"]
script = ExtResource( 1 )
[node name="text" type="TextEdit" parent="."]
margin_left = 208.0
margin_top = 162.0
margin_right = 739.0
margin_bottom = 453.0
custom_fonts/font = ExtResource( 2 )
text = "information goes in here"
readonly = true
highlight_current_line = true
syntax_highlighting = true
show_line_numbers = true
draw_tabs = true
draw_spaces = true
smooth_scrolling = true
wrap_enabled = true
__meta__ = {
"_edit_use_anchors_": false
}
[node name="shifter" type="Label" parent="."]
margin_left = 678.0
margin_top = 10.0
margin_right = 718.0
margin_bottom = 26.0
text = "ping"
autowrap = true
__meta__ = {
"_edit_use_anchors_": false
}
[node name="MarginContainer" type="VBoxContainer" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
@ -22,16 +53,26 @@ __meta__ = {
"_edit_use_anchors_": false
}
[node name="Button" type="Button" parent="MarginContainer"]
[node name="leave_button" type="Button" parent="MarginContainer"]
margin_top = 18.0
margin_right = 669.0
margin_bottom = 38.0
text = "Leave"
__meta__ = {
"_edit_use_anchors_": false
}
[node name="peers" type="ItemList" parent="MarginContainer"]
[node name="Button" type="Button" parent="MarginContainer"]
margin_top = 42.0
margin_right = 669.0
margin_bottom = 51.0
margin_bottom = 62.0
text = "Shift"
[node name="peers" type="ItemList" parent="MarginContainer"]
margin_top = 66.0
margin_right = 669.0
margin_bottom = 75.0
auto_height = true
[connection signal="pressed" from="MarginContainer/leave_button" to="." method="_on_leave_button_pressed"]
[connection signal="pressed" from="MarginContainer/Button" to="." method="_on_Button_pressed"]

View file

@ -6,7 +6,8 @@ This module sets up WebRTC peer connections.
var multiplayer_url = "ws://localhost:8888"
var webrtc_ice_servers = [
{ "urls": ["stun:stun.l.google.com:19302"] }
{ "urls": ["stun:stun.l.google.com:19302"] },
# { "urls": ["stun:localhost:3478"] }
]
const SignallerClient = preload("signaller_client.gd")
@ -37,6 +38,8 @@ func connect_to_signaller():
signaller.connect_to_websocket_signaller(multiplayer_url)
func _create_peer(peer_id):
print("_create_peer: %d" % peer_id)
if peer_id == signaller.peer_id: return
var peer: WebRTCPeerConnection = WebRTCPeerConnection.new()
print("New Net Peer %s with peer_id %d" % [peer, peer_id])
var _peer_init_results = peer.initialize({"iceServers": webrtc_ice_servers})
@ -46,30 +49,29 @@ func _create_peer(peer_id):
var uid = mp.get_unique_id()
if peer_id > uid:
var offer_result = peer.create_offer()
print("Create Offer Result: ", offer_result)
print("Create Offer Result: %s" % offer_result)
else:
print("Did not create offer: %d <= %d" % [peer_id, uid])
return peer
func _new_ice_candidate(mid_name, index_name, sdp_name, peer_id):
print("New ICE Candidate: ", mid_name, index_name, sdp_name, peer_id)
print("New ICE Candidate - MID: %s, Index: %s, SDP: %s, PeerID: %s" % [mid_name, index_name, sdp_name, peer_id])
signaller.send_candidate(peer_id, mid_name, index_name, sdp_name)
func _offer_created(type, data, peer_id):
print("Offer Created", type, data, peer_id)
print("Offer created - Type: %s, PeerID: %s, Data: %s" % [type, peer_id, JSON.print(data)])
if not mp.has_peer(peer_id):
return
mp.get_peer(peer_id).connection.set_local_description(type, data)
if type == "offer":
# TODO: send_offer(id, data)
pass
signaller.send_offer(peer_id, data)
else:
# TODO: send_answer(id, data)
pass
signaller.send_answer(peer_id, data)
func _lobby_joined(id, peer_id):
print("Connected to lobby %s as peer %d" % [id, peer_id])
mp.initialize(peer_id, true)
get_tree().network_peer = mp
func _peer_joined(peers):
for i in range(len(peers)):
@ -78,24 +80,26 @@ func _peer_joined(peers):
_create_peer(peer_id)
func peer_disconnected(id):
print("Peer disconnected ", id)
print("Peer %s disconnected" % id)
if mp.has_peer(id):
mp.remove_peer(id)
func offer_received(data):
print("Got offer: ", data)
var id = data["peerId"]
if mp.has_peer(id):
print("Setting offer remote description: %s" % JSON.print(data))
mp.get_peer(id).connection.set_remote_description("offer", data["offer"])
func answer_received(data):
print("Got answer: ", data)
var id = data["peerId"]
if mp.has_peer(id):
mp.get_peer(id).connection.set_remote_description("answer", data["offer"])
print("Setting answer remote description: %s" % JSON.print(data))
mp.get_peer(id).connection.set_remote_description("answer", data["answer"])
func candidate_received(data):
print("Candidate Received: ", data)
var id = data["peerId"]
if mp.has_peer(id):
print("Adding ice candidate: %s" % JSON.print(data))
mp.get_peer(id).connection.add_ice_candidate(data["mid"], data["index"], data["sdp"])
else:
print("Received candidate for non-existant peer %s" % id)

154
server.ts
View file

@ -1,4 +1,4 @@
import { randomInt } from "./deps.ts";
// import { randomInt } from "./deps.ts";
const SERVER_VERSION = "0.1.0";
// TODO: version comparison
@ -45,7 +45,6 @@ class Client {
this.name = "Client";
this.lobby = null;
console.log(this);
allClients.set(this.id, this);
}
@ -84,7 +83,6 @@ class Client {
netClients.push({ id, name, peerId })
);
// TODO: chunk async?
this.send(buildMessage("peer_list", netClients));
}
lobbyList() {
@ -178,6 +176,7 @@ class Lobby {
crypto.getRandomValues(arr);
client.peerId = Math.abs(arr[0]);
}
client.send(buildMessage("your_peer_id", client.peerId.toString()));
this.broadcast(
buildMessage("peer_joined", {
id: client.id,
@ -185,6 +184,13 @@ class Lobby {
peerId: client.peerId,
}),
);
this.clients.forEach((c) =>
c.send(buildMessage("peer_joined", {
id: c.id,
name: c.name,
peerId: c.peerId,
}))
);
this.clients.set(client.id, client);
}
@ -215,20 +221,29 @@ function onMessage(client: Client, ev: MessageEvent) {
if (ev.data.startsWith("lobby_join:")) {
const id = ev.data.substr(11);
const lobby = allLobbies.get(id);
if (lobby) client.lobbyJoin(lobby);
else client.send(`[info] could not find lobby ${id}`);
if (lobby) {
client.lobbyJoin(lobby);
client.clientList();
} else client.send(`[info] could not find lobby ${id}`);
}
if (ev.data.startsWith("json:")) {
const data: ClientMessage = JSON.parse(ev.data.substr(5));
if (data.type in ["candidate", "answer", "offer"]) {
if (["candidate", "answer", "offer"].includes(data.type)) {
console.log("Received WebRTC Negotiation Message...");
if (typeof data.data === "object") {
const subdata = data.data;
if (typeof subdata["peerId"] === "number") {
const peerId: number = subdata["peerId"];
const payload = Object.assign({}, data);
payload.data.peerId = client.peerId;
for (const client of allClients.values()) {
client.send(ev.data);
// const payload = Object.assign({}, data);
// payload.data.peerId = client.peerId;
for (const iClient of client.lobby?.clients.values() || []) {
if (iClient.peerId == peerId) {
console.log(
`Forwarding WebRTC Negotiation Message from peer ${client.peerId} to peer ${peerId}...`,
);
iClient.send(ev.data);
break;
}
}
}
}
@ -236,8 +251,8 @@ function onMessage(client: Client, ev: MessageEvent) {
}
}
function onSocketOpen(_client: Client, _ev: Event) {
console.log("New Client");
function onSocketOpen(client: Client, _ev: Event) {
console.log("New Client", client.id);
}
function onClientLeave(client: Client) {
@ -264,7 +279,7 @@ for await (const conn of listener) {
for await (const { respondWith, request } of server) {
// console.debug("HTTP Request Received", request);
try {
console.warn(JSON.stringify([allClients, allLobbies]));
// console.warn(JSON.stringify([allClients, allLobbies]));
const { socket, response } = Deno.upgradeWebSocket(request);
const client = new Client(socket);
socket.onmessage = (ev) => onMessage(client, ev);
@ -284,116 +299,3 @@ for await (const conn of listener) {
}
})();
}
/*
function peerCreatesLobby(peer: Peer, lobbyName: string) {
// TODO: ensure we can create a lobby
lobbies.add({
})
}
*/
/*
function peerJoinsLobby(peer: Peer, lobby: Lobby) {
lobbyName = randomSecret();
lobbies.set(lobbyName, new Lobby(lobbyName, this.id));
console.log(`Peer ${this.id} created lobby ${lobbyName}`);
console.log(`Open lobbies: ${lobbies.size}`);
}
const lobby = lobbies.get(lobbyName);
if (!lobby) throw new ProtoError(4000, STR_LOBBY_DOES_NOT_EXIST);
if (lobby.sealed) throw new ProtoError(4000, STR_LOBBY_IS_SEALED);
this.lobby = lobbyName;
console.log(
`Peer ${this.id} joining lobby ${lobbyName} ` +
`with ${lobby.peers.length} peers`,
);
lobby.join(this);
this.ws.send(`J: ${lobbyName}\n`);
}
// TODO: ensure peer not already in a lobby
const assigned = this.getPeerId(peer);
peer.ws.send(`I: ${assigned}\n`);
this.peers.forEach((p) => {
p.ws.send(`N: ${assigned}\n`);
peer.ws.send(`N: ${this.getPeerId(p)}\n`);
});
this.peers.push(peer);
}
*/
/*
function peerLeavesLobby(peer: Peer) {
const idx = this.peers.findIndex((p) => peer === p);
if (idx === -1) return false;
const assigned = this.getPeerId(peer);
const close = assigned === 1;
this.peers.forEach((p) => {
try {
// room host disconnected
if (close) p.ws.close(4000, STR_HOST_DISCONNECTED);
// notify peers
else p.ws.send(`D: ${assigned}\n`);
} catch (e) {
console.error(`Error when leaving: ${e}`);
}
});
this.peers.splice(idx, 1);
if (close && this.closeTimer >= 0) {
// we are closing already.
clearTimeout(this.closeTimer);
this.closeTimer = -1;
}
return close;
}
*/
/*
function graduateLobby(lobby: Lobby) {
// only host can seal
if (peer.id !== this.host) {
throw new ProtoError(4000, STR_ONLY_HOST_CAN_SEAL);
}
this.sealed = true;
this.peers.forEach((p) => {
p.ws.send("S: \n");
});
console.log(
`Peer ${peer.id} sealed lobby ${this.name} ` +
`with ${this.peers.length} peers`,
);
this.closeTimer = setTimeout(() => {
// close peer connection to host (and thus the lobby)
this.peers.forEach((p) => {
p.ws.close(1000, STR_SEAL_COMPLETE);
});
}, SEAL_CLOSE_TIMEOUT);
}
}
*/
/*
function parseMsg(peer: Peer, msg: string) {
// TODO: modify this?
// O: Client is sending an offer.
// A: Client is sending an answer.
// C: Client is sending a candidate.
let destId = parseInt(cmd.substr(3).trim());
// Dest is not an ID.
if (!destId) throw new ProtoError(4000, STR_INVALID_DEST);
if (destId === 1) destId = lobby.host;
const dest = lobby.peers.find((p: Peer) => p.id === destId);
// Dest is not in this room.
if (!dest) throw new ProtoError(4000, STR_INVALID_DEST);
function isCmd(what: string) {
return cmd.startsWith(`${what}: `);
}
if (isCmd("O") || isCmd("A") || isCmd("C")) {
dest.ws.send(cmd[0] + ": " + lobby.getPeerId(peer) + data);
return;
}
throw new ProtoError(4000, STR_INVALID_CMD);
}
*/

View file

@ -35,19 +35,19 @@ func close():
ws.disconnect_from_host()
func connect_to_websocket_signaller(url: String):
print(ws)
print("WebSocket: %s" % ws)
close()
print("Attempting to connect to WebSocket signalling server at ", url)
print("Attempting to connect to WebSocket signalling server at %s" % url)
var _result = ws.connect_to_url(url)
func _closed(_unknown):
emit_signal("websocket_disconnected")
func _close_request(code: int, reason: String):
print("Received WebSocket close request from signalling server ", code, reason)
print("Received WebSocket close request from signalling server - Code: %s, Reason: %s" % [code, reason])
func _connected(protocol = ""):
print("WebSocket signaller connected via protocol ", protocol)
print("WebSocket signaller connected via protocol %s" % protocol)
ws.get_peer(1).set_write_mode(WebSocketPeer.WRITE_MODE_TEXT)
emit_signal("websocket_connected")
_send("init")
@ -74,17 +74,18 @@ func _send(s: String):
func _parse_msg():
var msg: String = ws.get_peer(1).get_packet().get_string_from_utf8()
print("Signaller sent: ", msg)
print("Signaller received message: %s" % msg)
if msg.begins_with("json:"):
var data = JSON.parse(msg.substr(5))
if data.error == OK:
handle_message(data.result)
else:
print("Unhandled Message: ", msg)
print("Unhandled message: %s" % msg)
func handle_message(data: Dictionary):
match data["type"]:
"your_id": client_id = data["data"]
"your_peer_id": peer_id = data["data"]
"lobby_new": emit_signal("lobby_new", [{"id": data["id"], "name": data["name"]}])
"lobby_delete": emit_signal("lobby_delete", data["id"])
"lobby_joined":
@ -99,15 +100,15 @@ func handle_message(data: Dictionary):
"peer_joined": emit_signal("peer_joined", [{"id": data["id"], "name": data["name"], "peerId": data["peerId"]}])
"peer_left": emit_signal("peer_left", data["id"])
"candidate":
print("Candidate received ", data["data"])
print("Candidate received - Data: %s" % JSON.print(data["data"]))
emit_signal("candidate_received", data["data"])
"offer":
print("Offer received ", data["data"])
print("Offer received - Data: %s" % JSON.print(data["data"]))
emit_signal("offer_received", data["data"])
"answer":
print("Answer received ", data["data"])
print("Answer received - Data: %s" % JSON.print(data["data"]))
emit_signal("answer_received", data["data"])
_: print("Unhandled Data Message: ", JSON.print(data))
_: print("Unhandled Message - Data: %s" % JSON.print(data))
func send_candidate(peerId, mid, index, sdp) -> int:
return _send("json:%s" % JSON.print({