Implement RCON (#72)

This commit is contained in:
memetrollsXD 2022-10-07 15:40:32 +02:00 committed by GitHub
parent 24c7f0e155
commit 038c4a44d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 360 additions and 13 deletions

11
package-lock.json generated
View File

@ -12,6 +12,7 @@
"mongodb": "^4.8.0",
"node-kcp-token": "github:memetrollsxd/node-kcp",
"protobufjs": "^7.0.0",
"rcon-server": "^0.1.1",
"typescript": "^4.7.4"
},
"devDependencies": {
@ -1202,6 +1203,11 @@
"node": ">= 0.8"
}
},
"node_modules/rcon-server": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/rcon-server/-/rcon-server-0.1.1.tgz",
"integrity": "sha512-oJ9+s0yxGc7in6GBBBxFXKRY2OyIVxFsoAEHlo5iFXDCuK6O4gY4qJ9XndMBVmA+nABkyOrN6QAFzoE6wZiI7A=="
},
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@ -2568,6 +2574,11 @@
"unpipe": "1.0.0"
}
},
"rcon-server": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/rcon-server/-/rcon-server-0.1.1.tgz",
"integrity": "sha512-oJ9+s0yxGc7in6GBBBxFXKRY2OyIVxFsoAEHlo5iFXDCuK6O4gY4qJ9XndMBVmA+nABkyOrN6QAFzoE6wZiI7A=="
},
"readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",

View File

@ -13,6 +13,7 @@
"mongodb": "^4.8.0",
"node-kcp-token": "github:memetrollsxd/node-kcp",
"protobufjs": "^7.0.0",
"rcon-server": "^0.1.1",
"typescript": "^4.7.4"
}
}
}

View File

@ -27,22 +27,14 @@ export default class Interface {
private constructor() { }
public static readonly start = () => {
public static start() {
Interface.rl.question("", (_command) => {
if (!_command) {
Interface.start();
return;
}
const cmd = new Command(_command);
import(`./${alias[cmd.name] || cmd.name}`).then(async module => {
await module.default(cmd);
}).catch(err => {
if (err.code == "MODULE_NOT_FOUND") {
c.log(`Command ${cmd.name} not found.`);
return;
}
c.error(err);
});
Interface.handle(cmd);
Interface.start();
});
@ -51,4 +43,16 @@ export default class Interface {
process.exit(0);
});
}
public static handle(cmd: Command) {
import(`./${alias[cmd.name] || cmd.name}`).then(async module => {
await module.default(cmd);
}).catch(err => {
if (err.code == "MODULE_NOT_FOUND") {
c.log(`Command ${cmd.name} not found.`);
return;
}
c.error(err);
});
}
}

View File

@ -6,6 +6,7 @@
import Interface from "./commands/Interface";
import HttpServer from "./http/HttpServer";
import SRServer from "./server/kcp/SRServer";
import RCONServer from "./server/rcon/RCONServer";
import Banners from "./util/Banner";
import Logger from "./util/Logger";
import ProtoFactory from "./util/ProtoFactory"
@ -15,6 +16,7 @@ c.log(`Starting CrepeSR...`);
Banners.init();
ProtoFactory.init();
Interface.start();
Interface.start();
HttpServer.getInstance().start();
SRServer.getInstance().start();
SRServer.getInstance().start();
RCONServer.getInstance().start();

View File

@ -0,0 +1,54 @@
// @ts-ignore
import { RCONServer as RServer } from "rcon-server";
import { Writable } from "stream";
import Interface, { Command } from "../../commands/Interface";
import Config from "../../util/Config";
import Logger from "../../util/Logger";
const c = new Logger("RCON", "green");
export default class RCONServer {
private static _instance: RCONServer;
public RCON!: RServer;
private constructor() { }
public static getInstance(): RCONServer {
if (!this._instance) this._instance = new RCONServer();
return this._instance;
}
public start() {
if (!Config.RCON.RCON_ENABLED) return;
this.RCON = new RServer({
host: Config.HTTP.HTTP_HOST,
clientLimit: Config.RCON.RCON_CLIENT_LIMIT,
destroySocketOnLimitExceeded: false,
emitAdvancedEvents: false,
password: Config.RCON.RCON_PASSWORD,
port: Config.RCON.RCON_PORT
});
this.RCON.on("listening", () => {
c.log(`Listening on ${Config.RCON.RCON_PORT}`);
});
this.RCON.on("commandRequest", (cmd: {
size: number,
id: number,
type: number,
body: string
resolve: (value: string) => void,
}) => {
c.verbL(cmd);
Interface.handle(new Command(cmd.body));
cmd.resolve(`Command executed.`);
});
this.RCON.on("login", ({ pw, fail }: { pw: string, fail: boolean }) => {
c.debug(pw);
fail ? c.log(`Login failed`) : c.log(`Login successful`); // For some reason succ is flipped
});
this.RCON.connect();
}
}

263
src/server/rcon/rcon-server.d.ts vendored Normal file
View File

