diff --git a/.gitignore b/.gitignore
index e578779..b533c73 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@ pkg/
# Package files
*.tar.zst
+*.zst.DS_Store
*.zst
bun.lockb
-.env
\ No newline at end of file
+.env
diff --git a/GUI/index.html b/GUI/index.html
index 843aaad..c1d0399 100644
--- a/GUI/index.html
+++ b/GUI/index.html
@@ -431,6 +431,27 @@
+
+
+
+ Launcher Behavior
+
+
+
+
+
+
+
+
diff --git a/GUI/js/settings.js b/GUI/js/settings.js
index 0ec95e6..dd383be 100644
--- a/GUI/js/settings.js
+++ b/GUI/js/settings.js
@@ -3,9 +3,11 @@ let customJavaCheck;
let customJavaOptions;
let customJavaPath;
let browseJavaBtn;
-let settingsPlayerName;
-let discordRPCCheck;
-let gpuPreferenceRadios;
+let settingsPlayerName;
+let discordRPCCheck;
+let closeLauncherCheck;
+let gpuPreferenceRadios;
+
// UUID Management elements
let currentUuidDisplay;
@@ -159,9 +161,11 @@ function setupSettingsElements() {
customJavaOptions = document.getElementById('customJavaOptions');
customJavaPath = document.getElementById('customJavaPath');
browseJavaBtn = document.getElementById('browseJavaBtn');
- settingsPlayerName = document.getElementById('settingsPlayerName');
- discordRPCCheck = document.getElementById('discordRPCCheck');
- gpuPreferenceRadios = document.querySelectorAll('input[name="gpuPreference"]');
+ settingsPlayerName = document.getElementById('settingsPlayerName');
+ discordRPCCheck = document.getElementById('discordRPCCheck');
+ closeLauncherCheck = document.getElementById('closeLauncherCheck');
+ gpuPreferenceRadios = document.querySelectorAll('input[name="gpuPreference"]');
+
// UUID Management elements
currentUuidDisplay = document.getElementById('currentUuid');
@@ -190,9 +194,14 @@ function setupSettingsElements() {
settingsPlayerName.addEventListener('change', savePlayerName);
}
- if (discordRPCCheck) {
- discordRPCCheck.addEventListener('change', saveDiscordRPC);
- }
+ if (discordRPCCheck) {
+ discordRPCCheck.addEventListener('change', saveDiscordRPC);
+ }
+
+ if (closeLauncherCheck) {
+ closeLauncherCheck.addEventListener('change', saveCloseLauncher);
+ }
+
// UUID event listeners
if (copyUuidBtn) {
@@ -335,18 +344,43 @@ async function saveDiscordRPC() {
}
}
-async function loadDiscordRPC() {
- try {
- if (window.electronAPI && window.electronAPI.loadDiscordRPC) {
- const enabled = await window.electronAPI.loadDiscordRPC();
- if (discordRPCCheck) {
- discordRPCCheck.checked = enabled;
- }
- }
- } catch (error) {
- console.error('Error loading Discord RPC setting:', error);
- }
-}
+async function loadDiscordRPC() {
+ try {
+ if (window.electronAPI && window.electronAPI.loadDiscordRPC) {
+ const enabled = await window.electronAPI.loadDiscordRPC();
+ if (discordRPCCheck) {
+ discordRPCCheck.checked = enabled;
+ }
+ }
+ } catch (error) {
+ console.error('Error loading Discord RPC setting:', error);
+ }
+}
+
+async function saveCloseLauncher() {
+ try {
+ if (window.electronAPI && window.electronAPI.saveCloseLauncher && closeLauncherCheck) {
+ const enabled = closeLauncherCheck.checked;
+ await window.electronAPI.saveCloseLauncher(enabled);
+ }
+ } catch (error) {
+ console.error('Error saving close launcher setting:', error);
+ }
+}
+
+async function loadCloseLauncher() {
+ try {
+ if (window.electronAPI && window.electronAPI.loadCloseLauncher) {
+ const enabled = await window.electronAPI.loadCloseLauncher();
+ if (closeLauncherCheck) {
+ closeLauncherCheck.checked = enabled;
+ }
+ }
+ } catch (error) {
+ console.error('Error loading close launcher setting:', error);
+ }
+}
+
async function savePlayerName() {
try {
@@ -457,13 +491,15 @@ async function loadGpuPreference() {
}
}
-async function loadAllSettings() {
- await loadCustomJavaPath();
- await loadPlayerName();
- await loadCurrentUuid();
- await loadDiscordRPC();
- await loadGpuPreference();
-}
+async function loadAllSettings() {
+ await loadCustomJavaPath();
+ await loadPlayerName();
+ await loadCurrentUuid();
+ await loadDiscordRPC();
+ await loadCloseLauncher();
+ await loadGpuPreference();
+}
+
async function openGameLocation() {
try {
diff --git a/GUI/locales/en.json b/GUI/locales/en.json
index b7981be..d142831 100644
--- a/GUI/locales/en.json
+++ b/GUI/locales/en.json
@@ -122,7 +122,10 @@
"logsCopy": "Copy",
"logsRefresh": "Refresh",
"logsFolder": "Open Folder",
- "logsLoading": "Loading logs..."
+ "logsLoading": "Loading logs...",
+ "closeLauncher": "Launcher Behavior",
+ "closeOnStart": "Close Launcher on game start",
+ "closeOnStartDescription": "Automatically close the launcher after Hytale has launched"
},
"uuid": {
"modalTitle": "UUID Management",
diff --git a/GUI/locales/es.json b/GUI/locales/es.json
index 283108b..4bb89c8 100644
--- a/GUI/locales/es.json
+++ b/GUI/locales/es.json
@@ -122,7 +122,10 @@
"logsCopy": "Copiar",
"logsRefresh": "Actualizar",
"logsFolder": "Abrir Carpeta",
- "logsLoading": "Cargando registros..."
+ "logsLoading": "Cargando registros...",
+ "closeLauncher": "Comportamiento del Launcher",
+ "closeOnStart": "Cerrar Launcher al iniciar el juego",
+ "closeOnStartDescription": "Cierra automáticamente el launcher después de que Hytale se haya iniciado"
},
"uuid": {
"modalTitle": "Gestión de UUID",
diff --git a/GUI/locales/pt-BR.json b/GUI/locales/pt-BR.json
index e48c1b0..492440b 100644
--- a/GUI/locales/pt-BR.json
+++ b/GUI/locales/pt-BR.json
@@ -122,7 +122,10 @@
"logsCopy": "Copiar",
"logsRefresh": "Atualizar",
"logsFolder": "Abrir Pasta",
- "logsLoading": "Carregando registros..."
+ "logsLoading": "Carregando registros...",
+ "closeLauncher": "Comportamento do Lançador",
+ "closeOnStart": "Fechar Lançador ao iniciar o jogo",
+ "closeOnStartDescription": "Fechar automaticamente o lançador após o Hytale ter sido iniciado"
},
"uuid": {
"modalTitle": "Gerenciamento de UUID",
diff --git a/backend/core/config.js b/backend/core/config.js
index 23332a8..03cff49 100644
--- a/backend/core/config.js
+++ b/backend/core/config.js
@@ -156,6 +156,15 @@ function loadLanguage() {
return config.language || 'en';
}
+function saveCloseLauncherOnStart(enabled) {
+ saveConfig({ closeLauncherOnStart: !!enabled });
+}
+
+function loadCloseLauncherOnStart() {
+ const config = loadConfig();
+ return config.closeLauncherOnStart !== undefined ? config.closeLauncherOnStart : false;
+}
+
function saveModsToConfig(mods) {
try {
const config = loadConfig();
@@ -331,5 +340,8 @@ module.exports = {
resetCurrentUserUuid,
// GPU Preference exports
saveGpuPreference,
- loadGpuPreference
+ loadGpuPreference,
+ // Close Launcher export
+ saveCloseLauncherOnStart,
+ loadCloseLauncherOnStart
};
diff --git a/backend/launcher.js b/backend/launcher.js
index cadee5e..32a6c59 100644
--- a/backend/launcher.js
+++ b/backend/launcher.js
@@ -17,6 +17,8 @@ const {
loadDiscordRPC,
saveLanguage,
loadLanguage,
+ saveCloseLauncherOnStart,
+ loadCloseLauncherOnStart,
saveModsToConfig,
loadModsFromConfig,
getUuidForUser,
@@ -124,6 +126,10 @@ module.exports = {
saveLanguage,
loadLanguage,
+ // Close Launcher functions
+ saveCloseLauncherOnStart,
+ loadCloseLauncherOnStart,
+
// GPU Preference functions
saveGpuPreference,
loadGpuPreference,
diff --git a/main.js b/main.js
index e0ab2d0..9fe72d6 100644
--- a/main.js
+++ b/main.js
@@ -2,7 +2,8 @@ const path = require('path');
require('dotenv').config({ path: path.join(__dirname, '.env') });
const { app, BrowserWindow, ipcMain, dialog, shell } = require('electron');
const fs = require('fs');
-const { launchGame, launchGameWithVersionCheck, installGame, saveUsername, loadUsername, saveChatUsername, loadChatUsername, saveChatColor, loadChatColor, saveJavaPath, loadJavaPath, saveInstallPath, loadInstallPath, saveDiscordRPC, loadDiscordRPC, saveLanguage, loadLanguage, isGameInstalled, uninstallGame, repairGame, getHytaleNews, handleFirstLaunchCheck, proposeGameUpdate, markAsLaunched } = require('./backend/launcher');
+const { launchGame, launchGameWithVersionCheck, installGame, saveUsername, loadUsername, saveChatUsername, loadChatUsername, saveChatColor, loadChatColor, saveJavaPath, loadJavaPath, saveInstallPath, loadInstallPath, saveDiscordRPC, loadDiscordRPC, saveLanguage, loadLanguage, saveCloseLauncherOnStart, loadCloseLauncherOnStart, isGameInstalled, uninstallGame, repairGame, getHytaleNews, handleFirstLaunchCheck, proposeGameUpdate, markAsLaunched } = require('./backend/launcher');
+
const UpdateManager = require('./backend/updateManager');
const logger = require('./backend/logger');
const profileManager = require('./backend/managers/profileManager');
@@ -186,10 +187,21 @@ function createWindow() {
if (input.key === 'F12') {
event.preventDefault();
}
- if (input.key === 'F5') {
- event.preventDefault();
- }
- });
+ if (input.key === 'F5') {
+ event.preventDefault();
+ }
+
+ // Close application shortcuts
+ const isMac = process.platform === 'darwin';
+ const quitShortcut = (isMac && input.meta && input.key.toLowerCase() === 'q') ||
+ (!isMac && input.control && input.key.toLowerCase() === 'q') ||
+ (!isMac && input.alt && input.key === 'F4');
+
+ if (quitShortcut) {
+ app.quit();
+ }
+ });
+
mainWindow.webContents.on('context-menu', (e) => {
@@ -333,15 +345,14 @@ app.on('before-quit', () => {
cleanupDiscordRPC();
});
-app.on('window-all-closed', () => {
- console.log('=== LAUNCHER CLOSING ===');
-
- cleanupDiscordRPC();
-
- if (process.platform !== 'darwin') {
- app.quit();
- }
-});
+app.on('window-all-closed', () => {
+ console.log('=== LAUNCHER CLOSING ===');
+
+ cleanupDiscordRPC();
+
+ app.quit();
+});
+
ipcMain.handle('launch-game', async (event, playerName, javaPath, installPath, gpuPreference) => {
try {
@@ -358,9 +369,20 @@ ipcMain.handle('launch-game', async (event, playerName, javaPath, installPath, g
}
};
- const result = await launchGameWithVersionCheck(playerName, progressCallback, javaPath, installPath, gpuPreference);
-
- return result;
+ const result = await launchGameWithVersionCheck(playerName, progressCallback, javaPath, installPath, gpuPreference);
+
+ if (result.success && result.launched) {
+ const closeOnStart = loadCloseLauncherOnStart();
+ if (closeOnStart) {
+ console.log('Close Launcher on start enabled, quitting application...');
+ setTimeout(() => {
+ app.quit();
+ }, 1000);
+ }
+ }
+
+ return result;
+
} catch (error) {
console.error('Launch error:', error);
const errorMessage = error.message || error.toString();
@@ -475,11 +497,21 @@ ipcMain.handle('save-language', (event, language) => {
return { success: true };
});
-ipcMain.handle('load-language', () => {
- return loadLanguage();
-});
-
-ipcMain.handle('select-install-path', async () => {
+ipcMain.handle('load-language', () => {
+ return loadLanguage();
+});
+
+ipcMain.handle('save-close-launcher', (event, enabled) => {
+ saveCloseLauncherOnStart(enabled);
+ return { success: true };
+});
+
+ipcMain.handle('load-close-launcher', () => {
+ return loadCloseLauncherOnStart();
+});
+
+ipcMain.handle('select-install-path', async () => {
+
const result = await dialog.showOpenDialog(mainWindow, {
properties: ['openDirectory'],
title: 'Select Installation Folder'
@@ -804,11 +836,10 @@ ipcMain.handle('open-download-page', async () => {
try {
await shell.openExternal(updateManager.getDownloadUrl());
- setTimeout(() => {
- if (mainWindow && !mainWindow.isDestroyed()) {
- mainWindow.close();
- }
- }, 1000);
+ setTimeout(() => {
+ app.quit();
+ }, 1000);
+
return { success: true };
} catch (error) {
@@ -850,11 +881,10 @@ ipcMain.handle('get-detected-gpu', () => {
return global.detectedGpu;
});
-ipcMain.handle('window-close', () => {
- if (mainWindow && !mainWindow.isDestroyed()) {
- mainWindow.close();
- }
-});
+ipcMain.handle('window-close', () => {
+ app.quit();
+});
+
ipcMain.handle('window-minimize', () => {
if (mainWindow && !mainWindow.isDestroyed()) {
diff --git a/package-lock.json b/package-lock.json
index 90d6855..bb1bcd6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,11 +1,11 @@
{
- "name": "hytale-f2p-launcherv2",
+ "name": "hytale-f2p-launcher",
"version": "2.0.2b",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "hytale-f2p-launcherv2",
+ "name": "hytale-f2p-launcher",
"version": "2.0.2b",
"license": "MIT",
"dependencies": {
diff --git a/preload.js b/preload.js
index 759eb5e..b00d840 100644
--- a/preload.js
+++ b/preload.js
@@ -21,6 +21,8 @@ contextBridge.exposeInMainWorld('electronAPI', {
loadDiscordRPC: () => ipcRenderer.invoke('load-discord-rpc'),
saveLanguage: (language) => ipcRenderer.invoke('save-language', language),
loadLanguage: () => ipcRenderer.invoke('load-language'),
+ saveCloseLauncher: (enabled) => ipcRenderer.invoke('save-close-launcher', enabled),
+ loadCloseLauncher: () => ipcRenderer.invoke('load-close-launcher'),
selectInstallPath: () => ipcRenderer.invoke('select-install-path'),
browseJavaPath: () => ipcRenderer.invoke('browse-java-path'),
isGameInstalled: () => ipcRenderer.invoke('is-game-installed'),