Skip to content

Instantly share code, notes, and snippets.

@jayarc
Last active December 22, 2025 21:37
Show Gist options
  • Select an option

  • Save jayarc/39d19e142d042e6c39d9d813213befe6 to your computer and use it in GitHub Desktop.

Select an option

Save jayarc/39d19e142d042e6c39d9d813213befe6 to your computer and use it in GitHub Desktop.
Urban Terror Demo Analyzer and Cheat Detection

Urban Terror Demo Analyzer

A Node.js tool to parse and analyze Urban Terror demo files (.urtdemo), extract player information and chat messages, detect aimbot usage, and store data in PostgreSQL for searching.

Features

  • Parse Urban Terror demo files (protocol 70, Urban Terror 4.3.4)
  • Extract player information (names, teams, client IDs)
  • Extract chat messages (global chat, team chat, server messages)
  • Aimbot detection - multi-metric analysis system
  • Huffman decompression using precomputed lookup table (matches Q3 engine)
  • PostgreSQL storage with full-text search support
  • Metabase integration for analytics and searching

Aimbot Detection System

The analyzer includes a sophisticated aimbot detection system developed through extensive research and calibration against verified natural player demos (32 demos) and confirmed cheater demos (15 demos).

Detection Philosophy

Rather than relying on any single metric, the system uses multi-metric correlation. Each detection method contributes to an overall suspicion score. This approach:

  • Reduces false positives (natural players rarely trigger multiple flags)
  • Catches different aimbot types (snap aimbots, smooth aimbots, trigger bots)
  • Provides explainable results (specific flags indicate what was detected)

Suspicion Score Interpretation

Score Range Verdict Meaning
0-9 CLEAN No significant indicators
10-29 MINOR_FLAGS Worth noting but not conclusive
30-49 QUESTIONABLE Warrants closer manual review
50-69 SUSPICIOUS Multiple concerning indicators
70-100 HIGHLY_SUSPICIOUS Strong evidence of abnormal behavior

Current Performance (December 2025)

Metric Value Notes
False Positive Rate 3.1% (1/32) Natural demos incorrectly flagged ≥30
Detection Rate (≥30) 93.3% (14/15) Cheaters flagged as QUESTIONABLE+
High Confidence (≥50) 60.0% (9/15) Cheaters flagged as SUSPICIOUS+
Missed Cheaters 6.7% (1/15) Sophisticated smooth aimbot

The one missed cheater uses a very sophisticated smooth aimbot that perfectly mimics natural movement patterns. Detecting such advanced cheats would require machine learning techniques.

Calibration Baseline (32 Natural Player Demos)

These statistics define "normal" human aiming behavior:

Metric Min Max Average Notes
Max Angular Velocity 2040°/s 4169°/s 3338°/s Fastest single movement
95th Percentile Velocity 820°/s 2575°/s - Sustained high velocity
Snap Rate (>3500°/s) 0% 6.9% 3.5% Percentage of very fast movements
Medium Velocity Shots 0% 8.7% 3.6% Shots fired while adjusting aim
Zero Pre-Shot Adjustments 86% 99% 93% Stable aim before firing
Intermediate/Snap Ratio 2.9 27.0 7.7 Gradual movements per fast snap
Diagonal Movements 0 10 4.1 Mixed horizontal+vertical movement

Cheater vs Natural Comparison

Metric Natural Players Cheaters Separation
Max Velocity 2040-4169°/s 820-3540°/s Overlap (some)
Medium Velocity Shots 0-8.7% (avg 3.6%) 0-6.7% (avg 1.1%) 80% cheaters = 0%
Zero Pre-Shot Adj 86-99% 94-100% Cheaters higher
Direction Changes ~0.7% ~0.3% Cheaters lower
Overshoot Rate ~0.07 ~0.04 Cheaters lower

Key Finding: The most reliable single indicator is medium velocity shots. 80% of cheater demos have exactly 0% medium velocity shots (never fire while adjusting), compared to only 9% of natural demos.


Detection Methods Explained

This section explains each detection method in plain language so anyone can understand what we're looking for and why it indicates cheating.

How We Analyze Aim

