Skip to content

Commit 4c8f2cd

Browse files
author
Ecnama
committed
Log KDA
1 parent 46b1f89 commit 4c8f2cd

File tree

2 files changed

+187
-5
lines changed

2 files changed

+187
-5
lines changed

backend/src/match.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { Rcon } from './rcon-client';
2828
import { Settings } from './settings';
2929
import * as Storage from './storage';
3030
import * as Team from './team';
31+
import * as StatsLogger from './statsLogger';
3132

3233
const STORAGE_LOGS_PREFIX = 'logs_';
3334
const STORAGE_LOGS_SUFFIX = '.jsonl';
@@ -226,6 +227,7 @@ const setup = async (match: Match) => {
226227
await setTeamNames(match);
227228

228229
await execRcon(match, 'log on');
230+
await execRcon(match, 'mp_logdetail 1');
229231
await execRcon(match, 'mp_warmuptime 600');
230232
await execRcon(match, 'mp_warmup_pausetimer 1');
231233
await execRcon(match, 'mp_autokick 0');
@@ -596,6 +598,48 @@ const onPlayerLogLine = async (
596598
player = match.data.players.find((p) => p.steamId64 === steamId64);
597599
if (!player) {
598600
player = Player.create(match, steamId, name);
601+
const playerExists =
602+
(
603+
(await Storage.queryDB(
604+
`SELECT * FROM ${StatsLogger.PLAYERS_TABLE} WHERE steamId = '${player.steamId64}'`
605+
)) as any[]
606+
).length > 0;
607+
if (!playerExists) {
608+
await Storage.insertDB(
609+
StatsLogger.PLAYERS_TABLE,
610+
new Map<string, string | number>([
611+
['steamId', player.steamId64],
612+
['name', player.name],
613+
['tKills', 0],
614+
['tDeaths', 0],
615+
['tAssists', 0],
616+
['tDiff', 0],
617+
['tHits', 0],
618+
['tHeadshots', 0],
619+
['tHsPct', 0],
620+
['tRounds', 0],
621+
['tDamages', 0],
622+
['tAdr', 0],
623+
])
624+
);
625+
}
626+
await Storage.insertDB(
627+
StatsLogger.PLAYERS_TABLE,
628+
new Map<string, string | number>([
629+
['steamId', player.steamId64],
630+
['matchId', match.data.id],
631+
['kills', 0],
632+
['deaths', 0],
633+
['assists', 0],
634+
['diff', 0],
635+
['hits', 0],
636+
['headshots', 0],
637+
['hsPct', 0],
638+
['rounds', 0],
639+
['damages', 0],
640+
['adr', 0],
641+
])
642+
);
599643
match.log(`Player ${player.steamId64} (${name}) created`);
600644
match.data.players.push(player);
601645
player = match.data.players[match.data.players.length - 1]!; // re-assign to work nicely with changeListener (ProxyHandler)
@@ -669,6 +713,44 @@ const onPlayerLogLine = async (
669713
await onPlayerSay(match, player, message, isTeamChat, teamString);
670714
return;
671715
}
716+
717+
//attacked "PlayerName<1><U:1:12345678><CT>" [2397 2079 133] with "glock" (damage "117") (damage_armor "0") (health "0") (armor "0") (hitgroup "head")
718+
const damageMatch = remainingLine.match(
719+
/^attacked ".+<\d+><([\[\]\w:]+)><(?:TERRORIST|CT)>" \[-?\d+ -?\d+ -?\d+\] with "\w+" \(damage "(\d+)"\) \(damage_armor "(\d+)"\) \(health "(\d+)"\) \(armor "(\d+)"\) \(hitgroup "([\w ]+)"\)$/
720+
);
721+
if (damageMatch) {
722+
const victimId = damageMatch[1]!;
723+
const damage = Number(damageMatch[2]);
724+
const damageArmor = Number(damageMatch[3]);
725+
const headshot = damageMatch[4] == 'head';
726+
await StatsLogger.onDamage(match.data.id, steamId, victimId, damage, damageArmor, headshot);
727+
return;
728+
}
729+
730+
//killed "PlayerName<2><STEAM_1:1:12345678><TERRORIST>" [-100 150 60] with "ak47" (headshot)
731+
const killMatch = remainingLine.match(
732+
/^killed ".+<\d+><([\[\]\w:]+)><(?:|Unassigned|TERRORIST|CT)>" \[-?\d+ -?\d+ -?\d+\] with "\w+" ?\(?(headshot|penetrated|headshot penetrated)?\)?$/
733+
);
734+
if (killMatch) {
735+
const victimId = killMatch[1]!;
736+
await StatsLogger.onKill(match.data.id, steamId, victimId);
737+
return;
738+
}
739+
740+
//assisted killing "PlayerName2<3><STEAM_1:1:87654321><CT>"
741+
const assistMatch = remainingLine.match(/^assisted killing/);
742+
if (assistMatch) {
743+
await StatsLogger.onAssist(match.data.id, steamId);
744+
return;
745+
}
746+
747+
//committed suicide with "world"
748+
//was killed by the bomb
749+
const bombKillMatch = remainingLine.match(/^(?:was killed by the bomb|committed suicide with)/);
750+
if (bombKillMatch) {
751+
await StatsLogger.onOtherDeath(match.data.id, steamId);
752+
return;
753+
}
672754
};
673755

674756
const onPlayerSay = async (

backend/src/statsLogger.ts

Lines changed: 105 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { SqlAttribute, TableSchema } from './tableSchema';
2-
import { createTableDB } from './storage';
2+
import { createTableDB, queryDB, updateDB } from './storage';
33

4-
const PLAYERS_TABLE = 'players';
5-
const MATCHES_TABLE = 'matches';
6-
const PLAYER_MATCH_STATS_TABLE = 'playerMatchStats';
4+
export const PLAYERS_TABLE = 'players';
5+
export const MATCHES_TABLE = 'matches';
6+
export const PLAYER_MATCH_STATS_TABLE = 'playerMatchStats';
77

88
export const setup = async () => {
99
// Create players global stats table
@@ -14,7 +14,15 @@ export const setup = async () => {
1414
{ name: 'tDeaths', type: 'INTEGER' },
1515
{ name: 'tAssists', type: 'INTEGER' },
1616
{ name: 'tDiff', type: 'INTEGER' },
17+
{ name: 'tHits', type: 'INTEGER' },
1718
{ name: 'tHeadshots', type: 'INTEGER' },
19+
{
20+
name: 'tHsPct',
21+
type: 'FLOAT',
22+
constraints: 'CHECK (tHeadshots >= 0 AND tHeadshots <= 100)',
23+
},
24+
{ name: 'tRounds', type: 'INTEGER' },
25+
{ name: 'tDamages', type: 'INTEGER' },
1826
{ name: 'tAdr', type: 'INTEGER' },
1927
] as SqlAttribute[];
2028
const playersTableSchema = new TableSchema(PLAYERS_TABLE, playersAttributes, ['steamId']);
@@ -49,11 +57,15 @@ export const setup = async () => {
4957
{ name: 'deaths', type: 'INTEGER' },
5058
{ name: 'assists', type: 'INTEGER' },
5159
{ name: 'diff', type: 'INTEGER' },
60+
{ name: 'hits', type: 'INTEGER' },
61+
{ name: 'headshots', type: 'INTEGER' },
5262
{
53-
name: 'headshots',
63+
name: 'hsPct',
5464
type: 'FLOAT',
5565
constraints: 'CHECK (headshots >= 0 AND headshots <= 100)',
5666
},
67+
{ name: 'rounds', type: 'INTEGER' },
68+
{ name: 'damages', type: 'INTEGER' },
5769
{ name: 'adr', type: 'FLOAT' },
5870
] as SqlAttribute[];
5971
const playerMatchStatsTableSchema = new TableSchema(
@@ -63,3 +75,91 @@ export const setup = async () => {
6375
);
6476
await createTableDB(playerMatchStatsTableSchema);
6577
};
78+
79+
export const onDamage = async (
80+
matchId: string,
81+
attackerId: string,
82+
victimId: string,
83+
damage: number,
84+
damageArmor: number,
85+
headshot: boolean
86+
) => {
87+
// TODO
88+
};
89+
90+
export const onKill = async (matchId: string, killerId: string, victimId: string) => {
91+
const currentKillerMatchStats = (await queryDB(
92+
`SELECT kills FROM ${PLAYER_MATCH_STATS_TABLE} WHERE steamId = '${killerId}' AND matchId = '${matchId}'`
93+
)) as number;
94+
const currentVictimMatchStats = (await queryDB(
95+
`SELECT deaths FROM ${PLAYER_MATCH_STATS_TABLE} WHERE steamId = '${victimId}' AND matchId = '${matchId}'`
96+
)) as number;
97+
const currentKillerGlobalStats = (await queryDB(
98+
`SELECT tKills FROM ${PLAYERS_TABLE} WHERE steamId = '${killerId}'`
99+
)) as number;
100+
const currentVictimGlobalStats = (await queryDB(
101+
`SELECT tDeaths FROM ${PLAYERS_TABLE} WHERE steamId = '${victimId}'`
102+
)) as number;
103+
await updateDB(
104+
PLAYER_MATCH_STATS_TABLE,
105+
new Map<string, number>([['kills', currentKillerMatchStats + 1]]),
106+
`steamId = '${killerId}' AND matchId = '${matchId}'`
107+
);
108+
await updateDB(
109+
PLAYER_MATCH_STATS_TABLE,
110+
new Map<string, number>([['deaths', currentVictimMatchStats + 1]]),
111+
`steamId = '${victimId}' AND matchId = '${matchId}'`
112+
);
113+
await updateDB(
114+
PLAYERS_TABLE,
115+
new Map<string, number>([['tKills', currentKillerGlobalStats + 1]]),
116+
`steamId = '${killerId}'`
117+
);
118+
await updateDB(
119+
PLAYERS_TABLE,
120+
new Map<string, number>([['tDeaths', currentVictimGlobalStats + 1]]),
121+
`steamId = '${victimId}'`
122+
);
123+
};
124+
125+
export const onAssist = async (matchId: string, attackerId: string) => {
126+
const currentAttackerMatchStats = (await queryDB(
127+
`SELECT assists FROM ${PLAYER_MATCH_STATS_TABLE} WHERE steamId = '${attackerId}' AND matchId = '${matchId}'`
128+
)) as number;
129+
const currentAttackerGlobalStats = (await queryDB(
130+
`SELECT tAssists FROM ${PLAYERS_TABLE} WHERE steamId = '${attackerId}'`
131+
)) as number;
132+
await updateDB(
133+
PLAYER_MATCH_STATS_TABLE,
134+
new Map<string, number>([['assists', currentAttackerMatchStats + 1]]),
135+
`steamId = '${attackerId}' AND matchId = '${matchId}'`
136+
);
137+
await updateDB(
138+
PLAYERS_TABLE,
139+
new Map<string, number>([['tAssists', currentAttackerGlobalStats + 1]]),
140+
`steamId = '${attackerId}'`
141+
);
142+
};
143+
144+
export const onOtherDeath = async (matchId: string, victimId: string) => {
145+
const currentVictimMatchStats = (await queryDB(
146+
`SELECT deaths FROM ${PLAYER_MATCH_STATS_TABLE} WHERE steamId = '${victimId}' AND matchId = '${matchId}'`
147+
)) as number;
148+
const currentVictimGlobalStats = (await queryDB(
149+
`SELECT tDeaths FROM ${PLAYERS_TABLE} WHERE steamId = '${victimId}'`
150+
)) as number;
151+
await updateDB(
152+
PLAYER_MATCH_STATS_TABLE,
153+
new Map<string, number>([['deaths', currentVictimMatchStats + 1]]),
154+
`steamId = '${victimId}' AND matchId = '${matchId}'`
155+
);
156+
await updateDB(
157+
PLAYERS_TABLE,
158+
new Map<string, number>([['tDeaths', currentVictimGlobalStats + 1]]),
159+
`steamId = '${victimId}'`
160+
);
161+
};
162+
163+
export const updateRoundCount = async (matchId: string) => {
164+
//TODO
165+
};

0 commit comments

Comments
 (0)