DBLog Plugin

This commit is contained in:
Thomas Smyth 2020-12-08 12:13:53 +00:00
parent e02a5417f6
commit fc629ebc77
22 changed files with 455 additions and 97 deletions

View File

@ -78,13 +78,6 @@ Connectors should be named, for example the above is named `discord`, and should
See below for more details on connectors and their associated config. See below for more details on connectors and their associated config.
##### Discord
Connects to Discord via `discord.js`.
```json
"discord": "Discord Login Token",
```
Requires a Discord bot login token.
##### Squad Layer Filter ##### Squad Layer Filter
Connects to a filtered list of Squad layers and filters them either by an "initial filter" or an "active filter" that depends on current server information, e.g. player count. Connects to a filtered list of Squad layers and filters them either by an "initial filter" or an "active filter" that depends on current server information, e.g. player count.
```js ```js
@ -153,19 +146,33 @@ Connects to a filtered list of Squad layers and filters them either by an "initi
- `factionHistoryTolerance` - A faction can only be played again after this number of layers. Factions can be specified individually inside the object. If they are not listed then the filter is not applied. - `factionHistoryTolerance` - A faction can only be played again after this number of layers. Factions can be specified individually inside the object. If they are not listed then the filter is not applied.
- `factionRepetitiveTolerance` - A faction can only be played this number of times in a row. Factions can be specified individually inside the object. If they are not listed then the filter is not applied. - `factionRepetitiveTolerance` - A faction can only be played this number of times in a row. Factions can be specified individually inside the object. If they are not listed then the filter is not applied.
##### MySQL ##### Discord
Connects to a MySQL database. Connects to Discord via `discord.js`.
```json ```json
"mysql": { "discord": "Discord Login Token",
"connectionLimit": 10, ```
"host": "host", Requires a Discord bot login token.
"port": 3306,
"user": "squadjs",
"password": "password", ##### Databases
"database": "squadjs" SquadJS uses [Sequelize](https://sequelize.org/) to connect and use a wide range of SQL databases.
The connector should be configured using any of Sequelize's single argument configuration options.
For example:
```json
"mysql": "mysql://user:pass@example.com:5432/dbname"
```
or:
```json
"sqlite": {
"dialect": "sqlite",
"storage": "path/to/database.sqlite"
} }
``` ```
The config is a set of pool connection options as listed in the [Node.js mysql](https://www.npmjs.com/package/mysql) documentation.
See [Sequelize's documentation](https://sequelize.org/master/manual/getting-started.html#connecting-to-a-database) for more details.
#### Plugins #### Plugins
The `plugins` section in your config file lists all plugins built into SquadJS, e.g.: The `plugins` section in your config file lists all plugins built into SquadJS, e.g.:
@ -220,7 +227,14 @@ The following is a list of plugins built into SquadJS, you can click their title
<details> <details>
<summary>DBLog</summary> <summary>DBLog</summary>
<h2>DBLog</h2> <h2>DBLog</h2>
<p>The <code>DBLog</code> plugin will log server information to a Sequlize compatible DB.</p> <p>The <code>mysql-log</code> plugin will log various server statistics and events to a database. This is great for server performance monitoring and/or player stat tracking.
Grafana (NOT YET WORKING WITH V2):
* [Grafana](https://grafana.com/) is a cool way of viewing server statistics stored in the database.
* Install Grafana.
* Add your database as a datasource named <code>SquadJS</code>.
* Import the [SquadJS Dashboard](https://github.com/Thomas-Smyth/SquadJS/blob/master/plugins/mysql-log/SquadJS-Dashboard.json) to get a preconfigured MySQL only Grafana dashboard.
* Install any missing Grafana plugins.</p>
<h3>Options</h3> <h3>Options</h3>
<h4>database (Required)</h4> <h4>database (Required)</h4>
<h6>Description</h6> <h6>Description</h6>

View File

@ -81,7 +81,7 @@
}, },
{ {
"plugin": "DBLog", "plugin": "DBLog",
"enabled": true, "enabled": false,
"database": "mysql", "database": "mysql",
"overrideServerID": null "overrideServerID": null
}, },

View File

@ -17,7 +17,7 @@ async function main() {
await server.watch(); await server.watch();
// now mount the plugins // now mount the plugins
server.plugins.forEach((plugin) => plugin.mount()); await Promise.all(server.plugins.map(async (plugin) => await plugin.mount()));
} }
main(); main();

View File

@ -189,7 +189,6 @@ export default class SquadServer extends EventEmitter {
this.logParser.on('PLAYER_DIED', async (data) => { this.logParser.on('PLAYER_DIED', async (data) => {
data.victim = await this.getPlayerByName(data.victimName); data.victim = await this.getPlayerByName(data.victimName);
data.attacker = await this.getPlayerByName(data.attackerName);
if (data.victim && data.attacker) if (data.victim && data.attacker)
data.teamkill = data.teamkill =

View File

@ -25,11 +25,11 @@ export default class AutoTKWarn extends BasePlugin {
this.onTeamkill = this.onTeamkill.bind(this); this.onTeamkill = this.onTeamkill.bind(this);
} }
mount() { async mount() {
this.server.on('TEAMKILL', this.onTeamkill); this.server.on('TEAMKILL', this.onTeamkill);
} }
unmount() { async unmount() {
this.server.removeEventListener('TEAMKILL', this.onTeamkill); this.server.removeEventListener('TEAMKILL', this.onTeamkill);
} }

View File

@ -26,9 +26,9 @@ export default class BasePlugin {
async prepareToMount() {} async prepareToMount() {}
mount() {} async mount() {}
unmount() {} async unmount() {}
static get description() { static get description() {
throw new Error('Plugin missing "static get description()" method.'); throw new Error('Plugin missing "static get description()" method.');

View File

@ -36,7 +36,7 @@ export default class ChatCommands extends BasePlugin {
}; };
} }
mount() { async mount() {
for (const command of this.options.commands) { for (const command of this.options.commands) {
this.server.on(`CHAT_COMMAND:${command.command}`, async (data) => { this.server.on(`CHAT_COMMAND:${command.command}`, async (data) => {
if (command.ignoreChats.includes(data.chat)) return; if (command.ignoreChats.includes(data.chat)) return;

View File

@ -6,7 +6,17 @@ const { DataTypes } = Sequelize;
export default class DBLog extends BasePlugin { export default class DBLog extends BasePlugin {
static get description() { static get description() {
return 'The <code>DBLog</code> plugin will log server information to a Sequlize compatible DB.'; return (
'The <code>mysql-log</code> plugin will log various server statistics and events to a database. This is great ' +
'for server performance monitoring and/or player stat tracking.' +
'\n\n' +
'Grafana (NOT YET WORKING WITH V2):\n' +
' * [Grafana](https://grafana.com/) is a cool way of viewing server statistics stored in the database.\n' +
' * Install Grafana.\n' +
' * Add your database as a datasource named <code>SquadJS</code>.\n' +
' * Import the [SquadJS Dashboard](https://github.com/Thomas-Smyth/SquadJS/blob/master/plugins/mysql-log/SquadJS-Dashboard.json) to get a preconfigured MySQL only Grafana dashboard.\n' +
' * Install any missing Grafana plugins.'
);
} }
static get defaultEnabled() { static get defaultEnabled() {
@ -29,7 +39,11 @@ export default class DBLog extends BasePlugin {
}; };
} }
async prepareToMount() { constructor(server, options, connectors) {
super(server, options, connectors);
this.models = {};
this.createModel('Server', { this.createModel('Server', {
id: { id: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
@ -91,6 +105,12 @@ export default class DBLog extends BasePlugin {
dlc: { dlc: {
type: DataTypes.STRING type: DataTypes.STRING
}, },
mapClassname: {
type: DataTypes.STRING
},
layerClassname: {
type: DataTypes.STRING
},
map: { map: {
type: DataTypes.STRING type: DataTypes.STRING
}, },
@ -109,6 +129,148 @@ export default class DBLog extends BasePlugin {
} }
}); });
this.createModel('SteamUser', {
steamID: {
type: DataTypes.STRING,
primaryKey: true
},
lastName: {
type: DataTypes.STRING
}
});
this.createModel('Wound', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
time: {
type: DataTypes.DATE,
notNull: true
},
victimName: {
type: DataTypes.STRING
},
victimTeamID: {
type: DataTypes.INTEGER
},
victimSquadID: {
type: DataTypes.INTEGER
},
attackerName: {
type: DataTypes.STRING
},
attackerTeamID: {
type: DataTypes.INTEGER
},
attackerSquadID: {
type: DataTypes.INTEGER
},
damage: {
type: DataTypes.FLOAT
},
weapon: {
type: DataTypes.STRING
},
teamkill: {
type: DataTypes.BOOLEAN
}
});
this.createModel('Death', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
time: {
type: DataTypes.DATE,
notNull: true
},
woundTime: {
type: DataTypes.DATE
},
victimName: {
type: DataTypes.STRING
},
victimTeamID: {
type: DataTypes.INTEGER
},
victimSquadID: {
type: DataTypes.INTEGER
},
attackerName: {
type: DataTypes.STRING
},
attackerTeamID: {
type: DataTypes.INTEGER
},
attackerSquadID: {
type: DataTypes.INTEGER
},
damage: {
type: DataTypes.FLOAT
},
weapon: {
type: DataTypes.STRING
},
teamkill: {
type: DataTypes.BOOLEAN
}
});
this.createModel('Revive', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
time: {
type: DataTypes.DATE,
notNull: true
},
woundTime: {
type: DataTypes.DATE
},
victimName: {
type: DataTypes.STRING
},
victimTeamID: {
type: DataTypes.INTEGER
},
victimSquadID: {
type: DataTypes.INTEGER
},
attackerName: {
type: DataTypes.STRING
},
attackerTeamID: {
type: DataTypes.INTEGER
},
attackerSquadID: {
type: DataTypes.INTEGER
},
damage: {
type: DataTypes.FLOAT
},
weapon: {
type: DataTypes.STRING
},
teamkill: {
type: DataTypes.BOOLEAN
},
reviverName: {
type: DataTypes.BOOLEAN
},
reviverTeamID: {
type: DataTypes.INTEGER
},
reviverSquadID: {
type: DataTypes.INTEGER
}
});
this.models.Server.hasMany(this.models.TickRate, { this.models.Server.hasMany(this.models.TickRate, {
foreignKey: { name: 'server', allowNull: false }, foreignKey: { name: 'server', allowNull: false },
onDelete: 'CASCADE' onDelete: 'CASCADE'
@ -124,18 +286,77 @@ export default class DBLog extends BasePlugin {
onDelete: 'CASCADE' onDelete: 'CASCADE'
}); });
await this.models.Server.sync(); this.models.Server.hasMany(this.models.Wound, {
await this.models.TickRate.sync(); foreignKey: { name: 'server', allowNull: false },
await this.models.PlayerCount.sync(); onDelete: 'CASCADE'
await this.models.Match.sync(); });
let server = await this.models.Server.findOne({ id: this.server.id }); this.models.Server.hasMany(this.models.Death, {
if (server === null) { foreignKey: { name: 'server', allowNull: false },
server = await this.models.Server.create({ id: this.server.id }); onDelete: 'CASCADE'
} });
server.name = this.server.serverName; this.models.Server.hasMany(this.models.Revive, {
// await server.save(); foreignKey: { name: 'server', allowNull: false },
onDelete: 'CASCADE'
});
this.models.SteamUser.hasMany(this.models.Wound, {
foreignKey: { name: 'attacker' },
onDelete: 'CASCADE'
});
this.models.SteamUser.hasMany(this.models.Wound, {
foreignKey: { name: 'victim' },
onDelete: 'CASCADE'
});
this.models.SteamUser.hasMany(this.models.Death, {
foreignKey: { name: 'attacker' },
onDelete: 'CASCADE'
});
this.models.SteamUser.hasMany(this.models.Death, {
foreignKey: { name: 'victim' },
onDelete: 'CASCADE'
});
this.models.SteamUser.hasMany(this.models.Revive, {
foreignKey: { name: 'attacker' },
onDelete: 'CASCADE'
});
this.models.SteamUser.hasMany(this.models.Revive, {
foreignKey: { name: 'victim' },
onDelete: 'CASCADE'
});
this.models.SteamUser.hasMany(this.models.Revive, {
foreignKey: { name: 'reviver' },
onDelete: 'CASCADE'
});
this.models.Match.hasMany(this.models.Wound, {
foreignKey: { name: 'match' },
onDelete: 'CASCADE'
});
this.models.Match.hasMany(this.models.Death, {
foreignKey: { name: 'match' },
onDelete: 'CASCADE'
});
this.models.Match.hasMany(this.models.Revive, {
foreignKey: { name: 'match' },
onDelete: 'CASCADE'
});
this.onTickRate = this.onTickRate.bind(this);
this.onUpdatedA2SInformation = this.onUpdatedA2SInformation.bind(this);
this.onNewGame = this.onNewGame.bind(this);
this.onPlayerWounded = this.onPlayerWounded.bind(this);
this.onPlayerDied = this.onPlayerDied.bind(this);
this.onPlayerRevived = this.onPlayerRevived.bind(this);
} }
createModel(name, schema) { createModel(name, schema) {
@ -144,31 +365,47 @@ export default class DBLog extends BasePlugin {
}); });
} }
constructor(server, options, connectors) { async prepareToMount() {
super(server, options, connectors); await this.models.Server.sync();
await this.models.TickRate.sync();
this.models = {}; await this.models.PlayerCount.sync();
await this.models.Match.sync();
this.onTickRate = this.onTickRate.bind(this); await this.models.SteamUser.sync();
this.onUpdatedA2SInformation = this.onUpdatedA2SInformation.bind(this); await this.models.Wound.sync();
this.onNewGame = this.onNewGame.bind(this); await this.models.Death.sync();
await this.models.Revive.sync();
} }
mount() { async mount() {
await this.models.Server.upsert({
id: this.options.overrideServerID || this.server.id,
name: this.server.serverName
});
this.match = await this.models.Match.findOne({
where: { server: this.options.overrideServerID || this.server.id, endTime: null }
});
this.server.on('TICK_RATE', this.onTickRate); this.server.on('TICK_RATE', this.onTickRate);
this.server.on('UPDATED_A2S_INFORMATION', this.onUpdatedA2SInformation); this.server.on('UPDATED_A2S_INFORMATION', this.onUpdatedA2SInformation);
this.server.on('NEW_GAME', this.onNewGame); this.server.on('NEW_GAME', this.onNewGame);
this.server.on('PLAYER_WOUNDED', this.onPlayerWounded);
this.server.on('PLAYER_DIED', this.onPlayerDied);
this.server.on('PLAYER_REVIVED', this.onPlayerRevived);
} }
unmount() { async unmount() {
this.server.removeEventListener('TICK_RATE', this.onTickRate); this.server.removeEventListener('TICK_RATE', this.onTickRate);
this.server.removeEventListener('UPDATED_A2S_INFORMATION', this.onTickRate); this.server.removeEventListener('UPDATED_A2S_INFORMATION', this.onTickRate);
this.server.removeEventListener('NEW_GAME', this.onNewGame); this.server.removeEventListener('NEW_GAME', this.onNewGame);
this.server.removeEventListener('PLAYER_WOUNDED', this.onPlayerWounded);
this.server.removeEventListener('PLAYER_DIED', this.onPlayerDied);
this.server.removeEventListener('PLAYER_REVIVED', this.onPlayerRevived);
} }
async onTickRate(info) { async onTickRate(info) {
await this.models.TickRate.create({ await this.models.TickRate.create({
server: this.server.id, server: this.options.overrideServerID || this.server.id,
time: info.time, time: info.time,
tickRate: info.tickRate tickRate: info.tickRate
}); });
@ -176,7 +413,7 @@ export default class DBLog extends BasePlugin {
async onUpdatedA2SInformation() { async onUpdatedA2SInformation() {
await this.models.PlayerCount.create({ await this.models.PlayerCount.create({
server: this.server.id, server: this.options.overrideServerID || this.server.id,
players: this.server.a2sPlayerCount, players: this.server.a2sPlayerCount,
publicQueue: this.server.publicQueue, publicQueue: this.server.publicQueue,
reserveQueue: this.server.reserveQueue reserveQueue: this.server.reserveQueue
@ -184,19 +421,120 @@ export default class DBLog extends BasePlugin {
} }
async onNewGame(info) { async onNewGame(info) {
console.log(info);
await this.models.Match.update( await this.models.Match.update(
{ endTime: info.time, winner: info.winner }, { endTime: info.time, winner: info.winner },
{ where: { server: this.server.id, endTime: null } } { where: { server: this.options.overrideServerID || this.server.id, endTime: null } }
); );
await this.models.Match.create({ this.match = await this.models.Match.create({
server: this.server.id, server: this.options.overrideServerID || this.server.id,
dlc: info.dlc, dlc: info.dlc,
mapClassname: info.mapClassname,
layerClassname: info.layerClassname,
map: info.layer ? info.layer.map : null, map: info.layer ? info.layer.map : null,
layer: info.layer ? info.layer.layer : null, layer: info.layer ? info.layer.layer : null,
startTime: info.time startTime: info.time
}); });
} }
async onPlayerWounded(info) {
if (info.attacker)
await this.models.SteamUser.upsert({
steamID: info.attacker.steamID,
lastName: info.attacker.name
});
if (info.victim)
await this.models.SteamUser.upsert({
steamID: info.victim.steamID,
lastName: info.victim.name
});
await this.models.Wound.create({
server: this.options.overrideServerID || this.server.id,
match: this.match ? this.match.id : null,
time: info.time,
victim: info.victim ? info.victim.steamID : null,
victimName: info.victim ? info.victim.name : null,
victimTeamID: info.victim ? info.victim.teamID : null,
victimSquadID: info.victim ? info.victim.squadID : null,
attacker: info.attacker ? info.attacker.steamID : null,
attackerName: info.attacker ? info.attacker.name : null,
attackerTeamID: info.attacker ? info.attacker.teamID : null,
attackerSquadID: info.attacker ? info.attacker.squadID : null,
damage: info.damage,
weapon: info.weapon,
teamkill: info.teamkill
});
}
async onPlayerDied(info) {
if (info.attacker)
await this.models.SteamUser.upsert({
steamID: info.attacker.steamID,
lastName: info.attacker.name
});
if (info.victim)
await this.models.SteamUser.upsert({
steamID: info.victim.steamID,
lastName: info.victim.name
});
await this.models.Death.create({
server: this.options.overrideServerID || this.server.id,
match: this.match ? this.match.id : null,
time: info.time,
woundTime: info.woundTime,
victim: info.victim ? info.victim.steamID : null,
victimName: info.victim ? info.victim.name : null,
victimTeamID: info.victim ? info.victim.teamID : null,
victimSquadID: info.victim ? info.victim.squadID : null,
attacker: info.attacker ? info.attacker.steamID : null,
attackerName: info.attacker ? info.attacker.name : null,
attackerTeamID: info.attacker ? info.attacker.teamID : null,
attackerSquadID: info.attacker ? info.attacker.squadID : null,
damage: info.damage,
weapon: info.weapon,
teamkill: info.teamkill
});
}
async onPlayerRevived(info) {
if (info.attacker)
await this.models.SteamUser.upsert({
steamID: info.attacker.steamID,
lastName: info.attacker.name
});
if (info.victim)
await this.models.SteamUser.upsert({
steamID: info.victim.steamID,
lastName: info.victim.name
});
if (info.reviver)
await this.models.SteamUser.upsert({
steamID: info.reviver.steamID,
lastName: info.reviver.name
});
await this.models.Revive.create({
server: this.options.overrideServerID || this.server.id,
match: this.match ? this.match.id : null,
time: info.time,
woundTime: info.woundTime,
victim: info.victim ? info.victim.steamID : null,
victimName: info.victim ? info.victim.name : null,
victimTeamID: info.victim ? info.victim.teamID : null,
victimSquadID: info.victim ? info.victim.squadID : null,
attacker: info.attacker ? info.attacker.steamID : null,
attackerName: info.attacker ? info.attacker.name : null,
attackerTeamID: info.attacker ? info.attacker.teamID : null,
attackerSquadID: info.attacker ? info.attacker.squadID : null,
damage: info.damage,
weapon: info.weapon,
teamkill: info.teamkill,
reviver: info.reviver ? info.reviver.steamID : null,
reviverName: info.reviver ? info.reviver.name : null,
reviverTeamID: info.reviver ? info.reviver.teamID : null,
reviverSquadID: info.reviver ? info.reviver.squadID : null
});
}
} }

View File

@ -35,11 +35,11 @@ export default class DiscordAdminBroadcast extends DiscordBasePlugin {
this.onAdminBroadcast = this.onAdminBroadcast.bind(this); this.onAdminBroadcast = this.onAdminBroadcast.bind(this);
} }
mount() { async mount() {
this.server.on('ADMIN_BROADCAST', this.onAdminBroadcast); this.server.on('ADMIN_BROADCAST', this.onAdminBroadcast);
} }
unmount() { async unmount() {
this.server.removeEventListener('ADMIN_BROADCAST', this.onAdminBroadcast); this.server.removeEventListener('ADMIN_BROADCAST', this.onAdminBroadcast);
} }

View File

@ -35,12 +35,12 @@ export default class DiscordAdminCamLogs extends DiscordBasePlugin {
this.onPlayerUnPossess = this.onPlayerUnPossess.bind(this); this.onPlayerUnPossess = this.onPlayerUnPossess.bind(this);
} }
mount() { async mount() {
this.server.on('PLAYER_POSSESS', this.onPlayerPossess); this.server.on('PLAYER_POSSESS', this.onPlayerPossess);
this.server.on('PLAYER_UNPOSSESS', this.onPlayerUnPossess); this.server.on('PLAYER_UNPOSSESS', this.onPlayerUnPossess);
} }
unmount() { async unmount() {
this.server.removeEventListener('PLAYER_POSSESS', this.onPlayerPossess); this.server.removeEventListener('PLAYER_POSSESS', this.onPlayerPossess);
this.server.removeEventListener('PLAYER_UNPOSSESS', this.onPlayerUnPossess); this.server.removeEventListener('PLAYER_UNPOSSESS', this.onPlayerUnPossess);
} }

View File

@ -65,11 +65,11 @@ export default class DiscordAdminRequest extends DiscordBasePlugin {
this.onChatCommand = this.onChatCommand.bind(this); this.onChatCommand = this.onChatCommand.bind(this);
} }
mount() { async mount() {
this.server.on(`CHAT_COMMAND:${this.options.command}`, this.onChatCommand); this.server.on(`CHAT_COMMAND:${this.options.command}`, this.onChatCommand);
} }
unmount() { async unmount() {
this.server.removeEventListener(`CHAT_COMMAND:${this.options.command}`, this.onChatCommand); this.server.removeEventListener(`CHAT_COMMAND:${this.options.command}`, this.onChatCommand);
} }

View File

@ -43,11 +43,11 @@ export default class DiscordChat extends DiscordBasePlugin {
this.onChatMessage = this.onChatMessage.bind(this); this.onChatMessage = this.onChatMessage.bind(this);
} }
mount() { async mount() {
this.server.on('CHAT_MESSAGE', this.onChatMessage); this.server.on('CHAT_MESSAGE', this.onChatMessage);
} }
unmount() { async unmount() {
this.server.removeEventListener('CHAT_MESSAGE', this.onChatMessage); this.server.removeEventListener('CHAT_MESSAGE', this.onChatMessage);
} }

View File

@ -30,7 +30,7 @@ export default class DiscordDebug extends DiscordBasePlugin {
}; };
} }
mount() { async mount() {
for (const event of this.options.events) { for (const event of this.options.events) {
this.server.on(event, async (info) => { this.server.on(event, async (info) => {
await this.sendDiscordMessage(`\`\`\`${JSON.stringify({ ...info, event }, null, 2)}\`\`\``); await this.sendDiscordMessage(`\`\`\`${JSON.stringify({ ...info, event }, null, 2)}\`\`\``);

View File

@ -53,11 +53,11 @@ export default class DiscordRcon extends BasePlugin {
this.onMessage = this.onMessage.bind(this); this.onMessage = this.onMessage.bind(this);
} }
mount() { async mount() {
this.options.discordClient.on('message', this.onMessage); this.options.discordClient.on('message', this.onMessage);
} }
unmount() { async unmount() {
this.options.discordClient.removeEventListener('message', this.onMessage); this.options.discordClient.removeEventListener('message', this.onMessage);
} }

View File

@ -32,11 +32,11 @@ export default class DiscordRoundWinner extends DiscordBasePlugin {
this.onNewGame = this.onNewGame.bind(this); this.onNewGame = this.onNewGame.bind(this);
} }
mount() { async mount() {
this.server.on('NEW_GAME', this.onNewGame); this.server.on('NEW_GAME', this.onNewGame);
} }
unmount() { async unmount() {
this.server.removeEventListener('NEW_GAME', this.onNewGame); this.server.removeEventListener('NEW_GAME', this.onNewGame);
} }

View File

@ -47,11 +47,11 @@ export default class DiscordServerStatus extends BasePlugin {
this.update = this.update.bind(this); this.update = this.update.bind(this);
} }
mount() { async mount() {
this.interval = setInterval(this.update, this.options.updateInterval); this.interval = setInterval(this.update, this.options.updateInterval);
} }
unmount() { async unmount() {
clearInterval(this.interval); clearInterval(this.interval);
} }

View File

@ -39,11 +39,11 @@ export default class DiscordSubsystemRestarter extends BasePlugin {
this.onMessage = this.onMessage.bind(this); this.onMessage = this.onMessage.bind(this);
} }
mount() { async mount() {
this.options.discordClient.on('message', this.onMessage); this.options.discordClient.on('message', this.onMessage);
} }
unmount() { async unmount() {
this.options.discordClient.removeEventListener('message', this.onMessage); this.options.discordClient.removeEventListener('message', this.onMessage);
} }

View File

@ -40,11 +40,11 @@ export default class DiscordTeamkill extends DiscordBasePlugin {
this.onTeamkill = this.onTeamkill.bind(this); this.onTeamkill = this.onTeamkill.bind(this);
} }
mount() { async mount() {
this.server.on('TEAMKILL', this.onTeamkill); this.server.on('TEAMKILL', this.onTeamkill);
} }
unmount() { async unmount() {
this.server.removeEventListener('TEAMKILL', this.onTeamkill); this.server.removeEventListener('TEAMKILL', this.onTeamkill);
} }

View File

@ -34,11 +34,11 @@ export default class IntervalledBroadcasts extends BasePlugin {
this.broadcast = this.broadcast.bind(this); this.broadcast = this.broadcast.bind(this);
} }
mount() { async mount() {
this.interval = setInterval(this.broadcast, this.options.interval); this.interval = setInterval(this.broadcast, this.options.interval);
} }
unmount() { async unmount() {
clearInterval(this.interval); clearInterval(this.interval);
} }

View File

@ -54,11 +54,11 @@ export default class SeedingMode extends BasePlugin {
this.broadcast = this.broadcast.bind(this); this.broadcast = this.broadcast.bind(this);
} }
mount() { async mount() {
this.interval = setInterval(this.broadcast, this.options.interval); this.interval = setInterval(this.broadcast, this.options.interval);
} }
unmount() { async unmount() {
clearInterval(this.interval); clearInterval(this.interval);
} }

View File

@ -28,11 +28,11 @@ export default class TeamRandomizer extends BasePlugin {
this.onChatCommand = this.onChatCommand.bind(this); this.onChatCommand = this.onChatCommand.bind(this);
} }
mount() { async mount() {
this.server.on(`CHAT_COMMAND:${this.options.command}`, this.onChatCommand); this.server.on(`CHAT_COMMAND:${this.options.command}`, this.onChatCommand);
} }
unmount() { async unmount() {
this.server.removeEventListener(`CHAT_COMMAND:${this.options.command}`, this.onChatCommand); this.server.removeEventListener(`CHAT_COMMAND:${this.options.command}`, this.onChatCommand);
} }

View File

@ -78,13 +78,6 @@ Connectors should be named, for example the above is named `discord`, and should
See below for more details on connectors and their associated config. See below for more details on connectors and their associated config.
##### Discord
Connects to Discord via `discord.js`.
```json
"discord": "Discord Login Token",
```
Requires a Discord bot login token.
##### Squad Layer Filter ##### Squad Layer Filter
Connects to a filtered list of Squad layers and filters them either by an "initial filter" or an "active filter" that depends on current server information, e.g. player count. Connects to a filtered list of Squad layers and filters them either by an "initial filter" or an "active filter" that depends on current server information, e.g. player count.
```js ```js
@ -153,19 +146,33 @@ Connects to a filtered list of Squad layers and filters them either by an "initi
- `factionHistoryTolerance` - A faction can only be played again after this number of layers. Factions can be specified individually inside the object. If they are not listed then the filter is not applied. - `factionHistoryTolerance` - A faction can only be played again after this number of layers. Factions can be specified individually inside the object. If they are not listed then the filter is not applied.
- `factionRepetitiveTolerance` - A faction can only be played this number of times in a row. Factions can be specified individually inside the object. If they are not listed then the filter is not applied. - `factionRepetitiveTolerance` - A faction can only be played this number of times in a row. Factions can be specified individually inside the object. If they are not listed then the filter is not applied.
##### MySQL ##### Discord
Connects to a MySQL database. Connects to Discord via `discord.js`.
```json ```json
"mysql": { "discord": "Discord Login Token",
"connectionLimit": 10, ```
"host": "host", Requires a Discord bot login token.
"port": 3306,
"user": "squadjs",
"password": "password", ##### Databases
"database": "squadjs" SquadJS uses [Sequelize](https://sequelize.org/) to connect and use a wide range of SQL databases.
The connector should be configured using any of Sequelize's single argument configuration options.
For example:
```json
"mysql": "mysql://user:pass@example.com:5432/dbname"
```
or:
```json
"sqlite": {
"dialect": "sqlite",
"storage": "path/to/database.sqlite"
} }
``` ```
The config is a set of pool connection options as listed in the [Node.js mysql](https://www.npmjs.com/package/mysql) documentation.
See [Sequelize's documentation](https://sequelize.org/master/manual/getting-started.html#connecting-to-a-database) for more details.
#### Plugins #### Plugins
The `plugins` section in your config file lists all plugins built into SquadJS, e.g.: The `plugins` section in your config file lists all plugins built into SquadJS, e.g.: