Add Wiki Achievement System page
This commit is contained in:
479
wiki/Achievement-System.md
Normal file
479
wiki/Achievement-System.md
Normal file
@@ -0,0 +1,479 @@
|
||||
# 🏆 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
|
||||
Reference in New Issue
Block a user