480 lines
12 KiB
Markdown
480 lines
12 KiB
Markdown
# 🏆 Achievement System
|
|
|
|
Umfassende Dokumentation des Achievement-Systems für das Ninja Cross Parkour System.
|
|
|
|
## 📊 System-Übersicht
|
|
|
|
Das Achievement-System besteht aus:
|
|
- **32 verschiedene Achievements** in 4 Kategorien
|
|
- **Automatische tägliche Vergabe** am Ende des Tages
|
|
- **REST API Endpoints** für Frontend-Integration
|
|
- **PostgreSQL Funktionen** für effiziente Verarbeitung
|
|
|
|
## 🎯 Achievement-Kategorien
|
|
|
|
### 1. Konsistenz-basierte Achievements
|
|
- **Erste Schritte** 👶 - Erste Zeit aufgezeichnet (5 Punkte)
|
|
- **Durchhalter** 💪 - 3 Versuche an einem Tag (10 Punkte)
|
|
- **Fleißig** 🔥 - 5 Versuche an einem Tag (15 Punkte)
|
|
- **Besessen** 😤 - 10 Versuche an einem Tag (25 Punkte)
|
|
- **Regelmäßig** 📅 - 5 verschiedene Tage gespielt (20 Punkte)
|
|
- **Stammgast** ⭐ - 10 verschiedene Tage gespielt (30 Punkte)
|
|
- **Treue** 💎 - 20 verschiedene Tage gespielt (50 Punkte)
|
|
- **Veteran** 🏆 - 50 verschiedene Tage gespielt (100 Punkte)
|
|
|
|
### 2. Verbesserungs-basierte Achievements
|
|
- **Fortschritt** 📈 - Persönliche Bestzeit um 5 Sekunden verbessert (15 Punkte)
|
|
- **Durchbruch** ⚡ - Persönliche Bestzeit um 10 Sekunden verbessert (25 Punkte)
|
|
- **Transformation** 🔄 - Persönliche Bestzeit um 15 Sekunden verbessert (40 Punkte)
|
|
- **Perfektionist** ✨ - Persönliche Bestzeit um 20 Sekunden verbessert (60 Punkte)
|
|
|
|
### 3. Saisonale Achievements
|
|
- **Wochenend-Krieger** 🏁 - Am Wochenende gespielt (10 Punkte)
|
|
- **Nachmittags-Sportler** ☀️ - Zwischen 14-18 Uhr gespielt (10 Punkte)
|
|
- **Frühaufsteher** 🌅 - Vor 10 Uhr gespielt (15 Punkte)
|
|
- **Abend-Sportler** 🌙 - Nach 18 Uhr gespielt (10 Punkte)
|
|
|
|
### 4. Monatliche Achievements
|
|
- **Januar-Krieger** ❄️ bis **Dezember-Dynamo** 🎄 (je 20 Punkte)
|
|
|
|
### 5. Jahreszeiten-Achievements
|
|
- **Frühjahrs-Fighter** 🌱 - Im Frühling gespielt (30 Punkte)
|
|
- **Sommer-Sportler** ☀️ - Im Sommer gespielt (30 Punkte)
|
|
- **Herbst-Held** 🍂 - Im Herbst gespielt (30 Punkte)
|
|
- **Winter-Warrior** ❄️ - Im Winter gespielt (30 Punkte)
|
|
|
|
## 🗄️ Datenbank-Schema
|
|
|
|
### Tabelle: `achievements`
|
|
```sql
|
|
- id (uuid, PK)
|
|
- name (varchar) - Achievement-Name
|
|
- description (text) - Beschreibung
|
|
- category (varchar) - Kategorie
|
|
- condition_type (varchar) - Bedingungstyp
|
|
- condition_value (integer) - Bedingungswert
|
|
- icon (varchar) - Emoji-Icon
|
|
- points (integer) - Punkte
|
|
- is_active (boolean) - Aktiv
|
|
- created_at (timestamp)
|
|
```
|
|
|
|
### Tabelle: `player_achievements`
|
|
```sql
|
|
- id (uuid, PK)
|
|
- player_id (uuid, FK) - Verweis auf players.id
|
|
- achievement_id (uuid, FK) - Verweis auf achievements.id
|
|
- earned_at (timestamp) - Wann erreicht
|
|
- progress (integer) - Fortschritt
|
|
- is_completed (boolean) - Abgeschlossen
|
|
- created_at (timestamp)
|
|
```
|
|
|
|
## 🔧 PostgreSQL Funktionen
|
|
|
|
### `check_consistency_achievements(player_uuid)`
|
|
Überprüft alle Konsistenz-basierten Achievements für einen Spieler.
|
|
|
|
**Logik:**
|
|
- Zählt Gesamtläufe des Spielers
|
|
- Zählt Läufe pro Tag
|
|
- Zählt verschiedene Spieltage
|
|
- Vergibt entsprechende Achievements
|
|
|
|
### `check_improvement_achievements(player_uuid)`
|
|
Überprüft alle Verbesserungs-basierten Achievements für einen Spieler.
|
|
|
|
**Logik:**
|
|
- Ermittelt persönliche Bestzeit
|
|
- Berechnet Verbesserung seit erster Zeit
|
|
- Vergibt entsprechende Achievements
|
|
|
|
### `check_seasonal_achievements(player_uuid)`
|
|
Überprüft alle saisonalen und monatlichen Achievements für einen Spieler.
|
|
|
|
**Logik:**
|
|
- Prüft Wochentag (Wochenende)
|
|
- Prüft Tageszeit (morgens, nachmittags, abends)
|
|
- Prüft Monat (Januar bis Dezember)
|
|
- Prüft Jahreszeit (Frühling, Sommer, Herbst, Winter)
|
|
|
|
### `check_all_achievements(player_uuid)`
|
|
Führt alle Achievement-Überprüfungen für einen Spieler aus.
|
|
|
|
## 🚀 API Endpoints
|
|
|
|
### GET `/api/achievements`
|
|
Alle verfügbaren Achievements abrufen.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": [
|
|
{
|
|
"id": "uuid",
|
|
"name": "Erste Schritte",
|
|
"description": "Absolviere deinen ersten Lauf",
|
|
"category": "consistency",
|
|
"condition_type": "runs_count",
|
|
"condition_value": 1,
|
|
"icon": "👶",
|
|
"points": 5,
|
|
"is_active": true
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### GET `/api/achievements/player/:playerId`
|
|
Achievements eines bestimmten Spielers abrufen.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": [
|
|
{
|
|
"id": "uuid",
|
|
"achievement_id": "uuid",
|
|
"name": "Erste Schritte",
|
|
"description": "Absolviere deinen ersten Lauf",
|
|
"icon": "👶",
|
|
"points": 5,
|
|
"progress": 1,
|
|
"is_completed": true,
|
|
"earned_at": "2024-01-01T00:00:00Z"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### GET `/api/achievements/player/:playerId/stats`
|
|
Achievement-Statistiken eines Spielers abrufen.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"total_achievements": 32,
|
|
"completed_achievements": 5,
|
|
"total_points": 150,
|
|
"earned_points": 75,
|
|
"completion_percentage": 15.6
|
|
}
|
|
}
|
|
```
|
|
|
|
### POST `/api/achievements/check/:playerId`
|
|
Achievements für einen Spieler manuell überprüfen.
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"achievement_id": "uuid"
|
|
}
|
|
```
|
|
|
|
### POST `/api/achievements/daily-check`
|
|
Tägliche Achievement-Überprüfung für alle Spieler ausführen.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"message": "Daily achievement check completed",
|
|
"players_checked": 150,
|
|
"achievements_awarded": 25
|
|
}
|
|
```
|
|
|
|
### GET `/api/achievements/leaderboard?limit=10`
|
|
Bestenliste der Spieler nach Achievement-Punkten.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": [
|
|
{
|
|
"player_id": "uuid",
|
|
"firstname": "Max",
|
|
"lastname": "Mustermann",
|
|
"total_points": 500,
|
|
"completed_achievements": 15,
|
|
"rank": 1
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
## 📅 Automatisierung
|
|
|
|
### Tägliches Script
|
|
```bash
|
|
# Manuell ausführen
|
|
node scripts/daily_achievements.js
|
|
|
|
# Cron-Job einrichten
|
|
node scripts/setup_cron.js setup
|
|
|
|
# Cron-Job Status prüfen
|
|
node scripts/setup_cron.js status
|
|
|
|
# Cron-Job entfernen
|
|
node scripts/setup_cron.js remove
|
|
```
|
|
|
|
### Cron-Schedule
|
|
- **Zeit**: Täglich um 23:59 Uhr
|
|
- **Log**: `/var/log/ninjaserver_achievements.log`
|
|
|
|
### Script-Details
|
|
```javascript
|
|
// scripts/daily_achievements.js
|
|
const { checkAllAchievements } = require('../models/Achievement');
|
|
|
|
async function dailyCheck() {
|
|
try {
|
|
// Alle Spieler abrufen
|
|
const players = await getAllPlayers();
|
|
|
|
let totalAwarded = 0;
|
|
|
|
for (const player of players) {
|
|
const awarded = await checkAllAchievements(player.id);
|
|
totalAwarded += awarded;
|
|
}
|
|
|
|
console.log(`Daily check completed: ${totalAwarded} achievements awarded`);
|
|
} catch (error) {
|
|
console.error('Daily check failed:', error);
|
|
}
|
|
}
|
|
```
|
|
|
|
## 🎮 Frontend-Integration
|
|
|
|
### Beispiel: Achievement-Liste laden
|
|
```javascript
|
|
async function loadAchievements(playerId) {
|
|
try {
|
|
const response = await fetch(`/api/achievements/player/${playerId}`);
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
data.data.forEach(achievement => {
|
|
const status = achievement.is_completed ? '✅' : '❌';
|
|
console.log(`${achievement.icon} ${achievement.name}: ${status}`);
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading achievements:', error);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Beispiel: Statistiken anzeigen
|
|
```javascript
|
|
async function loadStats(playerId) {
|
|
try {
|
|
const response = await fetch(`/api/achievements/player/${playerId}/stats`);
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
const stats = data.data;
|
|
document.getElementById('total-points').textContent = stats.total_points;
|
|
document.getElementById('completed').textContent =
|
|
`${stats.completed_achievements}/${stats.total_achievements}`;
|
|
document.getElementById('percentage').textContent =
|
|
`${stats.completion_percentage}%`;
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading stats:', error);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Beispiel: Achievement-Animation
|
|
```javascript
|
|
function showAchievementNotification(achievement) {
|
|
const notification = document.createElement('div');
|
|
notification.className = 'achievement-notification';
|
|
notification.innerHTML = `
|
|
<div class="achievement-icon">${achievement.icon}</div>
|
|
<div class="achievement-text">
|
|
<h3>${achievement.name}</h3>
|
|
<p>${achievement.description}</p>
|
|
<span class="points">+${achievement.points} Punkte</span>
|
|
</div>
|
|
`;
|
|
|
|
document.body.appendChild(notification);
|
|
|
|
// Animation
|
|
setTimeout(() => {
|
|
notification.classList.add('show');
|
|
}, 100);
|
|
|
|
// Entfernen nach 5 Sekunden
|
|
setTimeout(() => {
|
|
notification.remove();
|
|
}, 5000);
|
|
}
|
|
```
|
|
|
|
## 🔍 Monitoring
|
|
|
|
### Logs überwachen
|
|
```bash
|
|
# Live-Logs anzeigen
|
|
tail -f /var/log/ninjaserver_achievements.log
|
|
|
|
# Letzte Ausführung prüfen
|
|
grep "Daily achievement check completed" /var/log/ninjaserver_achievements.log | tail -1
|
|
|
|
# Fehler-Logs anzeigen
|
|
grep "ERROR" /var/log/ninjaserver_achievements.log
|
|
```
|
|
|
|
### Datenbank-Status prüfen
|
|
```sql
|
|
-- Achievement-Statistiken
|
|
SELECT
|
|
COUNT(*) as total_achievements,
|
|
COUNT(CASE WHEN is_active = true THEN 1 END) as active_achievements
|
|
FROM achievements;
|
|
|
|
-- Spieler-Statistiken
|
|
SELECT
|
|
COUNT(DISTINCT player_id) as players_with_achievements,
|
|
COUNT(*) as total_earned_achievements
|
|
FROM player_achievements
|
|
WHERE is_completed = true;
|
|
|
|
-- Top-Spieler
|
|
SELECT
|
|
p.firstname,
|
|
p.lastname,
|
|
COUNT(pa.id) as achievements,
|
|
SUM(a.points) as total_points
|
|
FROM players p
|
|
JOIN player_achievements pa ON p.id = pa.player_id
|
|
JOIN achievements a ON pa.achievement_id = a.id
|
|
WHERE pa.is_completed = true
|
|
GROUP BY p.id, p.firstname, p.lastname
|
|
ORDER BY total_points DESC
|
|
LIMIT 10;
|
|
```
|
|
|
|
## 🛠️ Wartung
|
|
|
|
### Neue Achievements hinzufügen
|
|
1. Achievement in `achievements` Tabelle einfügen:
|
|
```sql
|
|
INSERT INTO achievements (name, description, category, condition_type, condition_value, icon, points)
|
|
VALUES ('Neues Achievement', 'Beschreibung', 'consistency', 'runs_count', 5, '🏆', 25);
|
|
```
|
|
|
|
2. Logik in entsprechenden PostgreSQL Funktionen erweitern
|
|
3. API Endpoints testen
|
|
|
|
### Achievement deaktivieren
|
|
```sql
|
|
UPDATE achievements SET is_active = false WHERE name = 'Achievement-Name';
|
|
```
|
|
|
|
### Daten zurücksetzen
|
|
```sql
|
|
-- Alle Spieler-Achievements löschen
|
|
DELETE FROM player_achievements;
|
|
|
|
-- Achievement-Statistiken zurücksetzen
|
|
UPDATE achievements SET created_at = NOW();
|
|
```
|
|
|
|
### Achievement-Import/Export
|
|
```bash
|
|
# Export
|
|
pg_dump -t achievements -t player_achievements ninjaserver > achievements_backup.sql
|
|
|
|
# Import
|
|
psql ninjaserver < achievements_backup.sql
|
|
```
|
|
|
|
## 📈 Performance
|
|
|
|
### Indizierung
|
|
```sql
|
|
-- Performance-Indizes
|
|
CREATE INDEX CONCURRENTLY idx_player_achievements_player_id
|
|
ON player_achievements(player_id);
|
|
|
|
CREATE INDEX CONCURRENTLY idx_player_achievements_achievement_id
|
|
ON player_achievements(achievement_id);
|
|
|
|
CREATE INDEX CONCURRENTLY idx_player_achievements_completed
|
|
ON player_achievements(is_completed) WHERE is_completed = true;
|
|
```
|
|
|
|
### Batch-Processing
|
|
- **Effiziente Verarbeitung** aller Spieler in einem Durchgang
|
|
- **Transaktionale Sicherheit** für Datenkonsistenz
|
|
- **Fehlerbehandlung** für einzelne Spieler
|
|
|
|
### Caching
|
|
- **Achievement-Definitionen** werden gecacht
|
|
- **Spieler-Statistiken** werden bei Änderungen neu berechnet
|
|
- **Leaderboard** wird periodisch aktualisiert
|
|
|
|
### Zeitzone-Behandlung
|
|
- **Korrekte Zeitzone** (Europe/Berlin) für alle Zeitberechnungen
|
|
- **Saisonale Achievements** berücksichtigen lokale Zeit
|
|
- **Tägliche Prüfung** erfolgt zur richtigen Zeit
|
|
|
|
## 🔒 Sicherheit
|
|
|
|
### API-Schutz
|
|
- **Alle Endpoints** über bestehende Authentifizierung
|
|
- **Admin-Endpoints** erfordern erweiterte Berechtigung
|
|
- **Rate Limiting** für häufige Anfragen
|
|
|
|
### SQL-Injection
|
|
- **Parametrisierte Queries** in allen Funktionen
|
|
- **Input-Validierung** vor Datenbankzugriff
|
|
- **Escape-Funktionen** für dynamische Inhalte
|
|
|
|
### Datenvalidierung
|
|
- **Eingabe-Validierung** in allen API-Endpoints
|
|
- **Typ-Überprüfung** für alle Parameter
|
|
- **Bereichs-Validierung** für numerische Werte
|
|
|
|
### Fehlerbehandlung
|
|
- **Umfassende Error-Handling** in allen Funktionen
|
|
- **Logging** aller Fehler und Warnungen
|
|
- **Graceful Degradation** bei Systemfehlern
|
|
|
|
## 🎯 Best Practices
|
|
|
|
### Achievement-Design
|
|
- **Klare Bedingungen** für alle Achievements
|
|
- **Angemessene Punkte** basierend auf Schwierigkeit
|
|
- **Motivierende Beschreibungen** für Spieler
|
|
|
|
### Performance-Optimierung
|
|
- **Batch-Processing** für große Datenmengen
|
|
- **Indizierung** für häufige Abfragen
|
|
- **Caching** für statische Daten
|
|
|
|
### Wartbarkeit
|
|
- **Modulare Struktur** für einfache Erweiterungen
|
|
- **Dokumentation** aller Funktionen
|
|
- **Tests** für kritische Komponenten
|
|
|
|
---
|
|
|
|
**Erstellt am**: $(date)
|
|
**Version**: 1.0.0
|
|
**Autor**: Ninja Cross Parkour System
|