various fixes and additions, files, but fixed, code cleanup etc

This commit is contained in:
Zoe
2025-09-02 23:36:13 -05:00
parent a89754f070
commit 2357e44923
3 changed files with 101 additions and 23 deletions

View File

@@ -1,4 +1,3 @@
import { json } from "@sveltejs/kit";
import { WebSocketServer } from "ws"; import { WebSocketServer } from "ws";
import type { WebSocket } from "ws"; import type { WebSocket } from "ws";
@@ -6,14 +5,20 @@ import type { WebSocket } from "ws";
const rooms = new Map<string, WebSocket[]>(); const rooms = new Map<string, WebSocket[]>();
enum MessageType { enum MessageType {
// requests
CREATE_ROOM = 'create', CREATE_ROOM = 'create',
JOIN_ROOM = 'join', JOIN_ROOM = 'join',
// responses
ROOM_CREATED = 'created', ROOM_CREATED = 'created',
ROOM_JOINED = 'joined', ROOM_JOINED = 'joined',
ROOM_READY = 'ready', ROOM_READY = 'ready',
// webrtc
ICE_CANDIDATE = 'ice-candidate', ICE_CANDIDATE = 'ice-candidate',
OFFER = 'offer', OFFER = 'offer',
ANSWER = 'answer', ANSWER = 'answer',
ERROR = 'error', ERROR = 'error',
} }
@@ -24,27 +29,50 @@ type Message = {
function createRoom(socket: WebSocket): string { function createRoom(socket: WebSocket): string {
let roomId = Math.random().toString(36).substring(2, 10); let roomId = Math.random().toString(36).substring(2, 10);
rooms.set(roomId, [socket]); rooms.set(roomId, []);
socket.send(JSON.stringify({ type: MessageType.ROOM_CREATED, data: roomId }));
joinRoom(roomId, socket);
return roomId; return roomId;
} }
function joinRoom(roomId: string, socket: WebSocket) { function joinRoom(roomId: string, socket: WebSocket) {
const room = rooms.get(roomId); let room = rooms.get(roomId);
console.log(room?.length);
// should be unreachable // should be unreachable
if (!room) { if (!room) {
throw new Error(`Room ${roomId} does not exist`); throw new Error(`Room ${roomId} does not exist`);
} }
if (room.length == 2) {
socket.send(JSON.stringify({ type: MessageType.ERROR, data: 'Room is full' }));
return;
}
// notify all clients in the room of the new client, except the client itself // notify all clients in the room of the new client, except the client itself
room.forEach(client => { room.forEach(client => {
client.send(JSON.stringify({ type: MessageType.JOIN_ROOM, data: roomId })); client.send(JSON.stringify({ type: MessageType.JOIN_ROOM, data: roomId }));
}); });
room.push(socket); room.push(socket);
// the client is now in the room and the peer knows about it socket.addEventListener('close', (ev) => {
socket.send(JSON.stringify({ type: MessageType.ROOM_JOINED, data: null })); room = rooms.get(roomId)
if (!room) {
return;
}
// for some reason, when you filter the array when the length is 1 it stays at 1, but we *know* that if its 1
// then when this client disconnects, the room should be deleted since the room is empty
if (room.length === 1) {
deleteRoom(roomId);
return;
}
rooms.set(roomId, room.filter(client => client !== ev.target));
});
// TODO: consider letting rooms get larger than 2 clients // TODO: consider letting rooms get larger than 2 clients
if (room.length == 2) { if (room.length == 2) {
@@ -63,8 +91,6 @@ export function confgiureWebsocketServer(ws: WebSocketServer) {
ws.on('connection', socket => { ws.on('connection', socket => {
// Handle messages from the client // Handle messages from the client
socket.on('message', event => { socket.on('message', event => {
console.log(event, typeof event);
let message; let message;
if (event instanceof Buffer) { // Assuming JSON is sent as a string if (event instanceof Buffer) { // Assuming JSON is sent as a string
@@ -92,8 +118,7 @@ export function confgiureWebsocketServer(ws: WebSocketServer) {
switch (type) { switch (type) {
case MessageType.CREATE_ROOM: case MessageType.CREATE_ROOM:
// else, create a new room // else, create a new room
const roomId = createRoom(socket); createRoom(socket);
socket.send(JSON.stringify({ type: MessageType.ROOM_CREATED, data: roomId }));
break; break;
case MessageType.JOIN_ROOM: case MessageType.JOIN_ROOM:
// if join message has a roomId, join the room // if join message has a roomId, join the room
@@ -109,6 +134,9 @@ export function confgiureWebsocketServer(ws: WebSocketServer) {
} }
joinRoom(message.data, socket); joinRoom(message.data, socket);
// the client is now in the room and the peer knows about it
socket.send(JSON.stringify({ type: MessageType.ROOM_JOINED, data: null }));
break; break;
case MessageType.OFFER: case MessageType.OFFER:
case MessageType.ANSWER: case MessageType.ANSWER:
@@ -130,10 +158,5 @@ export function confgiureWebsocketServer(ws: WebSocketServer) {
break; break;
} }
}); });
// Handle client disconnection
socket.on('close', () => {
// TODO: if this client was in a room, remove them from the room
});
}); });
} }

View File

@@ -10,6 +10,8 @@
} from "../utils/webrtcUtil"; } from "../utils/webrtcUtil";
let inputMessage: Writable<string> = writable(""); let inputMessage: Writable<string> = writable("");
let inputFile = writable(null);
let inputFileElement: HTMLInputElement;
function sendMessage() { function sendMessage() {
if (!$peer) { if (!$peer) {
@@ -17,13 +19,29 @@
return; return;
} }
$messages = [...$messages, `You: ${$inputMessage}`]; if (!$inputFile && !$inputMessage) {
$peer.send($inputMessage); return;
$inputMessage = ""; }
if ($inputFile != null && $inputFile[0] !== undefined) {
$messages = [...$messages, `You: ${$inputFile[0].name}`];
$peer.send($inputFile[0]);
$inputFile = null;
}
if ($inputMessage) {
$messages = [...$messages, `You: ${$inputMessage}`];
$peer.send($inputMessage);
$inputMessage = "";
}
}
function pickFile() {
inputFileElement.click();
} }
</script> </script>
{#if $room && $connected} {#if $room !== null && $connected === true}
{#if !$isRTCConnected} {#if !$isRTCConnected}
<p>Waiting for peer to connect...</p> <p>Waiting for peer to connect...</p>
{:else if !$dataChannelReady} {:else if !$dataChannelReady}
@@ -34,7 +52,12 @@
<p>{msg}</p> <p>{msg}</p>
{/each} {/each}
</div> </div>
<input
type="file"
bind:files={$inputFile}
bind:this={inputFileElement}
class="absolute opacity-0 -top-[9999px] -left-[9999px]"
/>
<div class="flex gap-2"> <div class="flex gap-2">
<input <input
type="text" type="text"
@@ -43,6 +66,27 @@
placeholder="Type your message..." placeholder="Type your message..."
class="flex-grow p-2 rounded bg-gray-700 border border-gray-600 text-gray-100 placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500" class="flex-grow p-2 rounded bg-gray-700 border border-gray-600 text-gray-100 placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500"
/> />
<button
on:click={pickFile}
disabled={!dataChannelReady}
aria-label="Pick file"
class="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded disabled:opacity-50 disabled:cursor-not-allowed"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
><!-- Icon from Tabler Icons by Paweł Kuna - https://github.com/tabler/tabler-icons/blob/master/LICENSE --><path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m15 7l-6.5 6.5a1.5 1.5 0 0 0 3 3L18 10a3 3 0 0 0-6-6l-6.5 6.5a4.5 4.5 0 0 0 9 9L21 13"
/></svg
>
</button>
<button <button
on:click={sendMessage} on:click={sendMessage}
disabled={!dataChannelReady} disabled={!dataChannelReady}
@@ -52,6 +96,4 @@
</button> </button>
</div> </div>
{/if} {/if}
{:else}
<p>Waiting for peer to connect...</p>
{/if} {/if}

View File

@@ -15,8 +15,23 @@ const callbacks = {
console.log("Connected to peer"); console.log("Connected to peer");
isRTCConnected.set(true); isRTCConnected.set(true);
}, },
//! TODO: come up with a more complex room system. This is largely for testing purposes
onMessage: (message: string | ArrayBuffer) => { onMessage: (message: string | ArrayBuffer) => {
console.log("Received message:", message); console.log("Received message:", message);
if (typeof message === 'object' && message instanceof Blob) {
// download the file
const url = URL.createObjectURL(message);
const a = document.createElement('a');
a.href = url;
a.download = message.name;
document.body.appendChild(a);
a.click();
setTimeout(() => {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 100);
}
messages.set([...get(messages), `Peer: ${message}`]); messages.set([...get(messages), `Peer: ${message}`]);
}, },
onDataChannelOpen: () => { onDataChannelOpen: () => {
@@ -67,8 +82,6 @@ export async function handleMessage(event: MessageEvent) {
await get(peer)?.createOffer(); await get(peer)?.createOffer();
} }
return; return;
default:
console.warn(`Unknown message type: ${message.type}`);
} }
if (!get(peer)) { if (!get(peer)) {