Merge pull request #318 from fantinodavide/eos-integration

EOS integration, major rewrite.
This commit is contained in:
Marek 2024-01-05 15:05:30 -06:00 committed by GitHub
commit dffc8b6119
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 561 additions and 160 deletions

View File

@ -16,9 +16,13 @@ export default class LogParser extends EventEmitter {
this.eventStore = { this.eventStore = {
disconnected: {}, // holding area, cleared on map change. disconnected: {}, // holding area, cleared on map change.
players: {}, // persistent data, steamid, controller, suffix. players: [], // persistent data, steamid, controller, suffix.
playersEOS: [], // proxies from EOSID to persistent data, steamid, controller, suffix.
connectionIdToSteamID: new Map(),
session: {}, // old eventstore, nonpersistent data session: {}, // old eventstore, nonpersistent data
clients: {} // used in the connection chain before we resolve a player. clients: {}, // used in the connection chain before we resolve a player.
lastConnection: {}, // used to store the last client connection data to then associate a steamid
joinRequests: []
}; };
this.linesPerMinute = 0; this.linesPerMinute = 0;

View File

@ -337,8 +337,7 @@ export default class Rcon extends EventEmitter {
if (type === SERVERDATA_AUTH) { if (type === SERVERDATA_AUTH) {
this.callbackIds.push({ id: this.count, cmd: body }); this.callbackIds.push({ id: this.count, cmd: body });
Logger.verbose('RCON', 2, `Writing Auth Packet`);
Logger.verbose('RCON', 4, `Writing packet with type "${type}" and body "${body}".`);
this.responseCallbackQueue.push(() => {}); this.responseCallbackQueue.push(() => {});
this.responseCallbackQueue.push((decodedPacket) => { this.responseCallbackQueue.push((decodedPacket) => {
this.client.removeListener('error', onError); this.client.removeListener('error', onError);
@ -352,7 +351,6 @@ export default class Rcon extends EventEmitter {
} }
}); });
} else { } else {
Logger.verbose('RCON', 2, `Writing packet with type "${type}" and body "${body}".`);
this.callbackIds.push({ id: this.count, cmd: body }); this.callbackIds.push({ id: this.count, cmd: body });
this.responseCallbackQueue.push((response) => { this.responseCallbackQueue.push((response) => {
this.client.removeListener('error', onError); this.client.removeListener('error', onError);

View File

@ -1,6 +1,6 @@
{ {
"name": "SquadJS", "name": "SquadJS",
"version": "3.8.2", "version": "4.0.0",
"repository": "https://github.com/Team-Silver-Sphere/SquadJS.git", "repository": "https://github.com/Team-Silver-Sphere/SquadJS.git",
"author": "Thomas Smyth <https://github.com/Thomas-Smyth>", "author": "Thomas Smyth <https://github.com/Thomas-Smyth>",
"license": "BSL-1.0", "license": "BSL-1.0",

View File

@ -1,7 +1,6 @@
import EventEmitter from 'events'; import EventEmitter from 'events';
import axios from 'axios'; import axios from 'axios';
import Gamedig from 'gamedig';
import Logger from 'core/logger'; import Logger from 'core/logger';
import { SQUADJS_API_DOMAIN } from 'core/constants'; import { SQUADJS_API_DOMAIN } from 'core/constants';
@ -19,7 +18,7 @@ export default class SquadServer extends EventEmitter {
constructor(options = {}) { constructor(options = {}) {
super(); super();
for (const option of ['host', 'queryPort']) for (const option of ['host'])
if (!(option in options)) throw new Error(`${option} must be specified.`); if (!(option in options)) throw new Error(`${option} must be specified.`);
this.id = options.id; this.id = options.id;
@ -73,13 +72,13 @@ export default class SquadServer extends EventEmitter {
this.admins = await fetchAdminLists(this.options.adminLists); this.admins = await fetchAdminLists(this.options.adminLists);
await this.rcon.connect(); await this.rcon.connect();
await this.logParser.watch();
await this.updateSquadList(); await this.updateSquadList();
await this.updatePlayerList(); await this.updatePlayerList(this);
await this.updateLayerInformation(); await this.updateLayerInformation();
await this.updateA2SInformation(); await this.updateA2SInformation();
await this.logParser.watch();
Logger.verbose('SquadServer', 1, `Watching ${this.serverName}...`); Logger.verbose('SquadServer', 1, `Watching ${this.serverName}...`);
await this.pingSquadJSAPI(); await this.pingSquadJSAPI();
@ -154,9 +153,12 @@ export default class SquadServer extends EventEmitter {
}); });
this.rcon.on('SQUAD_CREATED', async (data) => { this.rcon.on('SQUAD_CREATED', async (data) => {
data.player = await this.getPlayerBySteamID(data.playerSteamID, true); data.player = await this.getPlayerByEOSID(data.playerEOSID, true);
data.player.squadID = data.squadID;
delete data.playerName; delete data.playerName;
delete data.playerSteamID; delete data.playerSteamID;
delete data.playerEOSID;
this.emit('SQUAD_CREATED', data); this.emit('SQUAD_CREATED', data);
}); });
@ -207,7 +209,13 @@ export default class SquadServer extends EventEmitter {
}); });
this.logParser.on('PLAYER_CONNECTED', async (data) => { this.logParser.on('PLAYER_CONNECTED', async (data) => {
data.player = await this.getPlayerBySteamID(data.steamID); Logger.verbose(
'SquadServer',
1,
`Player connected ${data.playerSuffix} - SteamID: ${data.steamID} - EOSID: ${data.eosID} - IP: ${data.ip}`
);
data.player = await this.getPlayerByEOSID(data.eosID);
if (data.player) data.player.suffix = data.playerSuffix; if (data.player) data.player.suffix = data.playerSuffix;
delete data.steamID; delete data.steamID;
@ -217,7 +225,7 @@ export default class SquadServer extends EventEmitter {
}); });
this.logParser.on('PLAYER_DISCONNECTED', async (data) => { this.logParser.on('PLAYER_DISCONNECTED', async (data) => {
data.player = await this.getPlayerBySteamID(data.steamID); data.player = await this.getPlayerByEOSID(data.eosID);
delete data.steamID; delete data.steamID;
@ -226,12 +234,16 @@ export default class SquadServer extends EventEmitter {
this.logParser.on('PLAYER_DAMAGED', async (data) => { this.logParser.on('PLAYER_DAMAGED', async (data) => {
data.victim = await this.getPlayerByName(data.victimName); data.victim = await this.getPlayerByName(data.victimName);
data.attacker = await this.getPlayerByName(data.attackerName); data.attacker = await this.getPlayerByEOSID(data.attackerEOSID);
if (data.victim && data.attacker) if (data.attacker && !data.attacker.playercontroller && data.attackerController)
data.attacker.playercontroller = data.attackerController;
if (data.victim && data.attacker) {
data.teamkill = data.teamkill =
data.victim.teamID === data.attacker.teamID && data.victim.teamID === data.attacker.teamID &&
data.victim.steamID !== data.attacker.steamID; data.victim.steamID !== data.attacker.steamID;
}
delete data.victimName; delete data.victimName;
delete data.attackerName; delete data.attackerName;
@ -241,7 +253,7 @@ export default class SquadServer extends EventEmitter {
this.logParser.on('PLAYER_WOUNDED', async (data) => { this.logParser.on('PLAYER_WOUNDED', async (data) => {
data.victim = await this.getPlayerByName(data.victimName); data.victim = await this.getPlayerByName(data.victimName);
data.attacker = await this.getPlayerByName(data.attackerName); data.attacker = await this.getPlayerByEOSID(data.attackerEOSID);
if (!data.attacker) if (!data.attacker)
data.attacker = await this.getPlayerByController(data.attackerPlayerController); data.attacker = await this.getPlayerByController(data.attackerPlayerController);
@ -259,7 +271,7 @@ 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); data.attacker = await this.getPlayerByEOSID(data.attackerEOSID);
if (!data.attacker) if (!data.attacker)
data.attacker = await this.getPlayerByController(data.attackerPlayerController); data.attacker = await this.getPlayerByController(data.attackerPlayerController);
@ -275,9 +287,9 @@ export default class SquadServer extends EventEmitter {
}); });
this.logParser.on('PLAYER_REVIVED', async (data) => { this.logParser.on('PLAYER_REVIVED', async (data) => {
data.victim = await this.getPlayerByName(data.victimName); data.victim = await this.getPlayerByEOSID(data.victimEOSID);
data.attacker = await this.getPlayerByName(data.attackerName); data.attacker = await this.getPlayerByEOSID(data.attackerEOSID);
data.reviver = await this.getPlayerByName(data.reviverName); data.reviver = await this.getPlayerByEOSID(data.reviverEOSID);
delete data.victimName; delete data.victimName;
delete data.attackerName; delete data.attackerName;
@ -287,7 +299,7 @@ export default class SquadServer extends EventEmitter {
}); });
this.logParser.on('PLAYER_POSSESS', async (data) => { this.logParser.on('PLAYER_POSSESS', async (data) => {
data.player = await this.getPlayerByNameSuffix(data.playerSuffix); data.player = await this.getPlayerByEOSID(data.playerEOSID);
if (data.player) data.player.possessClassname = data.possessClassname; if (data.player) data.player.possessClassname = data.possessClassname;
delete data.playerSuffix; delete data.playerSuffix;
@ -296,7 +308,7 @@ export default class SquadServer extends EventEmitter {
}); });
this.logParser.on('PLAYER_UNPOSSESS', async (data) => { this.logParser.on('PLAYER_UNPOSSESS', async (data) => {
data.player = await this.getPlayerByNameSuffix(data.playerSuffix); data.player = await this.getPlayerByEOSID(data.playerEOSID);
delete data.playerSuffix; delete data.playerSuffix;
@ -310,6 +322,23 @@ export default class SquadServer extends EventEmitter {
this.logParser.on('TICK_RATE', (data) => { this.logParser.on('TICK_RATE', (data) => {
this.emit('TICK_RATE', data); this.emit('TICK_RATE', data);
}); });
this.logParser.on('CLIENT_EXTERNAL_ACCOUNT_INFO', (data) => {
this.rcon.addIds(data.steamID, data.eosID);
});
// this.logParser.on('CLIENT_CONNECTED', (data) => {
// Logger.verbose("SquadServer", 1, `Client connected. Connection: ${data.connection} - SteamID: ${data.steamID}`)
// })
// this.logParser.on('CLIENT_LOGIN_REQUEST', (data) => {
// Logger.verbose("SquadServer", 1, `Login request. ChainID: ${data.chainID} - Suffix: ${data.suffix} - EOSID: ${data.eosID}`)
// })
// this.logParser.on('RESOLVED_EOS_ID', (data) => {
// Logger.verbose("SquadServer", 1, `Resolved EOSID. ChainID: ${data.chainID} - Suffix: ${data.suffix} - EOSID: ${data.eosID}`)
// })
// this.logParser.on('ADDING_CLIENT_CONNECTION', (data) => {
// Logger.verbose("SquadServer", 1, `Adding client connection`, data)
// })
} }
async restartLogParser() { async restartLogParser() {
@ -352,7 +381,7 @@ export default class SquadServer extends EventEmitter {
} }
const players = []; const players = [];
for (const player of await this.rcon.getListPlayers()) for (const player of await this.rcon.getListPlayers(this))
players.push({ players.push({
...oldPlayerInfo[player.steamID], ...oldPlayerInfo[player.steamID],
...player, ...player,
@ -380,6 +409,13 @@ export default class SquadServer extends EventEmitter {
}); });
} }
if (this.a2sPlayerCount > 0 && players.length === 0)
Logger.verbose(
'SquadServer',
1,
`Real Player Count: ${this.a2sPlayerCount} but loaded ${players.length}`
);
this.emit('UPDATED_PLAYER_INFORMATION'); this.emit('UPDATED_PLAYER_INFORMATION');
} catch (err) { } catch (err) {
Logger.verbose('SquadServer', 1, 'Failed to update player list.', err); Logger.verbose('SquadServer', 1, 'Failed to update player list.', err);
@ -441,53 +477,70 @@ export default class SquadServer extends EventEmitter {
); );
} }
async updateA2SInformation() { updateA2SInformation() {
return this.updateServerInformation();
}
async updateServerInformation() {
if (this.updateA2SInformationTimeout) clearTimeout(this.updateA2SInformationTimeout); if (this.updateA2SInformationTimeout) clearTimeout(this.updateA2SInformationTimeout);
Logger.verbose('SquadServer', 1, `Updating A2S information...`); Logger.verbose('SquadServer', 1, `Updating server information...`);
try { try {
const data = await Gamedig.query({ const rawData = await this.rcon.execute(`ShowServerInfo`);
type: 'squad', Logger.verbose('SquadServer', 3, `Server information raw data`, rawData);
host: this.options.host, const data = JSON.parse(rawData);
port: this.options.queryPort Logger.verbose('SquadServer', 2, `Server information data`, JSON.data);
});
const info = { const info = {
raw: data.raw, raw: data,
serverName: data.name, serverName: data.ServerName_s,
maxPlayers: parseInt(data.maxplayers), maxPlayers: parseInt(data.MaxPlayers),
publicSlots: parseInt(data.raw.rules.NUMPUBCONN), publicQueueLimit: parseInt(data.PublicQueueLimit_I),
reserveSlots: parseInt(data.raw.rules.NUMPRIVCONN), reserveSlots: parseInt(data.PlayerReserveCount_I),
a2sPlayerCount: parseInt(data.raw.rules.PlayerCount_i), playerCount: parseInt(data.PlayerCount_I),
publicQueue: parseInt(data.raw.rules.PublicQueue_i), a2sPlayerCount: parseInt(data.PlayerCount_I),
reserveQueue: parseInt(data.raw.rules.ReservedQueue_i), publicQueue: parseInt(data.PublicQueue_I),
reserveQueue: parseInt(data.ReservedQueue_I),
matchTimeout: parseFloat(data.raw.rules.MatchTimeout_f), currentLayer: data.MapName_s,
gameVersion: data.raw.version nextLayer: data.NextLayer_s,
teamOne: data.TeamOne_s?.replace(new RegExp(data.MapName_s, 'i'), '') || '',
teamTwo: data.TeamTwo_s?.replace(new RegExp(data.MapName_s, 'i'), '') || '',
matchTimeout: parseFloat(data.MatchTimeout_d),
matchStartTime: this.getMatchStartTimeByPlaytime(data.PLAYTIME_I),
gameVersion: data.GameVersion_s
}; };
this.serverName = info.serverName; this.serverName = info.serverName;
this.maxPlayers = info.maxPlayers; this.maxPlayers = info.maxPlayers;
this.publicSlots = info.publicSlots; this.publicSlots = info.maxPlayers - info.reserveSlots;
this.reserveSlots = info.reserveSlots; this.reserveSlots = info.reserveSlots;
this.a2sPlayerCount = info.a2sPlayerCount; this.a2sPlayerCount = info.playerCount;
this.playerCount = info.playerCount;
this.publicQueue = info.publicQueue; this.publicQueue = info.publicQueue;
this.reserveQueue = info.reserveQueue; this.reserveQueue = info.reserveQueue;
this.matchTimeout = info.matchTimeout; this.matchTimeout = info.matchTimeout;
this.matchStartTime = info.matchStartTime;
this.gameVersion = info.gameVersion; this.gameVersion = info.gameVersion;
if (!this.currentLayer) this.currentLayer = Layers.getLayerByClassname(info.currentLayer);
if (!this.nextLayer) this.nextLayer = Layers.getLayerByClassname(info.nextLayer);
this.emit('UPDATED_A2S_INFORMATION', info); this.emit('UPDATED_A2S_INFORMATION', info);
this.emit('UPDATED_SERVER_INFORMATION', info);
} catch (err) { } catch (err) {
Logger.verbose('SquadServer', 1, 'Failed to update A2S information.', err); Logger.verbose('SquadServer', 1, 'Failed to update server information.', err);
} }
Logger.verbose('SquadServer', 1, `Updated A2S information.`); Logger.verbose('SquadServer', 1, `Updated server information.`);
this.updateA2SInformationTimeout = setTimeout( this.updateA2SInformationTimeout = setTimeout(
this.updateA2SInformation, this.updateA2SInformation,
@ -542,6 +595,10 @@ export default class SquadServer extends EventEmitter {
return this.getPlayerByCondition((player) => player.steamID === steamID, forceUpdate); return this.getPlayerByCondition((player) => player.steamID === steamID, forceUpdate);
} }
async getPlayerByEOSID(eosID, forceUpdate) {
return this.getPlayerByCondition((player) => player.eosID === eosID, forceUpdate);
}
async getPlayerByName(name, forceUpdate) { async getPlayerByName(name, forceUpdate) {
return this.getPlayerByCondition((player) => player.name === name, forceUpdate); return this.getPlayerByCondition((player) => player.name === name, forceUpdate);
} }
@ -606,4 +663,8 @@ export default class SquadServer extends EventEmitter {
this.pingSquadJSAPITimeout = setTimeout(this.pingSquadJSAPI, this.pingSquadJSAPIInterval); this.pingSquadJSAPITimeout = setTimeout(this.pingSquadJSAPI, this.pingSquadJSAPIInterval);
} }
getMatchStartTimeByPlaytime(playtime) {
return new Date(Date.now() - +playtime * 1000);
}
} }

View File

@ -0,0 +1,20 @@
export default {
regex:
/^\[([0-9.:-]+)]\[([ 0-9]*)]LogNet: AddClientConnection: Added client connection: \[UNetConnection\] RemoteAddr: ([\d.]+):[0-9]+, Name: (EOSIpNetConnection_[0-9]+), Driver: GameNetDriver (EOSNetDriver_[0-9]+), IsServer: YES, PC: NULL, Owner: NULL, UniqueId: INVALID/,
onMatch: (args, logParser) => {
const data = {
raw: args[0],
time: args[1],
chainID: args[2],
// steamID: args[ 3 ],
ip: args[3],
connection: args[4],
driver: args[5]
};
/* This is Called when unreal engine adds a client connection
First Step in Adding a Player to server
*/
logParser.eventStore['last-connection'] = data;
logParser.emit('ADDING_CLIENT_CONNECTION', data);
}
};

View File

@ -0,0 +1,15 @@
export default {
regex:
/^\[([0-9.:-]+)]\[([ 0-9]*)]LogSquadCommon: SQCommonStatics Check Permissions, UniqueId:([\da-f]+)$/,
onMatch: (args, logParser) => {
const data = {
raw: args[0],
time: args[1],
chainID: +args[2],
eosID: args[3]
};
logParser.eventStore.joinRequests[data.chainID].eosID = data.eosID;
logParser.emit('RESOLVED_EOS_ID', { ...logParser.eventStore.joinRequests[data.chainID] });
}
};

View File

@ -0,0 +1,21 @@
export default {
regex:
/^\[([0-9.:-]+)]\[([ 0-9]+)]LogEOS: Verbose: \[LogEOSConnect] FConnectClient::CacheExternalAccountInfo - ProductUserId: (?<eosId>[0-9a-f]{32}), AccountType: (\d), AccountId: (?<steamId>[0-9]{17}), DisplayName: <Redacted>/,
onMatch: (args, logParser) => {
const data = {
raw: args[0],
time: args[1],
chainID: args[2],
eosID: args.groups.eosId,
steamID: args.groups.steamId
};
logParser.eventStore.players[data.steamID] = {
eosID: data.eosID,
steamID: data.steamID
};
logParser.eventStore.playersEOS[data.eosID] = logParser.eventStore.players[data.steamID];
logParser.emit('CLIENT_EXTERNAL_ACCOUNT_INFO', data);
}
};

View File

@ -1,6 +1,6 @@
export default { export default {
regex: regex:
/^\[([0-9.:-]+)]\[([ 0-9]*)]LogSquad: Login: NewPlayer: SteamNetConnection \/Engine\/Transient\.(SteamNetConnection_[0-9]+)/, /^\[([0-9.:-]+)]\[([ 0-9]*)]LogSquad: Login: NewPlayer: EOSIpNetConnection \/Engine\/Transient\.(EOSIpNetConnection_[0-9]+)/,
onMatch: (args, logParser) => { onMatch: (args, logParser) => {
const data = { const data = {
raw: args[0], raw: args[0],
@ -13,7 +13,7 @@ export default {
2nd Step in player connected path 2nd Step in player connected path
*/ */
logParser.eventStore['client-login'] = logParser.eventStore.clients[args[3]]; logParser.eventStore.joinRequests[data.chainID].connection = data.connection;
delete logParser.eventStore.clients[args[3]]; delete logParser.eventStore.clients[args[3]];
logParser.emit('CLIENT_LOGIN', data); logParser.emit('CLIENT_LOGIN', data);
} }

View File

@ -16,10 +16,15 @@ import RoundEnded from './round-ended.js';
import RoundTickets from './round-tickets.js'; import RoundTickets from './round-tickets.js';
import RoundWinner from './round-winner.js'; import RoundWinner from './round-winner.js';
import ServerTickRate from './server-tick-rate.js'; import ServerTickRate from './server-tick-rate.js';
import ClientConnected from './client-connected.js'; import AddingClientConnection from './adding-client-connection.js';
import ClientLogin from './client-login.js'; import ClientLogin from './client-login.js';
import PendingConnectionDestroyed from './pending-connection-destroyed.js'; import PendingConnectionDestroyed from './pending-connection-destroyed.js';
import ClientExternalAccountInfo from './client-external-account-info.js';
import SendingAuthResult from './sending-auth-result.js';
import LoginRequest from './login-request.js';
import JoinRequest from './join-request.js';
import PlayerJoinSucceeded from './player-join-succeeded.js';
import CheckPermissionResolveEosid from './check-permission-resolve-eosid.js';
export default class SquadLogParser extends LogParser { export default class SquadLogParser extends LogParser {
constructor(options) { constructor(options) {
super('SquadGame.log', options); super('SquadGame.log', options);
@ -43,9 +48,15 @@ export default class SquadLogParser extends LogParser {
RoundTickets, RoundTickets,
RoundWinner, RoundWinner,
ServerTickRate, ServerTickRate,
ClientConnected, AddingClientConnection,
ClientLogin, ClientLogin,
PendingConnectionDestroyed PendingConnectionDestroyed,
ClientExternalAccountInfo,
SendingAuthResult,
LoginRequest,
JoinRequest,
PlayerJoinSucceeded,
CheckPermissionResolveEosid
]; ];
} }
} }

View File

@ -0,0 +1,15 @@
export default {
regex: /^\[([0-9.:-]+)]\[([ 0-9]*)]LogNet: Join request: .+\?Name=(.+)\?SplitscreenCount=\d$/,
onMatch: (args, logParser) => {
const data = {
raw: args[0],
time: args[1],
chainID: +args[2],
suffix: args[3]
};
logParser.eventStore.joinRequests[data.chainID] = data;
// console.log(logParser.eventStore.loginRequests[ data.chainID ])
logParser.emit('CLIENT_JOIN_REQUEST', data);
}
};

View File

@ -0,0 +1,17 @@
export default {
regex:
/^\[([0-9.:-]+)]\[([ 0-9]*)]LogNet: Login request: \?Name=(.+?)(?:\?PASSWORD=(?:.+?))? userId: RedpointEOS:([\da-f]{32}) platform: RedpointEOS/,
onMatch: (args, logParser) => {
const data = {
raw: args[0],
time: args[1],
chainID: +args[2],
suffix: args[3],
eosID: args[4]
};
// logParser.eventStore.loginRequests[ data.chainID ] = data;
// console.log(logParser.eventStore.loginRequests[ data.chainID ])
logParser.emit('CLIENT_LOGIN_REQUEST', data);
}
};

View File

@ -1,6 +1,6 @@
export default { export default {
regex: regex:
/^\[([0-9.:-]+)]\[([ 0-9]*)]LogNet: UNetConnection::PendingConnectionLost\. \[UNetConnection\] RemoteAddr: ([0-9]{17}):[0-9]+, Name: (SteamNetConnection_[0-9]+), Driver: GameNetDriver (SteamNetDriver_[0-9]+), IsServer: YES, PC: NULL, Owner: NULL, UniqueId: (?:Steam:UNKNOWN \[.+\]|INVALID) bPendingDestroy=0/, /^\[([0-9.:-]+)]\[([ 0-9]*)]LogNet: UNetConnection::PendingConnectionLost\. \[UNetConnection\] RemoteAddr: ([0-9a-f]{32}):[0-9]+, Name: (SteamNetConnection_[0-9]+), Driver: GameNetDriver (SteamNetDriver_[0-9]+), IsServer: YES, PC: NULL, Owner: NULL, UniqueId: (?:Steam:UNKNOWN \[.+\]|INVALID) bPendingDestroy=0/,
onMatch: (args, logParser) => { onMatch: (args, logParser) => {
const data = { const data = {
raw: args[0], raw: args[0],

View File

@ -1,27 +1,25 @@
export default { export default {
regex: /^\[([0-9.:-]+)]\[([ 0-9]*)]LogNet: Join succeeded: (.+)/, regex:
/^\[([0-9.:-]+)]\[([ 0-9]*)]LogSquad: PostLogin: NewPlayer: BP_PlayerController_C .+PersistentLevel\.([^\s]+) \(IP: ([\d.]+) \| Online IDs: EOS: ([0-9a-f]{32}) steam: (\d+)\)/,
onMatch: (args, logParser) => { onMatch: (args, logParser) => {
const data = { const data = {
raw: args[0], raw: args[0],
time: args[1], time: args[1],
chainID: args[2], chainID: +args[2],
playerSuffix: args[3], playercontroller: args[3],
steamID: logParser.eventStore['client-login'], // player connected ip: args[4],
controller: logParser.eventStore['player-controller'] // playercontroller connected eosID: args[5],
steamID: args[6]
}; };
delete logParser.eventStore['client-login']; const joinRequestData = logParser.eventStore.joinRequests[+args[2]];
delete logParser.eventStore['player-controller']; data.connection = joinRequestData.connection;
data.playerSuffix = joinRequestData.suffix;
if (!logParser.eventStore.players[data.steamID])
logParser.eventStore.players[data.steamID] = {};
logParser.eventStore.players[data.steamID].controller = data.playercontroller;
// Handle Reconnecting players
if (logParser.eventStore.disconnected[data.steamID]) {
delete logParser.eventStore.disconnected[data.steamID];
}
logParser.emit('PLAYER_CONNECTED', data); logParser.emit('PLAYER_CONNECTED', data);
logParser.eventStore.players[data.steamID] = {
steamID: data.steamID,
suffix: data.playerSuffix,
controller: data.controller
};
} }
}; };

View File

@ -1,6 +1,6 @@
export default { export default {
regex: regex:
/^\[([0-9.:-]+)]\[([ 0-9]*)]LogSquad: Player:(.+) ActualDamage=([0-9.]+) from (.+) caused by ([A-z_0-9-]+)_C/, /^\[([0-9.:-]+)]\[([ 0-9]*)]LogSquad: Player:(.+) ActualDamage=([0-9.]+) from (.+) \(Online IDs: EOS: ([0-9a-f]{32}) steam: (\d{17}) \| Player Controller ID: ([^ ]+)\)caused by ([A-z_0-9-]+)_C/,
onMatch: (args, logParser) => { onMatch: (args, logParser) => {
const data = { const data = {
raw: args[0], raw: args[0],
@ -9,11 +9,18 @@ export default {
victimName: args[3], victimName: args[3],
damage: parseFloat(args[4]), damage: parseFloat(args[4]),
attackerName: args[5], attackerName: args[5],
weapon: args[6] attackerEOSID: args[6],
attackerSteamID: args[7],
attackerController: args[8],
weapon: args[9]
}; };
logParser.eventStore.session[args[3]] = data; logParser.eventStore.session[args[3]] = data;
if (!logParser.eventStore.players[data.attackerSteamID])
logParser.eventStore.players[data.attackerSteamID] = {};
logParser.eventStore.players[data.attackerSteamID].controller = data.attackerController;
logParser.emit('PLAYER_DAMAGED', data); logParser.emit('PLAYER_DAMAGED', data);
} }
}; };

View File

@ -1,6 +1,6 @@
export default { export default {
regex: regex:
/^\[([0-9.:-]+)]\[([ 0-9]*)]LogSquadTrace: \[DedicatedServer](?:ASQSoldier::)?Die\(\): Player:(.+) KillingDamage=(?:-)*([0-9.]+) from ([A-z_0-9]+) caused by ([A-z_0-9-]+)_C/, /^\[([0-9.:-]+)]\[([ 0-9]*)]LogSquadTrace: \[DedicatedServer](?:ASQSoldier::)?Die\(\): Player:(.+) KillingDamage=(?:-)*([0-9.]+) from ([A-z_0-9]+) \(Online IDs: EOS: ([\w\d]{32}) steam: (\d{17}) \| Contoller ID: ([\w\d]+)\) caused by ([A-z_0-9-]+)_C/,
onMatch: (args, logParser) => { onMatch: (args, logParser) => {
const data = { const data = {
...logParser.eventStore.session[args[3]], ...logParser.eventStore.session[args[3]],
@ -11,7 +11,9 @@ export default {
victimName: args[3], victimName: args[3],
damage: parseFloat(args[4]), damage: parseFloat(args[4]),
attackerPlayerController: args[5], attackerPlayerController: args[5],
weapon: args[6] attackerEOSID: args[6],
attackerSteamID: args[7],
weapon: args[9]
}; };
logParser.eventStore.session[args[3]] = data; logParser.eventStore.session[args[3]] = data;

View File

@ -1,16 +1,18 @@
export default { export default {
regex: regex:
/^\[([0-9.:-]+)]\[([ 0-9]*)]LogNet: UChannel::Close: Sending CloseBunch\. ChIndex == [0-9]+\. Name: \[UChannel\] ChIndex: [0-9]+, Closing: [0-9]+ \[UNetConnection\] RemoteAddr: ([0-9]{17}):[0-9]+, Name: SteamNetConnection_[0-9]+, Driver: GameNetDriver SteamNetDriver_[0-9]+, IsServer: YES, PC: ([^ ]+PlayerController_C_[0-9]+), Owner: [^ ]+PlayerController_C_[0-9]+/, /^\[([0-9.:-]+)]\[([ 0-9]*)]LogNet: UChannel::Close: Sending CloseBunch\. ChIndex == [0-9]+\. Name: \[UChannel\] ChIndex: [0-9]+, Closing: [0-9]+ \[UNetConnection\] RemoteAddr: ([\d.]+):[\d]+, Name: EOSIpNetConnection_[0-9]+, Driver: GameNetDriver EOSNetDriver_[0-9]+, IsServer: YES, PC: ([^ ]+PlayerController_C_[0-9]+), Owner: [^ ]+PlayerController_C_[0-9]+, UniqueId: RedpointEOS:([\d\w]+)/,
onMatch: (args, logParser) => { onMatch: (args, logParser) => {
const data = { const data = {
raw: args[0], raw: args[0],
time: args[1], time: args[1],
chainID: args[2], chainID: args[2],
steamID: args[3], ip: args[3],
playerController: args[4] playerController: args[4],
eosID: args[5]
}; };
logParser.eventStore.disconnected[data.steamID] = true; logParser.eventStore.disconnected[data.steamID] = true;
logParser.emit('PLAYER_DISCONNECTED', data); logParser.emit('PLAYER_DISCONNECTED', data);
} }
}; };

View File

@ -0,0 +1,28 @@
export default {
regex: /^\[([0-9.:-]+)]\[([ 0-9]*)]LogNet: Join succeeded: (.+)/,
onMatch: (args, logParser) => {
const data = {
raw: args[0],
time: args[1],
chainID: +args[2],
playerSuffix: args[3]
};
const joinRequestsData = { ...logParser.eventStore.joinRequests[data.chainID] };
data.eosID = joinRequestsData.eosID;
data.controller = joinRequestsData.controller;
data.steamID = `${logParser.eventStore.connectionIdToSteamID.get(joinRequestsData.connection)}`;
logParser.eventStore.connectionIdToSteamID.delete(joinRequestsData.connection);
delete logParser.eventStore.joinRequests[+data.chainID];
// Handle Reconnecting players
if (logParser.eventStore.disconnected[data.steamID]) {
delete logParser.eventStore.disconnected[data.steamID];
}
logParser.emit('JOIN_SUCCEEDED', data);
}
};

View File

@ -1,13 +1,15 @@
export default { export default {
regex: regex:
/^\[([0-9.:-]+)]\[([ 0-9]*)]LogSquadTrace: \[DedicatedServer](?:ASQPlayerController::)?OnPossess\(\): PC=(.+) Pawn=([A-z0-9_]+)_C/, /^\[([0-9.:-]+)]\[([ 0-9]*)]LogSquadTrace: \[DedicatedServer](?:ASQPlayerController::)?OnPossess\(\): PC=(.+) \(Online IDs: EOS: ([\w\d]{32}) steam: (\d{17})\) Pawn=([A-z0-9_]+)_C/,
onMatch: (args, logParser) => { onMatch: (args, logParser) => {
const data = { const data = {
raw: args[0], raw: args[0],
time: args[1], time: args[1],
chainID: args[2], chainID: args[2],
playerSuffix: args[3], playerSuffix: args[3],
possessClassname: args[4], playerEOSID: args[4],
playerSteamID: args[5],
possessClassname: args[6],
pawn: args[5] pawn: args[5]
}; };

View File

@ -1,6 +1,7 @@
export default { export default {
// the names are currently the wrong way around in these logs // the names are currently the wrong way around in these logs
regex: /^\[([0-9.:-]+)]\[([ 0-9]*)]LogSquad: (.+) has revived (.+)\./, regex:
/^\[([0-9.:-]+)]\[([ 0-9]*)]LogSquad: (.+) \(Online IDs: EOS: ([0-9a-f]{32}) steam: (\d{17})\) has revived (.+) \(Online IDs: EOS: ([0-9a-f]{32}) steam: (\d{17})\)\./,
onMatch: (args, logParser) => { onMatch: (args, logParser) => {
const data = { const data = {
...logParser.eventStore.session[args[3]], ...logParser.eventStore.session[args[3]],
@ -8,7 +9,11 @@ export default {
time: args[1], time: args[1],
chainID: args[2], chainID: args[2],
reviverName: args[3], reviverName: args[3],
victimName: args[4] reviverEOSID: args[4],
reviverSteamID: args[5],
victimName: args[6],
victimEOSID: args[7],
victimSteamID: args[8]
}; };
logParser.emit('PLAYER_REVIVED', data); logParser.emit('PLAYER_REVIVED', data);

View File

@ -1,14 +1,16 @@
export default { export default {
regex: regex:
/^\[([0-9.:-]+)]\[([ 0-9]*)]LogSquadTrace: \[DedicatedServer](?:ASQPlayerController::)?OnUnPossess\(\): PC=(.+)/, /^\[([0-9.:-]+)]\[([ 0-9]*)]LogSquadTrace: \[DedicatedServer](?:ASQPlayerController::)?OnUnPossess\(\): PC=(.+) \(Online IDs: EOS: ([\w\d]{32}) steam: (\d{17})\)/,
onMatch: (args, logParser) => { onMatch: (args, logParser) => {
const data = { const data = {
raw: args[0], raw: args[0],
time: args[1], time: args[1],
chainID: args[2], chainID: args[2],
playerSuffix: args[3], playerSuffix: args[3],
playerEOSID: args[4],
playerSteamID: args[5],
switchPossess: switchPossess:
args[3] in logParser.eventStore.session && logParser.eventStore.session[args[3]] === args[2] args[4] in logParser.eventStore.session && logParser.eventStore.session[args[4]] === args[2]
}; };
delete logParser.eventStore.session[args[3]]; delete logParser.eventStore.session[args[3]];

View File

@ -1,6 +1,6 @@
export default { export default {
regex: regex:
/^\[([0-9.:-]+)]\[([ 0-9]*)]LogSquadTrace: \[DedicatedServer](?:ASQSoldier::)?Wound\(\): Player:(.+) KillingDamage=(?:-)*([0-9.]+) from ([A-z_0-9]+) caused by ([A-z_0-9-]+)_C/, /^\[([0-9.:-]+)]\[([ 0-9]*)]LogSquadTrace: \[DedicatedServer](?:ASQSoldier::)?Wound\(\): Player:(.+) KillingDamage=(?:-)*([0-9.]+) from ([A-z_0-9]+) \(Online IDs: EOS: ([\w\d]{32}) steam: (\d{17}) \| Controller ID: ([\w\d]+)\) caused by ([A-z_0-9-]+)_C/,
onMatch: (args, logParser) => { onMatch: (args, logParser) => {
const data = { const data = {
...logParser.eventStore.session[args[3]], ...logParser.eventStore.session[args[3]],
@ -10,7 +10,9 @@ export default {
victimName: args[3], victimName: args[3],
damage: parseFloat(args[4]), damage: parseFloat(args[4]),
attackerPlayerController: args[5], attackerPlayerController: args[5],
weapon: args[6] attackerEOSID: args[6],
attackerSteamID: args[7],
weapon: args[9]
}; };
logParser.eventStore.session[args[3]] = data; logParser.eventStore.session[args[3]] = data;

View File

@ -5,12 +5,11 @@ export default {
const data = { const data = {
raw: args[0], raw: args[0],
time: args[1], time: args[1],
chainID: args[2], chainID: +args[2],
controller: args[3] controller: args[3]
}; };
logParser.eventStore['player-controller'] = args[3]; logParser.eventStore.joinRequests[data.chainID].controller = data.controller;
logParser.emit('PLAYER_CONTROLLER_CONNECTED', data); logParser.emit('PLAYER_CONTROLLER_CONNECTED', data);
} }
}; };

View File

@ -0,0 +1,21 @@
export default {
regex:
/^\[([0-9.:-]+)]\[([ 0-9]*)]LogOnline: STEAM: AUTH HANDLER: Sending auth result to user (\d{17}) with flag success\? 1/,
onMatch: (args, logParser) => {
if (!logParser.eventStore['last-connection']) return;
const data = {
...logParser.eventStore['last-connection'],
steamID: args[3]
};
/* This is Called when unreal engine adds a client connection
First Step in Adding a Player to server
*/
logParser.eventStore.clients[data.connection] = data.steamID;
logParser.eventStore.connectionIdToSteamID.set(data.connection, data.steamID);
logParser.emit('CLIENT_CONNECTED', data);
delete logParser.eventStore['last-connection'];
}
};

View File

@ -2,7 +2,7 @@ import Sequelize from 'sequelize';
import BasePlugin from './base-plugin.js'; import BasePlugin from './base-plugin.js';
const { DataTypes } = Sequelize; const { DataTypes, QueryTypes } = Sequelize;
export default class DBLog extends BasePlugin { export default class DBLog extends BasePlugin {
static get description() { static get description() {
@ -146,6 +146,44 @@ export default class DBLog extends BasePlugin {
} }
); );
this.createModel(
'Player',
{
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
eosID: {
type: DataTypes.STRING,
unique: true
},
steamID: {
type: DataTypes.STRING,
notNull: true,
unique: true
},
lastName: {
type: DataTypes.STRING
},
lastIP: {
type: DataTypes.STRING
}
},
{
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci',
indexes: [
{
fields: ['eosID']
},
{
fields: ['steamID']
}
]
}
);
this.createModel( this.createModel(
'Wound', 'Wound',
{ {
@ -329,37 +367,44 @@ export default class DBLog extends BasePlugin {
onDelete: 'CASCADE' onDelete: 'CASCADE'
}); });
this.models.SteamUser.hasMany(this.models.Wound, { this.models.Player.hasMany(this.models.Wound, {
sourceKey: 'steamID',
foreignKey: { name: 'attacker' }, foreignKey: { name: 'attacker' },
onDelete: 'CASCADE' onDelete: 'CASCADE'
}); });
this.models.SteamUser.hasMany(this.models.Wound, { this.models.Player.hasMany(this.models.Wound, {
sourceKey: 'steamID',
foreignKey: { name: 'victim' }, foreignKey: { name: 'victim' },
onDelete: 'CASCADE' onDelete: 'CASCADE'
}); });
this.models.SteamUser.hasMany(this.models.Death, { this.models.Player.hasMany(this.models.Death, {
sourceKey: 'steamID',
foreignKey: { name: 'attacker' }, foreignKey: { name: 'attacker' },
onDelete: 'CASCADE' onDelete: 'CASCADE'
}); });
this.models.SteamUser.hasMany(this.models.Death, { this.models.Player.hasMany(this.models.Death, {
sourceKey: 'steamID',
foreignKey: { name: 'victim' }, foreignKey: { name: 'victim' },
onDelete: 'CASCADE' onDelete: 'CASCADE'
}); });
this.models.SteamUser.hasMany(this.models.Revive, { this.models.Player.hasMany(this.models.Revive, {
sourceKey: 'steamID',
foreignKey: { name: 'attacker' }, foreignKey: { name: 'attacker' },
onDelete: 'CASCADE' onDelete: 'CASCADE'
}); });
this.models.SteamUser.hasMany(this.models.Revive, { this.models.Player.hasMany(this.models.Revive, {
sourceKey: 'steamID',
foreignKey: { name: 'victim' }, foreignKey: { name: 'victim' },
onDelete: 'CASCADE' onDelete: 'CASCADE'
}); });
this.models.SteamUser.hasMany(this.models.Revive, { this.models.Player.hasMany(this.models.Revive, {
sourceKey: 'steamID',
foreignKey: { name: 'reviver' }, foreignKey: { name: 'reviver' },
onDelete: 'CASCADE' onDelete: 'CASCADE'
}); });
@ -392,9 +437,12 @@ export default class DBLog extends BasePlugin {
this.onTickRate = this.onTickRate.bind(this); this.onTickRate = this.onTickRate.bind(this);
this.onUpdatedA2SInformation = this.onUpdatedA2SInformation.bind(this); this.onUpdatedA2SInformation = this.onUpdatedA2SInformation.bind(this);
this.onNewGame = this.onNewGame.bind(this); this.onNewGame = this.onNewGame.bind(this);
this.onPlayerConnected = this.onPlayerConnected.bind(this);
this.onPlayerWounded = this.onPlayerWounded.bind(this); this.onPlayerWounded = this.onPlayerWounded.bind(this);
this.onPlayerDied = this.onPlayerDied.bind(this); this.onPlayerDied = this.onPlayerDied.bind(this);
this.onPlayerRevived = this.onPlayerRevived.bind(this); this.onPlayerRevived = this.onPlayerRevived.bind(this);
this.migrateSteamUsersIntoPlayers = this.migrateSteamUsersIntoPlayers.bind(this);
this.dropAllForeignKeys = this.dropAllForeignKeys.bind(this);
} }
createModel(name, schema) { createModel(name, schema) {
@ -409,12 +457,15 @@ export default class DBLog extends BasePlugin {
await this.models.TickRate.sync(); await this.models.TickRate.sync();
await this.models.PlayerCount.sync(); await this.models.PlayerCount.sync();
await this.models.SteamUser.sync(); await this.models.SteamUser.sync();
await this.models.Player.sync();
await this.models.Wound.sync(); await this.models.Wound.sync();
await this.models.Death.sync(); await this.models.Death.sync();
await this.models.Revive.sync(); await this.models.Revive.sync();
} }
async mount() { async mount() {
await this.migrateSteamUsersIntoPlayers();
await this.models.Server.upsert({ await this.models.Server.upsert({
id: this.options.overrideServerID || this.server.id, id: this.options.overrideServerID || this.server.id,
name: this.server.serverName name: this.server.serverName
@ -427,6 +478,7 @@ export default class DBLog extends BasePlugin {
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_CONNECTED', this.onPlayerConnected);
this.server.on('PLAYER_WOUNDED', this.onPlayerWounded); this.server.on('PLAYER_WOUNDED', this.onPlayerWounded);
this.server.on('PLAYER_DIED', this.onPlayerDied); this.server.on('PLAYER_DIED', this.onPlayerDied);
this.server.on('PLAYER_REVIVED', this.onPlayerRevived); this.server.on('PLAYER_REVIVED', this.onPlayerRevived);
@ -436,6 +488,7 @@ export default class DBLog extends BasePlugin {
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_CONNECTED', this.onPlayerConnected);
this.server.removeEventListener('PLAYER_WOUNDED', this.onPlayerWounded); this.server.removeEventListener('PLAYER_WOUNDED', this.onPlayerWounded);
this.server.removeEventListener('PLAYER_DIED', this.onPlayerDied); this.server.removeEventListener('PLAYER_DIED', this.onPlayerDied);
this.server.removeEventListener('PLAYER_REVIVED', this.onPlayerRevived); this.server.removeEventListener('PLAYER_REVIVED', this.onPlayerRevived);
@ -479,15 +532,27 @@ export default class DBLog extends BasePlugin {
async onPlayerWounded(info) { async onPlayerWounded(info) {
if (info.attacker) if (info.attacker)
await this.models.SteamUser.upsert({ await this.models.Player.upsert(
steamID: info.attacker.steamID, {
lastName: info.attacker.name eosID: info.attacker.eosID,
}); steamID: info.attacker.steamID,
lastName: info.attacker.name
},
{
conflictFields: ['steamID']
}
);
if (info.victim) if (info.victim)
await this.models.SteamUser.upsert({ await this.models.Player.upsert(
steamID: info.victim.steamID, {
lastName: info.victim.name eosID: info.victim.eosID,
}); steamID: info.victim.steamID,
lastName: info.victim.name
},
{
conflictFields: ['steamID']
}
);
await this.models.Wound.create({ await this.models.Wound.create({
server: this.options.overrideServerID || this.server.id, server: this.options.overrideServerID || this.server.id,
@ -509,15 +574,27 @@ export default class DBLog extends BasePlugin {
async onPlayerDied(info) { async onPlayerDied(info) {
if (info.attacker) if (info.attacker)
await this.models.SteamUser.upsert({ await this.models.Player.upsert(
steamID: info.attacker.steamID, {
lastName: info.attacker.name eosID: info.attacker.eosID,
}); steamID: info.attacker.steamID,
lastName: info.attacker.name
},
{
conflictFields: ['steamID']
}
);
if (info.victim) if (info.victim)
await this.models.SteamUser.upsert({ await this.models.Player.upsert(
steamID: info.victim.steamID, {
lastName: info.victim.name eosID: info.victim.eosID,
}); steamID: info.victim.steamID,
lastName: info.victim.name
},
{
conflictFields: ['steamID']
}
);
await this.models.Death.create({ await this.models.Death.create({
server: this.options.overrideServerID || this.server.id, server: this.options.overrideServerID || this.server.id,
@ -540,20 +617,38 @@ export default class DBLog extends BasePlugin {
async onPlayerRevived(info) { async onPlayerRevived(info) {
if (info.attacker) if (info.attacker)
await this.models.SteamUser.upsert({ await this.models.Player.upsert(
steamID: info.attacker.steamID, {
lastName: info.attacker.name eosID: info.attacker.eosID,
}); steamID: info.attacker.steamID,
lastName: info.attacker.name
},
{
conflictFields: ['steamID']
}
);
if (info.victim) if (info.victim)
await this.models.SteamUser.upsert({ await this.models.Player.upsert(
steamID: info.victim.steamID, {
lastName: info.victim.name eosID: info.victim.eosID,
}); steamID: info.victim.steamID,
lastName: info.victim.name
},
{
conflictFields: ['steamID']
}
);
if (info.reviver) if (info.reviver)
await this.models.SteamUser.upsert({ await this.models.Player.upsert(
steamID: info.reviver.steamID, {
lastName: info.reviver.name eosID: info.reviver.eosID,
}); steamID: info.reviver.steamID,
lastName: info.reviver.name
},
{
conflictFields: ['steamID']
}
);
await this.models.Revive.create({ await this.models.Revive.create({
server: this.options.overrideServerID || this.server.id, server: this.options.overrideServerID || this.server.id,
@ -577,4 +672,89 @@ export default class DBLog extends BasePlugin {
reviverSquadID: info.reviver ? info.reviver.squadID : null reviverSquadID: info.reviver ? info.reviver.squadID : null
}); });
} }
async onPlayerConnected(info) {
await this.models.Player.upsert(
{
eosID: info.eosID,
steamID: info.player.steamID,
lastName: info.player.name,
lastIP: info.ip
},
{
conflictFields: ['steamID']
}
);
}
async migrateSteamUsersIntoPlayers() {
try {
const steamUsersCount = await this.models.SteamUser.count();
const playersCount = await this.models.Player.count();
if (steamUsersCount < playersCount) {
this.verbose(
1,
`Skipping migration from SteamUsers to Players due to a previous successful migration.`
);
return;
}
await this.dropAllForeignKeys();
const steamUsers = (await this.models.SteamUser.findAll()).map((u) => u.dataValues);
await this.models.Player.bulkCreate(steamUsers);
this.verbose(1, `Migration from SteamUsers to Players successful`);
} catch (error) {
this.verbose(1, `Error during Migration from SteamUsers to Players: ${error}`);
}
}
async dropAllForeignKeys() {
this.verbose(
1,
`Starting to drop constraints on DB: ${this.options.database.config.database} related to DBLog_SteamUsers deptecated table.`
);
for (const modelName in this.models) {
const model = this.models[modelName];
const tableName = model.tableName;
try {
const result = await this.options.database.query(
`SELECT * FROM information_schema.key_column_usage WHERE referenced_table_name IS NOT NULL AND table_schema = '${this.options.database.config.database}' AND table_name = '${tableName}';`,
{ type: QueryTypes.SELECT }
);
for (const r of result) {
if (r.REFERENCED_TABLE_NAME === 'DBLog_SteamUsers') {
this.verbose(
1,
`Found constraint ${r.COLUMN_NAME} on table ${tableName}, referencing ${r.REFERENCED_COLUMN_NAME} on ${r.REFERENCED_TABLE_NAME}`
);
await this.options.database
.query(`ALTER TABLE ${tableName} DROP FOREIGN KEY ${r.CONSTRAINT_NAME}`, {
type: QueryTypes.RAW
})
.then(() => {
this.verbose(1, `Dropped foreign key ${r.COLUMN_NAME} on table ${tableName}`);
})
.catch((e) => {
this.verbose(
1,
`Error dropping foreign key ${r.COLUMN_NAME} on table ${tableName}:`,
e
);
});
}
}
} catch (error) {
this.verbose(1, `Error dropping foreign keys for table ${tableName}:`, error);
} finally {
model.sync();
}
}
await this.models.Player.sync();
}
} }

View File

@ -4,7 +4,7 @@ import Rcon from 'core/rcon';
export default class SquadRcon extends Rcon { export default class SquadRcon extends Rcon {
processChatPacket(decodedPacket) { processChatPacket(decodedPacket) {
const matchChat = decodedPacket.body.match( const matchChat = decodedPacket.body.match(
/\[(ChatAll|ChatTeam|ChatSquad|ChatAdmin)] \[SteamID:([0-9]{17})] (.+?) : (.*)/ /\[(ChatAll|ChatTeam|ChatSquad|ChatAdmin)] \[Online IDs:EOS: ([0-9a-f]{32}) steam: (\d{17})\] (.+?) : (.*)/
); );
if (matchChat) { if (matchChat) {
Logger.verbose('SquadRcon', 2, `Matched chat message: ${decodedPacket.body}`); Logger.verbose('SquadRcon', 2, `Matched chat message: ${decodedPacket.body}`);
@ -12,9 +12,10 @@ export default class SquadRcon extends Rcon {
this.emit('CHAT_MESSAGE', { this.emit('CHAT_MESSAGE', {
raw: decodedPacket.body, raw: decodedPacket.body,
chat: matchChat[1], chat: matchChat[1],
steamID: matchChat[2], eosID: matchChat[2],
name: matchChat[3], steamID: matchChat[3],
message: matchChat[4], name: matchChat[4],
message: matchChat[5],
time: new Date() time: new Date()
}); });
@ -22,14 +23,14 @@ export default class SquadRcon extends Rcon {
} }
const matchPossessedAdminCam = decodedPacket.body.match( const matchPossessedAdminCam = decodedPacket.body.match(
/\[SteamID:([0-9]{17})] (.+?) has possessed admin camera./ /\[Online Ids:EOS: ([0-9a-f]{32}) steam: (\d{17})\] (.+) has possessed admin camera\./
); );
if (matchPossessedAdminCam) { if (matchPossessedAdminCam) {
Logger.verbose('SquadRcon', 2, `Matched admin camera possessed: ${decodedPacket.body}`); Logger.verbose('SquadRcon', 2, `Matched admin camera possessed: ${decodedPacket.body}`);
this.emit('POSSESSED_ADMIN_CAMERA', { this.emit('POSSESSED_ADMIN_CAMERA', {
raw: decodedPacket.body, raw: decodedPacket.body,
steamID: matchPossessedAdminCam[1], steamID: matchPossessedAdminCam[2],
name: matchPossessedAdminCam[2], name: matchPossessedAdminCam[3],
time: new Date() time: new Date()
}); });
@ -37,14 +38,14 @@ export default class SquadRcon extends Rcon {
} }
const matchUnpossessedAdminCam = decodedPacket.body.match( const matchUnpossessedAdminCam = decodedPacket.body.match(
/\[SteamID:([0-9]{17})] (.+?) has unpossessed admin camera./ /\[Online IDs:EOS: ([0-9a-f]{32}) steam: (\d{17})\] (.+) has unpossessed admin camera\./
); );
if (matchUnpossessedAdminCam) { if (matchUnpossessedAdminCam) {
Logger.verbose('SquadRcon', 2, `Matched admin camera possessed: ${decodedPacket.body}`); Logger.verbose('SquadRcon', 2, `Matched admin camera possessed: ${decodedPacket.body}`);
this.emit('UNPOSSESSED_ADMIN_CAMERA', { this.emit('UNPOSSESSED_ADMIN_CAMERA', {
raw: decodedPacket.body, raw: decodedPacket.body,
steamID: matchUnpossessedAdminCam[1], steamID: matchUnpossessedAdminCam[2],
name: matchUnpossessedAdminCam[2], name: matchUnpossessedAdminCam[3],
time: new Date() time: new Date()
}); });
@ -68,7 +69,7 @@ export default class SquadRcon extends Rcon {
} }
const matchKick = decodedPacket.body.match( const matchKick = decodedPacket.body.match(
/Kicked player ([0-9]+)\. \[steamid=([0-9]{17})] (.*)/ /Kicked player ([0-9]+)\. \[Online IDs= EOS: ([0-9a-f]{32}) steam: (\d{17})] (.*)/
); );
if (matchKick) { if (matchKick) {
Logger.verbose('SquadRcon', 2, `Matched kick message: ${decodedPacket.body}`); Logger.verbose('SquadRcon', 2, `Matched kick message: ${decodedPacket.body}`);
@ -76,8 +77,8 @@ export default class SquadRcon extends Rcon {
this.emit('PLAYER_KICKED', { this.emit('PLAYER_KICKED', {
raw: decodedPacket.body, raw: decodedPacket.body,
playerID: matchKick[1], playerID: matchKick[1],
steamID: matchKick[2], steamID: matchKick[3],
name: matchKick[3], name: matchKick[4],
time: new Date() time: new Date()
}); });
@ -85,18 +86,14 @@ export default class SquadRcon extends Rcon {
} }
const matchSqCreated = decodedPacket.body.match( const matchSqCreated = decodedPacket.body.match(
/(.+) \(Steam ID: ([0-9]{17})\) has created Squad (\d+) \(Squad Name: (.+)\) on (.+)/ /(?<playerName>.+) \(Online IDs: EOS: (?<playerEOSID>[\da-f]{32})(?: steam: (?<playerSteamID>\d{17}))?\) has created Squad (?<squadID>\d+) \(Squad Name: (?<squadName>.+)\) on (?<teamName>.+)/
); );
if (matchSqCreated) { if (matchSqCreated) {
Logger.verbose('SquadRcon', 2, `Matched Squad Created: ${decodedPacket.body}`); Logger.verbose('SquadRcon', 2, `Matched Squad Created: ${decodedPacket.body}`);
this.emit('SQUAD_CREATED', { this.emit('SQUAD_CREATED', {
time: new Date(), time: new Date(),
playerName: matchSqCreated[1], ...matchSqCreated.groups
playerSteamID: matchSqCreated[2],
squadID: matchSqCreated[3],
squadName: matchSqCreated[4],
teamName: matchSqCreated[5]
}); });
return; return;
@ -143,19 +140,17 @@ export default class SquadRcon extends Rcon {
for (const line of response.split('\n')) { for (const line of response.split('\n')) {
const match = line.match( const match = line.match(
/ID: ([0-9]+) \| SteamID: ([0-9]{17}) \| Name: (.+) \| Team ID: ([0-9]+) \| Squad ID: ([0-9]+|N\/A) \| Is Leader: (True|False) \| Role: ([A-Za-z0-9_]*)\b/ /^ID: (?<playerID>\d+) \| Online IDs: EOS: (?<eosID>[a-f\d]{32}) (?:steam: (?<steamID>\d{17}) )?\| Name: (?<name>.+) \| Team ID: (?<teamID>\d|N\/A) \| Squad ID: (?<squadID>\d+|N\/A) \| Is Leader: (?<isLeader>True|False) \| Role: (?<role>.+)$/
); );
if (!match) continue; if (!match) continue;
players.push({ const data = match.groups;
playerID: match[1], data.playerID = +data.playerID;
steamID: match[2], data.isLeader = data.isLeader === 'True';
name: match[3], data.teamID = data.teamID !== 'N/A' ? +data.teamID : null;
teamID: match[4], data.squadID = data.squadID !== 'N/A' ? +data.squadID : null;
squadID: match[5] !== 'N/A' ? match[5] : null,
isLeader: match[6] === 'True', players.push(data);
role: match[7]
});
} }
return players; return players;
@ -172,21 +167,17 @@ export default class SquadRcon extends Rcon {
for (const line of responseSquad.split('\n')) { for (const line of responseSquad.split('\n')) {
const match = line.match( const match = line.match(
/ID: ([0-9]+) \| Name: (.+) \| Size: ([0-9]+) \| Locked: (True|False) \| Creator Name: (.+) \| Creator Steam ID: ([0-9]{17})/ /ID: (?<squadID>\d+) \| Name: (?<squadName>.+) \| Size: (?<size>\d+) \| Locked: (?<locked>True|False) \| Creator Name: (?<creatorName>.+) \| Creator Online IDs: EOS: (?<creatorEOSID>[a-f\d]{32})(?: steam: (?<creatorSteamID>\d{17}))?/
); );
const matchSide = line.match(/Team ID: (1|2) \((.+)\)/); const matchSide = line.match(/Team ID: (\d) \((.+)\)/);
if (matchSide) { if (matchSide) {
teamID = matchSide[1]; teamID = +matchSide[1];
teamName = matchSide[2]; teamName = matchSide[2];
} }
if (!match) continue; if (!match) continue;
match.groups.squadID = +match.groups.squadID;
squads.push({ squads.push({
squadID: match[1], ...match.groups,
squadName: match[2],
size: match[3],
locked: match[4],
creatorName: match[5],
creatorSteamID: match[6],
teamID: teamID, teamID: teamID,
teamName: teamName teamName: teamName
}); });