feat: data persistency

This commit is contained in:
Davide Fantino 2022-11-21 15:15:11 +01:00
parent e339b07647
commit b19310aa20
2 changed files with 101 additions and 6 deletions

View File

@ -183,6 +183,15 @@ The ID of the channel to log votes to.
```json
"112233445566778899"
```
#### persistentDataFile
###### Description
Path to file in which to store important data that should be restored after a restart.
###### Important
Make sure the persistent data file is not inside the plugin folder.
###### Default
```json
""
```
#### timezone
###### Description
Timezone relative to UTC time. 0 for UTC, 2 for CEST (UTC+2), -1 (UTC-1)
@ -235,6 +244,7 @@ Array of timeframes that allows to override options based on local time. See exa
"allowedSameMapEntries": 1,
"logToDiscord": true,
"channelID": "112233445566778899",
"persistentDataFile": "",
"timezone": 2,
"timeFrames": [
{

View File

@ -5,6 +5,8 @@ import DiscordBasePlugin from './discord-base-plugin.js';
import { Layers } from "../layers/index.js"
import axios from "axios"
import Layer from '../layers/layer.js';
import fs from 'fs'
import process from 'process'
export default class MapVote extends DiscordBasePlugin {
static get description() {
@ -134,6 +136,11 @@ export default class MapVote extends DiscordBasePlugin {
default: '',
example: '112233445566778899'
},
persistentDataFile: {
required: false,
description: 'Path to file in which to store important data that should be restored after a restart',
default: ""
},
timezone: {
required: false,
description: "Timezone relative to UTC time. 0 for UTC, 2 for CEST (UTC+2), -1 (UTC-1) ",
@ -159,7 +166,6 @@ export default class MapVote extends DiscordBasePlugin {
this.trackedVotes = {}; //player votes, keyed by steam id
this.tallies = []; //votes per layer, parellel with nominations
this.votingEnabled = false;
this.onConnectBound = false;
this.broadcastIntervalTask = null;
this.firstBroadcast = true;
this.newVoteTimeout = null;
@ -181,13 +187,21 @@ export default class MapVote extends DiscordBasePlugin {
this.setSeedingMode = this.setSeedingMode.bind(this);
this.logVoteToDiscord = this.logVoteToDiscord.bind(this);
this.timeframeOptionOverrider = this.timeframeOptionOverrider.bind(this);
this.savePersistentData = this.savePersistentData.bind(this)
this.restorePersistentData = this.restorePersistentData.bind(this)
this.broadcast = (msg) => { this.server.rcon.broadcast(msg); };
this.warn = (steamid, msg) => { this.server.rcon.warn(steamid, msg); };
process.on('uncaughtException', function (err) {
this.savePersistentData();
throw err;
});
}
async mount() {
await this.updateLayerList();
this.restorePersistentData();
this.server.on('NEW_GAME', this.onNewGame);
this.server.on('CHAT_MESSAGE', this.onChatMessage);
this.server.on('PLAYER_DISCONNECTED', this.onPlayerDisconnected);
@ -200,6 +214,7 @@ export default class MapVote extends DiscordBasePlugin {
// await this.checkUpdates();
this.timeframeOptionOverrider();
setInterval(this.timeframeOptionOverrider, 1 * 60 * 1000)
setInterval(this.savePersistentData, 20 * 1000)
}
async unmount() {
@ -399,6 +414,15 @@ export default class MapVote extends DiscordBasePlugin {
await this.warn(steamID, msg + `\nMapVote SquadJS plugin built by JetDave`);
return;
case "endsqjs":
case "closesqjs":
case "stopesqjs":
case "restartsqjs":
if (!isAdmin) return;
this.warn(steamID, "Saving persistent data.\nTerminating SquadJS process.\nIf managed by a process manager it will automatically restart.")
this.savePersistentData();
process.exit(0);
return;
default:
//give them an error
await this.warn(steamID, `Unknown vote subcommand: ${subCommand}`);
@ -495,10 +519,11 @@ export default class MapVote extends DiscordBasePlugin {
const sanitizedLayers = Layers.layers.filter((l) => l.layerid && l.map);
const maxOptions = this.options.showRerollOption ? 5 : 6;
if (!cmdLayers || cmdLayers.length == 0) {
const recentlyPlayedMaps = this.objArrToValArr(this.server.layerHistory.slice(0, this.options.numberRecentMapsToExlude), "layer", "map", "name");
this.verbose(1, "Recently played maps: " + recentlyPlayedMaps.filter((l) => l && l.map && l.map.name).map((l) => l.map.name).join(', '))
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(', '))
if (!cmdLayers || cmdLayers.length == 0) {
const all_layers = sanitizedLayers.filter((l) =>
this.options.gamemodeWhitelist.includes(l.gamemode.toUpperCase()) &&
![ this.server.currentLayer ? this.server.currentLayer.map.name : null, ...recentlyPlayedMaps ].includes(l.map.name) &&
@ -531,7 +556,7 @@ export default class MapVote extends DiscordBasePlugin {
return;
}
} else {
if (cmdLayers.length == 1) for (let i = 0; i < maxOptions; i++) cmdLayers.push(cmdLayers[ 0 ])
if (cmdLayers.length == 1) while (cmdLayers.length < maxOptions) cmdLayers.push(cmdLayers[ 0 ])
if (cmdLayers.length <= maxOptions) {
let i = 1;
@ -764,6 +789,66 @@ export default class MapVote extends DiscordBasePlugin {
}
}
restorePersistentData() {
this.verbose(1, `Restoring persistent data from: ${this.options.persistentDataFile}`)
if (this.options.persistentDataFile == "") return;
if (!fs.existsSync(this.options.persistentDataFile)) return;
let bkData = fs.readFileSync(this.options.persistentDataFile);
if (bkData == "") return;
try {
bkData = JSON.parse(bkData)
} catch (e) {
this.verbose(1, "Error restoring persistent data", e)
return
}
for (let k in bkData.server) this.server[ k ] = bkData.server[ k ];
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(', '))
for (let k in bkData.plugin) this[ k ] = bkData.plugin[ k ];
if (this.votingEnabled) {
this.broadcastIntervalTask = setInterval(this.broadcastNominations, toMils(this.options.voteBroadcastInterval));
}
}
savePersistentData() {
if (this.options.persistentDataFile == "") return;
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
},
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))
}
//calculates the current winner(s) of the vote and returns thier strings in an array
get currentWinners() {
const ties = [];