From 5039bcdadfd0b05791f807e74d8225b261e3982f Mon Sep 17 00:00:00 2001 From: AMIAY Date: Thu, 29 Jan 2026 17:07:29 +0100 Subject: [PATCH] added featured server list from api --- backend/managers/gameLauncher.js | 9 +++ backend/utils/serverListSync.js | 119 +++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 backend/utils/serverListSync.js diff --git a/backend/managers/gameLauncher.js b/backend/managers/gameLauncher.js index 0ac187d..4555844 100644 --- a/backend/managers/gameLauncher.js +++ b/backend/managers/gameLauncher.js @@ -13,6 +13,7 @@ const { getLatestClientVersion } = require('../services/versionManager'); const { updateGameFiles } = require('./gameManager'); const { syncModsForCurrentProfile } = require('./modManager'); const { getUserDataPath } = require('../utils/userDataMigration'); +const { syncServerList } = require('../utils/serverListSync'); // Client patcher for custom auth server (sanasol.ws) let clientPatcher = null; @@ -103,6 +104,14 @@ function generateLocalTokens(uuid, name) { } async function launchGame(playerName = 'Player', progressCallback, javaPathOverride, installPathOverride, gpuPreference = 'auto', branchOverride = null) { + // Synchronize server list on every game launch + try { + console.log('[Launcher] Synchronizing server list...'); + await syncServerList(); + } catch (syncError) { + console.warn('[Launcher] Server list sync failed, continuing launch:', syncError.message); + } + const branch = branchOverride || loadVersionBranch(); const customAppDir = getResolvedAppDir(installPathOverride); const customGameDir = path.join(customAppDir, branch, 'package', 'game', 'latest'); diff --git a/backend/utils/serverListSync.js b/backend/utils/serverListSync.js new file mode 100644 index 0000000..59e7e47 --- /dev/null +++ b/backend/utils/serverListSync.js @@ -0,0 +1,119 @@ +const fs = require('fs'); +const path = require('path'); +const axios = require('axios'); +const { v4: uuidv4 } = require('uuid'); +const { getHytaleSavesDir } = require('../core/paths'); + +const SERVER_LIST_URL = 'https://assets.authbp.xyz/server.json'; + + +function getLocalDateTime() { + return formatLocalDateTime(new Date()); +} + +function formatLocalDateTime(date) { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + const hours = String(date.getHours()).padStart(2, '0'); + const minutes = String(date.getMinutes()).padStart(2, '0'); + const seconds = String(date.getSeconds()).padStart(2, '0'); + const milliseconds = String(date.getMilliseconds()).padStart(3, '0'); + const offsetMinutes = -date.getTimezoneOffset(); + const offsetHours = Math.floor(Math.abs(offsetMinutes) / 60); + const offsetMins = Math.abs(offsetMinutes) % 60; + const offsetSign = offsetMinutes >= 0 ? '+' : '-'; + const offset = `${offsetSign}${String(offsetHours).padStart(2, '0')}:${String(offsetMins).padStart(2, '0')}`; + + return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}.${milliseconds}0000${offset}`; +} + +async function syncServerList() { + try { + const hytaleSavesDir = getHytaleSavesDir(); + const serverListPath = path.join(hytaleSavesDir, 'ServerList.json'); + console.log('[ServerListSync] Fetching server list from', SERVER_LIST_URL); + let remoteData; + try { + const response = await axios.get(SERVER_LIST_URL, { + timeout: 40000, + headers: { + 'User-Agent': 'Hytale-F2P-Launcher' + } + }); + remoteData = response.data; + } catch (fetchError) { + console.warn('[ServerListSync] Failed to fetch remote server list:', fetchError.message); + remoteData = { SavedServers: [] }; + } + let localData = { SavedServers: [] }; + if (fs.existsSync(serverListPath)) { + try { + const localContent = fs.readFileSync(serverListPath, 'utf-8'); + localData = JSON.parse(localContent); + console.log('[ServerListSync] Loaded existing local server list with', localData.SavedServers?.length || 0, 'servers'); + } catch (parseError) { + console.warn('[ServerListSync] Failed to parse local server list, creating new one:', parseError.message); + localData = { SavedServers: [] }; + } + } else { + console.log('[ServerListSync] Local server list does not exist, creating new one'); + } + + if (!localData.SavedServers) { + localData.SavedServers = []; + } + if (!remoteData.SavedServers) { + remoteData.SavedServers = []; + } + + const existingServersByAddress = new Map(); + const userServers = []; + + for (const server of localData.SavedServers) { + existingServersByAddress.set(server.Address.toLowerCase(), server); + } + + const remoteAddresses = new Set(remoteData.SavedServers.map(s => s.Address.toLowerCase())); + for (const server of localData.SavedServers) { + if (!remoteAddresses.has(server.Address.toLowerCase())) { + userServers.push(server); + } + } + + const currentDate = getLocalDateTime(); + + + const apiServers = []; + for (const remoteServer of remoteData.SavedServers) { + const serverToAdd = { + Id: uuidv4(), + Name: "@ " + remoteServer.Name, + Address: remoteServer.Address, + DateSaved: currentDate + }; + apiServers.push(serverToAdd); + console.log('[ServerListSync] Added/Updated server with new ID:', remoteServer.Name); + } + + localData.SavedServers = [...apiServers, ...userServers]; + + const addedCount = apiServers.length; + + if (!fs.existsSync(hytaleSavesDir)) { + fs.mkdirSync(hytaleSavesDir, { recursive: true }); + } + + fs.writeFileSync(serverListPath, JSON.stringify(localData, null, 2), 'utf-8'); + console.log('[ServerListSync] Server list synchronized:', addedCount, 'API servers added, total:', localData.SavedServers.length); + + return { success: true, added: addedCount, total: localData.SavedServers.length }; + } catch (error) { + console.error('[ServerListSync] Failed to synchronize server list:', error.message); + return { success: false, error: error.message }; + } +} + +module.exports = { + syncServerList +};