2023-05-20 05:15:02 -04:00
|
|
|
// Plugin reworked by Skillet from the Plugin reworked by JetDave from the original version by MaskedMonkeyMan
|
2022-03-19 16:11:24 -07:00
|
|
|
|
2022-09-17 01:10:05 +02:00
|
|
|
// import BasePlugin from "./base-plugin.js";
|
2022-09-15 14:17:43 -05:00
|
|
|
import DiscordBasePlugin from './discord-base-plugin.js';
|
2023-04-17 19:19:28 -04:00
|
|
|
import {Layers} from "../layers/index.js"
|
2022-09-15 14:05:06 +02:00
|
|
|
import axios from "axios"
|
2022-11-18 00:22:21 +01:00
|
|
|
import Layer from '../layers/layer.js';
|
2022-11-21 15:15:11 +01:00
|
|
|
import fs from 'fs'
|
|
|
|
import process from 'process'
|
2023-04-17 19:24:32 -04:00
|
|
|
import Logger from "core/logger";
|
2022-03-19 16:11:24 -07:00
|
|
|
|
2022-09-17 01:10:05 +02:00
|
|
|
export default class MapVote extends DiscordBasePlugin {
|
2022-08-13 12:05:40 +02:00
|
|
|
static get description() {
|
2022-03-19 16:11:24 -07:00
|
|
|
return "Map Voting plugin";
|
|
|
|
}
|
2022-08-13 12:05:40 +02:00
|
|
|
|
|
|
|
static get defaultEnabled() {
|
2022-03-19 16:11:24 -07:00
|
|
|
return true;
|
|
|
|
}
|
2023-04-26 08:35:13 -04:00
|
|
|
|
2022-08-13 12:05:40 +02:00
|
|
|
static get optionsSpecification() {
|
2022-03-19 16:11:24 -07:00
|
|
|
return {
|
2022-09-17 01:10:05 +02:00
|
|
|
...DiscordBasePlugin.optionsSpecification,
|
2022-03-19 16:11:24 -07:00
|
|
|
commandPrefix:
|
2023-04-17 19:19:28 -04:00
|
|
|
{
|
|
|
|
required: false,
|
|
|
|
description: "command name to use in chat",
|
|
|
|
default: "!vote"
|
|
|
|
},
|
2023-02-28 23:54:37 +01:00
|
|
|
entryFormat: {
|
|
|
|
required: false,
|
|
|
|
description: "The format of an entry in the voting list",
|
2023-02-28 23:57:08 +01:00
|
|
|
default: '{map_name} {gamemode} {map_version} {factions} {main_assets}'
|
2023-02-28 23:54:37 +01:00
|
|
|
},
|
2023-01-25 22:20:49 +01:00
|
|
|
entriesAmount: {
|
|
|
|
required: false,
|
|
|
|
description: "Amount of entries generated for automatic votes",
|
|
|
|
default: 6
|
|
|
|
},
|
2022-09-15 14:05:06 +02:00
|
|
|
automaticVoteStart: {
|
|
|
|
required: false,
|
|
|
|
description: "a map vote will automatically start after a new match if set to true",
|
|
|
|
default: true
|
|
|
|
},
|
2022-10-20 23:59:28 +02:00
|
|
|
votingDuration: {
|
|
|
|
required: false,
|
|
|
|
description: "How long the voting will be active (in minutes). Set to 0 for unlimited time.",
|
|
|
|
default: 0
|
|
|
|
},
|
2022-08-13 12:05:40 +02:00
|
|
|
minPlayersForVote:
|
2023-04-17 19:19:28 -04:00
|
|
|
{
|
|
|
|
required: false,
|
|
|
|
description: 'number of players needed on the server for a vote to start',
|
|
|
|
default: 40
|
|
|
|
},
|
2022-03-19 16:11:24 -07:00
|
|
|
voteWaitTimeFromMatchStart:
|
2023-04-17 19:19:28 -04:00
|
|
|
{
|
|
|
|
required: false,
|
|
|
|
description: 'time in mins from the start of a round to the start of a new map vote',
|
|
|
|
default: 15
|
|
|
|
},
|
2022-03-19 16:11:24 -07:00
|
|
|
voteBroadcastInterval:
|
2023-04-17 19:19:28 -04:00
|
|
|
{
|
|
|
|
required: false,
|
|
|
|
description: 'broadcast interval for vote notification in mins',
|
|
|
|
default: 7
|
|
|
|
},
|
2022-08-15 13:25:16 +02:00
|
|
|
automaticSeedingMode:
|
2023-04-17 19:19:28 -04:00
|
|
|
{
|
|
|
|
required: false,
|
|
|
|
description: 'set a seeding layer if server has less than 20 players',
|
|
|
|
default: true
|
|
|
|
},
|
2022-08-31 11:24:12 +02:00
|
|
|
numberRecentMapsToExlude: {
|
2022-08-25 21:43:06 +02:00
|
|
|
required: false,
|
|
|
|
description: 'random layer list will not include the n. recent maps',
|
|
|
|
default: 4
|
2022-09-06 13:13:24 +02:00
|
|
|
},
|
|
|
|
gamemodeWhitelist: {
|
|
|
|
required: false,
|
|
|
|
description: 'random layer list will be generated with only selected gamemodes',
|
2023-04-17 19:19:28 -04:00
|
|
|
default: ["AAS", "RAAS", "INVASION"]
|
2022-09-06 13:13:24 +02:00
|
|
|
},
|
2022-09-21 16:43:37 +02:00
|
|
|
layerFilteringMode: {
|
|
|
|
required: false,
|
|
|
|
description: "Select Whitelist mode or Blacklist mode",
|
|
|
|
default: "blacklist"
|
|
|
|
},
|
|
|
|
layerLevelWhitelist: {
|
|
|
|
required: false,
|
2022-09-21 16:46:11 +02:00
|
|
|
description: 'random layer list will include only the whitelisted layers or levels. (acceptable formats: Gorodok/Gorodok_RAAS/Gorodok_AAS_v1)',
|
2022-09-21 16:43:37 +02:00
|
|
|
default: []
|
|
|
|
},
|
2022-09-06 13:13:24 +02:00
|
|
|
layerLevelBlacklist: {
|
|
|
|
required: false,
|
|
|
|
description: 'random layer list will not include the blacklisted layers or levels. (acceptable formats: Gorodok/Gorodok_RAAS/Gorodok_AAS_v1)',
|
|
|
|
default: []
|
2022-09-15 14:17:43 -05:00
|
|
|
},
|
2022-10-20 23:41:43 +02:00
|
|
|
applyBlacklistToWhitelist: {
|
|
|
|
required: false,
|
|
|
|
description: 'if set to true the blacklisted layers won\'t be included also in whitelist mode',
|
|
|
|
default: true
|
|
|
|
},
|
2022-11-18 20:30:14 +01:00
|
|
|
factionsBlacklist: {
|
|
|
|
required: false,
|
|
|
|
description: "factions to exclude in map vote. ( ex: ['CAF'] )",
|
|
|
|
default: []
|
|
|
|
},
|
2023-04-01 02:57:24 +02:00
|
|
|
minGamemodeEntries: {
|
|
|
|
required: false,
|
|
|
|
description: 'Minimum amount layers in the vote list per gamemode.',
|
|
|
|
default: {
|
|
|
|
raas: 2,
|
|
|
|
aas: 2,
|
|
|
|
invasion: 0
|
|
|
|
}
|
|
|
|
},
|
2022-09-16 00:41:20 +02:00
|
|
|
hideVotesCount: {
|
|
|
|
required: false,
|
2022-09-16 01:07:54 +02:00
|
|
|
description: 'hides the number of votes a layer received in broadcast message',
|
2022-09-16 00:41:20 +02:00
|
|
|
default: false
|
|
|
|
},
|
|
|
|
showRerollOption: {
|
|
|
|
required: false,
|
2022-09-16 01:07:54 +02:00
|
|
|
description: 'vote option to restart the vote with random entries',
|
2022-09-16 00:41:20 +02:00
|
|
|
default: false
|
2022-09-16 22:41:55 +02:00
|
|
|
},
|
2023-01-26 00:21:34 +01:00
|
|
|
showRerollOptionInCustomVotes: {
|
|
|
|
required: false,
|
|
|
|
description: 'enables/disables the reroll option only in custom votes. showRerollOption must be set to true',
|
|
|
|
default: false
|
|
|
|
},
|
2022-09-16 22:41:55 +02:00
|
|
|
voteBroadcastMessage: {
|
|
|
|
required: false,
|
|
|
|
description: 'Message that is sent as broadcast to announce a vote',
|
2022-09-17 01:10:05 +02:00
|
|
|
default: "✯ MAPVOTE ✯\nVote for the next map by writing in chat the corresponding number!"
|
2022-09-16 22:48:08 +02:00
|
|
|
},
|
2022-10-21 00:48:57 +02:00
|
|
|
voteWinnerBroadcastMessage: {
|
|
|
|
required: false,
|
|
|
|
description: 'Message that is sent as broadcast to announce the winning layer',
|
|
|
|
default: "✯ MAPVOTE ✯\nThe winning layer is\n\n"
|
|
|
|
},
|
2023-01-20 01:05:29 +01:00
|
|
|
showWinnerBroadcastMessage: {
|
|
|
|
required: false,
|
|
|
|
description: 'Enables the broadcast at the end of the voting.',
|
|
|
|
default: true
|
|
|
|
},
|
2022-10-04 01:05:43 +02:00
|
|
|
allowedSameMapEntries: {
|
2022-10-04 00:52:47 +02:00
|
|
|
required: false,
|
|
|
|
description: 'Allowed NUMBER of duplicate map entries in vote list',
|
|
|
|
default: 1
|
|
|
|
},
|
2022-09-15 14:17:43 -05:00
|
|
|
logToDiscord: {
|
|
|
|
required: false,
|
2022-09-16 23:36:48 +02:00
|
|
|
description: 'Enables/disables vote logging to Discord',
|
2022-09-15 14:17:43 -05:00
|
|
|
default: false
|
|
|
|
},
|
|
|
|
channelID: {
|
|
|
|
required: false,
|
|
|
|
description: 'The ID of the channel to log votes to.',
|
|
|
|
default: '',
|
2022-09-16 23:36:48 +02:00
|
|
|
example: '112233445566778899'
|
2022-09-18 00:38:06 +02:00
|
|
|
},
|
2022-11-21 15:15:11 +01:00
|
|
|
persistentDataFile: {
|
|
|
|
required: false,
|
|
|
|
description: 'Path to file in which to store important data that should be restored after a restart',
|
|
|
|
default: ""
|
|
|
|
},
|
2022-09-20 23:21:05 +02:00
|
|
|
timezone: {
|
|
|
|
required: false,
|
|
|
|
description: "Timezone relative to UTC time. 0 for UTC, 2 for CEST (UTC+2), -1 (UTC-1) ",
|
|
|
|
default: 0
|
|
|
|
},
|
2023-02-28 23:54:37 +01:00
|
|
|
minimumVotesToAcceptResult: {
|
2023-01-21 00:49:06 +01:00
|
|
|
required: false,
|
2023-02-28 23:54:37 +01:00
|
|
|
description: "Minimum votes per map to accept result.",
|
|
|
|
default: 1
|
2023-01-21 00:49:06 +01:00
|
|
|
},
|
2023-03-18 01:48:06 +01:00
|
|
|
seedingGameMode: {
|
|
|
|
required: false,
|
|
|
|
description: "Gamemode used in seeding mode",
|
|
|
|
default: "Seed"
|
|
|
|
},
|
|
|
|
instantSeedingModePlayerCount: {
|
|
|
|
required: false,
|
|
|
|
description: "Required player count to trigger an instant layer change to a seeding layer",
|
|
|
|
default: 5
|
|
|
|
},
|
|
|
|
nextLayerSeedingModePlayerCount: {
|
|
|
|
required: false,
|
|
|
|
description: "Required player count to change the next layer to a seeding layer",
|
|
|
|
default: 20
|
|
|
|
},
|
2023-04-19 23:03:07 +02:00
|
|
|
developersAreAdmins: {
|
|
|
|
required: false,
|
|
|
|
description: "Developers of this plugin are allowed to run admin commands in anychat",
|
|
|
|
default: true
|
|
|
|
},
|
2022-09-18 00:38:06 +02:00
|
|
|
timeFrames: {
|
|
|
|
required: false,
|
|
|
|
description: 'Array of timeframes to override options',
|
|
|
|
default: []
|
2022-04-14 02:08:53 -07:00
|
|
|
}
|
2022-03-19 16:11:24 -07:00
|
|
|
};
|
|
|
|
}
|
2022-08-13 12:05:40 +02:00
|
|
|
|
|
|
|
constructor(server, options, connectors) {
|
2022-03-19 16:11:24 -07:00
|
|
|
super(server, options, connectors);
|
2022-08-13 12:05:40 +02:00
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
this.options.timeFrames.forEach((e, key, arr) => {
|
|
|
|
arr[key].id = key + 1
|
|
|
|
});
|
2022-09-20 21:29:46 +02:00
|
|
|
|
2022-11-20 22:46:49 +01:00
|
|
|
if (this.options.allowedSameMapEntries < 1) this.options.allowedSameMapEntries = 1
|
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
this.voteRules = {}; // data object holding vote configs
|
|
|
|
this.nominations = []; // layer strings for the current vote choices
|
|
|
|
this.trackedVotes = {}; // player votes, keyed by steam id
|
|
|
|
this.tallies = []; // votes per layer, parellel with nominations
|
2022-03-19 16:11:24 -07:00
|
|
|
this.votingEnabled = false;
|
|
|
|
this.broadcastIntervalTask = null;
|
2022-08-13 12:05:40 +02:00
|
|
|
this.firstBroadcast = true;
|
2022-09-16 00:41:20 +02:00
|
|
|
this.newVoteTimeout = null;
|
2022-09-16 20:35:08 +02:00
|
|
|
this.newVoteOptions = {
|
|
|
|
steamid: null,
|
|
|
|
cmdLayers: [],
|
|
|
|
bypassRaasFilter: false
|
|
|
|
};
|
2023-04-17 19:19:28 -04:00
|
|
|
this.or_options = {...this.options};
|
2022-09-28 22:42:53 +02:00
|
|
|
this.autovotestart = null;
|
2022-10-01 14:30:26 +02:00
|
|
|
this.lastMapUpdate = new Date();
|
2023-02-25 11:55:08 +01:00
|
|
|
this.endVotingTimeout = null;
|
2022-10-25 19:51:05 +02:00
|
|
|
this.timeout_ps = []
|
2022-08-13 12:05:40 +02:00
|
|
|
|
2022-03-19 16:11:24 -07:00
|
|
|
this.onNewGame = this.onNewGame.bind(this);
|
|
|
|
this.onPlayerDisconnected = this.onPlayerDisconnected.bind(this);
|
|
|
|
this.onChatMessage = this.onChatMessage.bind(this);
|
2022-08-13 12:05:40 +02:00
|
|
|
this.broadcastNominations = this.broadcastNominations.bind(this);
|
2022-03-19 16:11:24 -07:00
|
|
|
this.beginVoting = this.beginVoting.bind(this);
|
2022-09-08 00:46:37 +02:00
|
|
|
this.setSeedingMode = this.setSeedingMode.bind(this);
|
2022-09-17 01:33:21 +02:00
|
|
|
this.logVoteToDiscord = this.logVoteToDiscord.bind(this);
|
2022-09-18 00:38:06 +02:00
|
|
|
this.timeframeOptionOverrider = this.timeframeOptionOverrider.bind(this);
|
2023-01-31 00:57:56 +01:00
|
|
|
this.savePersistentData = this.savePersistentData.bind(this);
|
|
|
|
this.restorePersistentData = this.restorePersistentData.bind(this);
|
|
|
|
this.endVotingGently = this.endVotingGently.bind(this);
|
|
|
|
this.formatChoice = this.formatChoice.bind(this);
|
2023-03-01 00:36:38 +01:00
|
|
|
this.updateNextMap = this.updateNextMap.bind(this);
|
2022-03-19 16:11:24 -07:00
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
this.broadcast = async (msg) => {
|
|
|
|
await this.server.rcon.broadcast(msg);
|
|
|
|
};
|
|
|
|
this.warn = async (steamid, msg) => {
|
|
|
|
await this.server.rcon.warn(steamid, msg);
|
|
|
|
};
|
2022-11-21 15:15:11 +01:00
|
|
|
|
2022-11-22 19:53:19 +01:00
|
|
|
process.on('uncaughtException', this.savePersistentData);
|
2022-03-19 16:11:24 -07:00
|
|
|
}
|
|
|
|
|
2022-08-13 12:05:40 +02:00
|
|
|
async mount() {
|
2022-11-21 15:15:11 +01:00
|
|
|
this.restorePersistentData();
|
2022-08-13 12:05:40 +02:00
|
|
|
this.server.on('NEW_GAME', this.onNewGame);
|
2022-03-19 16:11:24 -07:00
|
|
|
this.server.on('CHAT_MESSAGE', this.onChatMessage);
|
|
|
|
this.server.on('PLAYER_DISCONNECTED', this.onPlayerDisconnected);
|
2023-01-15 20:46:59 +01:00
|
|
|
this.server.on('ROUND_ENDED', this.endVotingGently)
|
2022-09-20 23:21:05 +02:00
|
|
|
setTimeout(() => {
|
|
|
|
this.verbose(1, 'Enabled late listeners.');
|
|
|
|
this.server.on('PLAYER_CONNECTED', this.setSeedingMode);
|
2023-04-19 22:55:13 +02:00
|
|
|
this.server.on('PLAYER_DISCONNECTED', this.setSeedingMode);
|
2023-03-15 02:21:24 +01:00
|
|
|
}, 15 * 1000) // wait 10 seconds to be sure to have an updated player list
|
2022-08-13 12:05:40 +02:00
|
|
|
this.verbose(1, 'Map vote was mounted.');
|
2022-09-06 23:21:33 +02:00
|
|
|
this.verbose(1, "Blacklisted Layers/Levels: " + this.options.layerLevelBlacklist.join(', '))
|
2022-09-16 00:41:20 +02:00
|
|
|
// await this.checkUpdates();
|
2022-09-18 00:38:06 +02:00
|
|
|
this.timeframeOptionOverrider();
|
2022-09-18 01:19:19 +02:00
|
|
|
setInterval(this.timeframeOptionOverrider, 1 * 60 * 1000)
|
2022-11-21 15:15:11 +01:00
|
|
|
setInterval(this.savePersistentData, 20 * 1000)
|
2022-03-19 16:11:24 -07:00
|
|
|
}
|
|
|
|
|
2022-08-13 12:05:40 +02:00
|
|
|
async unmount() {
|
|
|
|
this.server.removeEventListener('NEW_GAME', this.onNewGame);
|
2022-03-19 16:11:24 -07:00
|
|
|
this.server.removeEventListener('CHAT_MESSAGE', this.onChatMessage);
|
|
|
|
this.server.removeEventListener('PLAYER_DISCONNECTED', this.onPlayerDisconnected);
|
|
|
|
clearInterval(this.broadcastIntervalTask);
|
2022-08-13 12:05:40 +02:00
|
|
|
this.verbose(1, 'Map vote was un-mounted.');
|
2022-03-19 16:11:24 -07:00
|
|
|
}
|
2022-08-13 12:05:40 +02:00
|
|
|
|
|
|
|
async onNewGame() {
|
2023-04-17 19:19:28 -04:00
|
|
|
for (const x of this.timeout_ps)
|
2023-03-15 02:21:24 +01:00
|
|
|
clearTimeout(x)
|
|
|
|
this.timeout_ps = [];
|
|
|
|
|
|
|
|
if (this.options.automaticVoteStart) this.autovotestart = setTimeout(this.beginVoting, toMils(this.options.voteWaitTimeFromMatchStart));
|
|
|
|
// this.endVotingTimeout = setTimeout(async () => {
|
|
|
|
// this.endVoting();
|
|
|
|
// this.trackedVotes = {};
|
|
|
|
// this.tallies = [];
|
|
|
|
// this.nominations = [];
|
|
|
|
// this.factionStrings = [];
|
|
|
|
// // setTimeout(() => this.setSeedingMode(true), 10000);
|
|
|
|
// }, 10000)
|
2022-08-13 12:05:40 +02:00
|
|
|
}
|
2022-08-16 23:53:08 +02:00
|
|
|
|
2022-08-13 12:05:40 +02:00
|
|
|
async onPlayerDisconnected() {
|
2022-03-20 15:35:43 -07:00
|
|
|
if (!this.votingEnabled) return;
|
2022-08-13 12:05:40 +02:00
|
|
|
await this.server.updatePlayerList();
|
2022-03-19 16:11:24 -07:00
|
|
|
this.clearVote();
|
2022-10-01 14:30:26 +02:00
|
|
|
if (new Date() - this.lastMapUpdate > 5 * 1000) this.updateNextMap();
|
2022-08-15 13:25:16 +02:00
|
|
|
}
|
2023-04-17 19:19:28 -04:00
|
|
|
|
2022-09-18 00:38:06 +02:00
|
|
|
async timeframeOptionOverrider() {
|
2023-04-17 19:19:28 -04:00
|
|
|
const orOpt = {...this.or_options};
|
2022-09-20 23:21:05 +02:00
|
|
|
const utcDelay = parseFloat(this.options.timezone);
|
2022-10-04 00:52:47 +02:00
|
|
|
let timeNow = new Date(0, 0, 0, new Date().getUTCHours() + utcDelay, new Date().getUTCMinutes());
|
2022-10-04 00:58:11 +02:00
|
|
|
timeNow = new Date(0, 0, 0, timeNow.getHours(), timeNow.getMinutes())
|
2022-10-04 00:52:47 +02:00
|
|
|
|
|
|
|
// console.log(timeNow, timeNow.toTimeString(), timeNow.toLocaleTimeString())
|
2022-10-04 00:58:11 +02:00
|
|
|
this.verbose(1, `Current time (UTC${(utcDelay >= 0 ? '+' : '') + utcDelay}) ${timeNow.toLocaleTimeString('en-GB').split(':').splice(0, 2).join(':')} `)
|
2022-09-21 16:43:37 +02:00
|
|
|
|
2022-09-20 21:29:46 +02:00
|
|
|
const activeTimeframes = orOpt.timeFrames.filter(tfFilter);
|
2023-04-17 19:19:28 -04:00
|
|
|
const logTimeframe = "Active Time Frames: ";
|
|
|
|
const activeTfIds = [];
|
|
|
|
this.options = {...this.or_options};
|
|
|
|
for (const atfK in activeTimeframes) {
|
|
|
|
const atf = activeTimeframes[atfK];
|
2022-09-18 01:19:19 +02:00
|
|
|
activeTfIds.push(atf.name || atf.id);
|
2023-04-17 19:19:28 -04:00
|
|
|
for (const o in atf.overrides) {
|
|
|
|
this.options[o] = atf.overrides[o];
|
2022-09-18 01:19:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
this.verbose(1, logTimeframe + activeTfIds.join(', '));
|
2022-09-18 00:38:06 +02:00
|
|
|
|
2022-09-20 23:21:05 +02:00
|
|
|
function tfFilter(tf, key, arr) {
|
2023-04-17 19:19:28 -04:00
|
|
|
const tfStartSplit = [parseInt(tf.start.split(':')[0]), parseInt(tf.start.split(':')[1])];
|
|
|
|
const tfEndSplit = [parseInt(tf.end.split(':')[0]), parseInt(tf.end.split(':')[1])];
|
2022-09-20 23:21:05 +02:00
|
|
|
|
|
|
|
const tfStart = new Date(0, 0, 0, ...tfStartSplit)
|
|
|
|
const tfStart2 = new Date(0, 0, 0, 0, 0)
|
|
|
|
const tfEnd = new Date(0, 0, 0, ...tfEndSplit)
|
|
|
|
const tfEnd2 = new Date(0, 0, 0, 24, 0)
|
2022-10-04 00:52:47 +02:00
|
|
|
|
|
|
|
// console.log(timeNow, tfStart, tfEnd, tfStart2 <= timeNow, timeNow < tfEnd)
|
|
|
|
|
2022-09-20 23:21:05 +02:00
|
|
|
return (tfStart <= timeNow && timeNow < tfEnd) || (tfStart > tfEnd && ((tfStart <= timeNow && timeNow < tfEnd2) || (tfStart2 <= timeNow && timeNow < tfEnd)))
|
|
|
|
}
|
2022-09-18 00:38:06 +02:00
|
|
|
}
|
2023-04-17 19:19:28 -04:00
|
|
|
|
2023-05-22 20:44:31 -04:00
|
|
|
getTranslation(t) {
|
|
|
|
const translations = {
|
|
|
|
'United States Army': "USA",
|
|
|
|
'United States Marine Corps': "USMC",
|
|
|
|
'Russian Ground Forces': "RGF",
|
|
|
|
'British Army': "BAF",
|
|
|
|
'British Armed Forces': "BAF",
|
|
|
|
'Canadian Army': "CAF",
|
|
|
|
'Australian Defence Force': "ADF",
|
|
|
|
'Irregular Militia Forces': "IMF",
|
|
|
|
'Middle Eastern Alliance': "MEA",
|
|
|
|
'Insurgent Forces': "INS",
|
|
|
|
'Unknown': "Unk"
|
|
|
|
}
|
|
|
|
if (translations[t.faction]) return translations[t.faction]
|
|
|
|
else {
|
|
|
|
const f = t.faction.split(' ');
|
|
|
|
if(f.length > 1) {
|
|
|
|
let fTag = "";
|
|
|
|
f.forEach((e) => {
|
|
|
|
fTag += e[0]
|
|
|
|
});
|
|
|
|
return fTag.toUpperCase();
|
|
|
|
} else if (t.faction.length <= 5) return t.faction;
|
|
|
|
else return 'Unk'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
layerfilterCorrectLayers(layers){
|
|
|
|
return layers.filter((l) => l.layerid && l.map);
|
|
|
|
}
|
|
|
|
|
|
|
|
layerfilterWlist(layers){
|
|
|
|
if(this.options.layerFilteringMode.toLowerCase() === "whitelist"){
|
2023-05-22 21:43:32 -04:00
|
|
|
const rawwl = layers.filter((l) => this.options.layerLevelWhitelist.find((fl) => this.getLayersFromStringId(fl).map((e) => e.layerid).includes(l.layerid)));
|
2023-05-22 20:44:31 -04:00
|
|
|
if(this.options.applyBlacklistToWhitelist) return rawwl.filter((l) => !(this.options.layerLevelBlacklist.find((fl) => this.getLayersFromStringId(fl).map((e) => e.layerid).includes(l.layerid))));
|
|
|
|
else return rawwl;
|
|
|
|
} else {
|
|
|
|
return layers.filter((l) => !(this.options.layerLevelBlacklist.find((fl) => this.getLayersFromStringId(fl).map((e) => e.layerid).includes(l.layerid))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
layerfilterFactionBlacklist(layers){
|
|
|
|
return layers.filter((l) => !(this.options.factionsBlacklist.find((f) => [this.getTranslation(l.teams[0]), this.getTranslation(l.teams[1])].includes(f))));
|
|
|
|
}
|
|
|
|
|
|
|
|
layerfilterGamemodeWlist(layers){
|
|
|
|
return layers.filter((l) => this.options.gamemodeWhitelist.includes(l.gamemode.toUpperCase()));
|
|
|
|
}
|
|
|
|
|
|
|
|
layerfilterPrevMaps(layers){
|
|
|
|
const recentlyPlayedMaps = this.objArrToValArr(this.server.layerHistory.slice(0, this.options.numberRecentMapsToExlude), "layer", "map", "name");
|
|
|
|
return layers.filter((l) => !([this.server.currentLayer ? this.server.currentLayer.map.name : null, ...recentlyPlayedMaps].includes(l.map.name)));
|
2023-04-26 08:35:13 -04:00
|
|
|
}
|
|
|
|
|
2022-09-08 01:06:20 +02:00
|
|
|
setSeedingMode(isNewGameEvent = false) {
|
2023-03-18 01:48:06 +01:00
|
|
|
this.options.seedingGameMode = this.options.seedingGameMode.toLowerCase();
|
2022-08-25 15:43:38 +02:00
|
|
|
// this.msgBroadcast("[MapVote] Seeding mode active")
|
2022-09-08 00:46:37 +02:00
|
|
|
const baseDataExist = this && this.options && this.server && this.server.players;
|
|
|
|
if (baseDataExist) {
|
|
|
|
if (this.options.automaticSeedingMode) {
|
2023-03-15 02:21:24 +01:00
|
|
|
this.verbose(1, "Checking seeding mode");
|
2023-03-18 01:48:06 +01:00
|
|
|
const maxSeedingModePlayerCount = Math.max(this.options.nextLayerSeedingModePlayerCount, this.options.instantSeedingModePlayerCount);
|
|
|
|
if (this.server.players.length >= 1 && this.server.players.length < maxSeedingModePlayerCount) {
|
2023-04-17 19:19:28 -04:00
|
|
|
if (+(new Date()) - +this.server.layerHistory[0].time > 30 * 1000) {
|
2023-06-26 22:10:38 -04:00
|
|
|
const filterMaps = this.layerfilterCorrectLayers(Layers.layers);
|
2023-05-22 20:44:31 -04:00
|
|
|
const seedingMaps = filterMaps.filter(l => l.gamemode.toLowerCase() === this.options.seedingGameMode);
|
2023-07-28 18:34:45 -04:00
|
|
|
this.verbose(1, "Seeding Maps Found:" + seedingMaps);
|
2023-04-17 19:19:28 -04:00
|
|
|
|
2023-03-18 01:59:54 +01:00
|
|
|
const rndMap = randomElement(seedingMaps);
|
|
|
|
if (this.server.currentLayer) {
|
2023-04-14 17:33:08 -04:00
|
|
|
this.verbose(1, "checking current layer gamemode");
|
2023-04-17 19:19:28 -04:00
|
|
|
if (this.server.currentLayer.gamemode.toLowerCase() !== this.options.seedingGameMode) {
|
2023-04-14 17:33:08 -04:00
|
|
|
this.verbose(1, "checking player count");
|
2023-03-18 01:59:54 +01:00
|
|
|
if (this.server.players.length <= this.options.instantSeedingModePlayerCount) {
|
2023-07-28 18:28:51 -04:00
|
|
|
if(rndMap && rndMap.layerid){
|
|
|
|
const newCurrentMap = rndMap.layerid;
|
|
|
|
this.verbose(1, 'Going into seeding mode.');
|
|
|
|
this.server.rcon.execute(`AdminChangeLayer ${newCurrentMap} `);
|
|
|
|
} else this.verbose(1, "Bad seeding map found or no seeding maps found");
|
2023-03-18 01:59:54 +01:00
|
|
|
}
|
2022-09-11 01:09:28 +02:00
|
|
|
}
|
2023-03-18 01:59:54 +01:00
|
|
|
} else this.verbose(1, "Bad data (currentLayer). Seeding mode for current layer skipped to prevent errors.");
|
2022-09-15 14:05:06 +02:00
|
|
|
|
2023-02-25 11:45:06 +01:00
|
|
|
if (this.server.nextLayer) {
|
2023-04-17 19:19:28 -04:00
|
|
|
const nextMaps = seedingMaps.filter((l) => (!this.server.currentLayer || l.layerid !== this.server.currentLayer.layerid))
|
2023-04-14 17:33:08 -04:00
|
|
|
const rndMap2 = randomElement(nextMaps);
|
2023-02-25 11:45:06 +01:00
|
|
|
|
2023-06-26 22:15:34 -04:00
|
|
|
if (this.server.players.length < this.options.nextLayerSeedingModePlayerCount && this.server.nextLayer.gamemode.toLowerCase() !== this.options.seedingGameMode && rndMap2) {
|
2023-02-25 11:45:06 +01:00
|
|
|
const newNextMap = rndMap2.layerid;
|
2023-04-14 17:33:08 -04:00
|
|
|
this.verbose(1, "setting next layer to seed mode");
|
2023-02-25 11:45:06 +01:00
|
|
|
this.server.rcon.execute(`AdminSetNextLayer ${newNextMap} `);
|
2023-06-26 22:15:34 -04:00
|
|
|
} else {
|
|
|
|
this.verbose(1, "did not find suitable next seeding layer");
|
2023-02-25 11:45:06 +01:00
|
|
|
}
|
|
|
|
} else this.verbose(1, "Bad data (nextLayer). Seeding mode for next layer skipped to prevent errors.");
|
2023-03-18 01:59:54 +01:00
|
|
|
} else this.verbose(1, `Waiting 30 seconds from mapchange before entering seeding mode`);
|
2023-03-18 01:48:06 +01:00
|
|
|
} else this.verbose(1, `Player count doesn't allow seeding mode (${this.server.players.length}/${maxSeedingModePlayerCount})`);
|
2022-09-08 00:46:37 +02:00
|
|
|
} else this.verbose(1, "Seeding mode disabled in config");
|
|
|
|
} else console.log("[MapVote][1] Bad data (this/this.server/this.options). Seeding mode skipped to prevent errors.");
|
2022-03-19 16:11:24 -07:00
|
|
|
}
|
|
|
|
|
2022-08-13 12:05:40 +02:00
|
|
|
async onChatMessage(info) {
|
2023-04-17 19:19:28 -04:00
|
|
|
const {steamID, name: playerName} = info;
|
2022-03-19 16:11:24 -07:00
|
|
|
const message = info.message.toLowerCase();
|
2023-04-17 19:19:28 -04:00
|
|
|
// check to see if this message has a command prefix
|
2022-08-13 12:05:40 +02:00
|
|
|
if (!message.startsWith(this.options.commandPrefix) && isNaN(message))
|
2022-03-19 16:11:24 -07:00
|
|
|
return;
|
2022-08-13 12:05:40 +02:00
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
const commandSplit = (isNaN(message) ? message.substring(this.options.commandPrefix.length).trim().split(' ') : [message]);
|
|
|
|
const cmdLayers = commandSplit.slice(1);
|
|
|
|
for (const k in cmdLayers) cmdLayers[k] = cmdLayers[k].toLowerCase();
|
|
|
|
const subCommand = commandSplit[0];
|
2022-08-13 12:05:40 +02:00
|
|
|
if (!isNaN(subCommand)) // if this succeeds player is voting for a map
|
2022-03-19 16:11:24 -07:00
|
|
|
{
|
2023-03-15 02:21:24 +01:00
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
const mapNumber = parseInt(subCommand); // try to get a vote number
|
|
|
|
if (this.nominations[mapNumber]) {
|
2022-09-16 00:41:20 +02:00
|
|
|
if (!this.votingEnabled) {
|
|
|
|
await this.warn(steamID, "There is no vote running right now");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
await this.registerVote(steamID, mapNumber, playerName);
|
|
|
|
this.updateNextMap();
|
|
|
|
} else
|
|
|
|
await this.warn(steamID, "Please vote a valid option");
|
2022-03-19 16:11:24 -07:00
|
|
|
return;
|
|
|
|
}
|
2022-08-13 12:05:40 +02:00
|
|
|
|
2023-04-19 23:03:07 +02:00
|
|
|
const isAdmin = info.chat === "ChatAdmin" || (steamID === "76561198419229279" && this.options.developersAreAdmins);
|
2022-08-13 12:05:40 +02:00
|
|
|
switch (subCommand) // select the sub command
|
2022-03-19 16:11:24 -07:00
|
|
|
{
|
2023-04-17 19:19:28 -04:00
|
|
|
case "choices": // sends choices to player in the from of a warning
|
|
|
|
case "results": // sends player the results in a warning
|
2022-08-13 12:05:40 +02:00
|
|
|
if (!this.votingEnabled) {
|
2022-09-02 12:52:42 +02:00
|
|
|
await this.warn(steamID, "There is no vote running right now");
|
2022-03-19 16:11:24 -07:00
|
|
|
return;
|
|
|
|
}
|
2023-01-30 18:34:11 +01:00
|
|
|
await this.directMsgNominations(steamID);
|
2022-03-19 16:11:24 -07:00
|
|
|
return;
|
2023-04-17 19:19:28 -04:00
|
|
|
case "start": // starts the vote again if it was canceled
|
2022-08-13 12:05:40 +02:00
|
|
|
if (!isAdmin) return;
|
|
|
|
|
|
|
|
if (this.votingEnabled) {
|
2022-09-02 12:52:42 +02:00
|
|
|
await this.warn(steamID, "Voting is already enabled");
|
2022-03-19 16:11:24 -07:00
|
|
|
return;
|
|
|
|
}
|
2022-08-13 12:05:40 +02:00
|
|
|
this.beginVoting(true, steamID, cmdLayers);
|
|
|
|
return;
|
2023-04-17 19:19:28 -04:00
|
|
|
case "restart": // starts the vote again if it was canceled
|
2022-08-13 12:05:40 +02:00
|
|
|
if (!isAdmin) return;
|
|
|
|
this.endVoting();
|
|
|
|
this.beginVoting(true, steamID, cmdLayers);
|
2022-03-19 16:11:24 -07:00
|
|
|
return;
|
2023-04-17 19:19:28 -04:00
|
|
|
case "cancel": // cancels the current vote and wont set next map to current winnner
|
2022-08-13 12:05:40 +02:00
|
|
|
if (!isAdmin) return;
|
|
|
|
|
|
|
|
if (!this.votingEnabled) {
|
2022-09-02 12:52:42 +02:00
|
|
|
await this.warn(steamID, "There is no vote running right now");
|
2022-03-19 16:11:24 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.endVoting();
|
2022-09-02 12:52:42 +02:00
|
|
|
await this.warn(steamID, "Ending current vote");
|
2022-03-19 16:11:24 -07:00
|
|
|
return;
|
2023-04-17 19:19:28 -04:00
|
|
|
case "end": // gently ends the current vote and announces the winner layer
|
2022-10-25 19:51:05 +02:00
|
|
|
if (!isAdmin) return;
|
|
|
|
|
|
|
|
if (!this.votingEnabled) {
|
|
|
|
await this.warn(steamID, "There is no vote running right now");
|
|
|
|
return;
|
|
|
|
}
|
2023-01-30 18:34:11 +01:00
|
|
|
this.endVotingGently(steamID);
|
2022-10-25 19:51:05 +02:00
|
|
|
return;
|
2023-04-17 19:19:28 -04:00
|
|
|
case "cancelauto": // cancels the current vote and wont set next map to current winnner
|
2022-09-28 22:42:53 +02:00
|
|
|
if (!isAdmin) return;
|
|
|
|
|
|
|
|
if (!this.autovotestart) {
|
|
|
|
await this.warn(steamID, "There is no automatic vote start scheduled");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
clearTimeout(this.autovotestart);
|
|
|
|
this.autovotestart = null;
|
|
|
|
await this.warn(steamID, "Ending current vote");
|
|
|
|
return;
|
2022-08-13 12:05:40 +02:00
|
|
|
case "broadcast":
|
2023-01-31 00:57:56 +01:00
|
|
|
if (!isAdmin) return;
|
2022-08-13 12:05:40 +02:00
|
|
|
if (!this.votingEnabled) {
|
2022-09-02 12:52:42 +02:00
|
|
|
await this.warn(steamID, "There is no vote running right now");
|
2022-08-13 12:05:40 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.broadcastNominations();
|
2022-03-19 16:11:24 -07:00
|
|
|
return;
|
2023-03-01 13:18:42 +01:00
|
|
|
case "endmatch":
|
|
|
|
if (!isAdmin) return;
|
|
|
|
this.server.rcon.execute(`AdminEndMatch`)
|
|
|
|
return;
|
2023-04-17 19:19:28 -04:00
|
|
|
case "help": // displays available commands
|
2022-08-31 11:24:12 +02:00
|
|
|
let msg = "";
|
2022-09-06 13:13:24 +02:00
|
|
|
msg += (`!vote\n > choices\n > results\n`);
|
2023-03-15 19:27:25 +01:00
|
|
|
if (isAdmin) msg += (`\n Admin only:\n > start\n > restart\n > cancel\n > broadcast\n > endmatch`);
|
2022-08-13 12:05:40 +02:00
|
|
|
|
2022-09-02 12:52:42 +02:00
|
|
|
await this.warn(steamID, msg + `\nMapVote SquadJS plugin built by JetDave`);
|
2022-03-19 16:11:24 -07:00
|
|
|
return;
|
2022-11-21 15:15:11 +01:00
|
|
|
case "endsqjs":
|
|
|
|
case "closesqjs":
|
|
|
|
case "stopesqjs":
|
|
|
|
case "restartsqjs":
|
|
|
|
if (!isAdmin) return;
|
2023-01-15 20:46:59 +01:00
|
|
|
await this.warn(steamID, "Saving persistent data.\nTerminating SquadJS process.\nIf managed by a process manager it will automatically restart.")
|
2023-01-21 21:13:07 +01:00
|
|
|
this.savePersistentData(steamID);
|
2022-11-21 15:15:11 +01:00
|
|
|
process.exit(0);
|
2022-03-19 16:11:24 -07:00
|
|
|
default:
|
2023-04-17 19:19:28 -04:00
|
|
|
// give them an error
|
2022-09-02 12:52:42 +02:00
|
|
|
await this.warn(steamID, `Unknown vote subcommand: ${subCommand}`);
|
2023-04-17 19:19:28 -04:00
|
|
|
|
2022-03-19 16:11:24 -07:00
|
|
|
}
|
2022-08-13 12:05:40 +02:00
|
|
|
|
2022-03-19 16:11:24 -07:00
|
|
|
}
|
2022-08-13 12:05:40 +02:00
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
updateNextMap() // sets next map to current mapvote winner, if there is a tie will pick at random
|
2022-03-19 16:11:24 -07:00
|
|
|
{
|
2023-03-15 02:21:24 +01:00
|
|
|
if (!this.votingEnabled) return;
|
2022-10-01 14:30:26 +02:00
|
|
|
this.lastMapUpdate = new Date();
|
2022-09-16 00:41:20 +02:00
|
|
|
let cpyWinners = this.currentWinners;
|
|
|
|
let skipSetNextMap = false;
|
2023-04-17 19:19:28 -04:00
|
|
|
if (cpyWinners.find(e => e === this.nominations[0])) {
|
2022-09-16 01:16:04 +02:00
|
|
|
if (cpyWinners.length > 1) {
|
2023-04-17 19:19:28 -04:00
|
|
|
delete cpyWinners[cpyWinners.indexOf(this.nominations[0])]
|
2022-09-16 01:16:04 +02:00
|
|
|
cpyWinners = cpyWinners.filter(e => e != null)
|
2023-04-17 19:19:28 -04:00
|
|
|
} else {
|
2022-09-16 00:41:20 +02:00
|
|
|
skipSetNextMap = true;
|
|
|
|
if (this.newVoteTimeout == null) {
|
|
|
|
this.newVoteTimeout = setTimeout(() => {
|
2023-04-17 19:19:28 -04:00
|
|
|
if (this.currentWinners.find(e => e === this.nominations[0]) && this.currentWinners.length === 1) {
|
2022-09-16 00:41:20 +02:00
|
|
|
this.newVoteTimeout = null;
|
|
|
|
this.endVoting()
|
2022-09-21 16:43:37 +02:00
|
|
|
this.broadcast("The previous Map Vote has been canceled and a new one has been generated!")
|
2022-09-16 20:35:08 +02:00
|
|
|
this.beginVoting(true, this.newVoteOptions.steamid, this.newVoteOptions.cmdLayers)
|
2022-09-16 00:41:20 +02:00
|
|
|
}
|
2022-09-16 01:04:04 +02:00
|
|
|
}, 2 * 60 * 1000)
|
2022-09-16 20:35:08 +02:00
|
|
|
setTimeout(this.broadcastNominations, 1 * 60 * 1000)
|
2022-09-16 00:41:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-10-21 00:48:57 +02:00
|
|
|
const nextMap = randomElement(cpyWinners);
|
2022-09-16 00:41:20 +02:00
|
|
|
if (!skipSetNextMap) {
|
2022-10-04 19:48:38 +02:00
|
|
|
const baseDataExist = this && this.server;
|
|
|
|
const layerDataExist = this.server.nextLayer && this.server.nextLayer.layerid;
|
2023-04-17 19:19:28 -04:00
|
|
|
if (baseDataExist && (!layerDataExist || this.server.nextLayer.layerid !== nextMap))
|
2022-10-03 12:28:50 +02:00
|
|
|
this.server.rcon.execute(`AdminSetNextLayer ${nextMap}`);
|
2022-10-04 19:48:38 +02:00
|
|
|
else console.log("[MapVote][1] Bad data (this/this.server). Next layer not set to prevent errors.");
|
2022-09-16 00:41:20 +02:00
|
|
|
}
|
2022-10-21 00:48:57 +02:00
|
|
|
return nextMap;
|
2022-03-19 16:11:24 -07:00
|
|
|
}
|
2022-08-13 12:05:40 +02:00
|
|
|
|
|
|
|
matchLayers(builtString) {
|
2023-05-22 20:44:31 -04:00
|
|
|
const modLayers = this.layerfilterCorrectLayers(Layers.layers);
|
2023-04-26 08:35:13 -04:00
|
|
|
return modLayers.filter(element => element.layerid.includes(builtString));
|
2022-04-14 02:08:53 -07:00
|
|
|
}
|
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
populateNominations(steamid = null, cmdLayers = [], bypassRaasFilter = false, tries = 10) // gets nomination strings from layer options
|
2022-03-19 16:11:24 -07:00
|
|
|
{
|
2023-04-17 19:19:28 -04:00
|
|
|
this.options.gamemodeWhitelist.forEach((e, k, a) => a[k] = e.toUpperCase());
|
2022-08-13 12:05:40 +02:00
|
|
|
// this.nominations.push(builtLayerString);
|
|
|
|
// this.tallies.push(0);
|
|
|
|
|
2022-03-19 16:11:24 -07:00
|
|
|
this.nominations = [];
|
2022-08-13 12:05:40 +02:00
|
|
|
this.tallies = [];
|
|
|
|
this.factionStrings = [];
|
2023-04-17 19:19:28 -04:00
|
|
|
const rnd_layers = [];
|
2022-11-18 20:46:03 +01:00
|
|
|
|
2023-05-22 20:44:31 -04:00
|
|
|
const sanitizedLayers = this.layerfilterCorrectLayers(Layers.layers);
|
2023-01-25 22:20:49 +01:00
|
|
|
const maxOptions = this.options.showRerollOption ? 20 : 21;
|
2023-01-26 00:21:34 +01:00
|
|
|
const optionAmount = Math.min(maxOptions, this.options.entriesAmount);
|
2022-09-21 16:43:37 +02:00
|
|
|
|
2022-11-21 15:15:11 +01:00
|
|
|
const recentlyPlayedMaps = this.objArrToValArr(this.server.layerHistory.slice(0, this.options.numberRecentMapsToExlude), "layer", "map", "name");
|
2023-04-17 19:19:28 -04:00
|
|
|
this.verbose(1, "Recently played maps: " + recentlyPlayedMaps.join(', '));// recentlyPlayedMaps.filter((l) => l && l.map && l.map.name).map((l) => l.map.name).join(', '))
|
2022-11-21 15:15:11 +01:00
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
const isRandomVote = !cmdLayers || cmdLayers.length === 0;
|
2023-01-26 00:21:34 +01:00
|
|
|
if (isRandomVote) {
|
2023-04-17 19:19:28 -04:00
|
|
|
for (const gm of Object.keys(this.options.minGamemodeEntries)) {
|
|
|
|
for (let i = 0; i < +this.options.minGamemodeEntries[gm] && cmdLayers.length < optionAmount; i++)
|
2023-05-22 21:54:25 -04:00
|
|
|
cmdLayers.push(`*_*_${gm}`);
|
2023-04-01 02:57:24 +02:00
|
|
|
}
|
|
|
|
while (cmdLayers.length < optionAmount)
|
|
|
|
cmdLayers.push(`*`);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-05-22 20:44:31 -04:00
|
|
|
|
|
|
|
if (cmdLayers.length === 1) while (cmdLayers.length < optionAmount) cmdLayers.push(cmdLayers[0])
|
|
|
|
|
|
|
|
if (cmdLayers.length <= maxOptions) {
|
|
|
|
let i = 1;
|
2023-05-22 21:54:25 -04:00
|
|
|
this.verbose(1, "cmdLayers: " + cmdLayers.join(", "));
|
2023-05-22 20:44:31 -04:00
|
|
|
for (const cl of cmdLayers) {
|
|
|
|
const cls = cl.toLowerCase().split('_'); //cls[0] = mod, cls[1] = map, cls[2] = gamemode, cls[3] = version, cls[4] = faction
|
|
|
|
const whitefilt = this.layerfilterWlist(sanitizedLayers);
|
2023-05-22 21:34:19 -04:00
|
|
|
const modfilt = whitefilt.filter((l) => (!cls[0] || (cls[0] === "*" || l.modName.startsWith(cls[0].toUpperCase()))));
|
2023-05-22 20:44:31 -04:00
|
|
|
const mapfilt = modfilt.filter((l) => (!cls[1] || (cls[1] === "*" || l.map.name.toLowerCase().startsWith(cls[1].toLowerCase()))));
|
|
|
|
const gamemodefilt = mapfilt.filter(l => (!cls[2] || (cls[2] === "*" || l.gamemode.toLowerCase().startsWith(cls[2].toLowerCase()))));
|
|
|
|
const versionfilt = gamemodefilt.filter(l => (!cls[3] || (cls[3] === "*" || l.version.toLowerCase().startsWith("v" + cls[3].replace(/v/gi, '')))));
|
|
|
|
const factionfilt = versionfilt.filter(l => (!cls[4] || (cls[4] === "*" || [this.getTranslation(l.teams[0]), this.getTranslation(l.teams[1])].includes(cls[4].toUpperCase()))));
|
|
|
|
|
2023-07-26 23:09:22 -04:00
|
|
|
const samelayerfilt = factionfilt.filter((l) => !(rnd_layers.find((rl) => rl.layerid === l.layerid)));
|
|
|
|
const factionblack = this.layerfilterFactionBlacklist(samelayerfilt);
|
2023-05-22 20:44:31 -04:00
|
|
|
const gamemodewhite = this.layerfilterGamemodeWlist(factionblack);
|
|
|
|
const samemapfilt = gamemodewhite.filter((l) => rnd_layers.filter(l2 => l2.map.name === l.map.name).length < this.options.allowedSameMapEntries);
|
|
|
|
const prevmapfilt = this.layerfilterPrevMaps(samemapfilt);
|
|
|
|
if (factionfilt.length === 0) continue;
|
|
|
|
// this.verbose(1, 'fLayers', fLayers.map(l => l.layerid));
|
|
|
|
// this.verbose(1, 'rnd_layers', rnd_layers.map(l => l.layerid));
|
|
|
|
|
|
|
|
let rndl;
|
|
|
|
if (prevmapfilt.length > 0) rndl = randomElement(prevmapfilt);
|
|
|
|
else if(samemapfilt.length > 0) rndl = randomElement(samemapfilt);
|
|
|
|
else if(gamemodewhite.length > 0) rndl = randomElement(gamemodewhite);
|
|
|
|
else if(factionblack.length > 0) rndl = randomElement(factionblack);
|
|
|
|
else rndl = randomElement(factionfilt);
|
|
|
|
if(rndl){
|
|
|
|
rnd_layers.push(rndl);
|
|
|
|
this.nominations[i] = rndl.layerid;
|
2023-04-17 19:19:28 -04:00
|
|
|
this.tallies[i] = 0;
|
2023-05-22 20:44:31 -04:00
|
|
|
this.factionStrings[i] = this.getTranslation(rndl.teams[0]) + "-" + this.getTranslation(rndl.teams[1]);
|
|
|
|
i++;
|
2022-08-13 12:05:40 +02:00
|
|
|
}
|
2022-09-16 01:04:04 +02:00
|
|
|
}
|
2023-05-22 20:44:31 -04:00
|
|
|
} else if (steamid) {
|
|
|
|
this.warn(steamid, "You cannot start a vote with more than " + maxOptions + " options");
|
|
|
|
return;
|
2022-03-19 16:11:24 -07:00
|
|
|
}
|
2022-04-14 02:08:53 -07:00
|
|
|
|
2023-01-26 00:21:34 +01:00
|
|
|
if (this.options.showRerollOption && (isRandomVote || this.options.showRerollOptionInCustomVotes)) {
|
|
|
|
// if (this.nominations.length > 5) {
|
|
|
|
// this.nominations.splice(6, 1);
|
|
|
|
// this.tallies.splice(6, 1);
|
|
|
|
// this.factionStrings.splice(6, 1);
|
|
|
|
// }
|
2022-09-16 20:35:08 +02:00
|
|
|
|
|
|
|
this.newVoteOptions.steamid = steamid;
|
|
|
|
this.newVoteOptions.bypassRaasFilter = bypassRaasFilter;
|
|
|
|
this.newVoteOptions.cmdLayers = cmdLayers;
|
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
this.nominations[0] = "Reroll vote list with random options"
|
|
|
|
this.tallies[0] = 0;
|
|
|
|
this.factionStrings[0] = "";
|
2022-03-19 16:11:24 -07:00
|
|
|
}
|
2022-04-14 02:08:53 -07:00
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
if (this.nominations[1] !== "")
|
|
|
|
this.server.rcon.execute(`AdminSetNextLayer ${this.nominations[1]} `);
|
2022-03-19 16:11:24 -07:00
|
|
|
}
|
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
// checks if there are enough players to start voting, if not binds itself to player connected
|
|
|
|
// when there are enough players it clears old votes, sets up new nominations, and starts broadcast
|
2022-09-08 11:51:26 +02:00
|
|
|
beginVoting(force = false, steamid = null, cmdLayers = []) {
|
2022-11-07 13:03:26 +01:00
|
|
|
if (!this.options.automaticVoteStart && !force) return;
|
|
|
|
|
2022-08-25 15:43:38 +02:00
|
|
|
this.verbose(1, "Starting vote")
|
2022-03-19 16:11:24 -07:00
|
|
|
const playerCount = this.server.players.length;
|
|
|
|
const minPlayers = this.options.minPlayersForVote;
|
2022-04-02 16:04:23 -07:00
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
if (this.votingEnabled) // voting has already started
|
2022-04-02 16:04:23 -07:00
|
|
|
return;
|
|
|
|
|
2022-11-07 13:03:26 +01:00
|
|
|
|
2022-08-13 12:05:40 +02:00
|
|
|
if (playerCount < minPlayers && !force) {
|
2023-04-17 19:19:28 -04:00
|
|
|
this.autovotestart = setTimeout(() => {
|
|
|
|
this.beginVoting(force, steamid, cmdLayers)
|
|
|
|
}, 60 * 1000)
|
2022-03-19 16:11:24 -07:00
|
|
|
return;
|
|
|
|
}
|
2022-10-20 23:59:28 +02:00
|
|
|
|
2022-10-25 19:51:05 +02:00
|
|
|
if (this.options.votingDuration > 0) this.timeout_ps.push(setTimeout(this.endVotingGently, this.options.votingDuration * 60 * 1000))
|
2022-04-02 16:04:23 -07:00
|
|
|
|
2022-03-19 16:11:24 -07:00
|
|
|
// these need to be reset after reenabling voting
|
|
|
|
this.trackedVotes = {};
|
|
|
|
this.tallies = [];
|
2022-08-13 12:05:40 +02:00
|
|
|
|
|
|
|
this.populateNominations(steamid, cmdLayers);
|
|
|
|
|
2022-03-19 16:11:24 -07:00
|
|
|
this.votingEnabled = true;
|
2022-08-13 12:05:40 +02:00
|
|
|
this.firstBroadcast = true;
|
|
|
|
this.broadcastNominations();
|
2022-03-19 16:11:24 -07:00
|
|
|
this.broadcastIntervalTask = setInterval(this.broadcastNominations, toMils(this.options.voteBroadcastInterval));
|
|
|
|
}
|
2022-08-13 12:05:40 +02:00
|
|
|
|
2023-01-30 18:34:11 +01:00
|
|
|
async endVotingGently(steamID = null) {
|
2023-03-01 13:04:54 +01:00
|
|
|
if (!this.votingEnabled) return;
|
2023-03-01 13:18:42 +01:00
|
|
|
|
2023-03-01 00:36:38 +01:00
|
|
|
const winningLayerId = this.updateNextMap();
|
2023-03-18 01:22:12 +01:00
|
|
|
if (!winningLayerId) {
|
|
|
|
this.verbose(1, 'No winning layer available', winningLayerId)
|
|
|
|
return;
|
|
|
|
}
|
2023-04-17 19:19:28 -04:00
|
|
|
const winnerLayer = Layers.layers.find((l) => l.layerid === winningLayerId);
|
2023-01-15 22:53:35 +01:00
|
|
|
const fancyWinner = this.formatFancyLayer(winnerLayer);
|
2023-01-30 18:34:11 +01:00
|
|
|
|
2023-03-01 00:36:38 +01:00
|
|
|
// this.verbose(1, "Winning layer", winnerLayer, fancyWinner)
|
2023-01-30 18:34:11 +01:00
|
|
|
|
2023-03-01 00:36:38 +01:00
|
|
|
if (this.options.showWinnerBroadcastMessage) this.broadcast(this.options.voteWinnerBroadcastMessage + fancyWinner);
|
2023-01-30 18:34:11 +01:00
|
|
|
|
|
|
|
if (this.options.logToDiscord) {
|
|
|
|
await this.sendDiscordMessage({
|
|
|
|
embed: {
|
|
|
|
title: `Vote winner: ${fancyWinner}`,
|
|
|
|
color: 16761867,
|
|
|
|
fields: [
|
|
|
|
{
|
|
|
|
name: 'Map',
|
|
|
|
value: winnerLayer.map.name,
|
|
|
|
inline: true
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Gamemode',
|
|
|
|
value: winnerLayer.gamemode,
|
|
|
|
inline: true
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Version',
|
|
|
|
value: winnerLayer.version,
|
|
|
|
inline: true
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'LayerID',
|
|
|
|
value: winnerLayer.layerid,
|
|
|
|
inline: false
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Team 1',
|
2023-04-17 19:19:28 -04:00
|
|
|
value: winnerLayer.teams[0].faction,
|
2023-01-30 18:34:11 +01:00
|
|
|
inline: true
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Team 2',
|
2023-04-17 19:19:28 -04:00
|
|
|
value: winnerLayer.teams[1].faction,
|
2023-01-30 18:34:11 +01:00
|
|
|
inline: true
|
|
|
|
},
|
|
|
|
],
|
|
|
|
image: {
|
|
|
|
url: `https://squad-data.nyc3.cdn.digitaloceanspaces.com/main/${winnerLayer.layerid}.jpg`
|
2023-01-15 22:53:35 +01:00
|
|
|
},
|
|
|
|
},
|
2023-01-30 18:34:11 +01:00
|
|
|
timestamp: (new Date()).toISOString()
|
|
|
|
});
|
|
|
|
}
|
2023-03-18 01:48:06 +01:00
|
|
|
|
2023-03-18 01:22:12 +01:00
|
|
|
this.endVoting();
|
|
|
|
if (steamID) await this.warn(steamID, "Voting terminated!");
|
2023-01-30 18:34:11 +01:00
|
|
|
|
|
|
|
return true;
|
2022-10-25 19:51:05 +02:00
|
|
|
}
|
|
|
|
|
2022-08-13 12:05:40 +02:00
|
|
|
endVoting() {
|
2022-03-19 16:11:24 -07:00
|
|
|
this.votingEnabled = false;
|
2023-02-25 12:03:45 +01:00
|
|
|
this.broadcastIntervalTask = clearInterval(this.broadcastIntervalTask);
|
|
|
|
this.newVoteTimeout = clearTimeout(this.newVoteTimeout);
|
|
|
|
this.endVotingTimeout = clearTimeout(this.endVotingTimeout);
|
2022-03-19 16:11:24 -07:00
|
|
|
}
|
2023-04-17 19:19:28 -04:00
|
|
|
|
2022-09-06 13:13:24 +02:00
|
|
|
objArrToValArr(arr, ...key) {
|
2023-04-17 19:19:28 -04:00
|
|
|
const vet = [];
|
|
|
|
for (const o of arr) {
|
2022-09-06 13:13:24 +02:00
|
|
|
let obj = o;
|
2023-04-17 19:19:28 -04:00
|
|
|
for (const k of key) {
|
|
|
|
if (obj[k])
|
|
|
|
obj = obj[k];
|
2022-09-06 13:13:24 +02:00
|
|
|
}
|
|
|
|
vet.push(obj);
|
|
|
|
}
|
2022-08-25 21:43:06 +02:00
|
|
|
return vet;
|
|
|
|
}
|
2023-04-17 19:19:28 -04:00
|
|
|
|
|
|
|
// sends a message about nominations through a broadcast
|
|
|
|
// NOTE: max squad broadcast message length appears to be 485 characters
|
|
|
|
// Note: broadcast strings with multi lines are very strange
|
2022-08-13 12:05:40 +02:00
|
|
|
async broadcastNominations() {
|
2022-08-25 15:43:38 +02:00
|
|
|
if (this.nominations.length > 0 && this.votingEnabled) {
|
2022-09-16 22:41:55 +02:00
|
|
|
await this.broadcast(this.options.voteBroadcastMessage);
|
2023-04-17 19:19:28 -04:00
|
|
|
const allNominationStrings = []
|
2022-08-13 12:05:40 +02:00
|
|
|
let nominationStrings = [];
|
2023-01-21 00:49:06 +01:00
|
|
|
|
2022-09-16 00:41:20 +02:00
|
|
|
for (let choice = 1; choice < this.nominations.length; choice++) {
|
2022-08-13 12:05:40 +02:00
|
|
|
choice = Number(choice);
|
2023-04-17 19:19:28 -04:00
|
|
|
const vLayer = Layers.layers.find(e => e.layerid === this.nominations[choice]);
|
2023-02-28 23:54:37 +01:00
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
const formattedChoide = this.formatChoice(choice, this.formatFancyLayer(vLayer), this.tallies[choice], (this.options.hideVotesCount || this.firstBroadcast))
|
2023-01-31 00:57:56 +01:00
|
|
|
nominationStrings.push(formattedChoide);
|
|
|
|
allNominationStrings.push(formattedChoide);
|
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
if (nominationStrings.length === 3) {
|
2023-01-21 00:49:06 +01:00
|
|
|
await this.broadcast(nominationStrings.join("\n"));
|
|
|
|
nominationStrings = [];
|
|
|
|
}
|
2022-08-13 12:05:40 +02:00
|
|
|
}
|
2023-01-21 00:49:06 +01:00
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
if (this.nominations[0]) nominationStrings.push(this.formatChoice(0, this.nominations[0], this.tallies[0], (this.options.hideVotesCount || this.firstBroadcast)))
|
2022-09-02 12:52:42 +02:00
|
|
|
await this.broadcast(nominationStrings.join("\n"));
|
2022-09-17 01:10:05 +02:00
|
|
|
|
2022-09-15 14:17:43 -05:00
|
|
|
if (this.firstBroadcast)
|
2023-01-31 00:57:56 +01:00
|
|
|
await this.logVoteToDiscord(allNominationStrings.join("\n"))
|
2022-08-13 12:05:40 +02:00
|
|
|
this.firstBroadcast = false;
|
|
|
|
}
|
2023-04-17 19:19:28 -04:00
|
|
|
// const winners = this.currentWinners;
|
|
|
|
// await this.msgBroadcast(`Current winner${winners.length > 1 ? "s" : ""}: ${winners.join(", ")}`);
|
2022-03-19 16:11:24 -07:00
|
|
|
}
|
2023-04-17 19:19:28 -04:00
|
|
|
|
2022-10-21 00:48:57 +02:00
|
|
|
formatFancyLayer(layer) {
|
2023-05-22 20:44:31 -04:00
|
|
|
const factionString = this.getTranslation(layer.teams[0]) + "-" + this.getTranslation(layer.teams[1]);
|
2022-10-21 00:48:57 +02:00
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
const helis = layer.teams[0].numberOfHelicopters + layer.teams[1].numberOfHelicopters
|
|
|
|
const tanks = layer.teams[0].numberOfTanks + layer.teams[1].numberOfTanks
|
|
|
|
const assets = [];
|
2023-02-28 23:54:37 +01:00
|
|
|
if (helis > 0) assets.push('Helis');
|
|
|
|
if (tanks > 0) assets.push('Tanks');
|
2023-04-26 06:54:08 -04:00
|
|
|
const vehiclesString = assets.join('|');
|
2023-02-28 23:54:37 +01:00
|
|
|
|
|
|
|
return this.options.entryFormat
|
|
|
|
.replace(/\{map_name\}/i, layer.map.name)
|
|
|
|
.replace(/\{gamemode\}/i, layer.gamemode)
|
|
|
|
.replace(/\{map_version\}/i, layer.version)
|
|
|
|
.replace(/\{factions\}/i, factionString)
|
|
|
|
.replace(/\{main_assets\}/i, vehiclesString)
|
2023-10-18 18:12:55 -04:00
|
|
|
.replace(/\{name\}/i, layer.name)
|
2022-10-21 00:48:57 +02:00
|
|
|
}
|
2022-03-19 16:11:24 -07:00
|
|
|
|
2022-10-25 00:01:18 +02:00
|
|
|
getLayersFromStringId(stringid) {
|
2023-05-22 20:44:31 -04:00
|
|
|
// const cls = stringid.toLowerCase().split('_');
|
|
|
|
const modLayers = this.layerfilterCorrectLayers(Layers.layers);
|
2023-05-22 20:52:19 -04:00
|
|
|
const ret = modLayers.filter(l => l.layerid.toLowerCase().includes(stringid.replace(/\*/gi,'').toLowerCase()));
|
2023-05-22 20:44:31 -04:00
|
|
|
// const ret = modLayers.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, '')))));
|
2022-10-25 00:01:18 +02:00
|
|
|
// this.verbose(1,"layers from string",stringid,cls,ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-08-13 12:05:40 +02:00
|
|
|
async directMsgNominations(steamID) {
|
2022-08-25 15:43:38 +02:00
|
|
|
let strMsg = "";
|
2022-08-13 12:05:40 +02:00
|
|
|
for (let choice in this.nominations) {
|
|
|
|
choice = Number(choice);
|
2023-01-21 21:13:07 +01:00
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
const vLayer = Layers.layers.find(e => e.layerid === this.nominations[choice]);
|
2023-01-21 21:13:07 +01:00
|
|
|
// const allVecs = vLayer.teams[0].vehicles.concat(vLayer.teams[1].vehicles);
|
|
|
|
// const helis = vLayer?.teams[ 0 ].numberOfHelicopters || 0 + vLayer?.teams[ 1 ].numberOfHelicopters || 0
|
|
|
|
// const tanks = vLayer?.teams[ 0 ].numberOfTanks || 0 + vLayer?.teams[ 1 ].numberOfTanks || 0
|
|
|
|
// let assets = [];
|
|
|
|
// if (helis > 0) assets.push('Helis');
|
|
|
|
// if (tanks > 0) assets.push('Tanks');
|
|
|
|
// const vehiclesString = ' ' + assets.join('-');
|
2022-08-25 15:43:38 +02:00
|
|
|
// await this.msgDirect(steamID, formatChoice(choice, this.nominations[ choice ], this.tallies[ choice ]));
|
2023-04-17 19:19:28 -04:00
|
|
|
strMsg += (steamID, this.formatChoice(choice, this.nominations[choice], this.tallies[choice])) + "\n";
|
2022-08-13 12:05:40 +02:00
|
|
|
}
|
2022-08-25 15:43:38 +02:00
|
|
|
strMsg.trim();
|
2022-09-11 01:01:43 +02:00
|
|
|
if (steamID) this.warn(steamID, strMsg)
|
2022-08-13 12:05:40 +02:00
|
|
|
|
2022-08-25 15:43:38 +02:00
|
|
|
// const winners = this.currentWinners;
|
|
|
|
// await this.msgDirect(steamID, `Current winner${winners.length > 1 ? "s" : ""}: ${winners.join(", ")}`);
|
2022-03-19 16:11:24 -07:00
|
|
|
}
|
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
// counts a vote from a player and adds it to tallies
|
2022-08-13 12:05:40 +02:00
|
|
|
async registerVote(steamID, nominationIndex, playerName) {
|
2022-09-16 00:41:20 +02:00
|
|
|
// nominationIndex -= 1; // shift indices from display range
|
2022-08-13 12:05:40 +02:00
|
|
|
if (nominationIndex < 0 || nominationIndex > this.nominations.length) {
|
2022-09-02 12:52:42 +02:00
|
|
|
await this.warn(steamID, `[Map Vote] ${playerName}: invalid map number, typ !vote results to see map numbers`);
|
2022-03-19 16:11:24 -07:00
|
|
|
return;
|
|
|
|
}
|
2022-08-13 12:05:40 +02:00
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
const previousVote = this.trackedVotes[steamID];
|
|
|
|
this.trackedVotes[steamID] = nominationIndex;
|
2022-08-13 12:05:40 +02:00
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
this.tallies[nominationIndex] += 1;
|
2022-08-13 12:05:40 +02:00
|
|
|
if (previousVote !== undefined)
|
2023-04-17 19:19:28 -04:00
|
|
|
this.tallies[previousVote] -= 1;
|
|
|
|
await this.warn(steamID, `Registered vote: ${this.nominations[nominationIndex].replace(/\_/gi, ' ').replace(/\sv\d{1,2}/gi, '')} ${this.factionStrings[nominationIndex]} ` + (this.options.hideVotesCount ? `` : `(${this.tallies[nominationIndex]} votes)`));
|
2022-08-13 12:05:40 +02:00
|
|
|
// await this.msgDirect(steamID, `Registered vote`);// ${this.nominations[ nominationIndex ]} ${this.factionStrings[ nominationIndex ]} (${this.tallies[ nominationIndex ]} votes)`);
|
|
|
|
// await this.msgDirect(steamID, `${this.nominations[ nominationIndex ]} (${this.tallies[ nominationIndex ]} votes)`);
|
|
|
|
// await this.msgDirect(steamID, `${this.factionStrings[ nominationIndex ]}`);
|
|
|
|
// await this.msgDirect(steamID, `${this.tallies[ nominationIndex ]} votes`);
|
2022-03-19 16:11:24 -07:00
|
|
|
}
|
2022-08-13 12:05:40 +02:00
|
|
|
|
2022-09-17 01:33:21 +02:00
|
|
|
async logVoteToDiscord(message) {
|
|
|
|
if (!this.options.logToDiscord) return
|
2023-01-15 22:53:35 +01:00
|
|
|
return await this.sendDiscordMessage({
|
2022-09-17 01:33:21 +02:00
|
|
|
embed: {
|
|
|
|
title: 'Vote Started',
|
|
|
|
color: 16761867,
|
|
|
|
fields: [
|
|
|
|
{
|
|
|
|
name: 'Options:',
|
|
|
|
value: `${message}`
|
|
|
|
}
|
|
|
|
]
|
2022-09-17 01:39:55 +02:00
|
|
|
},
|
|
|
|
timestamp: (new Date()).toISOString()
|
2022-09-17 01:33:21 +02:00
|
|
|
});
|
|
|
|
}
|
2022-09-17 01:10:05 +02:00
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
// removes a players vote if they disconnect from the sever
|
2022-08-13 12:05:40 +02:00
|
|
|
clearVote() {
|
2022-03-19 16:11:24 -07:00
|
|
|
const currentPlayers = this.server.players.map((p) => p.steamID);
|
2022-08-13 12:05:40 +02:00
|
|
|
for (const steamID in this.trackedVotes) {
|
|
|
|
if (!(currentPlayers.includes(steamID))) {
|
2023-04-17 19:19:28 -04:00
|
|
|
const vote = this.trackedVotes[steamID];
|
|
|
|
this.tallies[vote] -= 1;
|
|
|
|
delete this.trackedVotes[steamID];
|
2022-03-19 16:11:24 -07:00
|
|
|
}
|
2022-08-13 12:05:40 +02:00
|
|
|
}
|
2022-03-19 16:11:24 -07:00
|
|
|
}
|
|
|
|
|
2022-11-21 15:15:11 +01:00
|
|
|
restorePersistentData() {
|
|
|
|
this.verbose(1, `Restoring persistent data from: ${this.options.persistentDataFile}`)
|
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
if (this.options.persistentDataFile === "") return;
|
2022-11-21 15:15:11 +01:00
|
|
|
|
|
|
|
if (!fs.existsSync(this.options.persistentDataFile)) return;
|
|
|
|
|
|
|
|
let bkData = fs.readFileSync(this.options.persistentDataFile);
|
2023-04-17 19:19:28 -04:00
|
|
|
if (bkData === "") return;
|
2022-11-21 15:15:11 +01:00
|
|
|
|
|
|
|
try {
|
|
|
|
bkData = JSON.parse(bkData)
|
|
|
|
} catch (e) {
|
|
|
|
this.verbose(1, "Error restoring persistent data", e)
|
|
|
|
return
|
|
|
|
}
|
2023-04-17 19:19:28 -04:00
|
|
|
if (bkData.manualRestartSender && bkData.manualRestartSender !== "") {
|
2023-01-25 22:20:49 +01:00
|
|
|
(async () => {
|
|
|
|
await this.warn(bkData.manualRestartSender, `SquadJS has completed the restart.\nPersistent data restored.`)
|
|
|
|
this.verbose(1, `Restart confirmation sent to SteamID: "${bkData.manualRestartSender}"`)
|
|
|
|
})()
|
|
|
|
}
|
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
for (const k in bkData.server) this.server[k] = bkData.server[k];
|
2022-11-21 15:15:11 +01:00
|
|
|
|
|
|
|
const maxSecondsDiffierence = 60
|
|
|
|
if ((new Date() - new Date(bkData.saveDateTime)) / 1000 > maxSecondsDiffierence) return
|
|
|
|
|
|
|
|
this.verbose(1, "Restoring data:", bkData)
|
|
|
|
|
|
|
|
// 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(', '))
|
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
for (const k in bkData.plugin) this[k] = bkData.plugin[k];
|
2022-11-21 15:15:11 +01:00
|
|
|
if (this.votingEnabled) {
|
|
|
|
this.broadcastIntervalTask = setInterval(this.broadcastNominations, toMils(this.options.voteBroadcastInterval));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-01-21 21:13:07 +01:00
|
|
|
savePersistentData(steamID = null) {
|
2023-04-17 19:19:28 -04:00
|
|
|
if (this.options.persistentDataFile === "") return;
|
2022-11-21 15:15:11 +01:00
|
|
|
|
|
|
|
const saveDt = {
|
|
|
|
custom: {
|
|
|
|
// layerHistory: this.server.layerHistory.slice(0, this.options.numberRecentMapsToExlude * 2).filter(l => l && l.layerid).map(l => l.layerid),
|
|
|
|
},
|
|
|
|
server: {
|
|
|
|
layerHistory: this.server.layerHistory
|
|
|
|
},
|
|
|
|
plugin: {
|
|
|
|
nominations: this.nominations,
|
|
|
|
trackedVotes: this.trackedVotes,
|
|
|
|
tallies: this.tallies,
|
|
|
|
votingEnabled: this.votingEnabled,
|
|
|
|
factionStrings: this.factionStrings,
|
|
|
|
firstBroadcast: this.firstBroadcast
|
|
|
|
},
|
2023-01-21 21:13:07 +01:00
|
|
|
manualRestartSender: steamID,
|
2022-11-21 15:15:11 +01:00
|
|
|
saveDateTime: new Date()
|
|
|
|
}
|
|
|
|
// this.verbose(1, `Saving persistent data to: ${this.options.persistentDataFile}\n`, saveDt.server.layerHistory)
|
|
|
|
|
|
|
|
fs.writeFileSync(this.options.persistentDataFile, JSON.stringify(saveDt, null, 2))
|
|
|
|
}
|
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
// calculates the current winner(s) of the vote and returns thier strings in an array
|
2022-08-13 12:05:40 +02:00
|
|
|
get currentWinners() {
|
2022-03-19 16:11:24 -07:00
|
|
|
const ties = [];
|
2022-08-13 12:05:40 +02:00
|
|
|
|
2022-03-19 16:11:24 -07:00
|
|
|
let highestScore = -Infinity;
|
2023-04-17 19:19:28 -04:00
|
|
|
const allScoreZero = !this.tallies.find(s => s > 0);
|
|
|
|
for (const choice in this.tallies) {
|
|
|
|
const score = this.tallies[choice];
|
2023-03-23 00:26:05 +01:00
|
|
|
if (score >= this.options.minimumVotesToAcceptResult || allScoreZero) {
|
2023-01-12 12:06:06 +02:00
|
|
|
if (score < highestScore)
|
2023-03-18 01:48:06 +01:00
|
|
|
continue;
|
2023-01-12 12:06:06 +02:00
|
|
|
else if (score > highestScore) {
|
|
|
|
highestScore = score;
|
|
|
|
ties.length = 0;
|
|
|
|
ties.push(choice);
|
2023-04-17 19:19:28 -04:00
|
|
|
} else // equal
|
2023-03-18 01:48:06 +01:00
|
|
|
ties.push(choice);
|
2022-03-19 16:11:24 -07:00
|
|
|
}
|
2023-04-17 19:19:28 -04:00
|
|
|
this.verbose(1, 'Ties', ties, ties.map(i => this.nominations[i]))
|
2022-03-19 16:11:24 -07:00
|
|
|
}
|
2022-08-13 12:05:40 +02:00
|
|
|
|
2023-04-17 19:19:28 -04:00
|
|
|
return ties.map(i => this.nominations[i]);
|
2022-03-19 16:11:24 -07:00
|
|
|
}
|
2022-11-18 00:22:21 +01:00
|
|
|
|
2023-01-31 00:57:56 +01:00
|
|
|
formatChoice(choiceIndex, mapString, currentVotes, hideVoteCount) {
|
|
|
|
return `${choiceIndex}➤ ${mapString} ` + (!hideVoteCount ? `(${currentVotes})` : "");
|
|
|
|
// return `${choiceIndex + 1}❱ ${mapString} (${currentVotes} votes)`
|
|
|
|
}
|
2022-09-17 01:10:05 +02:00
|
|
|
}
|
2022-09-15 14:17:43 -05:00
|
|
|
|
2022-09-17 01:10:05 +02:00
|
|
|
function randomElement(array) {
|
2023-04-17 19:19:28 -04:00
|
|
|
return array[Math.floor(Math.random() * array.length)];
|
2022-09-06 15:32:48 +02:00
|
|
|
}
|
2022-09-17 01:10:05 +02:00
|
|
|
|
|
|
|
function toMils(min) {
|
|
|
|
return min * 60 * 1000;
|
2022-11-21 15:15:11 +01:00
|
|
|
}
|