craftping
Version:
A universal ping/query library for Minecraft servers.
139 lines (123 loc) • 3.27 kB
JavaScript
import {EventEmitter} from "node:events";
import QueuedPromise from "../QueuedPromise.js";
import NetworkError from "../Error/NetworkError.js";
export default class UDPClient extends EventEmitter {
/** @type {string} */ address;
/** @type {number} */ port;
/** @type {SharedUDPSocket} */ socket;
/** @type {UDPMessage[]} */ inboundQueue = []
/** @type {QueuedPromise<UDPMessage>[]} */ readQueue = [];
/** @type {?AbortSignal} */ signal;
/** @type {boolean} */ open = false;
/**
* @param {string} address
* @param {number} port
* @param {SharedUDPSocket} socket
* @param {?AbortSignal} signal
*/
constructor(address, port, socket, signal = null) {
super();
this.address = address;
this.port = port;
this.socket = socket;
this.signal = signal;
this.signal?.addEventListener("abort", this.handleAbort.bind(this));
}
/**
* @return {Promise<UDPClient>}
*/
async connect() {
await this.socket.register(this);
this.open = true;
return this;
}
/**
* @return {this}
*/
handleAbort() {
if (this.open) {
this.close(new NetworkError("Operation was aborted"));
}
return this;
}
/**
* @param {?Error} error
* @return {this}
*/
close(error = null) {
this.emit("dispose", this);
this.handleClose(error);
this.removeAllListeners();
return this;
}
/**
* @param {UDPMessage} message
* @return {boolean}
*/
appliesTo(message) {
return this.address === message.getAddress() && this.port === message.getPort();
}
/**
* @param {UDPMessage} message
* @return {this}
*/
handleMessage(message) {
if (!this.appliesTo(message)) {
return this;
}
if (this.readQueue.length > 0) {
this.readQueue.shift().resolve(message);
return this;
}
this.inboundQueue.push(message);
return this;
}
/**
* @param {Error} error
* @return {UDPClient}
*/
handleError(error) {
if (this.listenerCount("error") > 0) {
this.emit("error", error);
}
return this;
}
/**
* @param {?Error} error
* @return {this}
*/
handleClose(error = null) {
this.open = false;
this.emit("close");
for (const promise of this.readQueue) {
promise.reject(error ?? new NetworkError("Client was closed"));
}
this.readQueue = [];
return this;
}
/**
* @param {Buffer} msg
* @return {Promise<this>}
*/
async send(msg) {
await this.socket.send(msg, this.port, this.address);
return this;
}
/**
* @return {Promise<UDPMessage>}
*/
read() {
if (this.inboundQueue.length > 0) {
return Promise.resolve(this.inboundQueue.shift());
}
let promise = new QueuedPromise();
this.readQueue.push(promise);
return promise.getPromise();
}
/**
* @return {Promise<Buffer>}
*/
async readData() {
return (await this.read()).getData();
}
}