UNPKG

@brayjamin/cursed-network

Version:

Protocols for cross-server communication under the CursedMC proxy.

621 lines (620 loc) 28.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.serializeResponse = exports.sql = exports.fn = exports.Server = exports.Client = exports.Protocol = exports.Port = exports.Proxy = exports.encode = exports.decode = exports.register = exports.thisClient = exports.sendFormatted = exports.returnFormatted = exports.paginate = exports.feedback = exports.getFriendList = void 0; const stdlib_paper_1 = require("@grakkit/stdlib-paper"); const socket_1 = require("@grakkit/socket"); Object.defineProperty(exports, "Client", { enumerable: true, get: function () { return socket_1.Client; } }); Object.defineProperty(exports, "Server", { enumerable: true, get: function () { return socket_1.Server; } }); function decode(content) { let index = 0; let string = ''; let extra1, extra2; while (index < content.length) { let char = content[index++]; switch (char >> 4) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: string += String.fromCharCode(char); break; case 12: case 13: extra1 = content[index++]; string += String.fromCharCode(((char & 0x1f) << 6) | (extra1 & 0x3f)); break; case 14: extra1 = content[index++]; extra2 = content[index++]; string += String.fromCharCode(((char & 0x0f) << 12) | ((extra1 & 0x3f) << 6) | ((extra2 & 0x3f) << 0)); break; } } return string; } exports.decode = decode; function encode(content) { let index = 0; const array = new Uint8Array(content.length); while (index < content.length) { array[index] = content.charCodeAt(index++); } return array; } exports.encode = encode; let Proxy; exports.Proxy = Proxy; let Port; exports.Port = Port; let NodeType; let NodeName; let Driver; let sql; exports.sql = sql; const insertAt = (str, sub, pos) => `${str.slice(0, pos)}${sub}${str.slice(pos)}`; setImmediate(() => { if (stdlib_paper_1.manager.getPlugin('MySQL').isEnabled()) { //@ts-expect-error Driver = (0, stdlib_paper_1.type)('com.mysql.cj.jdbc.Driver'); new Driver(); //@ts-expect-error exports.sql = sql = (0, stdlib_paper_1.type)('me.vagdedes.mysql.database.MySQL'); try { sql.disconnect(); } catch (e) { } sql.connect(); console.info(`[grakkit] §dCursed Network §f« » §3MySQL §aSUCCESS`); } else console.info(`[grakkit] §dCursed Network §f« » §3MySQL §cFAILURE§r! Is MySQL plugin present and enabled?`); }); const UUID = (0, stdlib_paper_1.type)('java.util.UUID'); const thisClient = new socket_1.Client(); exports.thisClient = thisClient; function register(type, port, name) { exports.Port = Port = port; NodeType = type; NodeName = name; switch (type) { case 'server': { exports.Proxy = Proxy = new socket_1.Server(); Proxy.start(port); try { thisClient.connect(port); } catch (e) { console.error(e); } break; } case 'client': { try { thisClient.connect(port); } catch (e) { console.error(e); } exports.Proxy = Proxy = thisClient.server; break; } } console.info('[grakkit] §dCursed Network §rinit §aSUCCESS'); console.info(`§fNode: §3${NodeType}§r §fPort: §3${Port}§r`); /* Register Proxy Listeners */ } exports.register = register; function serializeRow(response, columns) { const row = {}; for (let i = 0; typeof columns[i] != 'undefined'; i++) { row[columns[i]] = response.getString(columns[i]); } return row; } function serializeResponse(response, columns) { const arrResponse = []; let i = 0; if (response == null) return []; if (!response.next()) return []; for (let result = true; result; result = response.next()) { arrResponse[i++] = serializeRow(response, columns); } return arrResponse; } exports.serializeResponse = serializeResponse; function justify(...array) { return array.join(' '); } const fn = { getPendingRequestsSent(friender_id) { return serializeResponse(sql.query(justify(`SELECT * FROM users AS \`friend\``, `JOIN friendships ON friender_id = friend.id OR friendee_id = friend.id`, `WHERE '${friender_id}' IN (friender_id) AND friend.id <> '${friender_id}'`, `AND accepted_at IS null AND blocked_at IS null;`)), ['friendee_id', 'created_at']); }, getPendingRequestsReceived(friendee_id) { return serializeResponse(sql.query(justify(`SELECT * FROM users AS \`friend\``, `JOIN friendships ON friender_id = friend.id`, `WHERE friendee_id = '${friendee_id}'`, `AND accepted_at IS null and blocked_at IS null`)), ['friender_id', 'created_at']); }, sendMessage(id, message) { Protocol('SEND_MESSAGE', { uuid: id, message: message }); }, getFriends(id) { return serializeResponse(sql.query(justify(`SELECT * FROM users AS \`friend\``, `JOIN friendships ON friender_id = friend.id OR friendee_id = friend.id`, `WHERE '${id}' IN (friendee_id, friender_id) AND friend.id <> '${id}'`, `AND accepted_at IS NOT null AND blocked_at IS null;`)), ['id', 'name', 'created_at', 'accepted_at']); }, removeFriend(friender_id, friendee_id) { if (!fn.userExists(friendee_id)) return 'FRIENDEE_ID_INVALID'; // if friends const q = sql.query(justify(`SELECT * FROM users AS \`friend\``, `JOIN friendships ON friender_id = friend.id OR friendee_id = friend.id`, `WHERE`, `('${friender_id}' in (friender_id) OR`, `'${friendee_id}' in (friender_id))`, `AND ('${friendee_id}' in (friendee_id)`, `OR '${friender_id}' in (friendee_id))`, `AND accepted_at IS NOT null`)); if (q != null && q.next() && q.getString('accepted_at') != null) { // remove friend return sql.update(justify(`DELETE FROM friendships`, `WHERE friender_id = '${friender_id}' AND friendee_id = '${friendee_id}' OR friender_id = '${friendee_id}' AND friendee_id = '${friender_id}'`)); } else return 'NOT_FRIENDS'; }, userExists(id) { const q = sql.query(`SELECT * FROM users WHERE id = '${id}'`); if (q != null && q.next() && q.getString('id') != null) return true; return false; }, declineRequest(friender_id, friendee_id) { if (!fn.userExists(friendee_id)) return 'FRIENDEE_ID_INVALID'; const q = sql.query(justify(`SELECT * FROM users AS \`friend\``, `JOIN friendships ON friender_id = friend.id OR friendee_id = friend.id`, `WHERE`, `('${friender_id}' in (friender_id) OR`, `'${friendee_id}' in (friender_id))`, `AND ('${friendee_id}' in (friendee_id)`, `OR '${friender_id}' in (friendee_id))`, `AND accepted_at IS null AND blocked_at IS null;`)); if (q != null && q.next()) return sql.update(justify(`DELETE FROM friendships`, `WHERE`, `('${friender_id}' in (friender_id) OR`, `'${friendee_id}' in (friender_id))`, `AND ('${friendee_id}' in (friendee_id)`, `OR '${friender_id}' in (friendee_id))`, `AND accepted_at IS null;`)); else return false; }, addFriend(friender_id, friendee_id) { /* If a friend request exists */ if (!fn.userExists(friender_id)) return 'FRIENDER_ID_INVALID'; if (!fn.userExists(friendee_id)) return 'FRIENDEE_ID_INVALID'; const q = sql.query(`SELECT * FROM friendships WHERE friendee_id = '${friendee_id}' AND friender_id = '${friender_id}' AND accepted_at IS null;`); if (q != null && q.next()) if (!sql.update(justify('UPDATE friendships', 'SET accepted_at = now()', `WHERE friendee_id = '${friendee_id}' AND friender_id = '${friender_id}'`, `AND accepted_at IS null`))) /* add as a friend */ return sql.update(justify('UPDATE friendships', 'SET accepted_at = now()', `WHERE friendee_id = '${friender_id}' AND friender_id = '${friendee_id}'`, `AND accepted_at IS null`)); else return true; else { const q2 = sql.query(`SELECT * FROM friendships WHERE friendee_id = '${friendee_id}' AND friender_id = '${friender_id}' AND accepted_at IS NOT null;`); if (q2 != null && q2.next()) return 'ALREADY_FRIEND'; else return 'REQUEST_NOT_ACTIVE'; } }, test(friender_id, friendee_id) { return sql.query(justify(`SELECT * FROM users AS \`friend\``, `JOIN friendships ON friender_id = friend.id OR friendee_id = friend.id`, `WHERE`, `('${friender_id}' in (friender_id) OR`, `'${friendee_id}' in (friender_id))`, `AND ('${friendee_id}' in (friendee_id)`, `OR '${friender_id}' in (friendee_id))`, `AND accepted_at IS null AND blocked_at IS null`)); }, // send friend request from friender_id to friendee_id // use sql // if user doesn't exist return 'FRIENDEE_ID_INVALID' // if friend request already exists return 'REQUEST_ALREADY_EXISTS' // if friendee_id already sent a friend request to friender_id, automatically accept the request // if friendee_id is already a friend with friender_id, return 'ALREADY_FRIEND' // if friendee_id is blocked by friender_id, return 'FRIENDEE_BLOCKED' sendFriendRequest(friender_id, friendee_id) { if (!fn.userExists(friendee_id)) return 'FRIENDEE_ID_INVALID'; const q = sql.query(justify(`SELECT * FROM friendships`, `WHERE friendee_id = '${friendee_id}' AND friender_id = '${friender_id}' AND accepted_at IS null;`)); if (q != null && q.next()) return 'REQUEST_ALREADY_EXISTS'; const q2 = sql.query(justify(`SELECT * FROM friendships`, `WHERE friendee_id = '${friender_id}' AND friender_id = '${friendee_id}' AND accepted_at IS null;`)); if (q2 != null && q2.next()) { fn.addFriend(friendee_id, friender_id); return 'AUTO_ACCEPTED'; } const q3 = sql.query(justify(`SELECT * FROM friendships`, `WHERE`, `('${friender_id}' in (friender_id) OR`, `'${friendee_id}' in (friender_id))`, `AND ('${friendee_id}' in (friendee_id)`, `OR '${friender_id}' in (friendee_id))`, `AND accepted_at IS NOT null;`)); if (q3 != null && q3.next()) return 'ALREADY_FRIEND'; const q4 = sql.query(justify(`SELECT * FROM friendships`, `WHERE`, `('${friender_id}' in (friender_id) OR`, `'${friendee_id}' in (friender_id))`, `AND ('${friendee_id}' in (friendee_id)`, `OR '${friender_id}' in (friendee_id))`, `AND blocked_at IS NOT null;`)); if (q4 != null && q4.next()) return 'FRIENDEE_BLOCKED'; sql.update(justify(`INSERT INTO friendships`, `(friender_id, friendee_id, created_at)`, `VALUES`, `('${friender_id}', '${friendee_id}', now())`)); return 'REQUEST_SENT'; // if the friendee_id sent a friend request to friender_id, automatically accept the request }, sendFriendRequestBackup(friender_id, friendee_id) { /* If a friend request exists */ if (!fn.userExists(friendee_id)) return 'FRIENDEE_ID_INVALID'; const q = sql.query(justify(`SELECT * FROM users AS \`friend\``, `JOIN friendships ON friender_id = friend.id OR friendee_id = friend.id`, `WHERE`, `('${friender_id}' in (friender_id) OR`, `'${friendee_id}' in (friender_id))`, `AND ('${friendee_id}' in (friendee_id)`, `OR '${friender_id}' in (friendee_id))`, `AND accepted_at IS null AND blocked_at IS null`)); if (q != null && q.next()) { switch (q.getString('friender_id')) { case friender_id: return 'ALREADY_SENT'; case friendee_id: { fn.addFriend(friendee_id, friender_id); return 'AUTO_ACCEPTED'; } } } else { const q2 = sql.query(`SELECT * FROM friendships WHERE friendee_id = '${friendee_id}' OR friendee_id = '${friender_id}' AND friender_id = '${friender_id}' OR friender_id = '${friendee_id}' AND accepted_at IS NOT null;`); if (q2 != null && q2.next()) return 'ALREADY_FRIEND'; else return sql.update(justify(`INSERT INTO friendships (friender_id, friendee_id) VALUES ('${friender_id}', '${friendee_id}');`)); } }, getNameFromUuid(id) { const q = sql.query(`SELECT * FROM users WHERE id = '${id}';`); const empty = !q.next(); if (q != null && !empty && q.getString('name') != null) return q.getString('name'); const name = (0, stdlib_paper_1.fetch)(`https://sessionserver.mojang.com/session/minecraft/profile/${id}`).json().name; if (empty && q.getString('id') == null) sql.update(`INSERT INTO users (id, name, server) VALUES ('${id}', '${name}', '${NodeName}')`); else sql.update(`UPDATE users SET name = '${name}' WHERE id = '${id}';`); return name; }, getUuidFromName(name) { const request = (0, stdlib_paper_1.fetch)(`https://api.mojang.com/users/profiles/minecraft/${name}`); try { let id = request.json().id; id = insertAt(id, '-', 8); id = insertAt(id, '-', 13); id = insertAt(id, '-', 18); id = insertAt(id, '-', 23); return id; } catch (e) { return null; } }, hasFriends(id) { const q = sql.query(justify(`SELECT * FROM friendships WHERE friendee_id = '${id}' OR friender_id = '${id}' AND accepted_at IS NOT null;`)); if (q != null && q.next()) return true; else return false; }, addUser(id) { const q = sql.query(`SELECT * FROM users WHERE id = '${id}'`); if (q != null && q.next() && q.getString('id') != null) return 'ALREADY_EXISTS'; return sql.update(`INSERT INTO users (id, name, server) VALUES ('${id}', '${(0, stdlib_paper_1.fetch)(`https://sessionserver.mojang.com/session/minecraft/profile/` + id).json().name}', '${NodeName}');`); }, getServer(id) { const q = sql.query(`SELECT * FROM users WHERE id = '${id}'`); if (q != null && q.next() && q.getString('server') != null) return q.getString('server'); return null; }, setServer(id, server) { return sql.update(`UPDATE users SET server = '${server}' WHERE id = '${id}'`); } }; exports.fn = fn; function Protocol(protocol, params) { thisClient.send(encode(JSON.stringify({ protocol: protocol, params }))); } exports.Protocol = Protocol; function filter(suggestions, key) { return suggestions.filter(word => { return word.toLowerCase().includes(key.toLowerCase()); }); } function getFriendList(player) { const friends = fn.getFriends(player.getUniqueId().toString()); if (friends.length === 0) return []; const names = []; let i = names.length; friends.forEach(friend => { //@ts-expect-error if (friend.name != null) names[i] = friend.name; else { //@ts-expect-error const q = sql.query(`SELECT * FROM users WHERE id = '${friend.id}';`); if (q != null && q.next() && q.getString('name') != null) names[i] = q.getString('name'); else { names[i] = (0, stdlib_paper_1.fetch)(`https://sessionserver.mojang.com/session/minecraft/profile/${q.getString('id')}`).json().name; //@ts-expect-error sql.update(`UPDATE users SET name = '${names[i]}' WHERE id = '${friend.id}';`); } } i++; }); return names; } exports.getFriendList = getFriendList; exports.feedback = { friends: { // return an an array of messages showing the user all the pending requests they have (make it pretty) getPending: (name, Requests) => { let requests = []; Requests.forEach(request => { requests[requests.length] = fn.getNameFromUuid(request); }); if (requests.length === 1) return [ `§f${requests[0]} §bhas requested to be your friend!`, '§5§m ', `To accept, type /friend accept [name]`, `To deny, type /friend deny [name]` ]; if (requests.length > 4) { return [ `§bYou have §f${requests.length} §bpending §bfriend requests!`, '§5§m ', `§f${requests.slice(0, 4).join('§b,§f ')}`, ` §r §r §d§o+ ${requests.length - 4} more`, '§5§m ', `§7To §aaccept§7, type §e/friend accept §d[name]`, `§7To §cdeny§7, type §e/friend deny §d[name]`, `§7To view all requests, type §e/friend pending` ]; } if (requests.length > 1) { return [ `§bYou have §f${requests.length} §bpending friend requests!`, '§5§m ', `§f${requests.join('§b,§f ')}`, '§5§m ', `§7Respond with §3/friend §8<§3accept§7 | §3deny§8> §d[name]` ]; } return null; }, friendee_blocked: '§cYou are blocked by this player.', help: { header: ['§fHelp for §b/friend', '§5§m '], body: [ '§3/f add §d[name]\n§8-> §7Send a friend request.', '§3/f remove §d[name]\n§8-> §7Unfriend a player.', '§3/f list\n§8-> §7View all your friends', '§3/f accept §d[name]\n§8-> §7Accept a friend request', '§3/f deny §d[name]\n§8-> §7Decline a friend request', '§3/f help §d[page]\n§8-> §7Show this dialogue' ] }, list: { header(player) { const friends = getFriendList(player).length; if (friends === 0) return ['§5§m ', '§fYou have no friends']; return ['§5§m ', `§fFriends (${friends}):`]; } }, already_friend: 'You are already friends with this player!', friendee_id_invalid: 'This user does not exist!', already_sent: "You've already sent a friend request to this player!", not_friends: "You're already not friends with this player!", request_not_active: "You haven't received a friend request from this player!", declined_request: 'Friend request declined!', auto_success: name => { return `${name} already had an active friend request so they were automatically friended.`; }, friended_success: name => { return `You're now friends with ${name}!`; }, request_sent: name => { return 'A friend request has been sent to ' + name + '!'; }, remove_success: name => { return `${name} has been removed from your friends list.`; } } }; // a function where plugging in an array and a page number will return at most 10 items a page, plugging in a page number will return those // if array.slice(start, end) is empty, return array.slice(start) function paginate(array, page, max = 10) { const start = (page - 1) * max; const end = page * max; if (array.slice(start, end).length === 0) return array.slice(start); return array.slice(start, end); } exports.paginate = paginate; // a function that returns the help header, an array of friends plugged into paginate, and a footer // if page is less than 1, return the first page function returnFormatted(options = { player: null, args: null, header: [], body: [], topfooter: [], page: 1, max: 10, enable_pages: false }) { const { player, args, header, body, topfooter, page, max, enable_pages } = options; if (enable_pages) { let Page = page; if (Page < 1) Page = 1; if (page > Math.ceil(body.length / max)) Page = Math.ceil(body.length / max); let footer = []; footer = [' \n§7Page ' + Page + ' of ' + Math.ceil(body.length / max)]; return [header, paginate(body, Page, max), topfooter, footer]; } return [header, body, topfooter]; } exports.returnFormatted = returnFormatted; function sendFormatted(options = { player: null, args: null, header: [], body: [], topfooter: [], page: 1, max: 10, enable_pages: false }) { const { player, args, header, body, topfooter, page, max, enable_pages } = options; let Page = 1; if (args && args[1]) Page = parseInt(args[1]); returnFormatted({ player: player, args: args, header: header, body: body, topfooter: topfooter, page: Page, max: max, enable_pages: enable_pages }).forEach(section => { if (section) section.forEach(line => { player.sendMessage(line); }); }); } exports.sendFormatted = sendFormatted; (0, stdlib_paper_1.command)({ name: 'friend', aliases: ['f', 'friends'], execute(player, ...args) { //@ts-expect-error const uuid = player.getUniqueId().toString(); switch (args[0]) { case 'help': { let page = 1; if (args[1]) page = parseInt(args[1]); sendFormatted({ player: player, args: args, header: exports.feedback.friends.help.header, body: exports.feedback.friends.help.body, page: page, max: 3, enable_pages: true }); break; } case 'list': { let page = 1; if (args[1]) page = parseInt(args[1]); sendFormatted({ player: player, header: exports.feedback.friends.list.header(player), body: getFriendList(player), topfooter: ['§5§m '], page: page, max: 10, enable_pages: fn.hasFriends(uuid) }); break; } case 'add': { if (typeof args[1] == 'undefined') return; switch (fn.sendFriendRequest(uuid, fn.getUuidFromName(args[1]))) { case 'ALREADY_FRIEND': return player.sendMessage(exports.feedback.friends.already_friend); case 'FRIENDEE_ID_INVALID': return player.sendMessage(exports.feedback.friends.friendee_id_invalid); case 'REQUEST_ALREADY_EXISTS': return player.sendMessage(exports.feedback.friends.already_sent); case 'AUTO_ACCEPTED': return player.sendMessage(exports.feedback.friends.auto_success(args[1])); case 'FRIENDEE_BLOCKED': return player.sendMessage(exports.feedback.friends.friendee_blocked); case 'REQUEST_SENT': return player.sendMessage(exports.feedback.friends.request_sent(args[1])); default: return player.sendMessage('fuck'); } break; } case 'remove': { if (typeof args[1] == 'undefined') return; switch (fn.removeFriend(uuid, fn.getUuidFromName(args[1]))) { case 'FRIENDEE_ID_INVALID': return player.sendMessage(exports.feedback.friends.friendee_id_invalid); case 'NOT_FRIENDS': return player.sendMessage(exports.feedback.friends.not_friends); case true: return player.sendMessage(exports.feedback.friends.remove_success(args[1])); default: return player.sendMessage('fuck'); } } case 'deny': { if (typeof args[1] == 'undefined') return; switch (fn.declineRequest(uuid, fn.getUuidFromName(args[1]))) { case 'FRIENDEE_ID_INVALID': return player.sendMessage(exports.feedback.friends.friendee_id_invalid); case true: return player.sendMessage(exports.feedback.friends.declined_request); } } case 'accept': { if (typeof args[1] == 'undefined') return; switch (fn.addFriend(fn.getUuidFromName(args[1]), uuid)) { case 'ALREADY_FRIEND': return player.sendMessage(exports.feedback.friends.already_friend); case 'FRIENDEE_ID_INVALID': return player.sendMessage(exports.feedback.friends.friendee_id_invalid); case 'REQUEST_NOT_ACTIVE': return player.sendMessage(exports.feedback.friends.request_not_active); case true: return player.sendMessage(exports.feedback.friends.friended_success(args[1])); } } default: { let page = 1; if (args[1]) page = parseInt(args[1]); sendFormatted({ player: player, args: args, header: exports.feedback.friends.help.header, body: exports.feedback.friends.help.body, page: page, max: 3, enable_pages: true }); break; } } }, tabComplete: (player, ...args) => { switch (args.length) { case 1: return filter(['list', 'add', 'remove', 'accept', 'deny', 'help', 'toggle'], args[0]); } } }); (0, stdlib_paper_1.event)('org.bukkit.event.player.PlayerJoinEvent', { priority: 'MONITOR', script: event => { const player = event.getPlayer(); const id = event.getPlayer().getUniqueId().toString(); if (!fn.userExists(id)) return fn.addUser(id); const requests = fn.getPendingRequestsReceived(id); if (requests.length === 0) return; core.task.timeout(() => { //@ts-expect-error player.sendMessage(' '); sendFormatted({ player: player, header: exports.feedback.friends.getPending(player.getName(), //@ts-expect-error requests.map(request => request.friender_id)) }); }, 2); } });