sourcequery
This commit is contained in:
parent
fdff86e318
commit
bad6d45963
4 changed files with 595 additions and 5 deletions
|
@ -749,7 +749,7 @@ async function bluesky(msg, url, spoiler = false, minimal = false) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
response: {
|
response: {
|
||||||
flags: 1 << 15,
|
flags: MessageFlags.IS_COMPONENTS_V2,
|
||||||
components: [warnings.length > 0 && warningText, container].filter((x) => !!x),
|
components: [warnings.length > 0 && warningText, container].filter((x) => !!x),
|
||||||
allowedMentions: {
|
allowedMentions: {
|
||||||
repliedUser: false,
|
repliedUser: false,
|
||||||
|
|
242
src/modules/misc/sourcequery.js
Normal file
242
src/modules/misc/sourcequery.js
Normal file
|
@ -0,0 +1,242 @@
|
||||||
|
const Command = require("#lib/command.js");
|
||||||
|
|
||||||
|
const SourceQuery = require("#util/sourcequery.js");
|
||||||
|
const Table = require("#util/table.js");
|
||||||
|
const {formatTime} = require("#util/time.js");
|
||||||
|
const {MessageFlags} = require("#util/dconstants.js");
|
||||||
|
const {pastelize, getTopColor} = require("#util/misc.js");
|
||||||
|
|
||||||
|
const FRIENDLY_USERAGENT =
|
||||||
|
"Mozilla/5.0 (compatible; HiddenPhox/sourcequery; +https://gitdab.com/Cynosphere/HiddenPhox) Discordbot/2.0";
|
||||||
|
|
||||||
|
const SERVER_TYPES = {
|
||||||
|
d: "Dedicated Server",
|
||||||
|
l: "Listen Server",
|
||||||
|
p: "SourceTV Relay",
|
||||||
|
};
|
||||||
|
const OPERATING_SYSTEMS = {
|
||||||
|
l: "Linux",
|
||||||
|
w: "Windows",
|
||||||
|
m: "macOS",
|
||||||
|
o: "macOS",
|
||||||
|
};
|
||||||
|
|
||||||
|
const sourcequery = new Command("sourcequery");
|
||||||
|
sourcequery.category = "misc";
|
||||||
|
sourcequery.helpText = "Query a Source engine server";
|
||||||
|
sourcequery.callback = async function (msg, line) {
|
||||||
|
const split = line.split(":");
|
||||||
|
const ip = split[0];
|
||||||
|
const port = split[1] ?? 27015;
|
||||||
|
|
||||||
|
await msg.addReaction("\uD83C\uDFD3");
|
||||||
|
|
||||||
|
let data;
|
||||||
|
try {
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
throw "timeout";
|
||||||
|
}, 5000);
|
||||||
|
const query = new SourceQuery(ip, port);
|
||||||
|
const info = await query.getInfo();
|
||||||
|
const players = await query.getPlayers();
|
||||||
|
const rules = await query.getRules();
|
||||||
|
|
||||||
|
clearTimeout(timeout);
|
||||||
|
data = {
|
||||||
|
info,
|
||||||
|
players,
|
||||||
|
rules,
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
await msg.removeReaction("\uD83C\uDFD3");
|
||||||
|
if (err == "timeout") {
|
||||||
|
return "Failed to query server after 5 seconds.";
|
||||||
|
} else {
|
||||||
|
return `:warning: An error occured while querying:\n\`\`\`\n${err.message}\n\`\`\``;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await msg.removeReaction("\uD83C\uDFD3");
|
||||||
|
if (data?.info == null) {
|
||||||
|
return "Failed to query any data.";
|
||||||
|
}
|
||||||
|
|
||||||
|
const components = [];
|
||||||
|
|
||||||
|
const {
|
||||||
|
name,
|
||||||
|
map,
|
||||||
|
appid,
|
||||||
|
game,
|
||||||
|
folder,
|
||||||
|
players,
|
||||||
|
maxplayers,
|
||||||
|
bots,
|
||||||
|
type,
|
||||||
|
os,
|
||||||
|
visibility,
|
||||||
|
vac,
|
||||||
|
version,
|
||||||
|
sourcetv,
|
||||||
|
tags,
|
||||||
|
} = data.info;
|
||||||
|
|
||||||
|
let hasBanner = false;
|
||||||
|
let bannerUrl;
|
||||||
|
if (map.startsWith("surf_")) {
|
||||||
|
const url = `https://ksf.surf/images/${map}.jpg`;
|
||||||
|
const valid = await fetch(url, {
|
||||||
|
method: "HEAD",
|
||||||
|
headers: {"User-Agent": FRIENDLY_USERAGENT},
|
||||||
|
}).then((res) => res.ok);
|
||||||
|
|
||||||
|
if (valid) {
|
||||||
|
hasBanner = true;
|
||||||
|
bannerUrl = url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!hasBanner &&
|
||||||
|
(map.startsWith("surf_") ||
|
||||||
|
map.startsWith("bhop_") ||
|
||||||
|
map.startsWith("sj_") ||
|
||||||
|
map.startsWith("rj") ||
|
||||||
|
map.startsWith("ahop_") ||
|
||||||
|
map.startsWith("df_") ||
|
||||||
|
map.startsWith("conc_"))
|
||||||
|
) {
|
||||||
|
const mmodData = await fetch(`https://api.momentum-mod.org/v1/maps/${map}?expand=info`, {
|
||||||
|
headers: {"User-Agent": FRIENDLY_USERAGENT},
|
||||||
|
})
|
||||||
|
.then((res) => res.json())
|
||||||
|
.catch(() => {});
|
||||||
|
const image = mmodData?.images?.[0];
|
||||||
|
if (image) {
|
||||||
|
const url = image.xl ?? image.large ?? image.medium ?? image.small;
|
||||||
|
if (url) {
|
||||||
|
hasBanner = true;
|
||||||
|
bannerUrl = url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bannerUrl) {
|
||||||
|
components.push({
|
||||||
|
type: 12,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
media: {
|
||||||
|
url: bannerUrl,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let icon;
|
||||||
|
if (!hasBanner) {
|
||||||
|
const url = `https://gitlab.com/worldspawn/sourcemapicons/-/raw/main/icons/${map}.png`;
|
||||||
|
const valid = await fetch(url, {
|
||||||
|
method: "HEAD",
|
||||||
|
headers: {"User-Agent": FRIENDLY_USERAGENT},
|
||||||
|
}).then((res) => res.ok);
|
||||||
|
|
||||||
|
if (valid) icon = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lines = [];
|
||||||
|
lines.push(`# [${name}](https://sour-dani.github.io/steamconnect/?${ip}:${port})`);
|
||||||
|
lines.push(map);
|
||||||
|
lines.push(`-# [${appid == "4000" ? "Garry's Mod" : game}](https://steamdb.info/app/${appid}) (\`${folder}\`)`);
|
||||||
|
lines.push("");
|
||||||
|
|
||||||
|
let plycount = `${players}/${maxplayers} players`;
|
||||||
|
if (bots > 0) plycount += ` (${bots} bots)`;
|
||||||
|
lines.push(plycount);
|
||||||
|
|
||||||
|
lines.push(SERVER_TYPES[type] ?? `<unknown type: \`${type}\`>`);
|
||||||
|
if (appid == "4000") lines.push("**Gamemode:** " + game);
|
||||||
|
lines.push("**OS:** " + (OPERATING_SYSTEMS[os] ?? `<unknown OS: \`${os}\``));
|
||||||
|
lines.push(`**VAC:** ${vac ? "En" : "Dis"}abled`);
|
||||||
|
lines.push(`**Version:** \`${version}\``);
|
||||||
|
if (visibility == 1) lines.push("*Passworded*");
|
||||||
|
|
||||||
|
if (sourcetv?.name) {
|
||||||
|
lines.push("## SourceTV");
|
||||||
|
lines.push(`${sourcetv.name} (port ${sourcetv.port})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tags?.length > 0) {
|
||||||
|
lines.push("## Tags");
|
||||||
|
lines.push("```ansi\n" + tags + "\n```");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.players?.length > 0) {
|
||||||
|
lines.push("## Players");
|
||||||
|
|
||||||
|
const plytbl = new Table(["Player Name", "Score", "Time"]);
|
||||||
|
plytbl.setChars("\u2502", "\u2500", "\u253c");
|
||||||
|
for (const {name, score, time} of data.players) {
|
||||||
|
plytbl.addRow([name, score.toString(), formatTime(time * 1000)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.push("```ansi\n" + plytbl.render() + "\n```");
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = {type: 10, content: lines.join("\n")};
|
||||||
|
|
||||||
|
if (icon) {
|
||||||
|
components.push({
|
||||||
|
type: 9,
|
||||||
|
accessory: {
|
||||||
|
type: 11,
|
||||||
|
media: {
|
||||||
|
url: icon,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: [content],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
components.push(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
const m = await msg.channel.createMessage({
|
||||||
|
flags: MessageFlags.IS_COMPONENTS_V2,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: 17,
|
||||||
|
accent_color: getTopColor(msg, hf.bot.user.id, pastelize(hf.bot.user.id)),
|
||||||
|
components,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
allowedMentions: {
|
||||||
|
repliedUser: false,
|
||||||
|
},
|
||||||
|
messageReference: {
|
||||||
|
messageID: msg.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const cvars = Object.entries(data.rules);
|
||||||
|
if (cvars.length > 0) {
|
||||||
|
const cvarMsg = {
|
||||||
|
attachments: [
|
||||||
|
{
|
||||||
|
file: Buffer.from(cvars.map(([name, value]) => `${name} "${value}"`).join("\n")),
|
||||||
|
filename: `cvars - ${ip}_${port}.txt`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
allowedMentions: {
|
||||||
|
repliedUser: false,
|
||||||
|
},
|
||||||
|
messageReference: {
|
||||||
|
messageID: m.id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await msg.channel.createMessage(cvarMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
hf.registerCommand(sourcequery);
|
341
src/util/sourcequery.js
Normal file
341
src/util/sourcequery.js
Normal file
|
@ -0,0 +1,341 @@
|
||||||
|
const dgram = require("node:dgram");
|
||||||
|
|
||||||
|
const REQUEST_INFO = [
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0x54, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x20,
|
||||||
|
0x51, 0x75, 0x65, 0x72, 0x79, 0x00,
|
||||||
|
];
|
||||||
|
const REQUEST_RULES = [0xff, 0xff, 0xff, 0xff, 0x56];
|
||||||
|
const REQUEST_PLAYER = [0xff, 0xff, 0xff, 0xff, 0x55];
|
||||||
|
|
||||||
|
module.exports = class SourceQuery {
|
||||||
|
constructor(ip, port) {
|
||||||
|
this.ip = ip;
|
||||||
|
this.port = port;
|
||||||
|
this.client = dgram.createSocket("udp4");
|
||||||
|
this.rulePackets = [];
|
||||||
|
this.chunks = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_handlerInfo(message, resolve, reject, wrapper) {
|
||||||
|
const msg = new DataView(message.buffer);
|
||||||
|
|
||||||
|
if (msg.getUint8(4) == 0x41) {
|
||||||
|
const challenge = [msg.getUint8(5), msg.getUint8(6), msg.getUint8(7), msg.getUint8(8)];
|
||||||
|
|
||||||
|
this.client.send(Buffer.from([...REQUEST_INFO, ...challenge]), this.port, this.ip, function (err) {
|
||||||
|
if (err) {
|
||||||
|
this.client.close();
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (msg.getUint8(4) == 0x49) {
|
||||||
|
const info = {};
|
||||||
|
|
||||||
|
const protocolVer = msg.getUint8(5);
|
||||||
|
info.protocolVersion = protocolVer;
|
||||||
|
|
||||||
|
let idx = 6;
|
||||||
|
|
||||||
|
const name = [];
|
||||||
|
|
||||||
|
while (msg.getUint8(idx) != 0) {
|
||||||
|
name.push(msg.getUint8(idx));
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
info.name = Buffer.from(name).toString("utf8");
|
||||||
|
idx++;
|
||||||
|
|
||||||
|
const map = [];
|
||||||
|
while (msg.getUint8(idx) != 0) {
|
||||||
|
map.push(msg.getUint8(idx));
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
info.map = Buffer.from(map).toString("utf8");
|
||||||
|
idx++;
|
||||||
|
|
||||||
|
const folder = [];
|
||||||
|
while (msg.getUint8(idx) != 0) {
|
||||||
|
folder.push(msg.getUint8(idx));
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
info.folder = Buffer.from(folder).toString("utf8");
|
||||||
|
idx++;
|
||||||
|
|
||||||
|
const game = [];
|
||||||
|
while (msg.getUint8(idx) != 0) {
|
||||||
|
game.push(msg.getUint8(idx));
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
info.game = Buffer.from(game).toString("utf8");
|
||||||
|
idx++;
|
||||||
|
|
||||||
|
info.appid = msg.getInt16(idx, true);
|
||||||
|
idx += 2;
|
||||||
|
|
||||||
|
info.players = msg.getUint8(idx);
|
||||||
|
idx++;
|
||||||
|
|
||||||
|
info.maxplayers = msg.getUint8(idx);
|
||||||
|
idx++;
|
||||||
|
|
||||||
|
info.bots = msg.getUint8(idx);
|
||||||
|
idx++;
|
||||||
|
|
||||||
|
info.type = String.fromCharCode(msg.getUint8(idx));
|
||||||
|
idx++;
|
||||||
|
|
||||||
|
info.os = String.fromCharCode(msg.getUint8(idx));
|
||||||
|
idx++;
|
||||||
|
|
||||||
|
info.visibility = msg.getUint8(idx);
|
||||||
|
idx++;
|
||||||
|
|
||||||
|
info.vac = msg.getUint8(idx);
|
||||||
|
idx++;
|
||||||
|
|
||||||
|
const version = [];
|
||||||
|
while (msg.getUint8(idx) != 0) {
|
||||||
|
version.push(msg.getUint8(idx));
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
info.version = Buffer.from(version).toString("utf8");
|
||||||
|
idx++;
|
||||||
|
|
||||||
|
const edf = msg.getUint8(idx);
|
||||||
|
info.edfRaw = edf;
|
||||||
|
info.edf = {};
|
||||||
|
info.sourcetv = {};
|
||||||
|
idx++;
|
||||||
|
|
||||||
|
if (edf & 0x80) {
|
||||||
|
info.edf.port = msg.getInt16(idx, true);
|
||||||
|
idx += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (edf & 0x10) {
|
||||||
|
info.edf.steamid = msg.getBigUint64(idx, true);
|
||||||
|
idx += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (edf & 0x40) {
|
||||||
|
info.sourcetv.port = msg.getInt16(idx, true);
|
||||||
|
idx += 2;
|
||||||
|
|
||||||
|
const stvname = [];
|
||||||
|
while (msg.getUint8(idx) != 0) {
|
||||||
|
stvname.push(msg.getUint8(idx));
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
info.sourcetv.name = Buffer.from(stvname).toString("utf8");
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (edf & 0x20) {
|
||||||
|
const tags = [];
|
||||||
|
while (msg.getUint8(idx) != 0) {
|
||||||
|
tags.push(msg.getUint8(idx));
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
info.tags = Buffer.from(tags).toString("utf8");
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (edf & 0x01) {
|
||||||
|
info.edf.appid = msg.getBigUint64(idx, true);
|
||||||
|
idx += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.client.close();
|
||||||
|
this.client.off("message", wrapper);
|
||||||
|
resolve(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getInfo() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.client = dgram.createSocket("udp4");
|
||||||
|
this.client.send(Buffer.from(REQUEST_INFO), this.port, this.ip, function (err) {
|
||||||
|
if (err) {
|
||||||
|
this.client.close();
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const wrapper = function wrapper(message) {
|
||||||
|
this._handlerInfo(message, resolve, reject, wrapper);
|
||||||
|
}.bind(this);
|
||||||
|
|
||||||
|
this.client.on("message", wrapper);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_handlerPlayer(message, resolve, reject, wrapper) {
|
||||||
|
const msg = new DataView(message.buffer);
|
||||||
|
if (msg.getUint8(4) == 0x41) {
|
||||||
|
const challenge = [msg.getUint8(5), msg.getUint8(6), msg.getUint8(7), msg.getUint8(8)];
|
||||||
|
|
||||||
|
this.client.send(Buffer.from([...REQUEST_PLAYER, ...challenge]), this.port, this.ip, function (err) {
|
||||||
|
if (err) {
|
||||||
|
this.client.close();
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (msg.getUint8(4) == 0x44) {
|
||||||
|
const numPlayers = msg.getUint8(5);
|
||||||
|
let idx = 6;
|
||||||
|
const players = [];
|
||||||
|
for (let i = 0; i < numPlayers; i++) {
|
||||||
|
idx++;
|
||||||
|
|
||||||
|
const _name = [];
|
||||||
|
while (msg.getUint8(idx) != 0) {
|
||||||
|
_name.push(msg.getUint8(idx));
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
idx++;
|
||||||
|
const name = Buffer.from(_name).toString("utf8");
|
||||||
|
|
||||||
|
const _score = [];
|
||||||
|
for (let j = 0; j < 4; j++) {
|
||||||
|
_score.push(msg.getUint8(idx));
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
const score = new DataView(new Uint8Array(_score.reverse()).buffer).getInt32(0);
|
||||||
|
|
||||||
|
const _time = [];
|
||||||
|
for (let t = 0; t < 4; t++) {
|
||||||
|
_time.push(msg.getUint8(idx));
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
const time = Buffer.from(_time).readFloatLE(0);
|
||||||
|
|
||||||
|
players.push({name, score, time});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.client.close();
|
||||||
|
this.client.off("message", wrapper);
|
||||||
|
resolve(players);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getPlayers() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.client = dgram.createSocket("udp4");
|
||||||
|
this.client.send(Buffer.from([...REQUEST_PLAYER, 0xff, 0xff, 0xff, 0xff]), this.port, this.ip, function (err) {
|
||||||
|
if (err) {
|
||||||
|
this.client.close();
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const wrapper = function wrapper(message) {
|
||||||
|
this._handlerPlayer(message, resolve, reject, wrapper);
|
||||||
|
}.bind(this);
|
||||||
|
|
||||||
|
this.client.on("message", wrapper);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_processRules() {
|
||||||
|
const data = this.rulePackets.flat();
|
||||||
|
const dataArray = new Uint8Array(data);
|
||||||
|
|
||||||
|
const rulesMsg = new DataView(dataArray.buffer);
|
||||||
|
|
||||||
|
if (rulesMsg.getUint8(4) == 0x45) {
|
||||||
|
let idx = 5;
|
||||||
|
|
||||||
|
const rule_count = rulesMsg.getInt16(idx, true);
|
||||||
|
idx += 2;
|
||||||
|
|
||||||
|
const rules = {};
|
||||||
|
for (let i = 0; i < rule_count; i++) {
|
||||||
|
if (idx >= rulesMsg.byteLength) break;
|
||||||
|
|
||||||
|
const _name = [];
|
||||||
|
while (rulesMsg.getUint8(idx) != 0) {
|
||||||
|
_name.push(rulesMsg.getUint8(idx));
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
idx++;
|
||||||
|
const name = String.fromCharCode(..._name);
|
||||||
|
|
||||||
|
if (idx >= rulesMsg.byteLength) break;
|
||||||
|
const _value = [];
|
||||||
|
while (rulesMsg.getUint8(idx) != 0) {
|
||||||
|
_value.push(rulesMsg.getUint8(idx));
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
idx++;
|
||||||
|
const value = String.fromCharCode(..._value);
|
||||||
|
|
||||||
|
rules[name] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rules;
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_handlerRules(message, resolve, reject, wrapper) {
|
||||||
|
const msg = new DataView(message.buffer);
|
||||||
|
|
||||||
|
if (msg.getUint8(4) == 0x41) {
|
||||||
|
const challenge = [msg.getUint8(5), msg.getUint8(6), msg.getUint8(7), msg.getUint8(8)];
|
||||||
|
|
||||||
|
this.client.send(Buffer.from([...REQUEST_RULES, ...challenge]), this.port, this.ip, function (err) {
|
||||||
|
if (err) {
|
||||||
|
this.client.close();
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (
|
||||||
|
msg.getUint8(0) == 0xfe &&
|
||||||
|
msg.getUint8(1) == 0xff &&
|
||||||
|
msg.getUint8(2) == 0xff &&
|
||||||
|
msg.getUint8(3) == 0xff
|
||||||
|
) {
|
||||||
|
this.chunks = msg.getUint8(8);
|
||||||
|
const chunkIdx = msg.getUint8(9);
|
||||||
|
const chunk = [];
|
||||||
|
for (let i = 12; i < msg.byteLength; i++) {
|
||||||
|
chunk.push(msg.getUint8(i));
|
||||||
|
}
|
||||||
|
this.rulePackets[chunkIdx] = chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.chunks != -1 && this.rulePackets.length == this.chunks) {
|
||||||
|
this.client.close();
|
||||||
|
const rules = this._processRules();
|
||||||
|
|
||||||
|
this.client.off("message", wrapper);
|
||||||
|
resolve(rules);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getRules() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.client = dgram.createSocket("udp4");
|
||||||
|
this.client.send(Buffer.from([...REQUEST_RULES, 0xff, 0xff, 0xff, 0xff]), this.port, this.ip, function (err) {
|
||||||
|
if (err) {
|
||||||
|
this.client.close();
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const wrapper = function wrapper(message) {
|
||||||
|
this._handlerRules(message, resolve, reject, wrapper);
|
||||||
|
}.bind(this);
|
||||||
|
|
||||||
|
this.client.on("message", wrapper);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
|
@ -7,6 +7,7 @@ module.exports = class Table {
|
||||||
this._widths.push(this._rows[i][_i].length + 2);
|
this._widths.push(this._rows[i][_i].length + 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this._chars = ["|", "-", "+"];
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateWidth(row) {
|
_updateWidth(row) {
|
||||||
|
@ -24,6 +25,10 @@ module.exports = class Table {
|
||||||
this._updateWidth(row);
|
this._updateWidth(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setChars(vert, horz, center) {
|
||||||
|
this._chars = [vert, horz, center];
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
function drawRow(ctx, row, index) {
|
function drawRow(ctx, row, index) {
|
||||||
const columns = [];
|
const columns = [];
|
||||||
|
@ -35,11 +40,13 @@ module.exports = class Table {
|
||||||
return columns;
|
return columns;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [vert, horz, center] = this._chars;
|
||||||
|
|
||||||
const toDraw = [];
|
const toDraw = [];
|
||||||
const queue = this._rows.splice(1);
|
const queue = this._rows.splice(1);
|
||||||
for (const row in queue) {
|
for (const row in queue) {
|
||||||
const _row = drawRow(this, queue[row]);
|
const _row = drawRow(this, queue[row]);
|
||||||
toDraw.push(_row.join("|"));
|
toDraw.push(_row.join(vert));
|
||||||
}
|
}
|
||||||
|
|
||||||
this._updateWidth(this._rows[0]);
|
this._updateWidth(this._rows[0]);
|
||||||
|
@ -53,13 +60,13 @@ module.exports = class Table {
|
||||||
trows.push(out);
|
trows.push(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
const title_row = trows.join("|");
|
const title_row = trows.join(vert);
|
||||||
|
|
||||||
let seperator_row = "";
|
let seperator_row = "";
|
||||||
for (const index in this._widths) {
|
for (const index in this._widths) {
|
||||||
seperator_row += "-".repeat(this._widths[index]);
|
seperator_row += horz.repeat(this._widths[index]);
|
||||||
if (index != this._widths.length - 1) {
|
if (index != this._widths.length - 1) {
|
||||||
seperator_row += "+";
|
seperator_row += center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue