UNPKG

rcon-node

Version:

A TypeScript RCON client library for modern game servers.

147 lines 5 kB
import { createConnection } from "node:net"; import { BaseClient } from "./base.client"; import { PACKET_TYPE_AUTH, PACKET_TYPE_AUTH_RESPONSE, PACKET_TYPE_COMMAND, PACKET_TYPE_RESPONSE, } from "../utils/packet"; import { buildValheimPacket, extractValheimPackets, } from "../utils/valheim.utils"; export class ValheimClient extends BaseClient { constructor() { super(...arguments); this.socket = null; this.requestId = 0; this.pending = new Map(); this.connected = false; this.authenticated = false; this.buffer = Buffer.alloc(0); this.authCallback = null; } connect() { return new Promise((resolve, reject) => { this.socket = createConnection({ host: this.options.host, port: this.options.port, }, () => { this.connected = true; this.emit("connect"); this.authenticate().then(resolve).catch(reject); }); this.socket.on("data", (data) => this.onData(data)); this.socket.on("error", (err) => this.emit("error", err)); this.socket.on("end", () => { this.connected = false; this.authenticated = false; this.emit("end"); }); if (this.options.timeout) { this.socket.setTimeout(this.options.timeout, () => { if (!this.connected) { const err = new Error("Connection timed out."); this.emit("error", err); this.end(); reject(err); } }); } }); } authenticate() { return new Promise((resolve, reject) => { const timeout = setTimeout(() => { this.authCallback?.(new Error("Authentication timed out.")); }, this.options.timeout ?? 5000); this.authCallback = (err) => { clearTimeout(timeout); if (err) { this.end(); reject(err); } else { this.authenticated = true; this.emit("authenticated"); resolve(); } this.authCallback = null; }; this.sendPacket(PACKET_TYPE_AUTH, this.options.password).catch((err) => { this.authCallback?.(err); }); }); } onData(data) { this.buffer = Buffer.concat([this.buffer, data]); const { packets, remaining } = extractValheimPackets(this.buffer); this.buffer = remaining; for (const packet of packets) { this.handlePacket(packet); } } handlePacket(packet) { const { id, type, body } = packet; if (this.authCallback) { if (type === PACKET_TYPE_AUTH_RESPONSE) { if (id === -1) { this.authCallback(new Error("Authentication failed.")); } else if (id === this.requestId) { this.authCallback(); } } else if (type === PACKET_TYPE_RESPONSE) { // Some servers send an empty response during auth; ignore it. } return; } if (type === PACKET_TYPE_RESPONSE) { const resolve = this.pending.get(id); if (resolve) { this.pending.delete(id); resolve(body); } else { this.emit("response", body); } } } send(command) { return new Promise((resolve, reject) => { if (!this.authenticated) { return reject(new Error("Not authenticated.")); } this.sendPacket(PACKET_TYPE_COMMAND, command) .then((id) => { this.pending.set(id, resolve); }) .catch(reject); }); } sendPacket(type, body) { return new Promise((resolve, reject) => { if (!this.socket) { return reject(new Error("Socket not connected.")); } const id = ++this.requestId; const buffer = buildValheimPacket(id, type, body); this.socket.write(buffer, (err) => { if (err) { return reject(err); } resolve(id); }); }); } end() { if (this.socket) { this.socket.end(); this.socket = null; } } async testAuthentication() { try { if (!this.connected) { await this.connect(); } } catch { throw new Error("Authentication failed."); } } } //# sourceMappingURL=valheim.client.js.map