Bleibende Sektor Elemente für UI fix
This commit is contained in:
@@ -34,129 +34,34 @@ export const getSectorsList = async (req, res) => {
|
||||
* @param {import('express').Response} res - Das Express Response-Objekt
|
||||
*/
|
||||
export const joinSector = async (req, res) => {
|
||||
const accountId = req.user.accountId;
|
||||
const { sectorId, localUsername, gender } = req.body;
|
||||
|
||||
const lobby_uuid = req.user.lobby_uuid;
|
||||
const { sector_id } = req.body;
|
||||
|
||||
if (!sector_id) {
|
||||
// Basis-Validierung: Fehlt ein Feld, brechen wir ab
|
||||
if (!sectorId || !localUsername || !gender) {
|
||||
return res.status(400).json({
|
||||
error: 'ERR_MISSING_SECTOR_ID'
|
||||
error: 'ERR_MISSING_FIELDS'
|
||||
});
|
||||
}
|
||||
|
||||
const client = await pool.connect();
|
||||
|
||||
try {
|
||||
// Spieler-Daten aus der Lobby-Datenbank laden
|
||||
const userQuery = await client.query(`
|
||||
SELECT username, gender
|
||||
FROM lobby_accounts
|
||||
WHERE lobby_uuid = $1
|
||||
`, [lobby_uuid]);
|
||||
|
||||
if (userQuery.rows.length === 0) {
|
||||
return res.status(404).json({
|
||||
error: 'ERR_USER_NOT_FOUND'
|
||||
});
|
||||
}
|
||||
const { username, gender } = userQuery.rows[0];
|
||||
|
||||
// Sektor-Daten laden, um die korrekte interne URL zu erfahren
|
||||
const sectorQuery = await client.query(`
|
||||
SELECT internal_url, registration_open
|
||||
FROM sectors
|
||||
WHERE id = $1
|
||||
`, [sector_id]);
|
||||
|
||||
if (sectorQuery.rows.length === 0) {
|
||||
return res.status(404).json({
|
||||
error: 'ERR_SECTOR_NOT_FOUND'
|
||||
});
|
||||
}
|
||||
|
||||
const sector = sectorQuery.rows[0];
|
||||
|
||||
// Sicherheits-Check: Ist die Registrierung für diesen Sektor noch offen?
|
||||
if (!sector.registration_open) {
|
||||
return res.status(403).json({
|
||||
error: 'ERR_SECTOR_REGISTRATION_CLOSED'
|
||||
});
|
||||
}
|
||||
|
||||
// Den internen POST-Request an das Sektor-Backend feuern
|
||||
const sectorApiUrl = `${sector.internal_url}/api/internal/accounts`;
|
||||
await sectorService.joinSector(accountId, sectorId, localUsername, gender);
|
||||
|
||||
const response = await fetch(sectorApiUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
lobby_uuid,
|
||||
username,
|
||||
gender
|
||||
})
|
||||
});
|
||||
|
||||
// Fehlerbehandlung bei negativen HTTP-Statuscodes
|
||||
// fetch springt bei HTTP-Fehlern nicht in den catch-Block, daher prüfen wir response.ok
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
const sectorError = errorData.error;
|
||||
|
||||
// Wenn der Sektor exakt in diesem Moment voll geworden ist
|
||||
if (sectorError === 'ERR_SECTOR_FULL') {
|
||||
// Sektor sicherheitshalber in der Lobby sofort schließen
|
||||
await client.query(`
|
||||
UPDATE sectors SET registration_open = false WHERE id = $1
|
||||
`, [sector_id]);
|
||||
return res.status(503).json({
|
||||
error: 'ERR_SECTOR_FULL'
|
||||
});
|
||||
}
|
||||
|
||||
if (sectorError === 'ERR_ACCOUNT_ALREADY_EXISTS') {
|
||||
return res.status(409).json({
|
||||
error: 'ERR_ACCOUNT_ALREADY_EXISTS'
|
||||
});
|
||||
}
|
||||
|
||||
throw new Error(`Sektor-Backend meldet unerwarteten Fehler: ${sectorError}`);
|
||||
}
|
||||
|
||||
const responseData = await response.json();
|
||||
const { triggerSectorClose } = responseData;
|
||||
|
||||
await client.query('BEGIN');
|
||||
|
||||
await client.query(`
|
||||
INSERT INTO account_sectors (lobby_uuid, sector_id)
|
||||
VALUES ($1, $2)
|
||||
`, [lobby_uuid, sector_id]);
|
||||
|
||||
// Auswertung der Notfall-Schließung
|
||||
// Wenn der Sektor uns meldet, dass das Puffer-Limit erreicht ist,
|
||||
// machen wir ihn dicht, damit er im Frontend verschwindet.
|
||||
if (triggerSectorClose) {
|
||||
console.warn(`[Lobby] Sektor ${sector_id} meldet Voll-Auslastung. Schließe Registrierung...`);
|
||||
await client.query(`
|
||||
UPDATE sectors
|
||||
SET registration_open = false
|
||||
WHERE id = $1
|
||||
`, [sector_id]);
|
||||
}
|
||||
|
||||
await client.query('COMMIT');
|
||||
|
||||
return res.status(200).json({
|
||||
message: 'MSG_SECTOR_JOINED_SUCCESSFULLY'
|
||||
message: 'MSG_SECTOR_JOINED_SUCCESS'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
await client.query('ROLLBACK');
|
||||
console.error('[Lobby] Fehler beim Beitritt zum Sektor:', error.message);
|
||||
return res.status(500).json({ error: 'ERR_INTERNAL_SERVER_ERROR' });
|
||||
} finally {
|
||||
client.release();
|
||||
console.error('[SectorController] Fehler beim Beitritt zum Sektor:', error.message);
|
||||
|
||||
if (error.code) {
|
||||
return res.status(error.code === 'ERR_SECTOR_FULL' ? 503 : 409).json({
|
||||
error: error.code
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(500).json({
|
||||
error: 'ERR_INTERNAL_SERVER'
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -21,9 +21,9 @@ export const initLobbyCronJobs = () => {
|
||||
try {
|
||||
// Alle Sektoren finden, deren Registrierung aktuell geschlossen ist
|
||||
const closedSectorsQuery = await client.query(`
|
||||
SELECT id, internal_url, name
|
||||
SELECT id, name, api_url
|
||||
FROM sectors
|
||||
WHERE registration_open = false
|
||||
WHERE is_registration_open = false
|
||||
`);
|
||||
|
||||
const closedSectors = closedSectorsQuery.rows;
|
||||
@@ -36,7 +36,7 @@ export const initLobbyCronJobs = () => {
|
||||
// Jeden geschlossenen Sektor einzeln über das Netzwerk abfragen
|
||||
for (const sector of closedSectors) {
|
||||
try {
|
||||
const response = await fetch(`${sector.internal_url}/api/internal/slots`);
|
||||
const response = await fetch(`${sector.api_url}/api/internal/slots`);
|
||||
|
||||
if (!response.ok) {
|
||||
console.warn(`⚠️ [CRON] Sektor ${sector.name} hat einen Fehler gemeldet (Status: ${response.status}). Überspringe.`);
|
||||
@@ -53,7 +53,7 @@ export const initLobbyCronJobs = () => {
|
||||
|
||||
await client.query(`
|
||||
UPDATE sectors
|
||||
SET registration_open = true
|
||||
SET is_registration_open = true
|
||||
WHERE id = $1
|
||||
`, [sector.id]);
|
||||
} else {
|
||||
|
||||
@@ -4,11 +4,21 @@ import pool from '../db.js';
|
||||
// Void-Genesis - Heartbeat Service
|
||||
// ==============================================================================
|
||||
// Dieser Service pingt alle registrierten Sektoren regelmäßig an.
|
||||
|
||||
export const pingSectors = async () => {
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
const result = await client.query('SELECT id, name, api_url FROM sectors WHERE api_url IS NOT NULL');
|
||||
const result = await client.query(`
|
||||
SELECT
|
||||
id, name, api_url, status,
|
||||
online_players, current_players, planets_count,
|
||||
max_galaxies, speed_economy, research_speed,
|
||||
fleet_speed_hostile, fleet_speed_peaceful, fleet_speed_friendly,
|
||||
fuel_consumption_factor, fleet_to_debris_percentage,
|
||||
defense_to_debris_percentage, fuel_included_in_debris,
|
||||
initial_res_premium, bonus_fields
|
||||
FROM sectors
|
||||
WHERE api_url IS NOT NULL
|
||||
`);
|
||||
const sectors = result.rows;
|
||||
|
||||
for (const sector of sectors) {
|
||||
@@ -23,63 +33,67 @@ export const pingSectors = async () => {
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
|
||||
if (data.status === 'ok') {
|
||||
await client.query(
|
||||
`UPDATE sectors SET
|
||||
status = 'online',
|
||||
online_players = $1,
|
||||
current_players = $2,
|
||||
planets_count = $3,
|
||||
max_galaxies = $4,
|
||||
speed_economy = $5,
|
||||
research_speed = $6,
|
||||
fleet_speed_hostile = $7,
|
||||
fleet_speed_peaceful = $8,
|
||||
fleet_speed_friendly = $9,
|
||||
fuel_consumption_factor = $10,
|
||||
fleet_to_debris_percentage = $11,
|
||||
defense_to_debris_percentage = $12,
|
||||
fuel_included_in_debris = $13,
|
||||
initial_res_premium = $14,
|
||||
bonus_fields = $15
|
||||
WHERE id = $16`,
|
||||
[
|
||||
data.stats.online_players,
|
||||
data.stats.total_players,
|
||||
data.stats.planets_count,
|
||||
data.config.max_galaxies,
|
||||
data.config.speed_economy,
|
||||
data.config.research_speed,
|
||||
data.config.fleet_speed_hostile,
|
||||
data.config.fleet_speed_peaceful,
|
||||
data.config.fleet_speed_friendly,
|
||||
data.config.fuel_consumption_factor,
|
||||
data.config.fleet_to_debris_percentage,
|
||||
data.config.defense_to_debris_percentage,
|
||||
data.config.fuel_included_in_debris,
|
||||
data.config.initial_res_premium,
|
||||
data.config.bonus_fields,
|
||||
sector.id
|
||||
]
|
||||
);
|
||||
|
||||
const hasChanged = checkSectorChanges(sector, data);
|
||||
|
||||
if (hasChanged) {
|
||||
await client.query(
|
||||
`UPDATE sectors SET
|
||||
status = 'online',
|
||||
online_players = $1,
|
||||
current_players = $2,
|
||||
planets_count = $3,
|
||||
max_galaxies = $4,
|
||||
speed_economy = $5,
|
||||
research_speed = $6,
|
||||
fleet_speed_hostile = $7,
|
||||
fleet_speed_peaceful = $8,
|
||||
fleet_speed_friendly = $9,
|
||||
fuel_consumption_factor = $10,
|
||||
fleet_to_debris_percentage = $11,
|
||||
defense_to_debris_percentage = $12,
|
||||
fuel_included_in_debris = $13,
|
||||
initial_res_premium = $14,
|
||||
bonus_fields = $15
|
||||
WHERE id = $16`,
|
||||
[
|
||||
data.stats.online_players,
|
||||
data.stats.total_players,
|
||||
data.stats.planets_count,
|
||||
data.config.max_galaxies,
|
||||
data.config.speed_economy,
|
||||
data.config.research_speed,
|
||||
data.config.fleet_speed_hostile,
|
||||
data.config.fleet_speed_peaceful,
|
||||
data.config.fleet_speed_friendly,
|
||||
data.config.fuel_consumption_factor,
|
||||
data.config.fleet_to_debris_percentage,
|
||||
data.config.defense_to_debris_percentage,
|
||||
data.config.fuel_included_in_debris,
|
||||
data.config.initial_res_premium,
|
||||
data.config.bonus_fields,
|
||||
sector.id
|
||||
]
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (sector.name === 'Development') {
|
||||
console.warn(`[Heartbeat-Trace] ⚠️ Fehler: JSON-Status war nicht 'ok', sondern:`, data.status);
|
||||
}
|
||||
await setSectorOffline(client, sector.id);
|
||||
await setSectorOffline(client, sector);
|
||||
}
|
||||
} else {
|
||||
if (sector.name === 'Development') {
|
||||
console.warn(`[Heartbeat-Trace] ⚠️ HTTP-Response nicht OK! Status-Code:`, response.status);
|
||||
}
|
||||
await setSectorOffline(client, sector.id);
|
||||
await setSectorOffline(client, sector);
|
||||
}
|
||||
} catch (error) {
|
||||
if (sector.name === 'Development') {
|
||||
console.error(`[Heartbeat-Trace] ❌ Crash bei Sektor 'Development'. Grund:`, error.message);
|
||||
}
|
||||
await setSectorOffline(client, sector.id);
|
||||
await setSectorOffline(client, sector);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -90,11 +104,55 @@ export const pingSectors = async () => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Hilfsfunktion, um den Status eines Sektors auf 'offline' zu setzen.
|
||||
* Hilfsfunktion zum Vergleichen des alten DB-Stands mit den frischen API-Daten.
|
||||
* Wir nutzen Number() und Boolean(), da PostgreSQL manche Datentypen
|
||||
* (wie NUMERIC oder BIGINT) im Node.js-Treiber als String zurückgibt.
|
||||
*/
|
||||
const setSectorOffline = async (client, sectorId) => {
|
||||
await client.query(
|
||||
"UPDATE sectors SET status = 'offline' WHERE id = $1",
|
||||
[sectorId]
|
||||
);
|
||||
const checkSectorChanges = (oldData, newData) => {
|
||||
// Wenn der Sektor vorher als offline oder in Wartung markiert war,
|
||||
// muss er definitiv geupdatet werden, da er jetzt wieder erreichbar ist.
|
||||
if (oldData.status !== 'online') return true;
|
||||
|
||||
// Spielerzahlen und Planeten prüfen
|
||||
if (Number(oldData.online_players) !== newData.stats.online_players) return true;
|
||||
if (Number(oldData.current_players) !== newData.stats.total_players) return true;
|
||||
if (Number(oldData.planets_count) !== newData.stats.planets_count) return true;
|
||||
|
||||
// Globale Sektor-Einstellungen auf Änderungen prüfen
|
||||
if (Number(oldData.max_galaxies) !== newData.config.max_galaxies) return true;
|
||||
if (Number(oldData.speed_economy) !== newData.config.speed_economy) return true;
|
||||
if (Number(oldData.research_speed) !== newData.config.research_speed) return true;
|
||||
if (Number(oldData.fleet_speed_hostile) !== newData.config.fleet_speed_hostile) return true;
|
||||
if (Number(oldData.fleet_speed_peaceful) !== newData.config.fleet_speed_peaceful) return true;
|
||||
if (Number(oldData.fleet_speed_friendly) !== newData.config.fleet_speed_friendly) return true;
|
||||
|
||||
// Float-Werte
|
||||
if (Number(oldData.fuel_consumption_factor) !== Number(newData.config.fuel_consumption_factor)) return true;
|
||||
if (Number(oldData.fleet_to_debris_percentage) !== newData.config.fleet_to_debris_percentage) return true;
|
||||
if (Number(oldData.defense_to_debris_percentage) !== newData.config.defense_to_debris_percentage) return true;
|
||||
|
||||
// Boolean-Werte
|
||||
if (Boolean(oldData.fuel_included_in_debris) !== Boolean(newData.config.fuel_included_in_debris)) return true;
|
||||
|
||||
// Start-Boni
|
||||
if (Number(oldData.initial_res_premium) !== newData.config.initial_res_premium) return true;
|
||||
if (Number(oldData.bonus_fields) !== newData.config.bonus_fields) return true;
|
||||
|
||||
// Wurde kein Unterschied gefunden, geben wir false zurück
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Setzt den Status eines Sektors auf 'offline'.
|
||||
* Prüft vorher, ob der Sektor nicht ohnehin schon als offline in der DB steht,
|
||||
* um auch hier sinnlose Schreibzugriffe und Checkpoint-Logs zu verhindern.
|
||||
*/
|
||||
const setSectorOffline = async (client, sector) => {
|
||||
if (sector.status !== 'offline') {
|
||||
await client.query(
|
||||
"UPDATE sectors SET status = 'offline' WHERE id = $1",
|
||||
[sector.id]
|
||||
);
|
||||
sector.status = 'offline';
|
||||
}
|
||||
};
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -591,32 +591,40 @@ function renderSectorList(sectors, type) {
|
||||
`;
|
||||
cardLink.onclick = (e) => {
|
||||
e.preventDefault();
|
||||
console.log(`Starte Onboarding für Sektor: ${sector.name} (ID: ${sector.id})`);
|
||||
|
||||
selectedSectorId = sector.id;
|
||||
if (type === 'my') {
|
||||
console.log(`Betrete bekannten Sektor: ${sector.name}`);
|
||||
// todo für später, hier muss dann variable der link stehen oder man müsste ihn aus der datenbank auslesen
|
||||
window.open('https://dev.void-genesis.de', '_blank');
|
||||
|
||||
} else {
|
||||
console.log(`Starte Onboarding für Sektor: ${sector.name} (ID: ${sector.id})`);
|
||||
|
||||
const sectorBrowserLayer = document.getElementById('sector-browser-layer');
|
||||
if (sectorBrowserLayer) {
|
||||
sectorBrowserLayer.style.opacity = 0.05;
|
||||
sectorBrowserLayer.style.pointerEvents = 'none';
|
||||
}
|
||||
selectedSectorId = sector.id;
|
||||
|
||||
const onboardingHtmlLayer = document.getElementById('sector-onboarding-layer');
|
||||
if (onboardingHtmlLayer) onboardingHtmlLayer.style.display = 'flex';
|
||||
|
||||
if (onboardingPixiContainer) onboardingPixiContainer.visible = true;
|
||||
const sectorBrowserLayer = document.getElementById('sector-browser-layer');
|
||||
if (sectorBrowserLayer) {
|
||||
sectorBrowserLayer.style.opacity = 0.05;
|
||||
sectorBrowserLayer.style.pointerEvents = 'none';
|
||||
}
|
||||
|
||||
const localUsernameInput = document.getElementById('local-username');
|
||||
if (localUsernameInput) {
|
||||
const userDataStr = sessionStorage.getItem('void_user') || localStorage.getItem('void_user');
|
||||
if (userDataStr) {
|
||||
try {
|
||||
const userData = JSON.parse(userDataStr);
|
||||
if (userData.username) {
|
||||
localUsernameInput.value = userData.username;
|
||||
const onboardingHtmlLayer = document.getElementById('sector-onboarding-layer');
|
||||
if (onboardingHtmlLayer) onboardingHtmlLayer.style.display = 'flex';
|
||||
|
||||
if (onboardingPixiContainer) onboardingPixiContainer.visible = true;
|
||||
|
||||
const localUsernameInput = document.getElementById('local-username');
|
||||
if (localUsernameInput) {
|
||||
const userDataStr = sessionStorage.getItem('void_user') || localStorage.getItem('void_user');
|
||||
if (userDataStr) {
|
||||
try {
|
||||
const userData = JSON.parse(userDataStr);
|
||||
if (userData.username) {
|
||||
localUsernameInput.value = userData.username;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Fehler beim Auslesen der User-Daten für das Onboarding:", err);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Fehler beim Auslesen der User-Daten für das Onboarding:", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -971,6 +979,7 @@ if (onboardingForm) {
|
||||
if (btnCancelOnboarding) btnCancelOnboarding.click();
|
||||
|
||||
loadAndRenderSectorBrowser();
|
||||
window.open('https://dev.void-genesis.de', '_blank');
|
||||
}, 1500);
|
||||
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user