@ -0,0 +1,263 @@
/** Declaration file generated by dts-gen */
import { EventEmitter } from "events";
declare module "rcon-server" {
export class RCONServer extends EventEmitter {
constructor(obj: {
port: number = 3839,
host: string = "127.0.0.1",
password: string = "password",
clientLimit: number = 1,
destroySocketOnLimitExceeded: boolean = true,
emitAdvancedEvents: boolean = false
});
connect(...args: any[]): void;
getConnectedSockets(...args: any[]): void;
getServerSettings(...args: any[]): void;
getSocketServer(...args: any[]): void;
static captureRejectionSymbol: any;
static captureRejections: boolean;
static defaultMaxListeners: number;
static errorMonitor: any;
static getEventListeners(emitterOrTarget: any, type: any): any;
static init(opts: any): void;
static kMaxEventTargetListeners: any;
static kMaxEventTargetListenersWarned: any;
static listenerCount(emitter: any, type: any): any;
static on(emitter: any, event: any, options: any): any;
static once(emitter: any, name: any, options: any): any;
static setMaxListeners(n: any, eventTargets: any): void;
static usingDomains: boolean;
}
export namespace RCONServer {
class EventEmitter {
constructor(opts: any);
addListener(type: any, listener: any): any;
emit(type: any, args: any): any;
eventNames(): any;
getMaxListeners(): any;
listenerCount(type: any): any;
listeners(type: any): any;
off(type: any, listener: any): any;
on(type: any, listener: any): any;
once(type: any, listener: any): any;
prependListener(type: any, listener: any): any;
prependOnceListener(type: any, listener: any): any;
rawListeners(type: any): any;
removeAllListeners(type: any, ...args: any[]): any;
removeListener(type: any, listener: any): any;
setMaxListeners(n: any): any;
static EventEmitter: any;
static captureRejectionSymbol: any;
static captureRejections: boolean;
static defaultMaxListeners: number;
static errorMonitor: any;
static getEventListeners(emitterOrTarget: any, type: any): any;
static init(opts: any): void;
static kMaxEventTargetListeners: any;
static kMaxEventTargetListenersWarned: any;
static listenerCount(emitter: any, type: any): any;
static on(emitter: any, event: any, options: any): any;
static once(emitter: any, name: any, options: any): any;
static setMaxListeners(n: any, eventTargets: any): void;
static usingDomains: boolean;
}
class EventEmitterAsyncResource {
constructor(...args: any[]);
emit(...args: any[]): void;
emitDestroy(...args: any[]): void;
static EventEmitterAsyncResource: any;
static captureRejectionSymbol: any;
static captureRejections: boolean;
static defaultMaxListeners: number;
static errorMonitor: any;
static getEventListeners(emitterOrTarget: any, type: any): any;
static init(opts: any): void;
static kMaxEventTargetListeners: any;
static kMaxEventTargetListenersWarned: any;
static listenerCount(emitter: any, type: any): any;
static on(emitter: any, event: any, options: any): any;
static once(emitter: any, name: any, options: any): any;
static setMaxListeners(n: any, eventTargets: any): void;
static usingDomains: boolean;
}
namespace EventEmitter {
class EventEmitterAsyncResource {
constructor(...args: any[]);
emit(...args: any[]): void;
emitDestroy(...args: any[]): void;
static EventEmitter: any;
static EventEmitterAsyncResource: any;
static captureRejectionSymbol: any;
static captureRejections: boolean;
static defaultMaxListeners: number;
static errorMonitor: any;
static getEventListeners(emitterOrTarget: any, type: any): any;
static init(opts: any): void;
static kMaxEventTargetListeners: any;
static kMaxEventTargetListenersWarned: any;
static listenerCount(emitter: any, type: any): any;
static on(emitter: any, event: any, options: any): any;
static once(emitter: any, name: any, options: any): any;
static setMaxListeners(n: any, eventTargets: any): void;
static usingDomains: boolean;
}
}
namespace EventEmitterAsyncResource {
class EventEmitter {
constructor(opts: any);
addListener(type: any, listener: any): any;
emit(type: any, args: any): any;
eventNames(): any;
getMaxListeners(): any;
listenerCount(type: any): any;
listeners(type: any): any;
off(type: any, listener: any): any;
on(type: any, listener: any): any;
once(type: any, listener: any): any;
prependListener(type: any, listener: any): any;
prependOnceListener(type: any, listener: any): any;
rawListeners(type: any): any;
removeAllListeners(type: any, ...args: any[]): any;
removeListener(type: any, listener: any): any;
setMaxListeners(n: any): any;
static EventEmitter: any;
static EventEmitterAsyncResource: any;
static captureRejectionSymbol: any;
static captureRejections: boolean;
static defaultMaxListeners: number;
static errorMonitor: any;
static getEventListeners(emitterOrTarget: any, type: any): any;
static init(opts: any): void;
static kMaxEventTargetListeners: any;
static kMaxEventTargetListenersWarned: any;
static listenerCount(emitter: any, type: any): any;
static on(emitter: any, event: any, options: any): any;
static once(emitter: any, name: any, options: any): any;
static setMaxListeners(n: any, eventTargets: any): void;
static usingDomains: boolean;
}
}
}
}

View File

@ -28,6 +28,12 @@ const DEFAULT_CONFIG = {
MAINTENANCE: false,
MAINTENANCE_MSG: "Server is in maintenance mode."
},
RCON: {
RCON_ENABLED: false,
RCON_PASSWORD: "password",
RCON_PORT: 22103,
RCON_CLIENT_LIMIT: 1
},
AUTO_ACCOUNT: false
}
type DefaultConfig = typeof DEFAULT_CONFIG;
@ -82,6 +88,12 @@ export default class Config {
MAINTENANCE_MSG: string;
} = Config.config.GAMESERVER;
public static AUTO_ACCOUNT: boolean = Config.config.AUTO_ACCOUNT;
public static RCON: {
RCON_ENABLED: boolean;
RCON_PASSWORD: string;
RCON_PORT: number;
RCON_CLIENT_LIMIT: number;
} = Config.config.RCON;
private constructor() { }
}