This commit is contained in:
Skillet 2023-04-17 19:19:28 -04:00
parent 2ac2fd4b1c
commit b4355321d1

View File

@ -211,7 +211,9 @@ export default class MapVote extends DiscordBasePlugin {
constructor(server, options, connectors) { constructor(server, options, connectors) {
super(server, options, connectors); super(server, options, connectors);
this.options.timeFrames.forEach((e, key, arr) => { arr[ key ].id = key + 1 }); this.options.timeFrames.forEach((e, key, arr) => {
arr[key].id = key + 1
});
if (this.options.allowedSameMapEntries < 1) this.options.allowedSameMapEntries = 1 if (this.options.allowedSameMapEntries < 1) this.options.allowedSameMapEntries = 1
@ -249,8 +251,12 @@ export default class MapVote extends DiscordBasePlugin {
this.updateNextMap = this.updateNextMap.bind(this); this.updateNextMap = this.updateNextMap.bind(this);
this.mapLayer = this.mapLayer.bind(this); this.mapLayer = this.mapLayer.bind(this);
this.broadcast = async (msg) => { await this.server.rcon.broadcast(msg); }; this.broadcast = async (msg) => {
this.warn = async (steamid, msg) => { await this.server.rcon.warn(steamid, msg); }; await this.server.rcon.broadcast(msg);
};
this.warn = async (steamid, msg) => {
await this.server.rcon.warn(steamid, msg);
};
process.on('uncaughtException', this.savePersistentData); process.on('uncaughtException', this.savePersistentData);
} }
@ -284,7 +290,7 @@ export default class MapVote extends DiscordBasePlugin {
} }
async onNewGame() { async onNewGame() {
for (let x of this.timeout_ps) for (const x of this.timeout_ps)
clearTimeout(x) clearTimeout(x)
this.timeout_ps = []; this.timeout_ps = [];
@ -305,6 +311,7 @@ export default class MapVote extends DiscordBasePlugin {
this.clearVote(); this.clearVote();
if (new Date() - this.lastMapUpdate > 5 * 1000) this.updateNextMap(); if (new Date() - this.lastMapUpdate > 5 * 1000) this.updateNextMap();
} }
async timeframeOptionOverrider() { async timeframeOptionOverrider() {
const orOpt = {...this.or_options}; const orOpt = {...this.or_options};
const utcDelay = parseFloat(this.options.timezone); const utcDelay = parseFloat(this.options.timezone);
@ -315,13 +322,13 @@ export default class MapVote extends DiscordBasePlugin {
this.verbose(1, `Current time (UTC${(utcDelay >= 0 ? '+' : '') + utcDelay}) ${timeNow.toLocaleTimeString('en-GB').split(':').splice(0, 2).join(':')} `) this.verbose(1, `Current time (UTC${(utcDelay >= 0 ? '+' : '') + utcDelay}) ${timeNow.toLocaleTimeString('en-GB').split(':').splice(0, 2).join(':')} `)
const activeTimeframes = orOpt.timeFrames.filter(tfFilter); const activeTimeframes = orOpt.timeFrames.filter(tfFilter);
let logTimeframe = "Active Time Frames: "; const logTimeframe = "Active Time Frames: ";
let activeTfIds = []; const activeTfIds = [];
this.options = {...this.or_options}; this.options = {...this.or_options};
for (let atfK in activeTimeframes) { for (const atfK in activeTimeframes) {
const atf = activeTimeframes[atfK]; const atf = activeTimeframes[atfK];
activeTfIds.push(atf.name || atf.id); activeTfIds.push(atf.name || atf.id);
for (let o in atf.overrides) { for (const o in atf.overrides) {
this.options[o] = atf.overrides[o]; this.options[o] = atf.overrides[o];
} }
} }
@ -341,6 +348,7 @@ export default class MapVote extends DiscordBasePlugin {
return (tfStart <= timeNow && timeNow < tfEnd) || (tfStart > tfEnd && ((tfStart <= timeNow && timeNow < tfEnd2) || (tfStart2 <= timeNow && timeNow < tfEnd))) return (tfStart <= timeNow && timeNow < tfEnd) || (tfStart > tfEnd && ((tfStart <= timeNow && timeNow < tfEnd2) || (tfStart2 <= timeNow && timeNow < tfEnd)))
} }
} }
setSeedingMode(isNewGameEvent = false) { setSeedingMode(isNewGameEvent = false) {
this.options.seedingGameMode = this.options.seedingGameMode.toLowerCase(); this.options.seedingGameMode = this.options.seedingGameMode.toLowerCase();
// this.msgBroadcast("[MapVote] Seeding mode active") // this.msgBroadcast("[MapVote] Seeding mode active")
@ -351,13 +359,13 @@ export default class MapVote extends DiscordBasePlugin {
const maxSeedingModePlayerCount = Math.max(this.options.nextLayerSeedingModePlayerCount, this.options.instantSeedingModePlayerCount); const maxSeedingModePlayerCount = Math.max(this.options.nextLayerSeedingModePlayerCount, this.options.instantSeedingModePlayerCount);
if (this.server.players.length >= 1 && this.server.players.length < maxSeedingModePlayerCount) { if (this.server.players.length >= 1 && this.server.players.length < maxSeedingModePlayerCount) {
if (+(new Date()) - +this.server.layerHistory[0].time > 30 * 1000) { if (+(new Date()) - +this.server.layerHistory[0].time > 30 * 1000) {
const seedingMaps = Layers.layers.filter((l) => l.layerid && l.gamemode.toLowerCase() == this.options.seedingGameMode && !this.options.layerLevelBlacklist.find((fl) => l.layerid.toLowerCase().startsWith(fl.toLowerCase()))) const seedingMaps = Layers.layers.filter((l) => l.layerid && l.gamemode.toLowerCase() === this.options.seedingGameMode && !this.options.layerLevelBlacklist.find((fl) => l.layerid.toLowerCase().startsWith(fl.toLowerCase())))
this.verbose(1, seedingMaps); this.verbose(1, seedingMaps);
const rndMap = randomElement(seedingMaps); const rndMap = randomElement(seedingMaps);
if (this.server.currentLayer) { if (this.server.currentLayer) {
this.verbose(1, "checking current layer gamemode"); this.verbose(1, "checking current layer gamemode");
if (this.server.currentLayer.gamemode.toLowerCase() != this.options.seedingGameMode) { if (this.server.currentLayer.gamemode.toLowerCase() !== this.options.seedingGameMode) {
this.verbose(1, "checking player count"); this.verbose(1, "checking player count");
if (this.server.players.length <= this.options.instantSeedingModePlayerCount) { if (this.server.players.length <= this.options.instantSeedingModePlayerCount) {
const newCurrentMap = rndMap.layerid; const newCurrentMap = rndMap.layerid;
@ -368,10 +376,10 @@ export default class MapVote extends DiscordBasePlugin {
} else this.verbose(1, "Bad data (currentLayer). Seeding mode for current layer skipped to prevent errors."); } else this.verbose(1, "Bad data (currentLayer). Seeding mode for current layer skipped to prevent errors.");
if (this.server.nextLayer) { if (this.server.nextLayer) {
const nextMaps = seedingMaps.filter((l) => (!this.server.currentLayer || l.layerid != this.server.currentLayer.layerid)) const nextMaps = seedingMaps.filter((l) => (!this.server.currentLayer || l.layerid !== this.server.currentLayer.layerid))
const rndMap2 = randomElement(nextMaps); const rndMap2 = randomElement(nextMaps);
if (this.server.players.length < this.options.nextLayerSeedingModePlayerCount && this.server.nextLayer.gamemode.toLowerCase() != this.options.seedingGameMode) { if (this.server.players.length < this.options.nextLayerSeedingModePlayerCount && this.server.nextLayer.gamemode.toLowerCase() !== this.options.seedingGameMode) {
const newNextMap = rndMap2.layerid; const newNextMap = rndMap2.layerid;
this.verbose(1, "setting next layer to seed mode"); this.verbose(1, "setting next layer to seed mode");
this.server.rcon.execute(`AdminSetNextLayer ${newNextMap} `); this.server.rcon.execute(`AdminSetNextLayer ${newNextMap} `);
@ -391,8 +399,8 @@ export default class MapVote extends DiscordBasePlugin {
return; return;
const commandSplit = (isNaN(message) ? message.substring(this.options.commandPrefix.length).trim().split(' ') : [message]); const commandSplit = (isNaN(message) ? message.substring(this.options.commandPrefix.length).trim().split(' ') : [message]);
let cmdLayers = commandSplit.slice(1); const cmdLayers = commandSplit.slice(1);
for (let k in cmdLayers) cmdLayers[ k ] = cmdLayers[ k ].toLowerCase(); for (const k in cmdLayers) cmdLayers[k] = cmdLayers[k].toLowerCase();
const subCommand = commandSplit[0]; const subCommand = commandSplit[0];
if (!isNaN(subCommand)) // if this succeeds player is voting for a map if (!isNaN(subCommand)) // if this succeeds player is voting for a map
{ {
@ -492,11 +500,10 @@ export default class MapVote extends DiscordBasePlugin {
await this.warn(steamID, "Saving persistent data.\nTerminating SquadJS process.\nIf managed by a process manager it will automatically restart.") await this.warn(steamID, "Saving persistent data.\nTerminating SquadJS process.\nIf managed by a process manager it will automatically restart.")
this.savePersistentData(steamID); this.savePersistentData(steamID);
process.exit(0); process.exit(0);
return;
default: default:
// give them an error // give them an error
await this.warn(steamID, `Unknown vote subcommand: ${subCommand}`); await this.warn(steamID, `Unknown vote subcommand: ${subCommand}`);
return;
} }
} }
@ -507,16 +514,15 @@ export default class MapVote extends DiscordBasePlugin {
this.lastMapUpdate = new Date(); this.lastMapUpdate = new Date();
let cpyWinners = this.currentWinners; let cpyWinners = this.currentWinners;
let skipSetNextMap = false; let skipSetNextMap = false;
if (cpyWinners.find(e => e == this.nominations[ 0 ])) { if (cpyWinners.find(e => e === this.nominations[0])) {
if (cpyWinners.length > 1) { if (cpyWinners.length > 1) {
delete cpyWinners[cpyWinners.indexOf(this.nominations[0])] delete cpyWinners[cpyWinners.indexOf(this.nominations[0])]
cpyWinners = cpyWinners.filter(e => e != null) cpyWinners = cpyWinners.filter(e => e != null)
} } else {
else {
skipSetNextMap = true; skipSetNextMap = true;
if (this.newVoteTimeout == null) { if (this.newVoteTimeout == null) {
this.newVoteTimeout = setTimeout(() => { this.newVoteTimeout = setTimeout(() => {
if (this.currentWinners.find(e => e == this.nominations[ 0 ]) && this.currentWinners.length == 1) { if (this.currentWinners.find(e => e === this.nominations[0]) && this.currentWinners.length === 1) {
this.newVoteTimeout = null; this.newVoteTimeout = null;
this.endVoting() this.endVoting()
this.broadcast("The previous Map Vote has been canceled and a new one has been generated!") this.broadcast("The previous Map Vote has been canceled and a new one has been generated!")
@ -531,7 +537,7 @@ export default class MapVote extends DiscordBasePlugin {
if (!skipSetNextMap) { if (!skipSetNextMap) {
const baseDataExist = this && this.server; const baseDataExist = this && this.server;
const layerDataExist = this.server.nextLayer && this.server.nextLayer.layerid; const layerDataExist = this.server.nextLayer && this.server.nextLayer.layerid;
if (baseDataExist && (!layerDataExist || this.server.nextLayer.layerid != nextMap)) if (baseDataExist && (!layerDataExist || this.server.nextLayer.layerid !== nextMap))
this.server.rcon.execute(`AdminSetNextLayer ${nextMap}`); this.server.rcon.execute(`AdminSetNextLayer ${nextMap}`);
else console.log("[MapVote][1] Bad data (this/this.server). Next layer not set to prevent errors."); else console.log("[MapVote][1] Bad data (this/this.server). Next layer not set to prevent errors.");
} }
@ -588,7 +594,7 @@ export default class MapVote extends DiscordBasePlugin {
this.nominations = []; this.nominations = [];
this.tallies = []; this.tallies = [];
this.factionStrings = []; this.factionStrings = [];
let rnd_layers = []; const rnd_layers = [];
const sanitizedLayers = Layers.layers.filter((l) => l.layerid && l.map); const sanitizedLayers = Layers.layers.filter((l) => l.layerid && l.map);
const maxOptions = this.options.showRerollOption ? 20 : 21; const maxOptions = this.options.showRerollOption ? 20 : 21;
@ -597,9 +603,9 @@ export default class MapVote extends DiscordBasePlugin {
const recentlyPlayedMaps = this.objArrToValArr(this.server.layerHistory.slice(0, this.options.numberRecentMapsToExlude), "layer", "map", "name"); const recentlyPlayedMaps = this.objArrToValArr(this.server.layerHistory.slice(0, this.options.numberRecentMapsToExlude), "layer", "map", "name");
this.verbose(1, "Recently played maps: " + recentlyPlayedMaps.join(', '));// recentlyPlayedMaps.filter((l) => l && l.map && l.map.name).map((l) => l.map.name).join(', ')) this.verbose(1, "Recently played maps: " + recentlyPlayedMaps.join(', '));// recentlyPlayedMaps.filter((l) => l && l.map && l.map.name).map((l) => l.map.name).join(', '))
const isRandomVote = !cmdLayers || cmdLayers.length == 0; const isRandomVote = !cmdLayers || cmdLayers.length === 0;
if (isRandomVote) { if (isRandomVote) {
for (let gm of Object.keys(this.options.minGamemodeEntries)) { for (const gm of Object.keys(this.options.minGamemodeEntries)) {
for (let i = 0; i < +this.options.minGamemodeEntries[gm] && cmdLayers.length < optionAmount; i++) for (let i = 0; i < +this.options.minGamemodeEntries[gm] && cmdLayers.length < optionAmount; i++)
cmdLayers.push(`*_${gm}`); cmdLayers.push(`*_${gm}`);
} }
@ -616,9 +622,9 @@ export default class MapVote extends DiscordBasePlugin {
this.options.gamemodeWhitelist.includes(l.gamemode.toUpperCase()) && this.options.gamemodeWhitelist.includes(l.gamemode.toUpperCase()) &&
![this.server.currentLayer ? this.server.currentLayer.map.name : null, ...recentlyPlayedMaps].includes(l.map.name) && ![this.server.currentLayer ? this.server.currentLayer.map.name : null, ...recentlyPlayedMaps].includes(l.map.name) &&
( (
(this.options.layerFilteringMode.toLowerCase() == "blacklist" && !this.options.layerLevelBlacklist.find((fl) => this.getLayersFromStringId(fl).map((e) => e.layerid).includes(l.layerid))) || (this.options.layerFilteringMode.toLowerCase() === "blacklist" && !this.options.layerLevelBlacklist.find((fl) => this.getLayersFromStringId(fl).map((e) => e.layerid).includes(l.layerid))) ||
( (
this.options.layerFilteringMode.toLowerCase() == "whitelist" this.options.layerFilteringMode.toLowerCase() === "whitelist"
&& this.options.layerLevelWhitelist.find((fl) => this.getLayersFromStringId(fl).map((e) => e.layerid).includes(l.layerid)) && this.options.layerLevelWhitelist.find((fl) => this.getLayersFromStringId(fl).map((e) => e.layerid).includes(l.layerid))
&& !(this.options.applyBlacklistToWhitelist && this.options.layerLevelBlacklist.find((fl) => this.getLayersFromStringId(fl).map((e) => e.layerid).includes(l.layerid))) && !(this.options.applyBlacklistToWhitelist && this.options.layerLevelBlacklist.find((fl) => this.getLayersFromStringId(fl).map((e) => e.layerid).includes(l.layerid)))
) )
@ -627,8 +633,9 @@ export default class MapVote extends DiscordBasePlugin {
); );
for (let i = 1; i <= Math.min(optionAmount, all_layers.length); i++) { for (let i = 1; i <= Math.min(optionAmount, all_layers.length); i++) {
const needMoreRAAS = !bypassRaasFilter && rnd_layers.filter((l) => l.gamemode.toUpperCase() === 'RAAS').length < this.options.minRaasEntries; const needMoreRAAS = !bypassRaasFilter && rnd_layers.filter((l) => l.gamemode.toUpperCase() === 'RAAS').length < this.options.minRaasEntries;
let l, maxtries = 20; let l;
do l = randomElement(needMoreRAAS ? all_layers.filter((l) => l.gamemode.toLowerCase() == "raas") : all_layers); while ((rnd_layers.find(lf => lf.layerid == l.layerid) || rnd_layers.filter(lf => lf.map.name == l.map.name).length > (this.options.allowedSameMapEntries - 1)) && --maxtries >= 0) let maxtries = 20;
do l = randomElement(needMoreRAAS ? all_layers.filter((l) => l.gamemode.toLowerCase() === "raas") : all_layers); while ((rnd_layers.find(lf => lf.layerid === l.layerid) || rnd_layers.filter(lf => lf.map.name == l.map.name).length > (this.options.allowedSameMapEntries - 1)) && --maxtries >= 0)
if (maxtries > 0 && l) { if (maxtries > 0 && l) {
// this.verbose(1,"Testing layer",l, maxtries); // this.verbose(1,"Testing layer",l, maxtries);
rnd_layers.push(l); rnd_layers.push(l);
@ -638,33 +645,33 @@ export default class MapVote extends DiscordBasePlugin {
} }
} }
// if (!bypassRaasFilter && this.options.gamemodeWhitelist.includes("RAAS") && rnd_layers.filter((l) => l.gamemode === 'RAAS').length < Math.floor(maxOptions / 2)) this.populateNominations(); // if (!bypassRaasFilter && this.options.gamemodeWhitelist.includes("RAAS") && rnd_layers.filter((l) => l.gamemode === 'RAAS').length < Math.floor(maxOptions / 2)) this.populateNominations();
if (this.nominations.length == 0) { if (this.nominations.length === 0) {
if (--tries > 0) this.populateNominations(steamid, cmdLayers, bypassRaasFilter, tries); if (--tries > 0) this.populateNominations(steamid, cmdLayers, bypassRaasFilter, tries);
else this.warn("") else this.warn("")
return; return;
} }
} else { } else {
if (cmdLayers.length == 1) while (cmdLayers.length < optionAmount) cmdLayers.push(cmdLayers[ 0 ]) if (cmdLayers.length === 1) while (cmdLayers.length < optionAmount) cmdLayers.push(cmdLayers[0])
if (cmdLayers.length <= maxOptions) { if (cmdLayers.length <= maxOptions) {
let i = 1; let i = 1;
for (let cl of cmdLayers) { for (const cl of cmdLayers) {
const cls = cl.toLowerCase().split('_'); const cls = cl.toLowerCase().split('_');
const fLayers = sanitizedLayers.filter((l) => ( const fLayers = sanitizedLayers.filter((l) => (
rnd_layers.filter(l2 => l2.map.name == l.map.name).length < this.options.allowedSameMapEntries && rnd_layers.filter(l2 => l2.map.name === l.map.name).length < this.options.allowedSameMapEntries &&
(![this.server.currentLayer ? this.server.currentLayer.map.name : null, ...recentlyPlayedMaps].includes(l.map.name) || cls[2]) && (![this.server.currentLayer ? this.server.currentLayer.map.name : null, ...recentlyPlayedMaps].includes(l.map.name) || cls[2]) &&
( (
( (
(this.options.layerFilteringMode.toLowerCase() == "blacklist" && !this.options.layerLevelBlacklist.find((fl) => this.getLayersFromStringId(fl).map((e) => e.layerid).includes(l.layerid))) || (this.options.layerFilteringMode.toLowerCase() === "blacklist" && !this.options.layerLevelBlacklist.find((fl) => this.getLayersFromStringId(fl).map((e) => e.layerid).includes(l.layerid))) ||
( (
this.options.layerFilteringMode.toLowerCase() == "whitelist" this.options.layerFilteringMode.toLowerCase() === "whitelist"
&& this.options.layerLevelWhitelist.find((fl) => this.getLayersFromStringId(fl).map((e) => e.layerid).includes(l.layerid)) && this.options.layerLevelWhitelist.find((fl) => this.getLayersFromStringId(fl).map((e) => e.layerid).includes(l.layerid))
&& !(this.options.applyBlacklistToWhitelist && this.options.layerLevelBlacklist.find((fl) => this.getLayersFromStringId(fl).map((e) => e.layerid).includes(l.layerid))) && !(this.options.applyBlacklistToWhitelist && this.options.layerLevelBlacklist.find((fl) => this.getLayersFromStringId(fl).map((e) => e.layerid).includes(l.layerid)))
) )
) || cls[2] ) || cls[2]
) )
&& ( && (
(cls[ 0 ] == "*" || l.layerid.toLowerCase().startsWith(cls[ 0 ])) (cls[0] === "*" || l.layerid.toLowerCase().startsWith(cls[0]))
|| (cls[0].toLowerCase().startsWith('f:') && [getTranslation(l.teams[0]), getTranslation(l.teams[1])].includes(cls[0].substring(2).toUpperCase())) || (cls[0].toLowerCase().startsWith('f:') && [getTranslation(l.teams[0]), getTranslation(l.teams[1])].includes(cls[0].substring(2).toUpperCase()))
) )
&& (l.gamemode.toLowerCase().startsWith(cls[1]) || (!cls[1] && this.options.gamemodeWhitelist.includes(l.gamemode.toUpperCase()))) && (l.gamemode.toLowerCase().startsWith(cls[1]) || (!cls[1] && this.options.gamemodeWhitelist.includes(l.gamemode.toUpperCase())))
@ -675,11 +682,12 @@ export default class MapVote extends DiscordBasePlugin {
|| this.options.factionsBlacklist.find((f) => [getTranslation(l.teams[0]), getTranslation(l.teams[1])].includes(f)) || this.options.factionsBlacklist.find((f) => [getTranslation(l.teams[0]), getTranslation(l.teams[1])].includes(f))
)) ))
)); ));
if (fLayers.length == 0) continue; if (fLayers.length === 0) continue;
// this.verbose(1, 'fLayers', fLayers.map(l => l.layerid)); // this.verbose(1, 'fLayers', fLayers.map(l => l.layerid));
// this.verbose(1, 'rnd_layers', rnd_layers.map(l => l.layerid)); // this.verbose(1, 'rnd_layers', rnd_layers.map(l => l.layerid));
let l, maxtries = 10; let l;
do l = randomElement(fLayers); while ((rnd_layers.filter(lf => lf.map.name == l.map.name).length > (this.options.allowedSameMapEntries - 1)) && --maxtries >= 0) let maxtries = 10;
do l = randomElement(fLayers); while ((rnd_layers.filter(lf => lf.map.name === l.map.name).length > (this.options.allowedSameMapEntries - 1)) && --maxtries >= 0)
if (l) { if (l) {
rnd_layers.push(l); rnd_layers.push(l);
this.nominations[i] = l.layerid this.nominations[i] = l.layerid
@ -688,8 +696,7 @@ export default class MapVote extends DiscordBasePlugin {
i++; i++;
} }
} }
} } else if (steamid) {
else if (steamid) {
this.warn(steamid, "You cannot start a vote with more than " + maxOptions + " options"); this.warn(steamid, "You cannot start a vote with more than " + maxOptions + " options");
return; return;
} }
@ -711,7 +718,7 @@ export default class MapVote extends DiscordBasePlugin {
this.factionStrings[0] = ""; this.factionStrings[0] = "";
} }
if (this.nominations[ 1 ] != "") if (this.nominations[1] !== "")
this.server.rcon.execute(`AdminSetNextLayer ${this.nominations[1]} `); this.server.rcon.execute(`AdminSetNextLayer ${this.nominations[1]} `);
function getTranslation(layer) { function getTranslation(layer) {
@ -719,7 +726,9 @@ export default class MapVote extends DiscordBasePlugin {
else if (layer.faction) { else if (layer.faction) {
const f = layer.faction.split(' '); const f = layer.faction.split(' ');
let fTag = ""; let fTag = "";
f.forEach((e) => { fTag += e[ 0 ] }); f.forEach((e) => {
fTag += e[0]
});
return fTag.toUpperCase(); return fTag.toUpperCase();
} else return "Unknown" } else return "Unknown"
} }
@ -739,7 +748,9 @@ export default class MapVote extends DiscordBasePlugin {
if (playerCount < minPlayers && !force) { if (playerCount < minPlayers && !force) {
this.autovotestart = setTimeout(() => { this.beginVoting(force, steamid, cmdLayers) }, 60 * 1000) this.autovotestart = setTimeout(() => {
this.beginVoting(force, steamid, cmdLayers)
}, 60 * 1000)
return; return;
} }
@ -765,7 +776,7 @@ export default class MapVote extends DiscordBasePlugin {
this.verbose(1, 'No winning layer available', winningLayerId) this.verbose(1, 'No winning layer available', winningLayerId)
return; return;
} }
const winnerLayer = Layers.layers.find((l) => l.layerid == winningLayerId); const winnerLayer = Layers.layers.find((l) => l.layerid === winningLayerId);
const fancyWinner = this.formatFancyLayer(winnerLayer); const fancyWinner = this.formatFancyLayer(winnerLayer);
// this.verbose(1, "Winning layer", winnerLayer, fancyWinner) // this.verbose(1, "Winning layer", winnerLayer, fancyWinner)
@ -829,11 +840,12 @@ export default class MapVote extends DiscordBasePlugin {
this.newVoteTimeout = clearTimeout(this.newVoteTimeout); this.newVoteTimeout = clearTimeout(this.newVoteTimeout);
this.endVotingTimeout = clearTimeout(this.endVotingTimeout); this.endVotingTimeout = clearTimeout(this.endVotingTimeout);
} }
objArrToValArr(arr, ...key) { objArrToValArr(arr, ...key) {
let vet = []; const vet = [];
for (let o of arr) { for (const o of arr) {
let obj = o; let obj = o;
for (let k of key) { for (const k of key) {
if (obj[k]) if (obj[k])
obj = obj[k]; obj = obj[k];
} }
@ -841,24 +853,25 @@ export default class MapVote extends DiscordBasePlugin {
} }
return vet; return vet;
} }
// sends a message about nominations through a broadcast // sends a message about nominations through a broadcast
// NOTE: max squad broadcast message length appears to be 485 characters // NOTE: max squad broadcast message length appears to be 485 characters
// Note: broadcast strings with multi lines are very strange // Note: broadcast strings with multi lines are very strange
async broadcastNominations() { async broadcastNominations() {
if (this.nominations.length > 0 && this.votingEnabled) { if (this.nominations.length > 0 && this.votingEnabled) {
await this.broadcast(this.options.voteBroadcastMessage); await this.broadcast(this.options.voteBroadcastMessage);
let allNominationStrings = [] const allNominationStrings = []
let nominationStrings = []; let nominationStrings = [];
for (let choice = 1; choice < this.nominations.length; choice++) { for (let choice = 1; choice < this.nominations.length; choice++) {
choice = Number(choice); choice = Number(choice);
let vLayer = Layers.layers.find(e => e.layerid == this.nominations[ choice ]); const vLayer = Layers.layers.find(e => e.layerid === this.nominations[choice]);
const formattedChoide = this.formatChoice(choice, this.formatFancyLayer(vLayer), this.tallies[choice], (this.options.hideVotesCount || this.firstBroadcast)) const formattedChoide = this.formatChoice(choice, this.formatFancyLayer(vLayer), this.tallies[choice], (this.options.hideVotesCount || this.firstBroadcast))
nominationStrings.push(formattedChoide); nominationStrings.push(formattedChoide);
allNominationStrings.push(formattedChoide); allNominationStrings.push(formattedChoide);
if (nominationStrings.length == 3) { if (nominationStrings.length === 3) {
await this.broadcast(nominationStrings.join("\n")); await this.broadcast(nominationStrings.join("\n"));
nominationStrings = []; nominationStrings = [];
} }
@ -874,6 +887,7 @@ export default class MapVote extends DiscordBasePlugin {
// const winners = this.currentWinners; // const winners = this.currentWinners;
// await this.msgBroadcast(`Current winner${winners.length > 1 ? "s" : ""}: ${winners.join(", ")}`); // await this.msgBroadcast(`Current winner${winners.length > 1 ? "s" : ""}: ${winners.join(", ")}`);
} }
formatFancyLayer(layer) { formatFancyLayer(layer) {
const translations = { const translations = {
'United States Army': "USA", 'United States Army': "USA",
@ -892,7 +906,7 @@ export default class MapVote extends DiscordBasePlugin {
const helis = layer.teams[0].numberOfHelicopters + layer.teams[1].numberOfHelicopters const helis = layer.teams[0].numberOfHelicopters + layer.teams[1].numberOfHelicopters
const tanks = layer.teams[0].numberOfTanks + layer.teams[1].numberOfTanks const tanks = layer.teams[0].numberOfTanks + layer.teams[1].numberOfTanks
let assets = []; const assets = [];
if (helis > 0) assets.push('Helis'); if (helis > 0) assets.push('Helis');
if (tanks > 0) assets.push('Tanks'); if (tanks > 0) assets.push('Tanks');
const vehiclesString = assets.join('-'); const vehiclesString = assets.join('-');
@ -909,7 +923,9 @@ export default class MapVote extends DiscordBasePlugin {
else { else {
const f = t.faction.split(' '); const f = t.faction.split(' ');
let fTag = ""; let fTag = "";
f.forEach((e) => { fTag += e[ 0 ] }); f.forEach((e) => {
fTag += e[0]
});
return fTag.toUpperCase(); return fTag.toUpperCase();
} }
} }
@ -917,7 +933,7 @@ export default class MapVote extends DiscordBasePlugin {
getLayersFromStringId(stringid) { getLayersFromStringId(stringid) {
const cls = stringid.toLowerCase().split('_'); const cls = stringid.toLowerCase().split('_');
const ret = Layers.layers.filter((l) => ((cls[ 0 ] == "*" || l.layerid.toLowerCase().startsWith(cls[ 0 ])) && (l.gamemode.toLowerCase().startsWith(cls[ 1 ]) || (!cls[ 1 ] && [ 'RAAS', 'AAS', 'INVASION' ].includes(l.gamemode.toUpperCase()))) && (!cls[ 2 ] || parseInt(l.version.toLowerCase().replace(/v/gi, '')) == parseInt(cls[ 2 ].replace(/v/gi, ''))))); const ret = Layers.layers.filter((l) => ((cls[0] === "*" || l.layerid.toLowerCase().startsWith(cls[0])) && (l.gamemode.toLowerCase().startsWith(cls[1]) || (!cls[1] && ['RAAS', 'AAS', 'INVASION'].includes(l.gamemode.toUpperCase()))) && (!cls[2] || parseInt(l.version.toLowerCase().replace(/v/gi, '')) == parseInt(cls[2].replace(/v/gi, '')))));
// this.verbose(1,"layers from string",stringid,cls,ret) // this.verbose(1,"layers from string",stringid,cls,ret)
return ret; return ret;
} }
@ -927,7 +943,7 @@ export default class MapVote extends DiscordBasePlugin {
for (let choice in this.nominations) { for (let choice in this.nominations) {
choice = Number(choice); choice = Number(choice);
let vLayer = Layers.layers.find(e => e.layerid == this.nominations[ choice ]); const vLayer = Layers.layers.find(e => e.layerid === this.nominations[choice]);
// const allVecs = vLayer.teams[0].vehicles.concat(vLayer.teams[1].vehicles); // const allVecs = vLayer.teams[0].vehicles.concat(vLayer.teams[1].vehicles);
// const helis = vLayer?.teams[ 0 ].numberOfHelicopters || 0 + vLayer?.teams[ 1 ].numberOfHelicopters || 0 // const helis = vLayer?.teams[ 0 ].numberOfHelicopters || 0 + vLayer?.teams[ 1 ].numberOfHelicopters || 0
// const tanks = vLayer?.teams[ 0 ].numberOfTanks || 0 + vLayer?.teams[ 1 ].numberOfTanks || 0 // const tanks = vLayer?.teams[ 0 ].numberOfTanks || 0 + vLayer?.teams[ 1 ].numberOfTanks || 0
@ -998,12 +1014,12 @@ export default class MapVote extends DiscordBasePlugin {
restorePersistentData() { restorePersistentData() {
this.verbose(1, `Restoring persistent data from: ${this.options.persistentDataFile}`) this.verbose(1, `Restoring persistent data from: ${this.options.persistentDataFile}`)
if (this.options.persistentDataFile == "") return; if (this.options.persistentDataFile === "") return;
if (!fs.existsSync(this.options.persistentDataFile)) return; if (!fs.existsSync(this.options.persistentDataFile)) return;
let bkData = fs.readFileSync(this.options.persistentDataFile); let bkData = fs.readFileSync(this.options.persistentDataFile);
if (bkData == "") return; if (bkData === "") return;
try { try {
bkData = JSON.parse(bkData) bkData = JSON.parse(bkData)
@ -1011,14 +1027,14 @@ export default class MapVote extends DiscordBasePlugin {
this.verbose(1, "Error restoring persistent data", e) this.verbose(1, "Error restoring persistent data", e)
return return
} }
if (bkData.manualRestartSender && bkData.manualRestartSender != "") { if (bkData.manualRestartSender && bkData.manualRestartSender !== "") {
(async () => { (async () => {
await this.warn(bkData.manualRestartSender, `SquadJS has completed the restart.\nPersistent data restored.`) await this.warn(bkData.manualRestartSender, `SquadJS has completed the restart.\nPersistent data restored.`)
this.verbose(1, `Restart confirmation sent to SteamID: "${bkData.manualRestartSender}"`) this.verbose(1, `Restart confirmation sent to SteamID: "${bkData.manualRestartSender}"`)
})() })()
} }
for (let k in bkData.server) this.server[ k ] = bkData.server[ k ]; for (const k in bkData.server) this.server[k] = bkData.server[k];
const maxSecondsDiffierence = 60 const maxSecondsDiffierence = 60
if ((new Date() - new Date(bkData.saveDateTime)) / 1000 > maxSecondsDiffierence) return if ((new Date() - new Date(bkData.saveDateTime)) / 1000 > maxSecondsDiffierence) return
@ -1028,7 +1044,7 @@ export default class MapVote extends DiscordBasePlugin {
// if (bkData.custom.layerHistory) this.server.layerHistory = Layers.layers.filter(l => bkData.custom.layerHistory.includes(l.layerid)); // if (bkData.custom.layerHistory) this.server.layerHistory = Layers.layers.filter(l => bkData.custom.layerHistory.includes(l.layerid));
this.verbose(1, "Recently played maps: " + this.server.layerHistory.filter((l) => l && l.map && l.map.name).map((l) => l.layer.map.name).join(', ')) this.verbose(1, "Recently played maps: " + this.server.layerHistory.filter((l) => l && l.map && l.map.name).map((l) => l.layer.map.name).join(', '))
for (let k in bkData.plugin) this[ k ] = bkData.plugin[ k ]; for (const k in bkData.plugin) this[k] = bkData.plugin[k];
if (this.votingEnabled) { if (this.votingEnabled) {
this.broadcastIntervalTask = setInterval(this.broadcastNominations, toMils(this.options.voteBroadcastInterval)); this.broadcastIntervalTask = setInterval(this.broadcastNominations, toMils(this.options.voteBroadcastInterval));
} }
@ -1036,7 +1052,7 @@ export default class MapVote extends DiscordBasePlugin {
savePersistentData(steamID = null) { savePersistentData(steamID = null) {
if (this.options.persistentDataFile == "") return; if (this.options.persistentDataFile === "") return;
const saveDt = { const saveDt = {
custom: { custom: {
@ -1066,8 +1082,8 @@ export default class MapVote extends DiscordBasePlugin {
const ties = []; const ties = [];
let highestScore = -Infinity; let highestScore = -Infinity;
const allScoreZero = this.tallies.find(s => s > 0) ? false : true; const allScoreZero = !this.tallies.find(s => s > 0);
for (let choice in this.tallies) { for (const choice in this.tallies) {
const score = this.tallies[choice]; const score = this.tallies[choice];
if (score >= this.options.minimumVotesToAcceptResult || allScoreZero) { if (score >= this.options.minimumVotesToAcceptResult || allScoreZero) {
if (score < highestScore) if (score < highestScore)
@ -1076,8 +1092,7 @@ export default class MapVote extends DiscordBasePlugin {
highestScore = score; highestScore = score;
ties.length = 0; ties.length = 0;
ties.push(choice); ties.push(choice);
} } else // equal
else // equal
ties.push(choice); ties.push(choice);
} }
this.verbose(1, 'Ties', ties, ties.map(i => this.nominations[i])) this.verbose(1, 'Ties', ties, ties.map(i => this.nominations[i]))
@ -1095,56 +1110,32 @@ export default class MapVote extends DiscordBasePlugin {
return; return;
} }
this.verbose(1, 'Pulling updated layer list...');
const response = await axios.get(
'https://raw.githubusercontent.com/Squad-Wiki/squad-wiki-pipeline-map-data/master/completed_output/_Current%20Version/finished.json'
);
for (const layer of response.data.Maps) { for (const layer of response.data.Maps) {
if (!Layers.layers.find((e) => e.layerid == layer.rawName)) Layers.layers.push(new Layer(layer)); if (!Layers.layers.find((e) => e.layerid === layer.rawName)){
const hellolayer = new Layer(layer);
Layers._layers.set(hellolayer.layerid, hellolayer);
}
} }
const sheetCsv = (await axios.get('https://docs.google.com/spreadsheets/d/14x8OMhZB1gfYjggKrNvxIAsPJ0IKTQgaKlsvSjnooQc/gviz/tq?tqx=out:csv&sheet=Map%20Layers')).data?.replace(/\"/g, '')?.split('\n') || []//.map((l) => l.split(','))
// this.verbose(1, 'Sheet', sheetCsv)
sheetCsv.shift();
// this.verbose(1, 'Sheet', Layers.layers.length, sheetCsv.length, sheetCsv.find(l => l.includes("Manicouagan_RAAS_v1")))
const rconLayers = (await this.server.rcon.execute('ListLayers'))?.split('\n') || []; const rconLayers = (await this.server.rcon.execute('ListLayers'))?.split('\n') || [];
rconLayers.shift(); rconLayers.shift();
if (rconLayers.length > 0) Layers.layers = Layers.layers.filter((l) => l != null && rconLayers.includes(l.layerid)) if (rconLayers.length > 0) Layers.layers = Layers.layers.filter((l) => l != null && rconLayers.includes(l.layerid))
// this.verbose(1, 'RCON Layers', rconLayers.length, this.mapLayer(rconLayers[ 0 ])) // this.verbose(1, 'RCON Layers', rconLayers.length, this.mapLayer(rconLayers[ 0 ]))
if (sheetCsv.length > 0) {
for (const layer of rconLayers) { for (const layer of rconLayers) {
if (!Layers.layers.find((e) => e?.layerid == layer)) { if (!Layers.layers.find((e) => e?.layerid === layer)) {
let newLayer = this.mapLayer(layer); const newLayer = this.mapLayer(layer);
if (!newLayer) continue; if (!newLayer) continue;
const csvLayer = sheetCsv.find(l => l.includes(newLayer?.layerid))?.split(',');
// console.log(newLayer.layerid, csvLayer[ 2 ]);
if (csvLayer) {
if (csvLayer[ 6 ]) newLayer.teams[ 0 ].faction = csvLayer[ 6 ]
newLayer.teams[ 0 ].name = newLayer.teams[ 0 ].faction
if (csvLayer[ 9 ]) newLayer.teams[ 0 ].numberOfTanks = parseNumberOfAssets(csvLayer[ 9 ])
if (csvLayer[ 13 ]) newLayer.teams[ 0 ].numberOfHelicopters = parseNumberOfAssets(csvLayer[ 13 ])
if (csvLayer[ 5 ]) newLayer.teams[ 0 ].commander = csvLayer[ 5 ].toLowerCase() == 'yes'
if (csvLayer[ 10 ]) newLayer.teams[ 1 ].faction = csvLayer[ 10 ]
newLayer.teams[ 1 ].name = newLayer.teams[ 1 ].faction
newLayer.teams[ 1 ].numberOfTanks = newLayer.teams[ 0 ].numberOfTanks
newLayer.teams[ 1 ].numberOfHelicopters = newLayer.teams[ 0 ].numberOfHelicopters
newLayer.teams[ 1 ].commander = newLayer.teams[ 0 ].commander
}
if (Layers._layers && Layers._layers instanceof Map) if (Layers._layers && Layers._layers instanceof Map)
Layers._layers.set(newLayer.layerid, newLayer); Layers._layers.set(newLayer.layerid, newLayer);
else else
Layers.layers.push(newLayer); Layers.layers.push(newLayer);
} }
} }
}
this.verbose(1, 'Layer list updated', Layers.layers.length, 'total layers'); this.verbose(1, 'Layer list updated', Layers.layers.length, 'total layers');
// this.verbose(1, 'Layers', Layers.layers); // this.verbose(1, 'Layers', Layers.layers);
function parseNumberOfAssets(string) { function parseNumberOfAssets(string) {
return /^x(\d)/.exec(string)[1] return /^x(\d)/.exec(string)[1]
@ -1156,11 +1147,11 @@ export default class MapVote extends DiscordBasePlugin {
// this.verbose(1, 'Parsing layer', l) // this.verbose(1, 'Parsing layer', l)
const gl = /^((?<mod>\w+_))?(?<level>\w+)_(?<gamemode>\w+)_(?<version>\w+)$/i.exec(l)?.groups const gl = /^((?<mod>\w+_))?(?<level>\w+)_(?<gamemode>\w+)_(?<version>\w+)$/i.exec(l)?.groups
// this.verbose(1, 'Parsed layer', gl) // this.verbose(1, 'Parsed layer', gl)
if (!gl || Object.keys(gl).length != 3) return; if (!gl || Object.keys(gl).length !== 3) return;
if (!gl.level) this.verbose(1, 'Empty level', gl) if (!gl.level) this.verbose(1, 'Empty level', gl)
let teams = [] const teams = []
for (const t of ['team1', 'team2']) { for (const t of ['team1', 'team2']) {
teams.push({ teams.push({
faction: 'Unknown', faction: 'Unknown',