UNPKG

@krp-races/krp-node-wrapper

Version:

A node.js wrapper for a dedicated or challenge server in kart racing pro.

191 lines (188 loc) 7.34 kB
import { _ as __awaiter } from '../tslib.es6-WQS2tr1v.js'; import { EventEmitter } from 'events'; import { SocketWrapper } from './SocketWrapper.mjs'; import { ClientStatus } from '../enums/ClientStatus.mjs'; import { readStringLines } from '../utils/readStringLines.mjs'; import { writeStringLines } from '../utils/writeStringLines.mjs'; import { timeout } from '../utils/timeout.mjs'; import { LivetimingReader } from './LivetimingReader.mjs'; import 'dgram'; import '../enums/DataType.mjs'; import '../enums/Event/ChallengeType.mjs'; import '../enums/Event/EventType.mjs'; import '../enums/Session/Classification/KartStatus.mjs'; import '../enums/Session/Classification/RaceStatus.mjs'; import '../enums/Session/SessionEntry/DriverStatusReason.mjs'; import '../enums/Session/SessionEntry/DriverStatusState.mjs'; import '../enums/Session/SessionState.mjs'; import '../enums/Session/SessionType.mjs'; import '../utils/Vec2.mjs'; import '../utils/Vec3.mjs'; const defaultData = { entries: new Map(), sessions: new Map(), contacts: [], }; class LivetimingClient extends EventEmitter { constructor(options) { super(); this.data = defaultData; this.enabled = false; this.status = ClientStatus.NOT_CONNECTED; this.options = options; this.socket = new SocketWrapper(options); this.socket.on("message", this.handleMessage.bind(this)); this.socket.on("error", this.handleError.bind(this)); this.on("connected", this.handleKeepAlive.bind(this)); } setEnabled(enabled) { const prevEnabled = this.enabled; this.enabled = enabled; if (!prevEnabled && enabled) this.handleReconnect(); if (prevEnabled && !enabled) this.disconnect(); } getEnabled() { return this.enabled; } setStatus(status) { this.status = status; } getStatus() { return this.status; } connect() { return __awaiter(this, void 0, void 0, function* () { if (this.status === ClientStatus.CONNECTED) return; this.setStatus(ClientStatus.CONNECTING); const data = writeStringLines(["CONNECT", this.options.password]); this.socket.send(data); const promise = new Promise((resolve, reject) => { const listener = (msg) => { const lines = readStringLines(msg); switch (lines[0]) { case "OK": if (lines[1] !== "krp") { reject(new Error("Wrong game")); this.setStatus(ClientStatus.NOT_CONNECTED); this.socket.removeListener("message", listener); break; } resolve(undefined); this.setStatus(ClientStatus.CONNECTED); this.socket.removeListener("message", listener); break; case "FULL": reject(new Error("Server is full")); this.setStatus(ClientStatus.NOT_CONNECTED); this.socket.removeListener("message", listener); break; case "WRONGPASSWORD": reject(new Error("Wrong password")); this.setStatus(ClientStatus.NOT_CONNECTED); this.socket.removeListener("message", listener); break; } }; this.socket.on("message", listener); }); return Promise.race([promise, timeout(5000)]).catch((err) => this.handleError(err)); }); } disconnect() { if (this.status !== ClientStatus.CONNECTED) return; const data = writeStringLines(["DISCONNECT"]); this.socket.send(data); this.setStatus(ClientStatus.NOT_CONNECTED); this.emit("disconnected"); } keepAlive() { return __awaiter(this, void 0, void 0, function* () { if (this.status !== ClientStatus.CONNECTED) return; const data = writeStringLines(["KEEPALIVE"]); this.socket.send(data); const promise = new Promise((resolve) => { const listener = (msg) => { const lines = readStringLines(msg); if (lines[0] === "ALIVE") { resolve(undefined); this.socket.removeListener("message", listener); } }; this.socket.on("message", listener); }); return Promise.race([promise, timeout(5000)]).catch((err) => this.handleError(err)); }); } start(trackPositions, collisions) { if (this.status !== ClientStatus.CONNECTED) return; const data = writeStringLines([ "START", trackPositions.toFixed(0), collisions.toFixed(0), ]); this.socket.send(data); } acknowledge(messageId) { if (this.status !== ClientStatus.CONNECTED) return; const data = writeStringLines(["ACK", messageId]); this.socket.send(data); } handleReconnect() { return __awaiter(this, void 0, void 0, function* () { // If not enabled, do not attempt to reconnect if (!this.enabled) return; yield this.connect().catch((err) => this.handleError(err)); // When connected to a server, do not attempt to reconnect if (this.status === ClientStatus.CONNECTED) { this.emit("connected"); this.start(this.options.trackPositions, this.options.collisions); return; } // Attempt to reconnect in x milliseconds setTimeout(this.handleReconnect.bind(this), 5000); }); } handleKeepAlive() { return __awaiter(this, void 0, void 0, function* () { if (this.status !== ClientStatus.CONNECTED) return; yield this.keepAlive().catch((err) => this.handleError(err)); setTimeout(this.handleKeepAlive.bind(this), 15000); }); } handleMessage(msg) { if (this.status !== ClientStatus.CONNECTED) return; const lines = readStringLines(msg); switch (lines[0]) { case "MSG": { const reader = new LivetimingReader(lines, this.data); this.data = reader.read(); this.acknowledge(lines[1]); this.emit("data", this.data); } break; case "DATA": { const reader = new LivetimingReader(lines, this.data, 1); this.data = reader.read(); this.emit("data", this.data); } break; } } handleError(err) { this.emit("error", err); this.disconnect(); } } export { LivetimingClient };