2020-12-06 15:21:41 -06:00
import Sequelize from 'sequelize' ;
import BasePlugin from './base-plugin.js' ;
const { DataTypes } = Sequelize ;
export default class DBLog extends BasePlugin {
static get description ( ) {
2020-12-08 06:13:53 -06:00
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' +
2021-03-12 13:11:48 -06:00
'Grafana:\n' +
2020-12-16 14:24:35 -06:00
'<ul><li> <a href="https://grafana.com/">Grafana</a> is a cool way of viewing server statistics stored in the database.</li>\n' +
'<li>Install Grafana.</li>\n' +
'<li>Add your database as a datasource named <code>SquadJS</code>.</li>\n' +
2022-04-15 07:15:21 -05:00
'<li>Import the <a href="https://github.com/Team-Silver-Sphere/SquadJS/blob/master/squad-server/templates/SquadJS-Dashboard-v2.json">SquadJS Dashboard</a> to get a preconfigured MySQL only Grafana dashboard.</li>\n' +
2020-12-16 14:24:35 -06:00
'<li>Install any missing Grafana plugins.</li></ul>'
2020-12-08 06:13:53 -06:00
) ;
2020-12-06 15:21:41 -06:00
}
static get defaultEnabled ( ) {
return false ;
}
static get optionsSpecification ( ) {
return {
database : {
required : true ,
connector : 'sequelize' ,
description : 'The Sequelize connector to log server information to.' ,
default : 'mysql'
} ,
overrideServerID : {
required : false ,
description : 'A overridden server ID.' ,
default : null
}
} ;
}
2020-12-08 06:13:53 -06:00
constructor ( server , options , connectors ) {
super ( server , options , connectors ) ;
this . models = { } ;
2020-12-06 15:23:05 -06:00
this . createModel ( 'Server' , {
id : {
type : DataTypes . INTEGER ,
primaryKey : true ,
autoIncrement : true
} ,
name : {
type : DataTypes . STRING
2020-12-06 15:21:41 -06:00
}
2020-12-06 15:23:05 -06:00
} ) ;
2020-12-08 11:58:31 -06:00
this . createModel ( 'Match' , {
id : {
type : DataTypes . INTEGER ,
primaryKey : true ,
autoIncrement : true
} ,
dlc : {
type : DataTypes . STRING
} ,
mapClassname : {
type : DataTypes . STRING
} ,
layerClassname : {
type : DataTypes . STRING
} ,
map : {
type : DataTypes . STRING
} ,
layer : {
type : DataTypes . STRING
} ,
startTime : {
type : DataTypes . DATE ,
notNull : true
} ,
endTime : {
type : DataTypes . DATE
} ,
winner : {
type : DataTypes . STRING
}
} ) ;
2020-12-06 15:23:05 -06:00
this . createModel ( 'TickRate' , {
id : {
type : DataTypes . INTEGER ,
primaryKey : true ,
autoIncrement : true
} ,
time : {
type : DataTypes . DATE ,
notNull : true
} ,
tickRate : {
type : DataTypes . FLOAT ,
notNull : true
2020-12-06 15:21:41 -06:00
}
2020-12-06 15:23:05 -06:00
} ) ;
2020-12-06 15:21:41 -06:00
2020-12-06 15:45:06 -06:00
this . createModel ( 'PlayerCount' , {
id : {
type : DataTypes . INTEGER ,
primaryKey : true ,
autoIncrement : true
} ,
time : {
type : DataTypes . DATE ,
notNull : true ,
defaultValue : DataTypes . NOW
} ,
players : {
type : DataTypes . INTEGER ,
notNull : true
} ,
publicQueue : {
type : DataTypes . INTEGER ,
notNull : true
} ,
reserveQueue : {
type : DataTypes . INTEGER ,
notNull : true
}
} ) ;
2021-03-05 11:32:31 -06:00
this . createModel (
'SteamUser' ,
{
steamID : {
type : DataTypes . STRING ,
primaryKey : true
} ,
lastName : {
type : DataTypes . STRING
}
} ,
{
charset : 'utf8mb4' ,
collate : 'utf8mb4_unicode_ci'
2020-12-08 06:13:53 -06:00
}
2021-03-05 11:32:31 -06:00
) ;
2020-12-08 06:13:53 -06:00
2021-03-05 11:32:31 -06:00
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
}
} ,
{
charset : 'utf8mb4' ,
collate : 'utf8mb4_unicode_ci'
2020-12-08 06:13:53 -06:00
}
2021-03-05 11:32:31 -06:00
) ;
2020-12-08 06:13:53 -06:00
2021-03-05 11:32:31 -06:00
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
}
} ,
{
charset : 'utf8mb4' ,
collate : 'utf8mb4_unicode_ci'
2020-12-08 06:13:53 -06:00
}
2021-03-05 11:32:31 -06:00
) ;
2020-12-08 06:13:53 -06:00
2021-03-05 11:32:31 -06:00
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 . STRING
} ,
reviverTeamID : {
type : DataTypes . INTEGER
} ,
reviverSquadID : {
type : DataTypes . INTEGER
}
} ,
{
charset : 'utf8mb4' ,
collate : 'utf8mb4_unicode_ci'
2020-12-08 06:13:53 -06:00
}
2021-03-05 11:32:31 -06:00
) ;
2020-12-08 06:13:53 -06:00
2020-12-06 15:23:05 -06:00
this . models . Server . hasMany ( this . models . TickRate , {
foreignKey : { name : 'server' , allowNull : false } ,
onDelete : 'CASCADE'
} ) ;
2020-12-06 15:21:41 -06:00
2020-12-06 15:45:06 -06:00
this . models . Server . hasMany ( this . models . PlayerCount , {
foreignKey : { name : 'server' , allowNull : false } ,
onDelete : 'CASCADE'
} ) ;
2020-12-06 17:09:33 -06:00
this . models . Server . hasMany ( this . models . Match , {
foreignKey : { name : 'server' , allowNull : false } ,
onDelete : 'CASCADE'
} ) ;
2020-12-08 06:13:53 -06:00
this . models . Server . hasMany ( this . models . Wound , {
foreignKey : { name : 'server' , allowNull : false } ,
onDelete : 'CASCADE'
} ) ;
2020-12-06 15:21:41 -06:00
2020-12-08 06:13:53 -06:00
this . models . Server . hasMany ( this . models . Death , {
foreignKey : { name : 'server' , allowNull : false } ,
onDelete : 'CASCADE'
} ) ;
2020-12-06 17:09:33 -06:00
2020-12-08 06:13:53 -06:00
this . models . Server . hasMany ( this . models . Revive , {
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'
} ) ;
2020-12-08 11:58:31 -06:00
this . models . Match . hasMany ( this . models . TickRate , {
foreignKey : { name : 'match' } ,
onDelete : 'CASCADE'
} ) ;
this . models . Match . hasMany ( this . models . PlayerCount , {
foreignKey : { name : 'match' } ,
onDelete : 'CASCADE'
} ) ;
2020-12-08 06:13:53 -06:00
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 ) ;
2020-12-06 15:21:41 -06:00
}
createModel ( name , schema ) {
2020-12-06 15:45:06 -06:00
this . models [ name ] = this . options . database . define ( ` DBLog_ ${ name } ` , schema , {
timestamps : false
} ) ;
2020-12-06 15:21:41 -06:00
}
2020-12-08 06:13:53 -06:00
async prepareToMount ( ) {
await this . models . Server . sync ( ) ;
2020-12-08 11:58:31 -06:00
await this . models . Match . sync ( ) ;
2020-12-08 06:13:53 -06:00
await this . models . TickRate . sync ( ) ;
await this . models . PlayerCount . sync ( ) ;
await this . models . SteamUser . sync ( ) ;
await this . models . Wound . sync ( ) ;
await this . models . Death . sync ( ) ;
await this . models . Revive . sync ( ) ;
}
2020-12-06 15:21:41 -06:00
2020-12-08 06:13:53 -06:00
async mount ( ) {
await this . models . Server . upsert ( {
id : this . options . overrideServerID || this . server . id ,
name : this . server . serverName
} ) ;
2020-12-06 15:21:41 -06:00
2020-12-08 06:13:53 -06:00
this . match = await this . models . Match . findOne ( {
where : { server : this . options . overrideServerID || this . server . id , endTime : null }
} ) ;
2020-12-06 15:21:41 -06:00
this . server . on ( 'TICK_RATE' , this . onTickRate ) ;
2020-12-06 15:45:06 -06:00
this . server . on ( 'UPDATED_A2S_INFORMATION' , this . onUpdatedA2SInformation ) ;
2020-12-06 17:09:33 -06:00
this . server . on ( 'NEW_GAME' , this . onNewGame ) ;
2020-12-08 06:13:53 -06:00
this . server . on ( 'PLAYER_WOUNDED' , this . onPlayerWounded ) ;
this . server . on ( 'PLAYER_DIED' , this . onPlayerDied ) ;
this . server . on ( 'PLAYER_REVIVED' , this . onPlayerRevived ) ;
2020-12-06 15:21:41 -06:00
}
2020-12-08 06:13:53 -06:00
async unmount ( ) {
2020-12-06 15:23:05 -06:00
this . server . removeEventListener ( 'TICK_RATE' , this . onTickRate ) ;
2020-12-06 15:45:06 -06:00
this . server . removeEventListener ( 'UPDATED_A2S_INFORMATION' , this . onTickRate ) ;
2020-12-06 17:09:33 -06:00
this . server . removeEventListener ( 'NEW_GAME' , this . onNewGame ) ;
2020-12-08 06:13:53 -06:00
this . server . removeEventListener ( 'PLAYER_WOUNDED' , this . onPlayerWounded ) ;
this . server . removeEventListener ( 'PLAYER_DIED' , this . onPlayerDied ) ;
this . server . removeEventListener ( 'PLAYER_REVIVED' , this . onPlayerRevived ) ;
2020-12-06 15:21:41 -06:00
}
async onTickRate ( info ) {
2020-12-06 15:23:05 -06:00
await this . models . TickRate . create ( {
2020-12-08 06:13:53 -06:00
server : this . options . overrideServerID || this . server . id ,
2020-12-08 11:58:31 -06:00
match : this . match ? this . match . id : null ,
2020-12-06 15:23:05 -06:00
time : info . time ,
tickRate : info . tickRate
} ) ;
2020-12-06 15:21:41 -06:00
}
2020-12-06 15:45:06 -06:00
2021-08-07 16:44:29 -05:00
async onUpdatedA2SInformation ( info ) {
2020-12-06 15:45:06 -06:00
await this . models . PlayerCount . create ( {
2020-12-08 06:13:53 -06:00
server : this . options . overrideServerID || this . server . id ,
2020-12-08 11:58:31 -06:00
match : this . match ? this . match . id : null ,
2021-08-07 16:44:29 -05:00
players : info . a2sPlayerCount ,
publicQueue : info . publicQueue ,
reserveQueue : info . reserveQueue
2020-12-06 15:45:06 -06:00
} ) ;
}
2020-12-06 17:09:33 -06:00
async onNewGame ( info ) {
await this . models . Match . update (
{ endTime : info . time , winner : info . winner } ,
2020-12-08 06:13:53 -06:00
{ where : { server : this . options . overrideServerID || this . server . id , endTime : null } }
2020-12-06 17:09:33 -06:00
) ;
2020-12-08 06:13:53 -06:00
this . match = await this . models . Match . create ( {
server : this . options . overrideServerID || this . server . id ,
2020-12-06 17:09:33 -06:00
dlc : info . dlc ,
2020-12-08 06:13:53 -06:00
mapClassname : info . mapClassname ,
layerClassname : info . layerClassname ,
2021-02-03 16:29:42 -06:00
map : info . layer ? info . layer . map . name : null ,
layer : info . layer ? info . layer . name : null ,
2020-12-06 17:09:33 -06:00
startTime : info . time
} ) ;
}
2020-12-08 06:13:53 -06:00
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
} ) ;
}
2020-12-06 15:21:41 -06:00
}