mirror of
https://github.com/AsgardEternal/SquadJS.git
synced 2024-09-28 16:24:25 -05:00
Merge branch 'v2' of https://github.com/Thomas-Smyth/SquadJS into Thomas-Smyth-v2
This commit is contained in:
commit
013ace439f
@ -17,7 +17,9 @@ export default class LogParser extends EventEmitter {
|
|||||||
this.eventStore = {};
|
this.eventStore = {};
|
||||||
|
|
||||||
this.linesPerMinute = 0;
|
this.linesPerMinute = 0;
|
||||||
this.linesPerMinuteInterval = null;
|
this.matchingLinesPerMinute = 0;
|
||||||
|
this.matchingLatency = 0;
|
||||||
|
this.parsingStatsInterval = null;
|
||||||
|
|
||||||
this.queue = async.queue(async (line) => {
|
this.queue = async.queue(async (line) => {
|
||||||
Logger.verbose('LogParser', 4, `Matching on line: ${line}`);
|
Logger.verbose('LogParser', 4, `Matching on line: ${line}`);
|
||||||
@ -33,6 +35,9 @@ export default class LogParser extends EventEmitter {
|
|||||||
|
|
||||||
rule.onMatch(match, this);
|
rule.onMatch(match, this);
|
||||||
|
|
||||||
|
this.matchingLinesPerMinute++;
|
||||||
|
this.matchingLatency += Date.now() - match[1];
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,15 +61,27 @@ export default class LogParser extends EventEmitter {
|
|||||||
await this.logReader.watch();
|
await this.logReader.watch();
|
||||||
Logger.verbose('LogParser', 1, 'Watching log file...');
|
Logger.verbose('LogParser', 1, 'Watching log file...');
|
||||||
|
|
||||||
this.linesPerMinuteInterval = setInterval(() => {
|
this.parsingStatsInterval = setInterval(() => {
|
||||||
Logger.verbose('LogParser', 1, `Processing ${this.linesPerMinute} lines per minute.`);
|
Logger.verbose(
|
||||||
|
'LogParser',
|
||||||
|
1,
|
||||||
|
`Lines parsed per minute: ${
|
||||||
|
this.linesPerMinute
|
||||||
|
} lines per minute | Matching lines per minute: ${
|
||||||
|
this.matchingLinesPerMinute
|
||||||
|
} matching lines per minute | Average matching latency: ${
|
||||||
|
this.matchingLatency / this.matchingLinesPerMinute
|
||||||
|
}ms`
|
||||||
|
);
|
||||||
this.linesPerMinute = 0;
|
this.linesPerMinute = 0;
|
||||||
|
this.matchingLinesPerMinute = 0;
|
||||||
|
this.matchingLatency = 0;
|
||||||
}, 60 * 1000);
|
}, 60 * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
async unwatch() {
|
async unwatch() {
|
||||||
await this.logReader.unwatch();
|
await this.logReader.unwatch();
|
||||||
|
|
||||||
clearInterval(this.linesPerMinuteInterval);
|
clearInterval(this.parsingStatsInterval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,9 @@
|
|||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint --fix . && prettier --write \"./**/*.js\"",
|
"lint": "eslint --fix . && prettier --write \"./**/*.js\"",
|
||||||
"build-config": "node squad-server/scripts/build-config.js",
|
"build-config": "node squad-server/scripts/build-config-file.js",
|
||||||
"build-readme": "node squad-server/scripts/build-readme.js",
|
"build-readme": "node squad-server/scripts/build-readme.js",
|
||||||
"build-all": "node squad-server/scripts/build-config.js && node squad-server/scripts/build-readme.js"
|
"build-all": "node squad-server/scripts/build-config-file.js && node squad-server/scripts/build-readme.js"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
202
rcon/index.js
202
rcon/index.js
@ -46,14 +46,18 @@ export default class Rcon extends EventEmitter {
|
|||||||
this.autoReconnect = false;
|
this.autoReconnect = false;
|
||||||
this.autoReconnectTimeout = null;
|
this.autoReconnectTimeout = null;
|
||||||
|
|
||||||
this.responseActionQueue = [];
|
this.incomingData = Buffer.from([]);
|
||||||
this.responsePacketQueue = [];
|
this.incomingResponse = [];
|
||||||
|
|
||||||
|
this.responseCallbackQueue = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
onData(buf) {
|
onData(data) {
|
||||||
Logger.verbose('RCON', 4, `Got data: ${this.bufToHexString(buf)}`);
|
Logger.verbose('RCON', 4, `Got data: ${this.bufToHexString(data)}`);
|
||||||
|
|
||||||
const packets = this.splitPackets(buf);
|
// the logic in this method simply splits data sent via the data event into packets regardless of how they're
|
||||||
|
// distributed in the event calls
|
||||||
|
const packets = this.decodeData(data);
|
||||||
|
|
||||||
for (const packet of packets) {
|
for (const packet of packets) {
|
||||||
Logger.verbose('RCON', 4, `Processing packet: ${this.bufToHexString(packet)}`);
|
Logger.verbose('RCON', 4, `Processing packet: ${this.bufToHexString(packet)}`);
|
||||||
@ -65,68 +69,85 @@ export default class Rcon extends EventEmitter {
|
|||||||
`Processing decoded packet: ${this.decodedPacketToString(decodedPacket)}`
|
`Processing decoded packet: ${this.decodedPacketToString(decodedPacket)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
if (decodedPacket.type === SERVERDATA_RESPONSE_VALUE)
|
switch (decodedPacket.type) {
|
||||||
this.processResponsePacket(decodedPacket);
|
case SERVERDATA_RESPONSE_VALUE:
|
||||||
else if (decodedPacket.type === SERVERDATA_AUTH_RESPONSE)
|
case SERVERDATA_AUTH_RESPONSE:
|
||||||
this.processAuthPacket(decodedPacket);
|
switch (decodedPacket.id) {
|
||||||
else if (decodedPacket.type === SERVERDATA_CHAT_VALUE) this.processChatPacket(decodedPacket);
|
case MID_PACKET_ID:
|
||||||
else
|
this.incomingResponse.push(decodedPacket);
|
||||||
Logger.verbose(
|
break;
|
||||||
'RCON',
|
case END_PACKET_ID:
|
||||||
2,
|
this.responseCallbackQueue.shift()(
|
||||||
`Unknown packet type ${decodedPacket.type} in: ${this.decodedPacketToString(
|
this.incomingResponse.map((packet) => packet.body).join()
|
||||||
decodedPacket
|
);
|
||||||
)}`
|
this.incomingResponse = [];
|
||||||
);
|
break;
|
||||||
|
default:
|
||||||
|
Logger.verbose(
|
||||||
|
'RCON',
|
||||||
|
1,
|
||||||
|
`Unknown packet ID ${decodedPacket.id} in: ${this.decodedPacketToString(
|
||||||
|
decodedPacket
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SERVERDATA_CHAT_VALUE:
|
||||||
|
this.processChatPacket(decodedPacket);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Logger.verbose(
|
||||||
|
'RCON',
|
||||||
|
1,
|
||||||
|
`Unknown packet type ${decodedPacket.type} in: ${this.decodedPacketToString(
|
||||||
|
decodedPacket
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
splitPackets(buf) {
|
decodeData(data) {
|
||||||
|
this.incomingData = Buffer.concat([this.incomingData, data]);
|
||||||
|
|
||||||
const packets = [];
|
const packets = [];
|
||||||
|
|
||||||
let offset = 0;
|
while (this.incomingData.byteLength > 0) {
|
||||||
|
const size = this.incomingData.readInt32LE(0);
|
||||||
|
const packetSize = size + 4;
|
||||||
|
|
||||||
while (offset < buf.byteLength) {
|
// The packet following an empty packet will report to be 10 long (14 including the size header bytes), but in
|
||||||
const size = buf.readInt32LE(offset);
|
// it should report 17 long (21 including the size header bytes). Therefore, if the packet is 10 in size
|
||||||
|
// and there's enough data for it to be a longer packet then we need to probe to check it's this broken packet.
|
||||||
|
const probeSize = 17;
|
||||||
|
const probePacketSize = 21;
|
||||||
|
|
||||||
const endOfPacket = offset + size + 4;
|
if (size === 10 && this.incomingData.byteLength >= probeSize) {
|
||||||
|
// copy the section of the incoming data of interest
|
||||||
// The packet following an empty pocked will appear to be 10 long, it's not.
|
const probeBuf = this.incomingData.slice(0, probePacketSize);
|
||||||
if (size === 10) {
|
// decode it
|
||||||
// it's 21 bytes long (or 17 when ignoring the 4 size bytes), 7 bytes longer than it should be.
|
const decodedProbePacket = this.decodePacket(probeBuf);
|
||||||
const probeEndOfPacket = endOfPacket + 7;
|
// check whether body matches
|
||||||
|
if (decodedProbePacket.body === '\x00\x00\x00\x01\x00\x00\x00') {
|
||||||
// check that there is room for the packet to be longer than it claims to be
|
// it does so it's the broken packet
|
||||||
if (probeEndOfPacket <= buf.byteLength) {
|
// remove the broken packet from the incoming data
|
||||||
// it is, so probe that section of the buffer
|
this.incomingData = this.incomingData.slice(probePacketSize);
|
||||||
const probeBuf = buf.slice(offset, probeEndOfPacket);
|
Logger.verbose('RCON', 4, `Ignoring some data: ${this.bufToHexString(probeBuf)}`);
|
||||||
|
continue;
|
||||||
// we decode to see it's contents
|
|
||||||
const decodedProbePacket = this.decodePacket(probeBuf);
|
|
||||||
|
|
||||||
// if it matches this body then it's the broken length packet
|
|
||||||
if (decodedProbePacket.body === '\x00\x00\x00\x01\x00\x00\x00') {
|
|
||||||
// update the offset with the new correct length, then skip this packet as we don't care about it anyway
|
|
||||||
offset = endOfPacket + 7;
|
|
||||||
Logger.verbose('RCON', 4, `Ignoring some data: ${this.bufToHexString(probeBuf)}`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const packet = buf.slice(offset, endOfPacket);
|
if (this.incomingData.byteLength < packetSize) {
|
||||||
|
Logger.verbose('RCON', 4, `Waiting for more data...`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const packet = this.incomingData.slice(0, packetSize);
|
||||||
packets.push(packet);
|
packets.push(packet);
|
||||||
|
|
||||||
offset = endOfPacket;
|
this.incomingData = this.incomingData.slice(packetSize);
|
||||||
}
|
|
||||||
|
|
||||||
if (packets.length !== 0) {
|
|
||||||
Logger.verbose(
|
|
||||||
'RCON',
|
|
||||||
4,
|
|
||||||
`Split data into packets: ${packets.map(this.bufToHexString).join(', ')}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return packets;
|
return packets;
|
||||||
@ -141,46 +162,6 @@ export default class Rcon extends EventEmitter {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
processResponsePacket(decodedPacket) {
|
|
||||||
if (decodedPacket.id === MID_PACKET_ID) {
|
|
||||||
Logger.verbose(
|
|
||||||
'RCON',
|
|
||||||
3,
|
|
||||||
`Pushing packet to queue: ${this.decodedPacketToString(decodedPacket)}`
|
|
||||||
);
|
|
||||||
this.responsePacketQueue.push(decodedPacket);
|
|
||||||
} else if (decodedPacket.id === END_PACKET_ID) {
|
|
||||||
Logger.verbose('RCON', 3, 'Initiating processing of packet queue.');
|
|
||||||
this.processCompleteResponse(this.responsePacketQueue);
|
|
||||||
this.responsePacketQueue = [];
|
|
||||||
this.ignoreNextResponsePacket = true;
|
|
||||||
} else {
|
|
||||||
Logger.verbose(
|
|
||||||
'RCON',
|
|
||||||
1,
|
|
||||||
`Unknown packet id ${decodedPacket.id} in: ${this.decodedPacketToString(decodedPacket)}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
processCompleteResponse(decodedPackets) {
|
|
||||||
Logger.verbose(
|
|
||||||
'RCON',
|
|
||||||
3,
|
|
||||||
`Processing complete decoded packet response: ${decodedPackets
|
|
||||||
.map(this.decodedPacketToString)
|
|
||||||
.join(', ')}`
|
|
||||||
);
|
|
||||||
|
|
||||||
const response = decodedPackets.map((packet) => packet.body).join();
|
|
||||||
|
|
||||||
this.responseActionQueue.shift()(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
processAuthPacket(decodedPacket) {
|
|
||||||
this.responseActionQueue.shift()(decodedPacket);
|
|
||||||
}
|
|
||||||
|
|
||||||
processChatPacket(decodedPacket) {
|
processChatPacket(decodedPacket) {
|
||||||
const match = decodedPacket.body.match(
|
const match = decodedPacket.body.match(
|
||||||
/\[(ChatAll|ChatTeam|ChatSquad|ChatAdmin)] \[SteamID:([0-9]{17})] (.+?) : (.*)/
|
/\[(ChatAll|ChatTeam|ChatSquad|ChatAdmin)] \[SteamID:([0-9]{17})] (.+?) : (.*)/
|
||||||
@ -301,9 +282,10 @@ export default class Rcon extends EventEmitter {
|
|||||||
|
|
||||||
const encodedPacket = this.encodePacket(
|
const encodedPacket = this.encodePacket(
|
||||||
type,
|
type,
|
||||||
type === SERVERDATA_AUTH ? END_PACKET_ID : MID_PACKET_ID,
|
type !== SERVERDATA_AUTH ? MID_PACKET_ID : END_PACKET_ID,
|
||||||
body
|
body
|
||||||
);
|
);
|
||||||
|
|
||||||
const encodedEmptyPacket = this.encodePacket(type, END_PACKET_ID, '');
|
const encodedEmptyPacket = this.encodePacket(type, END_PACKET_ID, '');
|
||||||
|
|
||||||
if (this.maximumPacketSize < encodedPacket.length) {
|
if (this.maximumPacketSize < encodedPacket.length) {
|
||||||
@ -311,9 +293,16 @@ export default class Rcon extends EventEmitter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let onResponse;
|
const onError = (err) => {
|
||||||
|
Logger.verbose('RCON', 1, 'Error occurred. Wiping response action queue.', err);
|
||||||
|
this.responseCallbackQueue = [];
|
||||||
|
reject(err);
|
||||||
|
};
|
||||||
|
|
||||||
|
// the auth packet also sends a normal response, so we add an extra empty action to ignore it
|
||||||
if (type === SERVERDATA_AUTH) {
|
if (type === SERVERDATA_AUTH) {
|
||||||
onResponse = (decodedPacket) => {
|
this.responseCallbackQueue.push(() => {});
|
||||||
|
this.responseCallbackQueue.push((decodedPacket) => {
|
||||||
this.client.removeListener('error', onError);
|
this.client.removeListener('error', onError);
|
||||||
if (decodedPacket.id === -1) {
|
if (decodedPacket.id === -1) {
|
||||||
Logger.verbose('RCON', 1, 'Authentication failed.');
|
Logger.verbose('RCON', 1, 'Authentication failed.');
|
||||||
@ -322,32 +311,21 @@ export default class Rcon extends EventEmitter {
|
|||||||
Logger.verbose('RCON', 1, 'Authentication succeeded.');
|
Logger.verbose('RCON', 1, 'Authentication succeeded.');
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
} else {
|
} else {
|
||||||
onResponse = (response) => {
|
this.responseCallbackQueue.push((response) => {
|
||||||
this.client.removeListener('error', onError);
|
this.client.removeListener('error', onError);
|
||||||
|
|
||||||
Logger.verbose(
|
Logger.verbose(
|
||||||
'RCON',
|
'RCON',
|
||||||
2,
|
2,
|
||||||
`Processing complete response: ${response.replace(/\r\n|\r|\n/g, '\\n')}`
|
`Returning complete response: ${response.replace(/\r\n|\r|\n/g, '\\n')}`
|
||||||
);
|
);
|
||||||
|
|
||||||
resolve(response);
|
resolve(response);
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const onError = (err) => {
|
|
||||||
Logger.verbose('RCON', 1, 'Error occurred. Wiping response action queue.', err);
|
|
||||||
this.responseActionQueue = [];
|
|
||||||
reject(err);
|
|
||||||
};
|
|
||||||
|
|
||||||
// the auth packet also sends a normal response, so we add an extra empty action to ignore it
|
|
||||||
if (type === SERVERDATA_AUTH) this.responseActionQueue.push(() => {});
|
|
||||||
|
|
||||||
this.responseActionQueue.push(onResponse);
|
|
||||||
|
|
||||||
this.client.once('error', onError);
|
this.client.once('error', onError);
|
||||||
|
|
||||||
Logger.verbose('RCON', 4, `Sending packet: ${this.bufToHexString(encodedPacket)}`);
|
Logger.verbose('RCON', 4, `Sending packet: ${this.bufToHexString(encodedPacket)}`);
|
||||||
|
@ -59,6 +59,26 @@ export default class SquadServer extends EventEmitter {
|
|||||||
this.pingSquadJSAPITimeout = null;
|
this.pingSquadJSAPITimeout = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async watch() {
|
||||||
|
await this.squadLayers.pull();
|
||||||
|
|
||||||
|
await this.rcon.connect();
|
||||||
|
await this.logParser.watch();
|
||||||
|
|
||||||
|
await this.updatePlayerList();
|
||||||
|
await this.updateLayerInformation();
|
||||||
|
await this.updateA2SInformation();
|
||||||
|
|
||||||
|
Logger.verbose('SquadServer', 1, `Watching ${this.serverName}...`);
|
||||||
|
|
||||||
|
await this.pingSquadJSAPI();
|
||||||
|
}
|
||||||
|
|
||||||
|
async unwatch() {
|
||||||
|
await this.rcon.disconnect();
|
||||||
|
await this.logParser.unwatch();
|
||||||
|
}
|
||||||
|
|
||||||
setupRCON() {
|
setupRCON() {
|
||||||
this.rcon = new Rcon({
|
this.rcon = new Rcon({
|
||||||
host: this.options.host,
|
host: this.options.host,
|
||||||
@ -234,13 +254,13 @@ export default class SquadServer extends EventEmitter {
|
|||||||
|
|
||||||
async setupAdminList(remoteAdminLists) {
|
async setupAdminList(remoteAdminLists) {
|
||||||
try {
|
try {
|
||||||
let remoteAdmins = {
|
const remoteAdmins = {
|
||||||
admins:{},
|
admins: {},
|
||||||
whitelist:{},
|
whitelist: {},
|
||||||
groups:{}
|
groups: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (let idx=0; idx < remoteAdminLists.length; idx++) {
|
for (let idx = 0; idx < remoteAdminLists.length; idx++) {
|
||||||
const list = remoteAdminLists[idx];
|
const list = remoteAdminLists[idx];
|
||||||
|
|
||||||
const resp = await axios({
|
const resp = await axios({
|
||||||
@ -261,7 +281,7 @@ export default class SquadServer extends EventEmitter {
|
|||||||
const perms = remoteAdmins.groups[`${idx}-${groupID}`];
|
const perms = remoteAdmins.groups[`${idx}-${groupID}`];
|
||||||
if (!(perms.includes('reserve') && perms.length === 1)) {
|
if (!(perms.includes('reserve') && perms.length === 1)) {
|
||||||
remoteAdmins.admins[steamID] = `${idx}-${groupID}`;
|
remoteAdmins.admins[steamID] = `${idx}-${groupID}`;
|
||||||
}else{
|
} else {
|
||||||
remoteAdmins.whitelist[steamID] = `${idx}-${groupID}`;
|
remoteAdmins.whitelist[steamID] = `${idx}-${groupID}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -386,26 +406,54 @@ export default class SquadServer extends EventEmitter {
|
|||||||
return this.getPlayerByCondition((player) => player.suffix === suffix, false);
|
return this.getPlayerByCondition((player) => player.suffix === suffix, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
async watch() {
|
async pingSquadJSAPI() {
|
||||||
await this.squadLayers.pull();
|
if (this.pingSquadJSAPITimeout) clearTimeout(this.pingSquadJSAPITimeout);
|
||||||
|
|
||||||
await this.rcon.connect();
|
Logger.verbose('SquadServer', 1, 'Pinging SquadJS API...');
|
||||||
await this.logParser.watch();
|
|
||||||
|
|
||||||
await this.updatePlayerList();
|
const config = {
|
||||||
await this.updateLayerInformation();
|
// send minimal information on server
|
||||||
await this.updateA2SInformation();
|
server: {
|
||||||
|
host: this.options.host,
|
||||||
|
queryPort: this.options.queryPort,
|
||||||
|
logReaderMode: this.options.logReaderMode
|
||||||
|
},
|
||||||
|
|
||||||
Logger.verbose('SquadServer', 1, `Watching ${this.serverName}...`);
|
// we send all plugin information as none of that is sensitive.
|
||||||
|
plugins: this.plugins.map((plugin) => ({
|
||||||
|
...plugin.optionsRaw,
|
||||||
|
plugin: plugin.constructor.name
|
||||||
|
})),
|
||||||
|
|
||||||
await this.pingSquadJSAPI();
|
// send additional information about SquadJS
|
||||||
}
|
version: SQUADJS_VERSION
|
||||||
|
};
|
||||||
async unwatch() {
|
|
||||||
await this.rcon.disconnect();
|
try {
|
||||||
await this.logParser.unwatch();
|
const { data } = await axios.post(SQUADJS_API_DOMAIN + '/api/v1/ping', { config });
|
||||||
|
|
||||||
|
if (data.error)
|
||||||
|
Logger.verbose(
|
||||||
|
'SquadServer',
|
||||||
|
1,
|
||||||
|
`Successfully pinged the SquadJS API. Got back error: ${data.error}`
|
||||||
|
);
|
||||||
|
else
|
||||||
|
Logger.verbose(
|
||||||
|
'SquadServer',
|
||||||
|
1,
|
||||||
|
`Successfully pinged the SquadJS API. Got back message: ${data.message}`
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
Logger.verbose('SquadServer', 1, 'Failed to ping the SquadJS API: ', err);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pingSquadJSAPITimeout = setTimeout(this.pingSquadJSAPI, this.pingSquadJSAPIInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ///////////////////////////////////////////////////////////////////////
|
||||||
|
// Should consider moving the following to a factory class of some kind. //
|
||||||
|
// ////////////////////////////////////////////////////////////////////////
|
||||||
static async buildFromConfig(config) {
|
static async buildFromConfig(config) {
|
||||||
// Setup logging levels
|
// Setup logging levels
|
||||||
for (const [module, verboseness] of Object.entries(config.verboseness)) {
|
for (const [module, verboseness] of Object.entries(config.verboseness)) {
|
||||||
@ -458,7 +506,7 @@ export default class SquadServer extends EventEmitter {
|
|||||||
connectorConfig.filter,
|
connectorConfig.filter,
|
||||||
connectorConfig.activeLayerFilter
|
connectorConfig.activeLayerFilter
|
||||||
);
|
);
|
||||||
} else if(option.connector === 'remoteAdminLists'){
|
} else if (option.connector === 'remoteAdminLists') {
|
||||||
Logger.verbose('SquadServer', 1, `Starting remoteAdminList connector...`);
|
Logger.verbose('SquadServer', 1, `Starting remoteAdminList connector...`);
|
||||||
connectors[connectorName] = await server.setupAdminList(connectorConfig);
|
connectors[connectorName] = await server.setupAdminList(connectorConfig);
|
||||||
} else {
|
} else {
|
||||||
@ -502,68 +550,111 @@ export default class SquadServer extends EventEmitter {
|
|||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
static buildFromConfigString(configString) {
|
static parseConfig(configString) {
|
||||||
let config;
|
|
||||||
try {
|
try {
|
||||||
config = JSON.parse(configString);
|
return JSON.parse(configString);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new Error('Unable to parse config file.');
|
throw new Error('Unable to parse config file.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return SquadServer.buildFromConfig(config);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static buildFromConfigFile(configPath = './config.json') {
|
static buildFromConfigString(configString) {
|
||||||
Logger.verbose('SquadServer', 1, 'Reading config file...');
|
Logger.verbose('SquadServer', 1, 'Parsing config string...');
|
||||||
|
return SquadServer.buildFromConfig(SquadServer.parseConfig(configString));
|
||||||
|
}
|
||||||
|
|
||||||
|
static readConfigFile(configPath = './config.json') {
|
||||||
configPath = path.resolve(__dirname, '../', configPath);
|
configPath = path.resolve(__dirname, '../', configPath);
|
||||||
if (!fs.existsSync(configPath)) throw new Error('Config file does not exist.');
|
if (!fs.existsSync(configPath)) throw new Error('Config file does not exist.');
|
||||||
const configString = fs.readFileSync(configPath, 'utf8');
|
return fs.readFileSync(configPath, 'utf8');
|
||||||
|
|
||||||
return SquadServer.buildFromConfigString(configString);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async pingSquadJSAPI() {
|
static buildFromConfigFile(configPath) {
|
||||||
if (this.pingSquadJSAPITimeout) clearTimeout(this.pingSquadJSAPITimeout);
|
Logger.verbose('SquadServer', 1, 'Reading config file...');
|
||||||
|
return SquadServer.buildFromConfigString(SquadServer.readConfigFile(configPath));
|
||||||
|
}
|
||||||
|
|
||||||
Logger.verbose('SquadServer', 1, 'Pinging SquadJS API...');
|
static buildConfig() {
|
||||||
|
const templatePath = path.resolve(__dirname, './templates/config-template.json');
|
||||||
|
const templateString = fs.readFileSync(templatePath, 'utf8');
|
||||||
|
const template = SquadServer.parseConfig(templateString);
|
||||||
|
|
||||||
const config = {
|
const pluginKeys = Object.keys(plugins).sort((a, b) =>
|
||||||
// send minimal information on server
|
a.name < b.name ? -1 : a.name > b.name ? 1 : 0
|
||||||
server: {
|
);
|
||||||
host: this.options.host,
|
|
||||||
queryPort: this.options.queryPort,
|
|
||||||
logReaderMode: this.options.logReaderMode
|
|
||||||
},
|
|
||||||
|
|
||||||
// we send all plugin information as none of that is sensitive.
|
for (const pluginKey of pluginKeys) {
|
||||||
plugins: this.plugins.map((plugin) => ({
|
const Plugin = plugins[pluginKey];
|
||||||
...plugin.optionsRaw,
|
|
||||||
plugin: plugin.constructor.name
|
|
||||||
})),
|
|
||||||
|
|
||||||
// send additional information about SquadJS
|
const pluginConfig = { plugin: Plugin.name, enabled: Plugin.defaultEnabled };
|
||||||
version: SQUADJS_VERSION
|
for (const [optionName, option] of Object.entries(Plugin.optionsSpecification)) {
|
||||||
};
|
pluginConfig[optionName] = option.default;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
template.plugins.push(pluginConfig);
|
||||||
const { data } = await axios.post(SQUADJS_API_DOMAIN + '/api/v1/ping', { config });
|
|
||||||
|
|
||||||
if (data.error)
|
|
||||||
Logger.verbose(
|
|
||||||
'SquadServer',
|
|
||||||
1,
|
|
||||||
`Successfully pinged the SquadJS API. Got back error: ${data.error}`
|
|
||||||
);
|
|
||||||
else
|
|
||||||
Logger.verbose(
|
|
||||||
'SquadServer',
|
|
||||||
1,
|
|
||||||
`Successfully pinged the SquadJS API. Got back message: ${data.message}`
|
|
||||||
);
|
|
||||||
} catch (err) {
|
|
||||||
Logger.verbose('SquadServer', 1, 'Failed to ping the SquadJS API: ', err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pingSquadJSAPITimeout = setTimeout(this.pingSquadJSAPI, this.pingSquadJSAPIInterval);
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
static buildConfigFile() {
|
||||||
|
const configPath = path.resolve(__dirname, '../config.json');
|
||||||
|
const config = JSON.stringify(SquadServer.buildConfig(), null, 2);
|
||||||
|
fs.writeFileSync(configPath, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
static buildReadmeFile() {
|
||||||
|
const pluginKeys = Object.keys(plugins).sort((a, b) =>
|
||||||
|
a.name < b.name ? -1 : a.name > b.name ? 1 : 0
|
||||||
|
);
|
||||||
|
|
||||||
|
const pluginInfo = [];
|
||||||
|
|
||||||
|
for (const pluginName of pluginKeys) {
|
||||||
|
const Plugin = plugins[pluginName];
|
||||||
|
|
||||||
|
const options = [];
|
||||||
|
for (const [optionName, option] of Object.entries(Plugin.optionsSpecification)) {
|
||||||
|
let optionInfo = `<h4>${optionName}${option.required ? ' (Required)' : ''}</h4>
|
||||||
|
<h6>Description</h6>
|
||||||
|
<p>${option.description}</p>
|
||||||
|
<h6>Default</h6>
|
||||||
|
<pre><code>${
|
||||||
|
typeof option.default === 'object'
|
||||||
|
? JSON.stringify(option.default, null, 2)
|
||||||
|
: option.default
|
||||||
|
}</code></pre>`;
|
||||||
|
|
||||||
|
if (option.example)
|
||||||
|
optionInfo += `<h6>Example</h6>
|
||||||
|
<pre><code>${
|
||||||
|
typeof option.example === 'object'
|
||||||
|
? JSON.stringify(option.example, null, 2)
|
||||||
|
: option.example
|
||||||
|
}</code></pre>`;
|
||||||
|
|
||||||
|
options.push(optionInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginInfo.push(
|
||||||
|
`<details>
|
||||||
|
<summary>${Plugin.name}</summary>
|
||||||
|
<h2>${Plugin.name}</h2>
|
||||||
|
<p>${Plugin.description}</p>
|
||||||
|
<h3>Options</h3>
|
||||||
|
${options.join('\n')}
|
||||||
|
</details>`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pluginInfoText = pluginInfo.join('\n\n');
|
||||||
|
|
||||||
|
const templatePath = path.resolve(__dirname, './templates/readme-template.md');
|
||||||
|
const template = fs.readFileSync(templatePath, 'utf8');
|
||||||
|
|
||||||
|
const readmePath = path.resolve(__dirname, '../README.md');
|
||||||
|
const readme = template.replace(/\/\/PLUGIN-INFO\/\//, pluginInfoText);
|
||||||
|
|
||||||
|
fs.writeFileSync(readmePath, readme);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
5
squad-server/scripts/build-config-file.js
Normal file
5
squad-server/scripts/build-config-file.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import SquadServer from '../index.js';
|
||||||
|
|
||||||
|
console.log('Building config...');
|
||||||
|
SquadServer.buildConfigFile();
|
||||||
|
console.log('Done.');
|
@ -1,28 +0,0 @@
|
|||||||
import fs from 'fs';
|
|
||||||
import { fileURLToPath } from 'url';
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
import plugins from '../plugins/index.js';
|
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
||||||
|
|
||||||
const template = JSON.parse(
|
|
||||||
fs.readFileSync(path.resolve(__dirname, './templates/config-template.json'), 'utf8')
|
|
||||||
);
|
|
||||||
|
|
||||||
const pluginKeys = Object.keys(plugins).sort((a, b) =>
|
|
||||||
a.name < b.name ? -1 : a.name > b.name ? 1 : 0
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const pluginKey of pluginKeys) {
|
|
||||||
const Plugin = plugins[pluginKey];
|
|
||||||
|
|
||||||
const pluginConfig = { plugin: Plugin.name, enabled: Plugin.defaultEnabled };
|
|
||||||
for (const [optionName, option] of Object.entries(Plugin.optionsSpecification)) {
|
|
||||||
pluginConfig[optionName] = option.default;
|
|
||||||
}
|
|
||||||
|
|
||||||
template.plugins.push(pluginConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.writeFileSync(path.resolve(__dirname, '../../config.json'), JSON.stringify(template, null, 2));
|
|
@ -1,59 +1,5 @@
|
|||||||
import fs from 'fs';
|
import SquadServer from '../index.js';
|
||||||
import { fileURLToPath } from 'url';
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
import plugins from '../plugins/index.js';
|
console.log('Building readme...');
|
||||||
|
SquadServer.buildReadmeFile();
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
console.log('Done.');
|
||||||
|
|
||||||
const pluginNames = Object.keys(plugins);
|
|
||||||
const sortedPluginNames = pluginNames.sort((a, b) =>
|
|
||||||
a.name < b.name ? -1 : a.name > b.name ? 1 : 0
|
|
||||||
);
|
|
||||||
|
|
||||||
const pluginInfo = [];
|
|
||||||
for (const pluginName of sortedPluginNames) {
|
|
||||||
const Plugin = plugins[pluginName];
|
|
||||||
|
|
||||||
const options = [];
|
|
||||||
for (const [optionName, option] of Object.entries(Plugin.optionsSpecification)) {
|
|
||||||
let optionInfo = `<h4>${optionName}${option.required ? ' (Required)' : ''}</h4>
|
|
||||||
<h6>Description</h6>
|
|
||||||
<p>${option.description}</p>
|
|
||||||
<h6>Default</h6>
|
|
||||||
<pre><code>${
|
|
||||||
typeof option.default === 'object'
|
|
||||||
? JSON.stringify(option.default, null, 2)
|
|
||||||
: option.default
|
|
||||||
}</code></pre>`;
|
|
||||||
|
|
||||||
if (option.example)
|
|
||||||
optionInfo += `<h6>Example</h6>
|
|
||||||
<pre><code>${
|
|
||||||
typeof option.example === 'object'
|
|
||||||
? JSON.stringify(option.example, null, 2)
|
|
||||||
: option.example
|
|
||||||
}</code></pre>`;
|
|
||||||
|
|
||||||
options.push(optionInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
pluginInfo.push(
|
|
||||||
`<details>
|
|
||||||
<summary>${Plugin.name}</summary>
|
|
||||||
<h2>${Plugin.name}</h2>
|
|
||||||
<p>${Plugin.description}</p>
|
|
||||||
<h3>Options</h3>
|
|
||||||
${options.join('\n')}
|
|
||||||
</details>`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const pluginInfoText = pluginInfo.join('\n\n');
|
|
||||||
|
|
||||||
fs.writeFileSync(
|
|
||||||
path.resolve(__dirname, '../../README.md'),
|
|
||||||
fs
|
|
||||||
.readFileSync(path.resolve(__dirname, './templates/readme-template.md'), 'utf8')
|
|
||||||
.replace(/\/\/PLUGIN-INFO\/\//, pluginInfoText)
|
|
||||||
);
|
|
||||||
|
Loading…
Reference in New Issue
Block a user