When you play a first-person shooter, your crosshair moves around the screen as you look for enemies. We record these movements many times per second and measure:

  • How fast the crosshair moves (angular velocity, measured in degrees per second)
  • Where the crosshair stops (the angle/position)
  • When shots are fired relative to crosshair movement
  • How smooth or jerky the movements are

By analyzing these patterns, we can identify behaviors that are physically impossible or statistically improbable for human players.


1. Angular Velocity Analysis

What it measures: How fast the player's view/crosshair moves.

When a human moves their mouse, there's natural variation - sometimes fast, sometimes slow, with smooth acceleration and deceleration. Aimbots often produce unnatural movement patterns.

Detection Flags Explained

AIMLOCK_LOW_MAX_VELOCITY (High Severity)

"The player's fastest crosshair movement is suspiciously slow."

Normal players occasionally make very fast flick movements (2000-4000+ degrees per second) when quickly turning or snapping to a target. If someone's fastest movement in an entire match is only 800-1500 deg/s, it suggests their aim is being "locked" by software that makes only tiny adjustments. The aimbot is doing the aiming, so the player never needs to make fast movements themselves.

Statistics: Natural players: 2040-4169°/s max. Flagged if below 2000°/s. One detected aimbot had max velocity of only 820°/s.

SPARSE_INTERMEDIATE_MOVEMENTS (High Severity)

"The player jumps directly from still to fast with nothing in between."

Imagine drawing a line from point A to point B. A human would gradually speed up, reach peak speed in the middle, then slow down approaching the target. Their movements would show a range of speeds: slow, medium, fast, medium, slow.

An aimbot often works differently: the crosshair is still (zero speed), then INSTANTLY at the target (high speed), then still again. There are very few "in-between" movements. We count how many medium-speed movements (100-2000 deg/s) occur relative to fast snaps (>2000 deg/s). Natural players have 5-10+ medium movements for every snap; aimbots often have fewer than 2.

Statistics: Natural intermediate/snap ratio: 2.9-27.0 (avg 7.7). Flagged if ratio below 2.0 (high severity) or 4.0 (medium severity).

PURE_SINGLE_AXIS_SNAPS (High Severity)

"All fast movements are perfectly horizontal OR vertical, never diagonal."

When humans aim, they rarely move the mouse in a perfectly straight horizontal or vertical line. Even when primarily moving left/right, there's usually a tiny bit of up/down mixed in - our hands aren't perfectly precise.

Aimbots often calculate the shortest path to a target and may only adjust one axis (horizontal OR vertical) at a time. If someone makes 20+ fast movements and NONE of them have any diagonal component, that's extremely suspicious.

Statistics: Natural players: 0-10 diagonal movements per demo (avg 4.1). Some aimbots show exactly 0 diagonal movements among 18-29 fast movements.

REPEATED_EXACT_ANGLES (High Severity)

"The player keeps making the exact same angle change, over and over."

When you snap to different enemies at different distances and positions, each snap should be a different angle. If 15%+ of a player's fast movements are IDENTICAL angle changes (down to 0.5 degrees), it suggests software is computing and executing the same programmed movement repeatedly. Humans simply cannot reproduce exact angles this consistently.

Example: If someone repeatedly snaps exactly 23.5° right and 4.0° down, the same amount each time, that's a telltale sign of automated aim.

REPEATED_EXACT_VELOCITY (Medium Severity)

"Fast movements happen at the exact same speed repeatedly."

Similar to repeated angles, aimbots often operate at fixed speeds. If 25%+ of high-velocity movements occur at the exact same speed (rounded to nearest 10 deg/s), it suggests automated movement rather than natural human variance.

CONSISTENT_SNAP_PITCH (High Severity)

"Most snaps end at the exact same vertical level."

"Pitch" is the up/down angle - looking up at the sky is high pitch, looking at the ground is low pitch. When aiming at enemies, you'd expect to hit different vertical levels depending on where enemies are, whether they're crouching, on stairs, etc.

If 70%+ of a player's snaps end at the SAME pitch (vertical angle), it suggests an aimbot is consistently targeting the same hitbox position (usually center mass or head height). Natural players show much more variation - some shots at head level, some at torso, some while enemies are jumping, etc.

