Änderungen: - Implementierung des neuen Routers für TaxiHighscore zur Verwaltung von Highscore-Daten. - Anpassung der Datenbankmodelle zur Unterstützung von TaxiHighscore-Associations. - Erweiterung der Vue-Komponenten zur Anzeige und Speicherung von Highscores im Taxi-Spiel. - Verbesserung der Statusanzeige im AppHeader zur besseren Benutzerinteraktion. Diese Anpassungen erweitern die Spielmechanik und Benutzererfahrung, indem sie die Verwaltung von Highscores integrieren und die Benutzeroberfläche optimieren.
272 lines
8.0 KiB
JavaScript
272 lines
8.0 KiB
JavaScript
import models from '../models/index.js';
|
|
import BaseService from './BaseService.js';
|
|
|
|
const { TaxiHighscore, User, TaxiMap } = models;
|
|
|
|
class TaxiHighscoreService extends BaseService {
|
|
constructor() {
|
|
super();
|
|
this.model = TaxiHighscore;
|
|
}
|
|
|
|
/**
|
|
* Speichert oder aktualisiert einen Highscore-Eintrag
|
|
* Jeder User kann nur einen Eintrag pro Map haben (der beste wird gespeichert)
|
|
* @param {Object} highscoreData - Die Highscore-Daten
|
|
* @param {number} highscoreData.userId - ID des Users
|
|
* @param {string} highscoreData.nickname - Nickname des Users
|
|
* @param {number} highscoreData.passengersDelivered - Anzahl abgelieferter Passagiere
|
|
* @param {number} highscoreData.playtime - Spielzeit in Sekunden
|
|
* @param {number} highscoreData.points - Erreichte Punkte
|
|
* @param {number} highscoreData.mapId - ID der Map
|
|
* @param {string} highscoreData.mapName - Name der Map
|
|
* @returns {Promise<Object>} Gespeicherter oder aktualisierter Highscore-Eintrag
|
|
*/
|
|
async createHighscore(highscoreData) {
|
|
try {
|
|
// Prüfen ob bereits ein Eintrag für diesen User und diese Map existiert
|
|
const existingHighscore = await this.model.findOne({
|
|
where: {
|
|
userId: highscoreData.userId,
|
|
mapId: highscoreData.mapId
|
|
}
|
|
});
|
|
|
|
if (existingHighscore) {
|
|
// Nur aktualisieren wenn der neue Score besser ist (mehr Punkte)
|
|
if (highscoreData.points > existingHighscore.points) {
|
|
await existingHighscore.update({
|
|
nickname: highscoreData.nickname,
|
|
passengersDelivered: highscoreData.passengersDelivered,
|
|
playtime: highscoreData.playtime,
|
|
points: highscoreData.points,
|
|
mapName: highscoreData.mapName
|
|
});
|
|
return existingHighscore;
|
|
} else {
|
|
// Neuer Score ist nicht besser, existierenden zurückgeben
|
|
return existingHighscore;
|
|
}
|
|
} else {
|
|
// Kein existierender Eintrag, neuen erstellen
|
|
const highscore = await this.model.create({
|
|
userId: highscoreData.userId,
|
|
nickname: highscoreData.nickname,
|
|
passengersDelivered: highscoreData.passengersDelivered,
|
|
playtime: highscoreData.playtime,
|
|
points: highscoreData.points,
|
|
mapId: highscoreData.mapId,
|
|
mapName: highscoreData.mapName
|
|
});
|
|
|
|
return highscore;
|
|
}
|
|
} catch (error) {
|
|
console.error('Fehler beim Erstellen/Aktualisieren des Highscores:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Holt die Top-Highscores für eine bestimmte Map
|
|
* @param {number} mapId - ID der Map (optional)
|
|
* @param {number} limit - Anzahl der Einträge (Standard: 10)
|
|
* @param {string} orderBy - Sortierung ('points', 'passengersDelivered', 'playtime')
|
|
* @returns {Promise<Array>} Array der Highscore-Einträge
|
|
*/
|
|
async getTopHighscores(mapId = null, limit = 10, orderBy = 'points') {
|
|
try {
|
|
const whereClause = mapId ? { mapId } : {};
|
|
|
|
const highscores = await this.model.findAll({
|
|
where: whereClause,
|
|
include: [
|
|
{
|
|
model: User,
|
|
as: 'user',
|
|
attributes: ['id', 'username'],
|
|
required: false // LEFT JOIN, da User gelöscht sein könnte
|
|
},
|
|
{
|
|
model: TaxiMap,
|
|
as: 'map',
|
|
attributes: ['id', 'name'],
|
|
required: false
|
|
}
|
|
],
|
|
order: [[orderBy, 'DESC']],
|
|
limit: parseInt(limit)
|
|
});
|
|
|
|
return highscores;
|
|
} catch (error) {
|
|
console.error('Fehler beim Laden der Highscores:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Holt die persönlichen Bestleistungen eines Users
|
|
* @param {number} userId - ID des Users
|
|
* @param {number} mapId - ID der Map (optional)
|
|
* @returns {Promise<Object>} Bestleistungen des Users
|
|
*/
|
|
async getUserBestScores(userId, mapId = null) {
|
|
try {
|
|
const whereClause = { userId };
|
|
if (mapId) {
|
|
whereClause.mapId = mapId;
|
|
}
|
|
|
|
const [bestPoints, bestPassengers, bestPlaytime] = await Promise.all([
|
|
this.model.findOne({
|
|
where: whereClause,
|
|
order: [['points', 'DESC']]
|
|
}),
|
|
this.model.findOne({
|
|
where: whereClause,
|
|
order: [['passengersDelivered', 'DESC']]
|
|
}),
|
|
this.model.findOne({
|
|
where: whereClause,
|
|
order: [['playtime', 'DESC']]
|
|
})
|
|
]);
|
|
|
|
return {
|
|
bestPoints,
|
|
bestPassengers,
|
|
bestPlaytime
|
|
};
|
|
} catch (error) {
|
|
console.error('Fehler beim Laden der User-Bestleistungen:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Holt alle Highscores eines Users
|
|
* @param {number} userId - ID des Users
|
|
* @param {number} limit - Anzahl der Einträge (Standard: 20)
|
|
* @returns {Promise<Array>} Array der Highscore-Einträge des Users
|
|
*/
|
|
async getUserHighscores(userId, limit = 20) {
|
|
try {
|
|
const highscores = await this.model.findAll({
|
|
where: { userId },
|
|
include: [
|
|
{
|
|
model: TaxiMap,
|
|
as: 'map',
|
|
attributes: ['id', 'name']
|
|
}
|
|
],
|
|
order: [['createdAt', 'DESC']],
|
|
limit: parseInt(limit)
|
|
});
|
|
|
|
return highscores;
|
|
} catch (error) {
|
|
console.error('Fehler beim Laden der User-Highscores:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Holt die Rangliste-Position eines Users für eine bestimmte Map
|
|
* @param {number} userId - ID des Users
|
|
* @param {number} mapId - ID der Map (optional)
|
|
* @param {string} orderBy - Sortierung ('points', 'passengersDelivered', 'playtime')
|
|
* @returns {Promise<number>} Rangliste-Position (1-basiert)
|
|
*/
|
|
async getUserRank(userId, mapId = null, orderBy = 'points') {
|
|
try {
|
|
const whereClause = mapId ? { mapId } : {};
|
|
|
|
const userHighscore = await this.model.findOne({
|
|
where: { userId, ...whereClause },
|
|
order: [[orderBy, 'DESC']]
|
|
});
|
|
|
|
if (!userHighscore) {
|
|
return null; // User hat noch keinen Highscore
|
|
}
|
|
|
|
const rank = await this.model.count({
|
|
where: {
|
|
...whereClause,
|
|
[orderBy]: {
|
|
[this.model.sequelize.Sequelize.Op.gt]: userHighscore[orderBy]
|
|
}
|
|
}
|
|
});
|
|
|
|
return rank + 1; // 1-basierte Position
|
|
} catch (error) {
|
|
console.error('Fehler beim Berechnen der User-Rangliste:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Löscht alle Highscores eines Users (z.B. bei Account-Löschung)
|
|
* @param {number} userId - ID des Users
|
|
* @returns {Promise<number>} Anzahl der gelöschten Einträge
|
|
*/
|
|
async deleteUserHighscores(userId) {
|
|
try {
|
|
const deletedCount = await this.model.destroy({
|
|
where: { userId }
|
|
});
|
|
|
|
return deletedCount;
|
|
} catch (error) {
|
|
console.error('Fehler beim Löschen der User-Highscores:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Holt Statistiken über die Highscores
|
|
* @returns {Promise<Object>} Statistiken
|
|
*/
|
|
async getHighscoreStats() {
|
|
try {
|
|
const [
|
|
totalHighscores,
|
|
totalPlayers,
|
|
averagePoints,
|
|
totalPassengersDelivered,
|
|
totalPlaytime
|
|
] = await Promise.all([
|
|
this.model.count(),
|
|
this.model.count({
|
|
distinct: true,
|
|
col: 'userId'
|
|
}),
|
|
this.model.findOne({
|
|
attributes: [
|
|
[this.model.sequelize.fn('AVG', this.model.sequelize.col('points')), 'avg']
|
|
],
|
|
raw: true
|
|
}),
|
|
this.model.sum('passengersDelivered'),
|
|
this.model.sum('playtime')
|
|
]);
|
|
|
|
return {
|
|
totalHighscores,
|
|
totalPlayers,
|
|
averagePoints: averagePoints ? parseFloat(averagePoints.avg).toFixed(2) : 0,
|
|
totalPassengersDelivered: totalPassengersDelivered || 0,
|
|
totalPlaytime: totalPlaytime || 0
|
|
};
|
|
} catch (error) {
|
|
console.error('Fehler beim Laden der Highscore-Statistiken:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
}
|
|
|
|
export default new TaxiHighscoreService();
|