UNPKG

http2byond

Version:

Communication layer between node.js and BYOND game servers.

171 lines 6.04 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createTopicConnection = exports.sendTopic = exports._sendTopic = void 0; const net_1 = require("net"); /** * @internal */ function _sendTopic(socket, _topic) { const topic = _topic[0] !== "?" ? "?" + _topic : _topic; return new Promise((resolve, reject) => { //Get rid of all listeners to prepare the socket to be reused socket.removeAllListeners(); function errorHandler(reason) { return function (originalError) { originalError ? reject(new Error(reason + "\n Caused by: " + originalError)) : reject(new Error(reason)); socket.destroy(); }; } socket.on("error", errorHandler("Socket errored")); socket.on("timeout", errorHandler("Connection timeout")); let byte = 0; //type(2) + length(2) const headerBuffer = Buffer.alloc(4); let bodyBuffer; function processResponse() { const type = bodyBuffer[0]; switch (type) { case 0x00: { return resolve(null); } case 0x2a: { return resolve(bodyBuffer.readFloatLE(1)); } case 0x06: { return resolve(bodyBuffer.subarray(0, -1).toString("utf-8")); } } } socket.on("data", data => { //Still waiting on a complete header if (byte < 4) { const copiedBytes = data.copy(headerBuffer); data = data.subarray(copiedBytes); byte += copiedBytes; //Got the full header! if (byte >= 4) { bodyBuffer = Buffer.alloc(headerBuffer.readUint16BE(2)); //Sucks to be you, maybe you'll get a full header later? } else { return; } } //We either just finished reading the header and got more to read, or we just got another body packet if (data.length) { const copiedBytes = data.copy(bodyBuffer); byte += copiedBytes; } //expected buffer length + the header length const fullLength = 4 + bodyBuffer.length; //We got too many bytes! if (byte > fullLength) { errorHandler("Data is larger than expected"); return; } //No more data to read if (byte === fullLength) { processResponse(); } }); function sendRequest() { const topicBuffer = Buffer.from(topic); //type(2) + length(2) + padding(5) + msg(n) + terminator(1) const dataBuffer = Buffer.alloc(2 + 2 + 5 + topicBuffer.length + 1); let ptr; //Packet type 0x0083 dataBuffer[0] = 0x00; dataBuffer[1] = 0x83; //Write length of buffer //padding(5) + msg(n) + terminator(1) dataBuffer.writeUInt16BE(5 + topicBuffer.length + 1, 2); //Write padding dataBuffer[4] = 0x00; dataBuffer[5] = 0x00; dataBuffer[6] = 0x00; dataBuffer[7] = 0x00; dataBuffer[8] = 0x00; //We're done with the header, we need to write the null terminated string to the 8th byte. ptr = 9; topicBuffer.copy(dataBuffer, ptr); ptr += topicBuffer.length; dataBuffer[ptr] = 0x00; socket.write(dataBuffer); } //@ts-expect-error https://github.com/DefinitelyTyped/DefinitelyTyped/pull/59397 lib definitions are missing a property //Send the request either as soon as the socket is ready or immediatly if its already ready if (socket.readyState === "open") { sendRequest(); } else { socket.once("ready", sendRequest); } }); } exports._sendTopic = _sendTopic; function sendTopic(config) { const socket = (0, net_1.createConnection)({ family: 4, host: config.host, port: config.port, timeout: config.timeout }); return _sendTopic(socket, config.topic).then(val => { socket.end(); return val; }); } exports.sendTopic = sendTopic; function createTopicConnection(config) { const socket = (0, net_1.createConnection)({ family: 4, host: config.host, port: config.port }); const queue = []; let busy = false; function runNextTopic(value) { const next = queue.shift(); if (!next) { busy = false; socket.setTimeout(0); return value; } const [topic, resolve, reject] = next; _sendTopic(socket, topic).then(runNextTopic).then(resolve).catch(reject); return value; } return { send: topic => { var _a; if (socket.destroyed) { return Promise.reject(new Error("Socket is destroyed")); } //Immediate if (!busy) { busy = true; socket.setTimeout((_a = config.timeout) !== null && _a !== void 0 ? _a : 10000); return _sendTopic(socket, topic).then(runNextTopic); } return new Promise((resolve, reject) => { queue.push([topic, resolve, reject]); }); }, destroy: () => { socket.destroy(); }, get queueLength() { //Busy means we are processing a query right now return queue.length + Number(busy); }, get destroyed() { return socket.destroyed; } }; } exports.createTopicConnection = createTopicConnection; //# sourceMappingURL=http2byond.js.map