BINARY_VELOCITY_PATTERN (Low Severity)

"Movements go instantly from zero to maximum speed with no acceleration."

When humans move their mouse fast, there's a brief moment of acceleration - you don't go from 0 to 3000 deg/s instantly. There's a tiny ramp-up period.

Some aimbots produce "binary" movement: either completely still OR at full speed, with nothing in between. The crosshair goes from motionless to maximum velocity in a single frame. While this can happen naturally with very quick flicks, if it happens constantly with NO gradual movements, it's notable.


2. Kill-Shot Correlation Analysis

What it measures: The relationship between aim movement and firing.

This is one of our most powerful detection methods based on a simple insight:

ZERO_MEDIUM_VEL_SHOTS (High Severity)

"The player ONLY fires when their aim is perfectly still OR just after snapping."

Think about how a normal player shoots:

  • Sometimes you shoot while your aim is steady (locked on target)
  • Sometimes you shoot while still making small adjustments (tracking a moving target)
  • Sometimes you shoot just after a fast snap to a new target

We categorize shots by the crosshair speed at the moment of firing:

  • Low velocity (<200 deg/s): Aim was essentially locked/still
  • Medium velocity (200-1000 deg/s): Still adjusting while firing
  • High velocity (>1000 deg/s): Fired during or just after a snap

Natural players: About 1-4% of shots are fired at medium velocity (adjusting while shooting).

Aimbots: Often 0% medium velocity shots. Why? Because the aimbot either:

  1. Locks aim perfectly still, THEN fires (low velocity), or
  2. Snaps to target, THEN fires (high velocity)

The aimbot never fires while "adjusting" because it doesn't need to adjust - it calculates the exact position and goes there directly. If someone fires 25+ shots and NOT A SINGLE ONE was at medium velocity, that's a strong indicator of automated aim.

Statistics:

  • Natural players: 0-8.7% medium velocity shots (average 3.6%)
  • Cheaters: 0-6.7% medium velocity shots (average 1.1%)
  • 80% of cheater demos have exactly 0% medium velocity shots
  • Only 9% of natural demos have 0% medium velocity shots
  • This is our most statistically significant indicator

3. Target Tracking Precision Analysis

What it measures: How precisely and repetitively the player hits targets.

EXTREMELY_LOW_PITCH_DIVERSITY (Low Severity)

"All shots land at almost the same vertical level."

If someone fires 30+ shots and only hits 2 different pitch levels (e.g., only head height and upper chest), it suggests an aimbot that always targets the same hitbox area. Natural gameplay produces much more variety because:

  • Enemies are at different heights (stairs, jumping, crouching)
  • You don't always have time to aim perfectly
  • Different situations call for different shot placements

PERFECT_PRE_AIM (Medium Severity)

"Every single shot has zero aim adjustment just before firing."

We look at the crosshair movement in the frame immediately before each shot. Natural players are usually still making tiny adjustments (tracking the target, compensating for their own movement). If 100% of shots have ZERO adjustment in the final frame, it suggests the aimbot achieved perfect lock before allowing the shot.

ALWAYS_ADJUSTING_BEFORE_SHOTS (High Severity)

"The player is always moving their aim when shooting, never stable."

The opposite extreme is also suspicious. Natural players stabilize their aim before most shots (86-99% have zero final adjustment). Some aimbots that constantly micro-adjust produce 0% stable shots - they're ALWAYS moving when the shot fires. This is unnatural and indicates software-controlled aim.

Statistics:

  • Natural players: 86-99% zero pre-shot adjustments (avg 93%)
  • Aimbots: 94-100% zero adjustments (toggled type) OR 0-50% (micro-adjusting type)
  • Natural unique pitch levels: 5-14 per demo
  • Aimbot unique pitch levels: 2-5 per demo

4. Pre-Shot Behavior Analysis (Trigger Bot Detection)

What it measures: The timing pattern of shots, specifically for "trigger bots."

A trigger bot is different from a traditional aimbot:

  • Traditional aimbot: Software moves your crosshair TO the enemy
  • Trigger bot: You aim naturally, but software automatically clicks when crosshair passes over an enemy

