docs: Comprehensive analysis of missing backend API data
Document all frontend expectations vs backend reality: - Critical: KAST%, ADR, weapon kills (all show 0) - High: Round winner/reason, damage stats - Medium: Utility stats, per-round performance, loss bonus Includes impact summary, code locations, expected formats, and backend implementation checklist. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,11 +1,157 @@
|
||||
# Missing Backend API Data
|
||||
|
||||
This document outlines data that the frontend is ready to display but is not currently provided by the backend API. These features would enhance the match analysis experience.
|
||||
This document outlines data that the frontend expects but is not currently provided by the backend API. These features would enhance the match analysis experience.
|
||||
|
||||
## Round Winner & Win Reason
|
||||
---
|
||||
|
||||
## Impact Summary
|
||||
|
||||
| Field | Severity | Status | Impact |
|
||||
| --------------- | ------------ | ------------- | ----------------------------- |
|
||||
| KAST % | **Critical** | Missing | Players display 0% KAST |
|
||||
| ADR | **Critical** | Missing | Statistics incomplete |
|
||||
| Weapon Kills | **High** | Missing | Weapon stats show 0 kills |
|
||||
| Round Winner | **High** | Missing | No round outcome analysis |
|
||||
| Win Reason | **High** | Missing | Can't show how rounds ended |
|
||||
| Headshot % | Medium | Partial | Sometimes missing |
|
||||
| Damage Stats | Medium | Not extracted | Data sent but unused |
|
||||
| Utility Stats | Medium | Missing | Grenade analysis unavailable |
|
||||
| Per-Round Stats | Medium | Missing | No round-by-round performance |
|
||||
| Loss Bonus | Low | Missing | Economy analysis incomplete |
|
||||
|
||||
---
|
||||
|
||||
## 1. Critical: KAST Percentage
|
||||
|
||||
**Endpoint**: `GET /match/{id}` (player stats)
|
||||
|
||||
**Location in Frontend**: `src/lib/api/players.ts:49-76`
|
||||
|
||||
**Issue**: KAST (Kill/Assist/Survive/Trade) is completely unavailable. Frontend returns placeholder 0.
|
||||
|
||||
```typescript
|
||||
// Current workaround in players.ts
|
||||
const totalKast = recentMatches.reduce((sum, _m) => {
|
||||
// KAST is a percentage, we need to calculate it
|
||||
// For now, we'll use a placeholder
|
||||
return sum + 0;
|
||||
}, 0);
|
||||
```
|
||||
|
||||
**Affected Components**:
|
||||
|
||||
- `/player/[id]/+page.svelte` - Player profile stats chart
|
||||
- `/match/[id]/+page.svelte:28-29` - Team average KAST
|
||||
- Match scoreboard KAST% column
|
||||
|
||||
**Expected Format**:
|
||||
|
||||
```json
|
||||
{
|
||||
"players": [
|
||||
{
|
||||
"kast": 72.5
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Backend Implementation**: Calculate per player per match:
|
||||
|
||||
```
|
||||
KAST% = (Rounds with Kill + Rounds with Assist + Rounds Survived + Rounds Traded) / Total Rounds * 100
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Critical: Average Damage per Round (ADR)
|
||||
|
||||
**Endpoint**: `GET /match/{id}` (player stats)
|
||||
|
||||
**Location in Frontend**: `src/lib/types/Match.ts:114-115`
|
||||
|
||||
**Issue**: ADR not provided. Components use `player.adr || 0` fallback.
|
||||
|
||||
**Affected Components**:
|
||||
|
||||
- `/match/[id]/+page.svelte:27, 44, 83` - Team and player ADR
|
||||
- `/match/[id]/damage/+page.svelte` - Damage analysis page
|
||||
- `/player/[id]/+page.svelte:280` - Player profile
|
||||
|
||||
**Expected Format**:
|
||||
|
||||
```json
|
||||
{
|
||||
"players": [
|
||||
{
|
||||
"adr": 85.3
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Backend Implementation**:
|
||||
|
||||
```
|
||||
ADR = Total Damage to Enemies / Rounds Played
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. High: Weapon Kill Counts
|
||||
|
||||
**Endpoint**: `GET /match/{id}/weapons`
|
||||
|
||||
**Location in Frontend**: `src/lib/api/transformers/weaponsTransformer.ts:85-87`
|
||||
|
||||
**Issue**: Backend provides damage/hits but NOT kill counts. All weapons show 0 kills.
|
||||
|
||||
```typescript
|
||||
// Current workaround
|
||||
weapon_stats.push({
|
||||
// Kill data not available - API only provides hit/damage events
|
||||
kills: 0, // HARDCODED ZERO - should be from backend
|
||||
damage: stats.damage,
|
||||
hits: stats.hits
|
||||
});
|
||||
```
|
||||
|
||||
**Expected Format**:
|
||||
|
||||
```json
|
||||
{
|
||||
"weapons": {
|
||||
"7": {
|
||||
"kills": 12,
|
||||
"damage": 1847,
|
||||
"hits": 45
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Backend Implementation**: Correlate `player_hurt` events with `player_death` events to determine which weapon dealt the killing blow.
|
||||
|
||||
---
|
||||
|
||||
## 4. High: Round Winner & Win Reason
|
||||
|
||||
**Endpoint**: `GET /match/{id}/rounds`
|
||||
|
||||
**Location in Frontend**: `src/lib/api/transformers/roundsTransformer.ts:71-76`
|
||||
|
||||
**Issue**: Backend only returns economy data, not round outcomes.
|
||||
|
||||
```typescript
|
||||
// Current hardcoded placeholders
|
||||
rounds.push({
|
||||
round: roundNum + 1,
|
||||
winner: 0, // HARDCODED - should be 2 (T) or 3 (CT)
|
||||
win_reason: '', // HARDCODED - should be enum
|
||||
players
|
||||
});
|
||||
```
|
||||
|
||||
**Currently Returns**:
|
||||
|
||||
```json
|
||||
@@ -15,28 +161,13 @@ This document outlines data that the frontend is ready to display but is not cur
|
||||
}
|
||||
```
|
||||
|
||||
**Missing Fields Needed**:
|
||||
|
||||
| Field | Type | Description |
|
||||
| ------------ | -------- | -------------------------------------- |
|
||||
| `winner` | `number` | Team ID that won the round (1=T, 2=CT) |
|
||||
| `win_reason` | `string` | How the round ended |
|
||||
|
||||
**Win Reason Values**:
|
||||
|
||||
- `elimination` - All enemies killed
|
||||
- `bomb_defused` - CT defused the bomb
|
||||
- `bomb_exploded` - Bomb detonated successfully
|
||||
- `time` - Round timer expired (CT win)
|
||||
- `target_saved` - Hostage rescued (rare mode)
|
||||
|
||||
**Expected Response Format**:
|
||||
**Expected Format**:
|
||||
|
||||
```json
|
||||
{
|
||||
"0": {
|
||||
"winner": 1,
|
||||
"win_reason": "elimination",
|
||||
"winner": 2,
|
||||
"win_reason": "bomb_exploded",
|
||||
"players": {
|
||||
"player_id": [bank, equipment, spent]
|
||||
}
|
||||
@@ -44,35 +175,150 @@ This document outlines data that the frontend is ready to display but is not cur
|
||||
}
|
||||
```
|
||||
|
||||
**Implementation**: Demo parser needs to capture `RoundEnd` game events and extract:
|
||||
**Win Reason Values**:
|
||||
| Value | Description |
|
||||
|-------|-------------|
|
||||
| `elimination` | All enemies killed |
|
||||
| `bomb_defused` | CT defused the bomb |
|
||||
| `bomb_exploded` | Bomb detonated (T win) |
|
||||
| `time` | Round timer expired (CT win) |
|
||||
| `target_saved` | Hostage rescued (rare) |
|
||||
|
||||
- `winner` field from the event
|
||||
- `reason` field from the event
|
||||
**Backend Implementation**: Extract from `RoundEnd` game events in demo parser.
|
||||
|
||||
---
|
||||
|
||||
## Per-Round Player Stats
|
||||
## 5. Medium: Damage Statistics (dmg_enemy, dmg_team)
|
||||
|
||||
**Endpoint**: `GET /match/{id}` (player stats)
|
||||
|
||||
**Location in Frontend**: `src/lib/api/transformers.ts:96`
|
||||
|
||||
**Issue**: Backend sends `dmg` as `Record<string, unknown>` but fields aren't extracted.
|
||||
|
||||
```typescript
|
||||
// In transformer - data received but not mapped
|
||||
dmg?: Record<string, unknown>; // NOT EXTRACTED to dmg_enemy/dmg_team
|
||||
```
|
||||
|
||||
**Type Definition** (`src/lib/types/Match.ts:134-135`):
|
||||
|
||||
```typescript
|
||||
dmg_enemy?: number; // Damage dealt to enemies
|
||||
dmg_team?: number; // Damage dealt to team (friendly fire)
|
||||
```
|
||||
|
||||
**Expected Format**:
|
||||
|
||||
```json
|
||||
{
|
||||
"players": [
|
||||
{
|
||||
"dmg": {
|
||||
"enemy": 2847,
|
||||
"team": 45
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Fix**: Either:
|
||||
|
||||
1. Backend returns flat `dmg_enemy` and `dmg_team` fields, OR
|
||||
2. Frontend transformer extracts from `dmg.enemy` and `dmg.team`
|
||||
|
||||
---
|
||||
|
||||
## 6. Medium: Utility/Grenade Statistics
|
||||
|
||||
**Endpoint**: `GET /match/{id}` (player stats)
|
||||
|
||||
**Location in Frontend**: `src/lib/types/Match.ts:144-148`
|
||||
|
||||
**Issue**: Not provided by backend at all.
|
||||
|
||||
**Missing Fields**:
|
||||
|
||||
```typescript
|
||||
ud_he?: number; // HE grenade damage dealt
|
||||
ud_flames?: number; // Molotov/Incendiary damage dealt
|
||||
ud_flash?: number; // Flash grenades thrown (count)
|
||||
ud_smoke?: number; // Smoke grenades thrown (count)
|
||||
ud_decoy?: number; // Decoy grenades thrown (count)
|
||||
```
|
||||
|
||||
**Expected Format**:
|
||||
|
||||
```json
|
||||
{
|
||||
"players": [
|
||||
{
|
||||
"ud_he": 156,
|
||||
"ud_flames": 89,
|
||||
"ud_flash": 8,
|
||||
"ud_smoke": 12,
|
||||
"ud_decoy": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Backend Implementation**: Count grenade events and sum damage from `player_hurt` events where weapon is HE/molotov/incendiary.
|
||||
|
||||
---
|
||||
|
||||
## 7. Medium: Per-Round Player Statistics
|
||||
|
||||
**Endpoint**: `GET /match/{id}/rounds`
|
||||
|
||||
**Issue**: No per-round performance data for players.
|
||||
|
||||
**Missing Fields per Player per Round**:
|
||||
|
||||
| Field | Type | Description |
|
||||
| ------------------ | -------- | -------------------------------------------- |
|
||||
| `kills_in_round` | `number` | Kills this player got in this specific round |
|
||||
| `damage_in_round` | `number` | Total damage dealt this round |
|
||||
| `assists_in_round` | `number` | Assists this round |
|
||||
```typescript
|
||||
kills_in_round?: number; // Kills in this specific round
|
||||
damage_in_round?: number; // Damage dealt this round
|
||||
assists_in_round?: number; // Assists this round
|
||||
```
|
||||
|
||||
**Implementation**: During demo parse, aggregate `player_death` and `player_hurt` events per round, grouped by attacking player.
|
||||
**Expected Format**:
|
||||
|
||||
```json
|
||||
{
|
||||
"1": {
|
||||
"winner": 2,
|
||||
"win_reason": "elimination",
|
||||
"players": {
|
||||
"76561198012345678": {
|
||||
"bank": 4000,
|
||||
"equipment": 3750,
|
||||
"spent": 2900,
|
||||
"kills": 2,
|
||||
"damage": 187,
|
||||
"assists": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Backend Implementation**: Aggregate `player_death` and `player_hurt` events per round, grouped by attacker.
|
||||
|
||||
---
|
||||
|
||||
## Loss Bonus Tracking
|
||||
## 8. Low: Loss Bonus Tracking
|
||||
|
||||
**Endpoint**: `GET /match/{id}/rounds`
|
||||
|
||||
**Issue**: Not tracked in backend.
|
||||
|
||||
**Missing Fields per Team per Round**:
|
||||
|
||||
| Field | Type | Description |
|
||||
| ------------- | -------- | ------------------------------ |
|
||||
| `loss_streak` | `number` | Consecutive round losses (0-4) |
|
||||
| `loss_bonus` | `number` | Current loss bonus amount |
|
||||
```typescript
|
||||
loss_streak?: number; // Consecutive round losses (0-4)
|
||||
loss_bonus?: number; // Current loss bonus amount
|
||||
```
|
||||
|
||||
**CS2 Loss Bonus Scale**:
|
||||
| Consecutive Losses | Bonus |
|
||||
@@ -83,7 +329,45 @@ This document outlines data that the frontend is ready to display but is not cur
|
||||
| 3 | $2,900 |
|
||||
| 4+ | $3,400 |
|
||||
|
||||
**Implementation**: Track `RoundEnd` events and maintain loss counter per team, resetting on win.
|
||||
**Backend Implementation**: Track `RoundEnd` events and maintain loss counter per team, resetting on win.
|
||||
|
||||
---
|
||||
|
||||
## 9. Optional Fields (Expected Gaps)
|
||||
|
||||
These fields are intentionally optional due to data availability:
|
||||
|
||||
| Field | Reason |
|
||||
| ------------ | ---------------------------------------------------------- |
|
||||
| `replay_url` | Only available for matches < 4 weeks old (Valve retention) |
|
||||
| `share_code` | Not all matches have share codes |
|
||||
| `tick_rate` | May not be in older demos |
|
||||
| `game_mode` | Legacy CS:GO matches don't have this |
|
||||
| `avg_ping` | Not always recorded in demos |
|
||||
|
||||
---
|
||||
|
||||
## Backend Implementation Checklist
|
||||
|
||||
### Priority 1: Critical
|
||||
|
||||
- [ ] Add KAST calculation per player per match
|
||||
- [ ] Add ADR calculation per player per match
|
||||
- [ ] Add weapon kill tracking (correlate damage with death events)
|
||||
- [ ] Add round winner from `RoundEnd` events
|
||||
- [ ] Add win_reason enumeration from `RoundEnd` events
|
||||
|
||||
### Priority 2: High
|
||||
|
||||
- [ ] Extract `dmg_enemy` and `dmg_team` in player stats
|
||||
- [ ] Add utility grenade statistics (ud_he, ud_flames, ud_flash, ud_smoke, ud_decoy)
|
||||
- [ ] Ensure `hs_percent` is always calculated
|
||||
|
||||
### Priority 3: Medium
|
||||
|
||||
- [ ] Add per-round player stats (kills, damage, assists per round)
|
||||
- [ ] Add loss bonus tracking per team per round
|
||||
- [ ] Ensure `tick_rate` is always provided when available
|
||||
|
||||
---
|
||||
|
||||
@@ -91,17 +375,77 @@ This document outlines data that the frontend is ready to display but is not cur
|
||||
|
||||
The frontend already has:
|
||||
|
||||
- Type definitions for all missing fields (`src/lib/types/RoundStats.ts`)
|
||||
- Zod validation schemas (`src/lib/schemas/roundStats.schema.ts`)
|
||||
- UI components ready to display win reasons with icons
|
||||
- Mock handlers showing expected data structure (`src/mocks/handlers/matches.ts`)
|
||||
- Type definitions for all fields (`src/lib/types/`)
|
||||
- Components ready to display data (using fallbacks currently)
|
||||
- Transformers prepared to extract nested data
|
||||
- UI for all features (showing placeholders/zeros)
|
||||
|
||||
Once the backend provides this data, the frontend will automatically display it.
|
||||
Once backend provides this data, frontend will automatically display it.
|
||||
|
||||
---
|
||||
|
||||
## Priority
|
||||
## API Response Examples
|
||||
|
||||
1. **High**: Round winner & win reason - enables win/loss correlation analysis
|
||||
2. **Medium**: Per-round player stats - enables round-by-round performance tracking
|
||||
3. **Low**: Loss bonus tracking - nice-to-have for economy analysis
|
||||
### Ideal Match Response
|
||||
|
||||
```json
|
||||
{
|
||||
"match_id": "123456",
|
||||
"players": [
|
||||
{
|
||||
"id": "76561198012345678",
|
||||
"name": "Player1",
|
||||
"kills": 25,
|
||||
"deaths": 18,
|
||||
"assists": 4,
|
||||
"kast": 72.5,
|
||||
"adr": 85.3,
|
||||
"hs_percent": 45.2,
|
||||
"dmg_enemy": 2847,
|
||||
"dmg_team": 12,
|
||||
"ud_he": 156,
|
||||
"ud_flames": 89,
|
||||
"ud_flash": 8,
|
||||
"ud_smoke": 12
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Ideal Rounds Response
|
||||
|
||||
```json
|
||||
{
|
||||
"1": {
|
||||
"winner": 2,
|
||||
"win_reason": "bomb_exploded",
|
||||
"t_loss_streak": 0,
|
||||
"ct_loss_streak": 1,
|
||||
"players": {
|
||||
"76561198012345678": {
|
||||
"bank": 4000,
|
||||
"equipment": 3750,
|
||||
"spent": 2900,
|
||||
"kills": 2,
|
||||
"damage": 187
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Ideal Weapons Response
|
||||
|
||||
```json
|
||||
{
|
||||
"weapons": {
|
||||
"7": {
|
||||
"name": "AK-47",
|
||||
"kills": 12,
|
||||
"damage": 1847,
|
||||
"hits": 45,
|
||||
"headshots": 8
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user