Skip to content

Commit 62b6170

Browse files
authored
implement (#446) (#447)
* implement * fix audit log model type filter
1 parent e71b09c commit 62b6170

File tree

5 files changed

+387
-17
lines changed

5 files changed

+387
-17
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,4 +270,7 @@ cncnet-api/database/migrations/2022_05_14_214858_createDummyTestAccounts.php
270270
docker-compose.test.yml
271271
cncnet-api/.bash_history
272272
**/debugging/
273-
**/vendor/
273+
**/vendor/
274+
275+
# Claude Code local settings
276+
.claude/settings.local.json

CLAUDE.md

Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
This is the **CnCNet Ladder API**, a Laravel 11-based competitive ladder/ranking system for classic Command & Conquer games (Red Alert, Yuri's Revenge, Tiberian Sun, Dune 2000). It provides 1v1, 2v2, and clan match support with automated Quick Match (QM) matchmaking, ELO ratings, player statistics, and achievement tracking.
8+
9+
**Tech Stack**: Laravel 11, PHP 8.3, FrankenPHP (Laravel Octane), MariaDB, Redis, Bootstrap 5, Vite
10+
11+
## Development Commands
12+
13+
### Initial Setup
14+
```bash
15+
# Build and start development containers
16+
docker compose -f docker-compose.dev.yml build
17+
docker compose -f docker-compose.dev.yml up -d
18+
19+
# Generate Laravel app key
20+
docker exec dev_cncnet_ladder_app php artisan key:generate
21+
22+
# Migrate database (or restore from backup)
23+
docker exec dev_cncnet_ladder_app php artisan migrate
24+
```
25+
26+
### Common Development Tasks
27+
```bash
28+
# Clear cache after .env changes
29+
docker exec dev_cncnet_ladder_app php artisan optimize:clear
30+
31+
# Run queue workers (manual in dev)
32+
docker exec -it dev_cncnet_ladder_app php artisan queue:listen --queue=findmatch,saveladderresult
33+
34+
# Run scheduler/cron manually
35+
docker exec dev_cncnet_ladder_app php artisan scheduler:run
36+
37+
# Start Vite dev server (hot reload)
38+
docker exec -it dev_cncnet_ladder_app npm run dev
39+
40+
# Watch SCSS changes
41+
npm run watch
42+
43+
# Open shell in container
44+
docker exec -it dev_cncnet_ladder_app bash
45+
46+
# Access database
47+
# Host: localhost, Port: 3307, User/Pass from .env
48+
```
49+
50+
### Testing
51+
```bash
52+
# Run PHPUnit tests
53+
docker exec dev_cncnet_ladder_app php artisan test
54+
```
55+
56+
### Production Commands (CI/CD automated)
57+
```bash
58+
# Build production containers
59+
docker compose build
60+
61+
# Clear config cache after deployment
62+
docker exec cncnet_ladder_app php artisan config:cache
63+
```
64+
65+
## Architecture & Code Structure
66+
67+
### Laravel Application Structure
68+
69+
The main application lives in `cncnet-api/`. Key architectural patterns:
70+
71+
**Controllers** (`app/Http/Controllers/`):
72+
- `ApiLadderController`: REST API for ladder data, game results, player stats
73+
- `ApiQuickMatchController`: QM matchmaking, map pools, rankings
74+
- `LadderController`: Web UI views
75+
- `AdminController`: Admin panel functionality
76+
- API versioning: v1 (main), v2 (new endpoints)
77+
78+
**Services** (`app/Http/Services/`):
79+
Business logic layer:
80+
- `QuickMatchService`: Core matchmaking logic
81+
- `EloService`: Rating calculations using ELO algorithm
82+
- `GameService`: Game result processing and validation
83+
- `LadderService`: Ladder operations and caching
84+
- `PlayerService`: Player management and ratings
85+
- `AchievementService`: Achievement tracking
86+
87+
**Queue System**:
88+
Two separate queues processed by dedicated workers:
89+
- `findmatch`: Handles opponent finding (`FindOpponentJob`)
90+
- `saveladderresult`: Processes game results (`SaveLadderResultJob`)
91+
92+
Queue jobs live in `app/Jobs/Qm/`
93+
94+
**Extensions** (`app/Extensions/`):
95+
Matchup handlers for different game modes:
96+
- `PlayerMatchupHandler`: 1v1 matchmaking
97+
- `TeamMatchupHandler`: 2v2+ matchmaking
98+
- `ClanMatchupHandler`: Clan match logic
99+
100+
### Database Schema (70+ Models)
101+
102+
**Core Models**:
103+
- `Ladder`: Game type definitions (RA2, YR, TS, etc.)
104+
- `LadderHistory`: Monthly ladder snapshots
105+
- `Player`: Player accounts (per-ladder)
106+
- `User`: Global user accounts
107+
- `Game`: Individual game records
108+
- `GameReport`: Player-submitted game results (can have multiple per game)
109+
- `PlayerGameReport`: Individual player performance in a game
110+
- `QmMatch`: Quick Match game records
111+
- `QmMatchPlayer`: Player participation in QM match
112+
- `QmQueueEntry`: Active matchmaking queue entries
113+
- `PlayerRating`/`UserRating`: ELO rating storage
114+
- `Clan`: Clan/team entities with ratings
115+
- `Ban`: Player ban records
116+
117+
**Important Relationships**:
118+
- `Game` has many `GameReport`s (one is marked `best_report`)
119+
- `GameReport` has many `PlayerGameReport`s
120+
- `Player` belongs to `Ladder` and `User`
121+
- `QmMatch` has many `QmMatchPlayer`s
122+
123+
### Quick Match Flow
124+
125+
1. Client requests match via `POST /api/v1/qm/{ladder}/{player}``MatchUpController`
126+
2. Request validated through middleware: ClientUpToDate, ShadowBan, Ban, VerifiedEmail
127+
3. `QmQueueEntry` created for player
128+
4. `FindOpponentJob` dispatched to queue
129+
5. Job selects appropriate handler (Player/Team/Clan MatchupHandler)
130+
6. Matching algorithm considers:
131+
- ELO ratings and tier placement
132+
- Map preferences/vetoes
133+
- Faction policies (allowed pairings)
134+
- Connection quality/ping
135+
7. `QmMatch` created with spawn parameters sent to clients
136+
8. Game results submitted via `POST /api/v1/result/ladder/{ladderId}/game/{gameId}/player/{playerId}/pings/{sent}/{received}`
137+
9. `SaveLadderResultJob` processes stats dump file
138+
10. Points calculated (`awardPlayerPoints`, `awardTeamPoints`, or `awardClanPoints` methods)
139+
11. ELO ratings updated via `EloService`
140+
12. Player cache updated via `PlayerCache` model
141+
142+
### Middleware & Caching
143+
144+
**Custom Cache Middleware** (all public):
145+
- `CacheUltraShortPublic`: 10 seconds (IRC endpoints)
146+
- `CacheShortPublic`: 30 seconds
147+
- `CachePublicMiddleware`: 1 minute (player stats, short-lived data)
148+
- `CacheLongPublicMiddleware`: 60 minutes (ladder listings, static data)
149+
150+
**Restriction Middleware**:
151+
- `Restrict`: Permission checking for admin actions
152+
- `BanMiddleware`: Checks active bans before QM
153+
- `VerifiedEmailMiddleware`: Requires verified email
154+
- `ClientUpToDateMiddleware`: Client version validation
155+
156+
### Docker Architecture
157+
158+
**Development** (`docker-compose.dev.yml`):
159+
- Single `app` container with volume mounts for hot reload
160+
- MySQL exposed on port 3307
161+
- PHPMyAdmin on port 8080
162+
- Vite dev server on port 5173
163+
- Queue/scheduler NOT running (manual start required)
164+
165+
**Production** (`docker-compose.yml`):
166+
- `app`: FrankenPHP web server (port 3000→8000)
167+
- `queue-findmatch`: Dedicated queue worker
168+
- `queue-saveladderresult`: Dedicated queue worker
169+
- `scheduler`: Laravel scheduler (cron tasks)
170+
- `mysql`: MariaDB database
171+
- `redis`: Cache and queue backend
172+
- `db-backup`: Automated backups using tiredofit/db-backup
173+
- `elogen`: ELO computation cron container
174+
175+
**Multi-stage Build**: Dockerfiles in `docker/frankenphp/` and `docker/workers/`
176+
177+
### Configuration Files
178+
179+
**Environment Files**:
180+
- `.env`: Docker-specific (HOST_USER, HOST_UID, APP_TAG, ports)
181+
- `.app.env`: Laravel application config (production)
182+
- `.backup.env`: Backup container settings
183+
- `cncnet-api/.env`: Development Laravel config
184+
185+
**Key Laravel Configs**:
186+
- `config/types.php`: Game type definitions (28KB of metadata)
187+
- `config/cameos.php`: Unit cameo mappings
188+
- `config/jwt.php`: JWT authentication
189+
- `config/octane.php`: FrankenPHP/Octane settings
190+
191+
### Scheduled Tasks
192+
193+
Defined in `bootstrap/app.php` (Laravel 11 structure):
194+
- **Daily**: Log pruning, stats cleanup
195+
- **Hourly**: Player cache updates (`UpdatePlayerCache` command)
196+
- **Monthly**: QM data pruning, player rating updates
197+
- **Every Minute**: Clear inactive queue entries
198+
199+
### Routes Structure
200+
201+
**Web Routes** (`routes/web.php`): 400+ lines
202+
- Ladder views, player profiles, admin panel
203+
- Grouped by authentication and permissions
204+
205+
**API Routes** (`routes/api.php`):
206+
- `v1`: Main API (auth, ladder, QM, results)
207+
- `v2`: New endpoints (bans, events, user accounts)
208+
- Middleware groups for caching and authentication
209+
210+
### Game Result Processing
211+
212+
When game results are submitted:
213+
214+
1. Stats dump file uploaded and moved to `config('filesystems.dmp')` directory
215+
2. `SaveLadderResultJob` queued
216+
3. Job calls `GameService::processStatsDmp()` to parse binary stats
217+
4. `GameReport` created or updated
218+
5. Dispute handling: Multiple reports compared, best report selected based on:
219+
- Finished status (prefer finished over disconnected)
220+
- Duration (prefer longer games)
221+
- Ping difference (prefer better connection)
222+
6. If both reports show disconnect/OOS: auto-wash game (create draw report)
223+
7. Points awarded via `awardPlayerPoints()`, `awardTeamPoints()`, or `awardClanPoints()`
224+
8. Achievement progress tracked (`updateAchievements()`)
225+
9. Player cache updated via `LadderService::updateCache()`
226+
227+
### Achievement System
228+
229+
Two types:
230+
- **CAREER**: Cumulative tracking (e.g., build 1000 tanks)
231+
- **IMMEDIATE**: Single-game threshold (e.g., build 50 tanks in one game)
232+
233+
Tracked via `Achievement`, `AchievementProgress`, and `GameObjectCounts` models.
234+
235+
### Anti-Cheat & Moderation
236+
237+
- IP address tracking via `IpAddress` and `IpAddressHistory`
238+
- Admin actions logged via Spatie ActivityLog
239+
- Game "washing" (marking as draw) for:
240+
- Mutual disconnects
241+
- Mutual out-of-sync
242+
- Suspected abuse
243+
- Shadow bans (queue but never match)
244+
- Admin panel for manual intervention
245+
246+
### Important Code Patterns
247+
248+
**Finding Ladder by Abbreviation**:
249+
```php
250+
$ladder = Ladder::where('abbreviation', '=', $game)->first();
251+
```
252+
253+
**Getting Current Ladder History** (monthly snapshot):
254+
```php
255+
$history = $ladder->currentHistory();
256+
```
257+
258+
**Querying Player Games with Joins**:
259+
Always join through `game_reports` and `games` tables, filtering on `valid` and `best_report`:
260+
```php
261+
PlayerGameReport::where('player_game_reports.player_id', '=', $playerId)
262+
->join('game_reports', 'game_reports.id', '=', 'player_game_reports.game_report_id')
263+
->join('games', 'games.id', '=', 'game_reports.game_id')
264+
->where('game_reports.valid', '=', true)
265+
->where('game_reports.best_report', '=', true)
266+
```
267+
268+
**Table Prefixing**: Always prefix ambiguous columns (e.g., `player_game_reports.player_id` not `player_id`) when joining multiple tables.
269+
270+
## CI/CD Pipeline
271+
272+
**GitHub Actions** (`.github/workflows/build-and-deploy.yml`):
273+
274+
1. **Build Job**: Builds 3 images in parallel (app, queue, scheduler)
275+
- Tagged with: `latest`, branch name, short SHA
276+
- Pushed to GitHub Container Registry (ghcr.io)
277+
278+
2. **Deploy Job** (main branch only):
279+
- Uploads `docker-compose.yml` via SCP
280+
- SSH to server
281+
- Updates `.env` with new image tag
282+
- Pulls images and restarts containers
283+
- Runs `php artisan config:cache`
284+
285+
**Deployment is automatic** on merge to main branch.
286+
287+
## Development Notes
288+
289+
### Laravel 11 Changes
290+
- No `app/Http/Kernel.php` - middleware in `bootstrap/app.php`
291+
- Scheduling in `bootstrap/app.php` instead of `app/Console/Kernel.php`
292+
- Slimmer directory structure
293+
294+
### FrankenPHP (Octane)
295+
- High-performance PHP application server
296+
- Uses Caddy web server under the hood
297+
- Configured via `OCTANE_SERVER=frankenphp` in `.env`
298+
- Startup script: `docker/frankenphp/octane.sh`
299+
300+
### Queue Workers
301+
- Run in separate containers for horizontal scaling
302+
- Each queue has dedicated worker (findmatch, saveladderresult)
303+
- Worker script: `docker/workers/queue.sh`
304+
305+
### Database Access
306+
- **Dev**: localhost:3307 (exposed)
307+
- **Dev UI**: PHPMyAdmin at localhost:8080
308+
- Migrations in `database/migrations/` (42 files)
309+
- Schema dumps in `database/schema/`
310+
311+
### Assets
312+
- Vite build system (`vite.config.ts`)
313+
- SCSS in `resources/stylesheets/`
314+
- TypeScript in `resources/typescript/`
315+
- Build output to `public/build/`
316+
- Dev server: port 5173 (hot reload)
317+
318+
### Elogen Container
319+
Separate cron-based ELO computation system:
320+
- Configured via `docker/elogen/crontab`
321+
- Reads/writes to `storage/app/rating/`
322+
- Independent calculation for validation
323+
324+
## Access URLs
325+
326+
- **Development Web UI**: http://localhost:3000
327+
- **Development PHPMyAdmin**: http://localhost:8080
328+
- **Development Vite**: http://localhost:5173
329+
- **Production**: https://ladder.cncnet.org
330+
331+
## Troubleshooting
332+
333+
**"No supported encrypter found"**:
334+
- Generate key: `docker exec dev_cncnet_ladder_app php artisan key:generate`
335+
- Update .env then rebuild: `docker compose -f docker-compose.dev.yml up -d`
336+
337+
**"Base table or view not found"**:
338+
- Restore from backup OR run migrations: `docker exec dev_cncnet_ladder_app php artisan migrate`
339+
340+
**Queue jobs not processing**:
341+
- In dev, queue workers don't auto-start
342+
- Manual start: `docker exec -it dev_cncnet_ladder_app php artisan queue:listen --queue=findmatch,saveladderresult`
343+
344+
**"Column 'X' is ambiguous" SQL errors**:
345+
- Always prefix columns with table name when joining (e.g., `player_game_reports.player_id`)
346+
347+
**WSL2 Docker integration issues**:
348+
- Check Docker Desktop → Settings → Resources → WSL Integration
349+
- Ensure Ubuntu distro is enabled
350+
- Restart Docker Desktop or run `wsl --shutdown` and restart

0 commit comments

Comments
 (0)