Trigger bots are harder to detect because the aim movement looks natural. But the TIMING gives them away.

HIGH_INSTANT_SHOT_RATE (High Severity)

"Most shots are fired the instant the crosshair settles on target."

Human reaction time has limits. When your crosshair moves over an enemy, your brain needs time to:

  1. Recognize the crosshair is on target
  2. Decide to shoot
  3. Send the signal to click

This takes at least 150-200ms for the fastest humans. Most players take 200-400ms.

A trigger bot fires INSTANTLY (under 50ms) when the crosshair touches the enemy. If 50%+ of shots are fired with 0ms of stable aim time, it's a strong trigger bot indicator.

CONSISTENT_SHOT_TIMING (Medium Severity)

"The delay between settling and shooting is suspiciously consistent."

Human reaction times VARY. Sometimes you're ready and react fast, sometimes you're distracted and slow. Your timing will have natural variance.

Trigger bots (or aimbots with fire delay) often have very consistent timing - the same delay every time. We measure this using "coefficient of variation" (CV). Natural players have CV around 0.2-0.3 (20-30% variance). A CV below 0.10 (less than 10% variance) suggests automated timing.

SETTLE_SNAP_PATTERN (High Severity)

"Most shots follow a pattern: moving → instant stop → instant fire."

This detects a specific trigger bot behavior:

  1. Player moves crosshair naturally
  2. Crosshair stops on enemy (trigger bot detects this)
  3. Shot fires immediately (<50ms after stopping)

If 70%+ of shots follow this exact pattern, it's a strong trigger bot signature.


5. Aim Path Characteristics

What it measures: The shape and corrections in aim movement paths.

ABSOLUTELY_NO_CORRECTIONS (Medium Severity)

"The player never reverses their aim direction - every movement goes directly to target."

When humans aim, we constantly make small corrections. We overshoot slightly and pull back, or undershoot and push forward. Our aim path wiggles.

Aimbots calculate the exact path and execute it perfectly. They never need to "correct" because they never make mistakes. If we see hundreds of movements with ZERO direction reversals, it suggests non-human aim.

NO_OVERSHOOT (Low Severity)

"The player never overshoots targets - they stop exactly on the enemy every time."

Overshooting is natural. You flick toward an enemy, go slightly past them, then correct back. Even skilled players overshoot occasionally.

Aimbots don't overshoot because they know exactly where to stop. If someone makes many fast movements and NEVER overshoots, their aim is suspiciously precise.

Statistics:

  • Natural direction change rate: ~0.7% (constant micro-corrections)
  • Cheater direction change rate: ~0.3% (fewer corrections needed)
  • Natural overshoot rate: ~0.07 (7% of fast movements overshoot)
  • Cheater overshoot rate: ~0.04 (4% of fast movements overshoot)

Summary Table

Flag What It Detects Severity
AIMLOCK_LOW_MAX_VELOCITY Aim never moves fast - locked by software High
SPARSE_INTERMEDIATE_MOVEMENTS Missing gradual movements - jumps from still to fast High
PURE_SINGLE_AXIS_SNAPS Only horizontal OR vertical movement, never diagonal High
REPEATED_EXACT_ANGLES Same angle change repeated - robotic precision High
CONSISTENT_SNAP_PITCH Always snaps to same vertical level - targets same hitbox High
ZERO_MEDIUM_VEL_SHOTS Never fires while adjusting - only when locked or after snap High
HIGH_INSTANT_SHOT_RATE Fires instantly when on target - trigger bot High
SETTLE_SNAP_PATTERN Move → instant stop → instant fire sequence High
ALWAYS_ADJUSTING_BEFORE_SHOTS Never stable before shooting - constant micro-adjustments High
REPEATED_EXACT_VELOCITY Same movement speed repeatedly - fixed aimbot speed Medium
LOW_SNAP_PITCH_DIVERSITY Low variety in where snaps land vertically Medium
CONSISTENT_SHOT_TIMING Shot timing too consistent - automated Medium
ABSOLUTELY_NO_CORRECTIONS Never corrects aim - perfect paths Medium
BINARY_VELOCITY_PATTERN Instant acceleration with no ramp-up Low
NO_OVERSHOOT Never overshoots targets - too precise Low
EXTREMELY_LOW_PITCH_DIVERSITY Shots cluster at very few vertical levels Low

