- Merge NEW_FEATURES.md into MISSING_BACKEND_API.md as "Future Feature Proposals" - Remove redundant NEW_FEATURES.md file - Add CLAUDE.md to .gitignore (local development config) - Include pending frontend component updates 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1173 lines
27 KiB
Markdown
1173 lines
27 KiB
Markdown
# Missing Backend API Data
|
||
|
||
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.
|
||
|
||
---
|
||
|
||
## 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
|
||
{
|
||
"0": { "player_id": [bank, equipment, spent] },
|
||
"1": { "player_id": [bank, equipment, spent] }
|
||
}
|
||
```
|
||
|
||
**Expected Format**:
|
||
|
||
```json
|
||
{
|
||
"0": {
|
||
"winner": 2,
|
||
"win_reason": "bomb_exploded",
|
||
"players": {
|
||
"player_id": [bank, equipment, spent]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**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) |
|
||
|
||
**Backend Implementation**: Extract from `RoundEnd` game events in demo parser.
|
||
|
||
---
|
||
|
||
## 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**:
|
||
|
||
```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
|
||
```
|
||
|
||
**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.
|
||
|
||
---
|
||
|
||
## 8. Low: Loss Bonus Tracking
|
||
|
||
**Endpoint**: `GET /match/{id}/rounds`
|
||
|
||
**Issue**: Not tracked in backend.
|
||
|
||
**Missing Fields per Team per Round**:
|
||
|
||
```typescript
|
||
loss_streak?: number; // Consecutive round losses (0-4)
|
||
loss_bonus?: number; // Current loss bonus amount
|
||
```
|
||
|
||
**CS2 Loss Bonus Scale**:
|
||
| Consecutive Losses | Bonus |
|
||
|-------------------|-------|
|
||
| 0 | $1,400 |
|
||
| 1 | $1,900 |
|
||
| 2 | $2,400 |
|
||
| 3 | $2,900 |
|
||
| 4+ | $3,400 |
|
||
|
||
**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
|
||
|
||
---
|
||
|
||
## Frontend Readiness
|
||
|
||
The frontend already has:
|
||
|
||
- 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 backend provides this data, frontend will automatically display it.
|
||
|
||
---
|
||
|
||
## API Response Examples
|
||
|
||
### 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
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
# Future Feature Proposals
|
||
|
||
The following features require backend changes to implement. They are organized into tiers based on implementation complexity.
|
||
|
||
---
|
||
|
||
## Tier 3: API Enhancements (Expose Existing Data)
|
||
|
||
These features use data that's **already stored** in the database but not currently exposed through the API.
|
||
|
||
### 3.1 Per-Weapon Kill Attribution
|
||
|
||
**Current State:**
|
||
|
||
- `weapon` table stores damage per hit with `hit_group`, `weapon_id`, and `dmg`
|
||
- Weapon damage is aggregated in player meta endpoint
|
||
|
||
**Missing:**
|
||
|
||
- Kill counts per weapon (correlating damage with deaths)
|
||
- Headshot kills vs body shot kills per weapon
|
||
|
||
**Proposed API Change:**
|
||
|
||
```typescript
|
||
// Extend GET /player/:id/meta/:limit response
|
||
interface WeaponStats {
|
||
weapon_id: number;
|
||
weapon_name: string;
|
||
kills: number; // NEW: Total kills with this weapon
|
||
headshot_kills: number; // NEW: HS kills with this weapon
|
||
damage: number; // Existing
|
||
shots: number; // Existing
|
||
hits: number; // Existing
|
||
}
|
||
```
|
||
|
||
**Implementation Notes:**
|
||
|
||
- Requires correlating weapon damage events with kill events
|
||
- Could be computed at match import time and stored, or calculated on-demand
|
||
|
||
---
|
||
|
||
### 3.2 Per-Map Detailed Statistics
|
||
|
||
**Current State:**
|
||
|
||
- All player stats exist per match with `map` field
|
||
- No pre-aggregated per-map statistics
|
||
|
||
**Missing:**
|
||
|
||
- K/D ratio per map
|
||
- ADR per map
|
||
- Headshot % per map
|
||
- Win rate per map
|
||
|
||
**Proposed API Change:**
|
||
|
||
```typescript
|
||
// Extend GET /player/:id/meta/:limit response
|
||
interface MapStats {
|
||
map: string;
|
||
matches_played: number;
|
||
wins: number;
|
||
losses: number;
|
||
ties: number;
|
||
avg_kills: number;
|
||
avg_deaths: number;
|
||
avg_adr: number;
|
||
headshot_pct: number;
|
||
}
|
||
```
|
||
|
||
**Use Case:** Players can see which maps they perform best on, helping with map vetoes and practice focus.
|
||
|
||
---
|
||
|
||
### 3.3 Hit Group Aggregates
|
||
|
||
**Current State:**
|
||
|
||
- `weapon.hit_group` stores where each shot landed (0=head, 1=chest, 2=stomach, 3=left arm, 4=right arm, 5=left leg, 6=right leg)
|
||
- Individual hits are stored but not aggregated
|
||
|
||
**Missing:**
|
||
|
||
- Aggregated hit distribution across all matches
|
||
- Per-weapon accuracy by body part
|
||
|
||
**Proposed API Change:**
|
||
|
||
```typescript
|
||
// Extend GET /player/:id/meta/:limit response
|
||
interface HitGroupStats {
|
||
total_hits: number;
|
||
head: number; // Count of head hits
|
||
chest: number; // Count of chest hits
|
||
stomach: number; // Count of stomach hits
|
||
arms: number; // Combined left+right arm hits
|
||
legs: number; // Combined left+right leg hits
|
||
head_pct: number; // Calculated percentage
|
||
body_pct: number; // chest + stomach percentage
|
||
limb_pct: number; // arms + legs percentage
|
||
}
|
||
```
|
||
|
||
**Use Case:** Visual body diagram showing where player typically lands shots, identifying spray control issues.
|
||
|
||
---
|
||
|
||
### 3.4 Round Economy Timeline
|
||
|
||
**Current State:**
|
||
|
||
- `roundstats` table contains `bank`, `equipment`, `spent` per round
|
||
- Data exists but not exposed in player endpoint
|
||
|
||
**Missing:**
|
||
|
||
- Economy trends over match history
|
||
- Buy pattern analysis (eco/force/full buy distribution)
|
||
- Equipment value vs damage dealt correlation
|
||
|
||
**Proposed API Change:**
|
||
|
||
```typescript
|
||
// New endpoint: GET /player/:id/economy
|
||
interface EconomyStats {
|
||
avg_equipment_value: number;
|
||
avg_bank_at_round_start: number;
|
||
eco_round_pct: number; // % of rounds with <$2000 equipment
|
||
force_buy_pct: number; // % of rounds with $2000-$4000
|
||
full_buy_pct: number; // % of rounds with >$4000
|
||
value_per_kill: number; // avg damage dealt / avg equipment value
|
||
save_rate: number; // % of rounds where player survived with <$2000
|
||
}
|
||
```
|
||
|
||
**Use Case:** Understanding economic efficiency - are they getting value for their money?
|
||
|
||
---
|
||
|
||
### 3.5 Opponent History (Head-to-Head)
|
||
|
||
**Current State:**
|
||
|
||
- All match participants are stored
|
||
- Can identify when two players were in the same match
|
||
|
||
**Missing:**
|
||
|
||
- Head-to-head records vs specific players
|
||
- Performance when playing with/against specific teammates
|
||
|
||
**Proposed API Change:**
|
||
|
||
```typescript
|
||
// New endpoint: GET /player/:id/opponents?limit=10
|
||
interface OpponentRecord {
|
||
opponent_id: string;
|
||
opponent_name: string;
|
||
opponent_avatar: string;
|
||
matches_played: number;
|
||
wins: number;
|
||
losses: number;
|
||
avg_kills_vs: number; // Avg kills when playing against this opponent
|
||
avg_deaths_vs: number; // Avg deaths when playing against this opponent
|
||
last_match_date: string;
|
||
}
|
||
```
|
||
|
||
**Use Case:** "Rival" detection - who do they frequently play against and how do they perform?
|
||
|
||
---
|
||
|
||
### 3.6 Crosshair History
|
||
|
||
**Current State:**
|
||
|
||
- `crosshair` code stored per match
|
||
- `color` enum stored (0=default, 1=green, 2=yellow, 3=blue, 4=cyan)
|
||
|
||
**Missing:**
|
||
|
||
- Not exposed in player meta endpoint
|
||
- No history of crosshair changes
|
||
|
||
**Proposed API Change:**
|
||
|
||
```typescript
|
||
// Extend GET /player/:id/meta/:limit response
|
||
interface CrosshairInfo {
|
||
current_crosshair: string; // Most recent crosshair code
|
||
current_color: number; // Most recent color
|
||
changes_count: number; // How often they change crosshair
|
||
history: Array<{
|
||
crosshair: string;
|
||
color: number;
|
||
first_seen: string;
|
||
matches_used: number;
|
||
}>;
|
||
}
|
||
```
|
||
|
||
**Use Case:** Players curious about what crosshair settings others use, track experimentation.
|
||
|
||
---
|
||
|
||
## Tier 4: Demo Parser Additions
|
||
|
||
These features require **new data collection** from demo files during parsing.
|
||
|
||
### 4.1 Kill/Death Event Log
|
||
|
||
**What to Capture:**
|
||
|
||
```go
|
||
type KillEvent struct {
|
||
MatchID string
|
||
RoundNumber int
|
||
Tick int
|
||
Timestamp float64 // Seconds into round
|
||
KillerID string
|
||
VictimID string
|
||
AssisterID string // nullable
|
||
WeaponID int
|
||
Headshot bool
|
||
Wallbang bool
|
||
Smoke bool // Through smoke
|
||
NoScope bool
|
||
Flashed bool // Victim was flashed
|
||
Distance float32 // Distance between players
|
||
KillerX float32
|
||
KillerY float32
|
||
KillerZ float32
|
||
VictimX float32
|
||
VictimY float32
|
||
VictimZ float32
|
||
}
|
||
```
|
||
|
||
**Enables:**
|
||
|
||
- Opening kill/death statistics (first blood)
|
||
- Trade detection (kill within X seconds of teammate death)
|
||
- Kill distance analysis (long range vs close quarters)
|
||
- Clutch situation detection
|
||
- Kill position heatmaps
|
||
|
||
**Storage Estimate:** ~20-30 kills per match × 20 bytes average = ~500 bytes per match
|
||
|
||
---
|
||
|
||
### 4.2 Round Winner Attribution
|
||
|
||
**What to Capture:**
|
||
|
||
```go
|
||
type RoundResult struct {
|
||
MatchID string
|
||
RoundNumber int
|
||
WinnerTeamID int // 2 or 3
|
||
WinReason int // 1=bomb, 2=defuse, 3=elimination, 4=time
|
||
ClutchPlayerID string // nullable - player who clutched (1vX)
|
||
ClutchSize int // 1v1, 1v2, etc.
|
||
MVPPlayerID string // Round MVP
|
||
}
|
||
```
|
||
|
||
**Enables:**
|
||
|
||
- Clutch win rate (1v1, 1v2, 1v3, etc.)
|
||
- Bomb plant/defuse success rates
|
||
- Round impact scoring
|
||
- MVP frequency
|
||
|
||
---
|
||
|
||
### 4.3 Position/Heatmap Data
|
||
|
||
**What to Capture:**
|
||
|
||
```go
|
||
type PlayerPosition struct {
|
||
MatchID string
|
||
RoundNumber int
|
||
Tick int // Sample every ~32 ticks (0.5 seconds)
|
||
PlayerID string
|
||
X float32
|
||
Y float32
|
||
Z float32
|
||
ViewAngleX float32 // Where they're looking
|
||
ViewAngleY float32
|
||
IsAlive bool
|
||
}
|
||
```
|
||
|
||
**Enables:**
|
||
|
||
- Kill heatmaps (where do they get kills/die)
|
||
- Site preference analysis (A vs B)
|
||
- Positioning patterns
|
||
- 2D round replay (future)
|
||
|
||
**Storage Estimate:** High - ~2000 position samples per player per match. Consider:
|
||
|
||
- Only sample during key moments (kills, bomb plant, round end)
|
||
- Downsample to 1 sample per second
|
||
- Store as compressed binary blob
|
||
|
||
---
|
||
|
||
### 4.4 Trade Detection
|
||
|
||
**What to Capture:**
|
||
|
||
- Already covered by Kill Event Log (4.1)
|
||
- Calculate at query time: If player A dies and teammate B kills A's killer within 5 seconds = trade
|
||
|
||
**Derived Stats:**
|
||
|
||
```typescript
|
||
interface TradeStats {
|
||
trades_given: number; // Times you avenged a teammate
|
||
trades_received: number; // Times a teammate avenged you
|
||
trade_success_rate: number; // trades_given / opportunities
|
||
trading_partner_id: string; // Who they trade with most often
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 4.5 Utility Timing Analysis
|
||
|
||
**What to Capture:**
|
||
|
||
```go
|
||
type UtilityEvent struct {
|
||
MatchID string
|
||
RoundNumber int
|
||
Tick int
|
||
PlayerID string
|
||
UtilityType int // flash, smoke, he, molotov
|
||
ThrowX float32
|
||
ThrowY float32
|
||
ThrowZ float32
|
||
LandX float32 // Where it detonated/landed
|
||
LandY float32
|
||
LandZ float32
|
||
PlayersFlashed []FlashedPlayer // For flashbangs
|
||
}
|
||
|
||
type FlashedPlayer struct {
|
||
PlayerID string
|
||
Duration float32 // Flash duration in seconds
|
||
IsTeammate bool
|
||
}
|
||
```
|
||
|
||
**Enables:**
|
||
|
||
- Flash effectiveness (avg flash duration)
|
||
- Team flash rate (already have `dmg_team` but duration is more precise)
|
||
- Smoke lineup success
|
||
- Molotov damage tracking
|
||
|
||
---
|
||
|
||
### 4.6 First Blood Statistics
|
||
|
||
**What to Capture:**
|
||
|
||
- Mark `is_first_blood` flag in Kill Event (4.1)
|
||
- First kill of each round
|
||
|
||
**Derived Stats:**
|
||
|
||
```typescript
|
||
interface FirstBloodStats {
|
||
first_blood_attempts: number; // Rounds where they got first kill OR first death
|
||
first_bloods: number; // Times they got first kill
|
||
first_deaths: number; // Times they died first
|
||
first_blood_rate: number; // first_bloods / first_blood_attempts
|
||
opening_duel_win_rate: number; // first_bloods / (first_bloods + first_deaths)
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 4.7 HLTV Rating 2.0
|
||
|
||
**Formula Components:**
|
||
|
||
```
|
||
Rating 2.0 = 0.0073*KAST + 0.3591*KPR - 0.5329*DPR + 0.2372*Impact + 0.0032*ADR + 0.1587
|
||
|
||
Where:
|
||
- KAST = % of rounds with Kill, Assist, Survived, or Traded
|
||
- KPR = Kills per round
|
||
- DPR = Deaths per round
|
||
- Impact = (2.13*KPR + 0.42*Assist per Round - 0.41)
|
||
- ADR = Average damage per round
|
||
```
|
||
|
||
**What's Needed:**
|
||
|
||
- KAST requires knowing if player survived or was traded (needs trade detection)
|
||
- All other components are calculable from existing data
|
||
|
||
**Proposed Implementation:**
|
||
|
||
- Add `kast_rounds` counter to match stats
|
||
- Calculate Rating 2.0 server-side for consistency
|
||
|
||
---
|
||
|
||
## Tier 5: New Systems
|
||
|
||
These features require **significant new infrastructure**.
|
||
|
||
### 5.1 Player Comparison Tool
|
||
|
||
**Description:** Side-by-side comparison of any two players' statistics.
|
||
|
||
**Requirements:**
|
||
|
||
- New UI page: `/compare/:player1/:player2`
|
||
- API endpoint: `GET /players/compare?ids=player1,player2`
|
||
- Returns normalized stats for fair comparison
|
||
|
||
**Comparison Metrics:**
|
||
|
||
```typescript
|
||
interface PlayerComparison {
|
||
players: [PlayerMeta, PlayerMeta];
|
||
stats_comparison: {
|
||
metric: string;
|
||
player1_value: number;
|
||
player2_value: number;
|
||
player1_percentile: number; // How they rank globally
|
||
player2_percentile: number;
|
||
}[];
|
||
head_to_head?: {
|
||
matches_played: number;
|
||
player1_wins: number;
|
||
player2_wins: number;
|
||
};
|
||
common_maps: string[];
|
||
common_teammates: PlayerMeta[];
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 5.2 Achievement System
|
||
|
||
**Description:** Badges and milestones for player accomplishments.
|
||
|
||
**Schema:**
|
||
|
||
```sql
|
||
CREATE TABLE achievements (
|
||
id SERIAL PRIMARY KEY,
|
||
name VARCHAR(100),
|
||
description TEXT,
|
||
icon VARCHAR(50),
|
||
category VARCHAR(50), -- 'kills', 'utility', 'milestones', 'special'
|
||
threshold INT,
|
||
stat_key VARCHAR(50) -- Which stat to check
|
||
);
|
||
|
||
CREATE TABLE player_achievements (
|
||
player_id VARCHAR(20),
|
||
achievement_id INT,
|
||
unlocked_at TIMESTAMP,
|
||
progress INT, -- For progressive achievements
|
||
PRIMARY KEY (player_id, achievement_id)
|
||
);
|
||
```
|
||
|
||
**Example Achievements:**
|
||
| Name | Description | Threshold |
|
||
|------|-------------|-----------|
|
||
| Centurion | 100 aces | 100 |
|
||
| Headhunter | 1000 headshot kills | 1000 |
|
||
| Support Main | 500 flash assists | 500 |
|
||
| Clutch Master | 50 1v3+ clutches | 50 |
|
||
| Map Scholar | Play 100 matches on each map | 700 total |
|
||
| Friendly Fire | Deal 1000 team damage (shame badge) | 1000 |
|
||
|
||
**Implementation:**
|
||
|
||
- Background job checks achievement progress after each match import
|
||
- Push notification system for newly unlocked achievements
|
||
- Achievement showcase on player profile
|
||
|
||
---
|
||
|
||
### 5.3 Global Leaderboards
|
||
|
||
**Description:** Rankings by various statistics.
|
||
|
||
**Schema:**
|
||
|
||
```sql
|
||
CREATE TABLE leaderboards (
|
||
stat_key VARCHAR(50),
|
||
time_period VARCHAR(20), -- 'all_time', 'monthly', 'weekly'
|
||
player_id VARCHAR(20),
|
||
value DECIMAL(10,2),
|
||
rank INT,
|
||
updated_at TIMESTAMP,
|
||
PRIMARY KEY (stat_key, time_period, player_id)
|
||
);
|
||
```
|
||
|
||
**Leaderboard Categories:**
|
||
|
||
- Highest K/D ratio (min 50 matches)
|
||
- Most headshot kills
|
||
- Highest ADR
|
||
- Most flash assists
|
||
- Most matches played
|
||
- Highest win rate (min 100 matches)
|
||
- Most aces
|
||
- Longest win streak
|
||
|
||
**Implementation:**
|
||
|
||
- Materialized view or dedicated table refreshed hourly/daily
|
||
- Pagination support for full leaderboard browsing
|
||
- Filter by region/rank bracket (future)
|
||
|
||
---
|
||
|
||
### 5.4 Improvement Tips Engine
|
||
|
||
**Description:** AI-generated suggestions based on weak stats.
|
||
|
||
**Rules Engine Approach:**
|
||
|
||
```typescript
|
||
interface ImprovementRule {
|
||
condition: (stats: PlayerStats) => boolean;
|
||
tip: string;
|
||
priority: number;
|
||
category: 'aim' | 'utility' | 'economy' | 'teamplay' | 'consistency';
|
||
}
|
||
|
||
const rules: ImprovementRule[] = [
|
||
{
|
||
condition: (s) => s.headshot_pct < 30,
|
||
tip: 'Your headshot percentage is below average. Focus on crosshair placement at head level.',
|
||
priority: 1,
|
||
category: 'aim'
|
||
},
|
||
{
|
||
condition: (s) => s.flash_assists < 1,
|
||
tip: "You're not getting many flash assists. Practice pop flashes for your teammates.",
|
||
priority: 2,
|
||
category: 'utility'
|
||
},
|
||
{
|
||
condition: (s) => s.team_damage_ratio > 3,
|
||
tip: 'Your team damage is high. Be more careful with utility and spray control.',
|
||
priority: 1,
|
||
category: 'teamplay'
|
||
}
|
||
// ... more rules
|
||
];
|
||
```
|
||
|
||
**Alternative - ML Approach:**
|
||
|
||
- Cluster players by playstyle
|
||
- Compare to higher-ranked players with similar style
|
||
- Identify key stat differences
|
||
- Generate personalized recommendations
|
||
|
||
---
|
||
|
||
### 5.5 Match Replay Integration (2D)
|
||
|
||
**Description:** Simple 2D overhead view of rounds.
|
||
|
||
**Requirements:**
|
||
|
||
- Position data from Tier 4.3
|
||
- Map radar images (already have some)
|
||
- WebGL or Canvas-based renderer
|
||
- Playback controls (play/pause/speed)
|
||
|
||
**Data Format:**
|
||
|
||
```typescript
|
||
interface ReplayFrame {
|
||
tick: number;
|
||
timestamp: number; // Seconds into round
|
||
players: {
|
||
id: string;
|
||
team: number;
|
||
x: number;
|
||
y: number;
|
||
angle: number;
|
||
health: number;
|
||
alive: boolean;
|
||
weapon: string;
|
||
}[];
|
||
events: {
|
||
type: 'kill' | 'plant' | 'defuse' | 'flash' | 'smoke';
|
||
tick: number;
|
||
data: any;
|
||
}[];
|
||
}
|
||
```
|
||
|
||
**Compression:**
|
||
|
||
- Delta encoding (only send changes)
|
||
- Binary format instead of JSON
|
||
- Stream in chunks for long rounds
|
||
|
||
---
|
||
|
||
### 5.6 Social Features
|
||
|
||
**Description:** Follow players, activity feed, sharing.
|
||
|
||
**Schema:**
|
||
|
||
```sql
|
||
CREATE TABLE follows (
|
||
follower_id VARCHAR(20),
|
||
following_id VARCHAR(20),
|
||
created_at TIMESTAMP,
|
||
PRIMARY KEY (follower_id, following_id)
|
||
);
|
||
|
||
CREATE TABLE activity_feed (
|
||
id SERIAL PRIMARY KEY,
|
||
player_id VARCHAR(20),
|
||
event_type VARCHAR(50), -- 'match_completed', 'achievement_unlocked', 'rank_changed'
|
||
event_data JSONB,
|
||
created_at TIMESTAMP
|
||
);
|
||
```
|
||
|
||
**Features:**
|
||
|
||
- Follow/unfollow players
|
||
- Activity feed showing followed players' recent matches
|
||
- Share profile/match links
|
||
- Privacy settings (public/private profiles)
|
||
|
||
---
|
||
|
||
## Data Schema Reference
|
||
|
||
### Current Tables Used
|
||
|
||
| Table | Key Fields | Notes |
|
||
| -------------- | ----------------------------- | --------------------- |
|
||
| `players` | id, name, avatar, tracked | Core player data |
|
||
| `matches` | match*id, map, date, score*\* | Match metadata |
|
||
| `matchplayers` | All per-player stats | Already comprehensive |
|
||
| `weapon` | hit_group, weapon_id, dmg | Hit registration |
|
||
| `roundstats` | bank, equipment, spent | Economy data |
|
||
| `spray` | spray (gob-encoded) | Spray patterns |
|
||
|
||
### Existing Fields Available but Not Exposed
|
||
|
||
| Field | Location | Potential Use |
|
||
| ----------- | ------------ | ------------------- |
|
||
| `crosshair` | matchplayers | Crosshair display |
|
||
| `color` | matchplayers | Crosshair color |
|
||
| `avg_ping` | matchplayers | Network quality |
|
||
| `hit_group` | weapon | Body part accuracy |
|
||
| `spray` | spray | Spray visualization |
|
||
|
||
---
|
||
|
||
## Priority Recommendations
|
||
|
||
### High Priority (High Value, Low Effort)
|
||
|
||
1. **3.1 Per-Weapon Kill Attribution** - Players want to know their best weapons
|
||
2. **3.2 Per-Map Detailed Statistics** - Essential for competitive players
|
||
3. **3.3 Hit Group Aggregates** - Visual appeal, easy to understand
|
||
|
||
### Medium Priority (High Value, Medium Effort)
|
||
|
||
4. **4.1 Kill/Death Event Log** - Unlocks many derived features
|
||
5. **4.6 First Blood Statistics** - Key competitive metric
|
||
6. **3.5 Opponent History** - Engaging "rivalry" feature
|
||
|
||
### Lower Priority (High Effort or Niche Appeal)
|
||
|
||
7. **5.3 Global Leaderboards** - Community engagement
|
||
8. **5.1 Player Comparison** - Nice-to-have
|
||
9. **5.2 Achievement System** - Gamification
|
||
10. **5.5 Match Replay** - High effort, impressive feature
|
||
|
||
---
|
||
|
||
## Questions for Discussion
|
||
|
||
1. **Storage constraints:** Position data (4.3) could be large. What's our storage budget?
|
||
2. **Processing time:** Kill event parsing (4.1) adds to import time. Acceptable?
|
||
3. **Caching strategy:** Meta stats are cached 30 days. Should new aggregates follow same pattern?
|
||
4. **API versioning:** Should we version the API or extend existing endpoints?
|
||
5. **Privacy considerations:** Should opponent history require consent?
|
||
|
||
---
|
||
|
||
_Document consolidated from separate feature proposals_
|
||
_Last updated: December 2024_
|