const HOST = "ncpo.net" /** * Encode string to bytes * @param {string} msg * @returns bytes */ const encodeString = msg => { return new TextEncoder().encode(msg); } /** * Decode bytes to string * @param {bytes} byte * @returns string */ const decodeString = byte => { return new TextDecoder().decode(byte) } /** * parseMessage converts Message instance to JSON bytes * @param {string} msg * @returns JSON bytes */ const parseMessage = msg => { data = { user: msg?.user, room: msg?.room, event: msg?.event, data: msg?.data, users: msg?.users, } return encodeString(JSON.stringify(data)); } /** * unparseMessage converts JSON bytes to Message instance * @param {bytes} data * @returns Message object */ const unparseMessage = data => { if(data.length <= 1) { return null; } data = JSON.parse(decodeString(data)) msg = { user: data.user, room: data.room, event: data.event, data: data.data, users: data.users, } if(msg.users[0]==""){ msg.users = []; } return msg; } /** * Connection base class for ncpo.net network */ class Connection { constructor() { this.room = ""; this.user = ""; this.users = null; this.onrecieve = null; this.onclose = null; this.binds = {}; this.active = false; this.sock = null; } /** * onRecieve sets the default callback for all events * @param {function(message)} callback */ onRecieve(callback) { this.onrecieve = callback; } /** * onClose sets the callback for when the connection ends * @param {function()} callback */ onClose(callback) { this.onclose = callback; } /** * send sends a message to the network * @param {string} event * @param {string} data * @param {string[]} users */ send(event, data, users) { if(this.active){ this.sock.send(parseMessage({ user: "", room: "", event: event, data: data, users: users, })) } else { throw Error("Connection is not active"); } } /** * bind binds a listener to an event * @param {string} event * @param {function(message)} callback */ bind(event, callback) { this.binds[event] = callback; } /** * unbind unbinds a listener from an event * @param {string} event */ unbind(event) { delete this.binds[event]; } /** * handle is the background task for Connection, this should not be called manualy */ handle() { let handler = msg => { if(msg.data.size>1){ let arrayBuffer, message; let fileReader = new FileReader(); fileReader.onload = function(event) { arrayBuffer = new Uint8Array(event.target.result); message = unparseMessage(arrayBuffer); this.users = message.users; if(message.event in this.binds) { this.binds[message.event](message); } if(this.onrecieve != null) { this.onrecieve(message); } }.bind(this); fileReader.readAsArrayBuffer(msg.data); } } this.sock.send(encodeString(this.room)); this.sock.send(encodeString(this.user)); this.sock.onmessage = handler.bind(this); } /** * connect starts the connection with the server * @param {string} room * @param {string} user */ connect(room, user) { if(this.active) { throw Error("Connection already established"); } this.active = true; this.room = room; this.user = user; this.sock = new WebSocket("ws://"+HOST+"/ws"); this.sock.onopen = this.handle.bind(this); this.sock.onclose = this.close.bind(this); this.sock.onerror = this.close.bind(this); } /** * server starts a server on the ncpo network * @param {string} room */ server(room) { this.connect(room, "server"); } /** * close closes the connection with the server */ close() { if (this.active) { this.active = false; this.sock.close(); if (this.onclose != null){ this.onclose(); } } } } /** * newConnection returns a new Connection instance * @returns new Connection */ const newConnection = () => { return new Connection() }