Aimbot Types Detected

Different cheats work in different ways, and each leaves distinct fingerprints. Here's what we can detect:

1. Snap/Toggle Aimbot

How it works: The player toggles the aimbot on (usually with a key press) when they see an enemy. The aimbot instantly snaps the crosshair to the target. The player then toggles it off to aim naturally until the next engagement.

What it looks like in-game: The crosshair is moving normally, then INSTANTLY jumps to an enemy, fires, then resumes normal movement. Very obvious when watching demos at slow speed.

Telltale signs we detect:

  • SPARSE_INTERMEDIATE_MOVEMENTS - Crosshair goes from still → snap → still with no gradual movement
  • PURE_SINGLE_AXIS_SNAPS - Snaps are perfectly horizontal or vertical (bot calculates shortest path)
  • REPEATED_EXACT_ANGLES - Same snap angle used repeatedly (bot uses same calculation)
  • CONSISTENT_SNAP_PITCH - Always snaps to same body part (bot targets same hitbox)

2. Silent/Lock Aimbot

How it works: The aimbot keeps the crosshair "locked" onto enemies at all times, but with very subtle movements. Some versions are "silent" - the player's view appears normal, but bullets are redirected to hit enemies regardless of where the crosshair appears to be pointing.

What it looks like in-game: The player's aim seems unnaturally stable, almost glued to targets. Very few sudden movements. Shots land even when aim appears slightly off.

Telltale signs we detect:

  • AIMLOCK_LOW_MAX_VELOCITY - Fastest movement is suspiciously slow (never needs to flick because aim is always locked)
  • AIMLOCK_MINIMAL_MOVEMENT - Very few aim movements overall (bot handles aiming)
  • ZERO_MEDIUM_VEL_SHOTS - Never fires while adjusting (aim is always perfectly locked)

3. Smooth Aimbot

How it works: Instead of snapping instantly, the aimbot moves the crosshair toward the target at a controlled, human-like speed. Designed to look natural and evade detection.

What it looks like in-game: Aim appears to "track" enemies smoothly, almost too smoothly. The crosshair follows targets without the jitter and corrections natural to human aim.

Telltale signs we detect:

  • LOW_INTERMEDIATE_MOVEMENTS - Missing the "searching" movements between engagements
  • BINARY_VELOCITY_PATTERN - Despite smooth appearance, still shows instant velocity changes
  • ABSOLUTELY_NO_CORRECTIONS - Never overshoots or corrects - perfect paths every time
  • Combined with low pitch diversity - always hits same target areas

Note: Sophisticated smooth aimbots are the hardest to detect because they're specifically designed to mimic human patterns. Our current system catches most, but the most advanced ones may require machine learning to identify.

4. Trigger Bot

How it works: Unlike traditional aimbots, trigger bots don't move the crosshair at all. The player aims naturally with their own skill. The bot only handles ONE thing: clicking the fire button the instant the crosshair passes over an enemy.

Trigger bots work by either:

  • Reading game memory: Checking if the crosshair ID points to an enemy entity
  • Screen analysis: Using computer vision to detect when crosshair is over an enemy color/shape

What it looks like in-game: Aim movement appears completely natural and human. But shots are fired with inhuman reaction time - the instant the crosshair touches an enemy, even when flicking past them at high speed.

Telltale signs we detect:

  • HIGH_INSTANT_SHOT_RATE - Shots fire instantly (0ms delay) when crosshair reaches target
  • CONSISTENT_SHOT_TIMING - Timing is suspiciously consistent (low variance in reaction time)
  • SETTLE_SNAP_PATTERN - Characteristic pattern of: moving → instant stop → instant fire

Why trigger bots are hard to detect: The aim itself is human, so movement analysis won't catch it. We focus on the TIMING - humans cannot react in under 150ms, but trigger bots fire in under 10ms consistently.

Detection Success by Type

Based on our testing with 15 confirmed cheater demos:

