mirror of
https://github.com/AsgardEternal/SquadJS.git
synced 2024-09-28 11:54:23 -05:00
DBLog Plugin
This commit is contained in:
parent
e02a5417f6
commit
fc629ebc77
50
README.md
50
README.md
@ -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>
|
||||||
|
@ -81,7 +81,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"plugin": "DBLog",
|
"plugin": "DBLog",
|
||||||
"enabled": true,
|
"enabled": false,
|
||||||
"database": "mysql",
|
"database": "mysql",
|
||||||
"overrideServerID": null
|
"overrideServerID": null
|
||||||
},
|
},
|
||||||
|
2
index.js
2
index.js
@ -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();
|
||||||
|
@ -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 =
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.');
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)}\`\`\``);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.:
|
||||||
|
Loading…
Reference in New Issue
Block a user