Aimbot Type Detection Rate Typical Score Common Flags Triggered
Snap/Toggle ~100% 50-80 SPARSE_INTERMEDIATE, REPEATED_EXACT_ANGLES
Silent/Lock ~100% 45-70 AIMLOCK_LOW_MAX_VELOCITY, ZERO_MEDIUM_VEL_SHOTS
Trigger Bot ~90% 30-50 HIGH_INSTANT_SHOT_RATE, CONSISTENT_SHOT_TIMING
Smooth Aimbot ~75% 30-45 LOW_INTERMEDIATE, BINARY_VELOCITY_PATTERN
Advanced Smooth ~0% 0-10 None - mimics human patterns too well

Detection Difficulty by Type

Aimbot Type Detection Difficulty Why
Snap/Toggle Easy Obvious instant snaps leave clear signatures
Silent/Lock Medium Unusual lack of movement is detectable
Trigger Bot Medium-Hard Aim looks natural, must detect timing anomalies
Smooth Aimbot Hard Specifically designed to mimic human patterns

Usage

Analyze a demo for aimbot

const AimAnalyzer = require('./src/analyzer/aim-analyzer');

const analyzer = new AimAnalyzer({ debug: false });
const results = await analyzer.analyzeDemo('./path/to/demo.urtdemo');

console.log(`Suspicion Score: ${results.suspicionScore}`);
console.log(`Verdict: ${results.verdict.level}`);
console.log(`Flags:`, results.viewAngleAnalysis?.flags);

Analyze multiple demos

const results = await analyzer.analyzeMultiple([
  './demo1.urtdemo',
  './demo2.urtdemo',
]);

for (const result of results) {
  console.log(`${result.filename}: ${result.suspicionScore} (${result.verdict.level})`);
}

Technical Implementation

Demo File Format

Urban Terror demo files use the Quake 3 engine demo format with Huffman compression:

  • Header: Version string + protocol number (70 for UT 4.3.4)
  • Packets: Each packet contains messages (gamestate, server commands, snapshots)
  • Huffman Compression: All packet data is Huffman-compressed using a static tree

Message Types (from msg.h)

Value Command Description
0x01 svc_nop No operation
0x02 svc_gamestate Initial game state (config strings, baselines)
0x03 svc_configstring Config string update (only in gamestate)
0x04 svc_baseline Entity baseline (only in gamestate)
0x05 svc_serverCommand Server command (chat, print, cs updates)
0x06 svc_download File download
0x07 svc_snapshot Game snapshot (entity states)
0x08 svc_EOF End of message

Config String Layout

Range Purpose
0 CS_SERVERINFO - Server info
1 CS_SYSTEMINFO - System info (pak files, etc.)
2-31 Various game settings
32-287 Models
288-543 Sounds
544-575 Players (CS_PLAYERS + clientNum)
576+ Items, locations, etc.

Huffman Decompression

The parser uses a precomputed Huffman lookup table for fast and accurate decompression:

  • Located in src/parser/huffman-table.js
  • 65536-entry lookup table (16-bit index)
  • Generated from the same frequency data (msg_hData) used by the Quake 3 engine
  • Handles variable-length codes (2-11 bits)

POV (Point of View) Detection

Identifying which player recorded the demo uses multiple methods:

  1. End Pattern Detection: Search for 0x00 clientNum 00 00 00 pattern at end of gamestate
  2. PM Welcome Detection: Parse [pm] [Authed] Welcome back PlayerName messages (only visible to POV player)
  3. Known Name Matching: Match against known player names

Current detection rate: ~83%


Relevant Links

Aimbot Detection Research


Setup

Prerequisites

  • Node.js (v16 or higher)
  • Docker and Docker Compose (for PostgreSQL, Metabase, and JSDoc)
  • Or standalone PostgreSQL if not using Docker

Installation

  1. Install dependencies:
npm install
  1. Copy environment file:
cp .env.example .env
  1. Start services with Docker Compose:
docker-compose up -d
  1. Run database migrations:
npm run migrate

Web Interfaces

After starting Docker Compose, the following services are available:

Service URL Description
Metabase http://localhost:3001 Analytics and database query UI
JSDoc http://localhost:3002 API documentation browser
PostgreSQL localhost:5432 Database (use with psql or clients)

Rebuilding JSDoc

If you update the code documentation and want to regenerate JSDoc:

# Rebuild the JSDoc container
docker-compose build jsdoc

# Restart the service
docker-compose up -d jsdoc

Local JSDoc Generation (without Docker)

You can also generate documentation locally:

# Install JSDoc
npm install -g jsdoc

# Generate docs
jsdoc -c jsdoc.json

# View in browser
npx serve docs

Usage

Parse demo files

Parse a single demo file:

npm run parse -- parse ./example-demos/example.urtdemo

Parse multiple demos:

npm run parse -- parse ./example-demos/*.urtdemo

Output as JSON (without database):

npm run parse -- parse ./example-demos/example.urtdemo -o json

Search chat messages

Search for keywords in chat:

npm run parse -- search "keyword"

Search with filters:

npm run parse -- search "keyword" --player "PlayerName" --team --limit 50

Database Schema

The database includes these main tables:

  • demos: Demo file metadata (filename, map, date, protocol)
  • player_profiles: Unique players with tracking flags
  • players: Per-demo player appearances (links to profiles)
  • chat_messages: All chat messages with full-text search support

See src/db/schema.sql for the complete schema.


Metabase Analytics

Metabase provides a powerful interface for searching, querying, and visualizing demo data.

First-Time Setup

  1. Start the services:

    docker-compose up -d
  2. Access Metabase at http://localhost:3001

  3. Complete the setup wizard:

    • Create an admin account
    • When asked to add a database, select PostgreSQL
    • Use these connection settings:
      • Host: postgres (container name)
      • Port: 5432
      • Database: urt_demo_analyzer
      • Username: urt_user
      • Password: urt_password

Example Queries

Find all chats containing a keyword:

SELECT player_name, message, d.filename, d.map_name
FROM chat_messages c
JOIN demos d ON c.demo_id = d.id
WHERE message ILIKE '%keyword%'
ORDER BY c.created_at DESC;

Find all demos a player appeared in:

SELECT DISTINCT d.filename, d.map_name, p.team
FROM players p
JOIN demos d ON p.demo_id = d.id
WHERE p.name ILIKE '%playername%'
ORDER BY d.created_at;

Mark a player as a suspected cheater:

UPDATE player_profiles SET suspected_cheater = TRUE, notes = 'Suspicious aiming patterns'
WHERE name ILIKE '%playername%';

Find all trolls and cheaters:

SELECT name, troll_count, is_troll, banned_for_cheating, suspected_cheater, notes, demos_played
FROM player_profiles
WHERE is_troll = TRUE OR banned_for_cheating = TRUE OR suspected_cheater = TRUE
ORDER BY troll_count DESC;

Project Structure

├── src/
│   ├── analyzer/
│   │   └── aim-analyzer.js     # Aimbot detection logic
│   ├── parser/
│   │   ├── demo-parser.js      # Main demo parsing logic
│   │   ├── message-reader.js   # Huffman-compressed message reading
│   │   ├── snapshot-parser.js  # Game snapshot parsing
│   │   ├── huffman-table.js    # Precomputed Huffman lookup table
│   │   └── index.js            # CLI entry point
│   └── db/
│       ├── schema.sql          # Database schema
│       └── migrate.js          # Migration script
├── training-demos/
│   ├── cheater-demos/          # Verified cheater demos for testing
│   └── natural-demos/          # Verified natural player demos
├── example-demos/              # Demo files for testing
└── docker-compose.yml          # Docker services configuration

Current Capabilities

✅ Parse Urban Terror 4.3.4 demo files (.urtdemo) ✅ Extract player names, teams, and client IDs ✅ Extract chat messages (global and team chat) ✅ Extract view angles for aim analysis ✅ Detect multiple aimbot types (snap, silent, smooth, trigger) ✅ Handle large config strings (up to 8192 chars) ✅ Huffman decompression with precomputed lookup table ✅ PostgreSQL storage with full-text search


Languages

Preferred

  • JavaScript/Node.js
  • Bash

Avoided

  • Python
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment