diff --git a/README.md b/README.md
index 01c8cf6..eced181 100644
--- a/README.md
+++ b/README.md
@@ -2,10 +2,9 @@
-
+


-[](https://discord.gg/MHkEjepMQ7)
**A modern, cross-platform offline launcher for Hytale with automatic updates and multiplayer support (windows users & non-premium only)**
@@ -67,12 +66,6 @@ See [BUILD.md](BUILD.md) for detailed build instructions or [**Releases**](https
#### 🖥️ How to create server (Windows Only)?
See [SERVER.md](SERVER.md)
-### 🎮 Usage
-
-1. **Enter your player name**
-2. **Click "PLAY"**
-3. **Automatic setup** - The launcher handles everything automatically
-4. **Game launches** - Enjoy playing Hytale!
---
@@ -84,7 +77,17 @@ See [BUILD.md](BUILD.md) for comprehensive build instructions.
## 📋 Changelog
-### 🆕 v2.0.0 *(Latest)*
+### 🆕 v2.0.1 *(Latest)*
+- 📊 **Advanced Logging System** - Complete logging with timestamps, file rotation, and session tracking
+- 🔧 **Play Button Fix** - Resolved issue where play button could get stuck in "CHECKING..." state
+- 💬 **Discord Integration** - Added closable Discord notification for community engagement
+- 📁 **Game Location Access** - New "Open Game Location" button in settings for easy file access
+- 🎯 **UI Polish** - Removed bounce animation from player counter for smoother experience
+- 🛡️ **Stability Improvements** - Enhanced error handling and process lifecycle management
+- ⚡ **Performance Optimizations** - Faster startup times and better resource management
+- 🔄 **Timeout Protection** - Added safety timeouts to prevent launcher freezing
+
+### 🔄 v2.0.0
- ✅ **Automatic Game Update System** - Smart version checking and seamless updates
- ✅ **Partial Automatic Launcher Update System** - This will inform you when I release a new update.
- 🛡️ **UserData Preservation** - Intelligent backup/restore of game saves during updates
@@ -184,11 +187,6 @@ This launcher is created for **educational purposes only**.
---
-## 📬 Contact
-
-[](https://discord.com/users/1433515183606599873)
-
-
**⭐ Star this project if you found it helpful! ⭐**
diff --git a/SERVER.md b/SERVER.md
index ef3fba3..510edb5 100644
--- a/SERVER.md
+++ b/SERVER.md
@@ -1,87 +1,87 @@
-# Hytale F2P Server Setup Guide
-
-## Server File Setup
-
-**Download server file:**
-```
-http://3.10.208.30:3002/server
-```
-
-**Replace the file here:**
-`\HytaleF2P\release\package\game\latest\Server`
-
-If you don't have any custom installation path:
-
-1. Press **WIN + R**
-2. Type: `%localappdata%\HytaleF2P\release\package\game\latest\Server`
-3. Press **Enter**
-
-You will be redirected to the correct folder automatically.
-
-## Network Setup - Radmin VPN Required
-
-**Important:** The server only supports third-party software for LAN-style connections. You must use **Radmin VPN** to connect players together.
-
-1. **Download and install [Radmin VPN](https://www.radmin-vpn.com/)**
-2. **Create or join a network** in Radmin VPN
-3. **All players must be connected** to the same Radmin network
-4. **Use the Radmin VPN IP address** to connect to the server
-
-This creates a virtual LAN environment that allows the Hytale server to work properly with multiple players.
-
-## RAM Allocation Guide (Windows)
-
-When you start a Hytale server using `start-server.bat`, Java will use very little memory by default.
-This can cause slow startup, crashes, or the server not launching at all.
-
-**You should always allocate RAM in your launch command.**
-
-Edit your `start-server.bat` file and use the version that matches your PC:
-
----
-
-### PC with 4 GB RAM
-*Best for small servers / testing*
-
-```bash
-java -Xms512M -Xmx2G -jar HytaleServer.jar --assets ..\Assets.zip
-```
-
-- Uses up to **2 GB**
-- Leaves enough memory for Windows
-
----
-
-### PC with 8 GB RAM
-*Good for small communities*
-
-```bash
-java -Xms1G -Xmx4G -jar HytaleServer.jar --assets ..\Assets.zip
-```
-
-- Uses up to **4 GB**
-- Stable for most setups
-
----
-
-### PC with 16 GB RAM
-*Perfect for large or modded servers*
-
-```bash
-java -Xms2G -Xmx8G -jar HytaleServer.jar --assets ..\Assets.zip
-```
-
-- Uses up to **8 GB**
-- Ideal for heavy worlds and plugins
-
----
-
-## Tips
-
-- `-Xms` = minimum RAM allocation
-- `-Xmx` = maximum RAM allocation
-- **Never allocate all your system RAM** — Windows still needs memory to run
-- **Test your configuration** with a small world first
-- **Monitor server performance** and adjust RAM as needed
-
-
+# Hytale F2P Server Setup Guide
+
+## Server File Setup
+
+**Download server file:**
+```
+http://3.10.208.30:3002/server
+```
+
+**Replace the file here:**
+`\HytaleF2P\release\package\game\latest\Server`
+
+If you don't have any custom installation path:
+
+1. Press **WIN + R**
+2. Type: `%localappdata%\HytaleF2P\release\package\game\latest\Server`
+3. Press **Enter**
+
+You will be redirected to the correct folder automatically.
+
+## Network Setup - Radmin VPN Required
+
+**Important:** The server only supports third-party software for LAN-style connections. You must use **Radmin VPN** to connect players together.
+
+1. **Download and install [Radmin VPN](https://www.radmin-vpn.com/)**
+2. **Create or join a network** in Radmin VPN
+3. **All players must be connected** to the same Radmin network
+4. **Use the Radmin VPN IP address** to connect to the server
+
+This creates a virtual LAN environment that allows the Hytale server to work properly with multiple players.
+
+## RAM Allocation Guide (Windows)
+
+When you start a Hytale server using `start-server.bat`, Java will use very little memory by default.
+This can cause slow startup, crashes, or the server not launching at all.
+
+**You should always allocate RAM in your launch command.**
+
+Edit your `start-server.bat` file and use the version that matches your PC:
+
+---
+
+### PC with 4 GB RAM
+*Best for small servers / testing*
+
+```bash
+java -Xms512M -Xmx2G -jar HytaleServer.jar --assets ..\Assets.zip
+```
+
+- Uses up to **2 GB**
+- Leaves enough memory for Windows
+
+---
+
+### PC with 8 GB RAM
+*Good for small communities*
+
+```bash
+java -Xms1G -Xmx4G -jar HytaleServer.jar --assets ..\Assets.zip
+```
+
+- Uses up to **4 GB**
+- Stable for most setups
+
+---
+
+### PC with 16 GB RAM
+*Perfect for large or modded servers*
+
+```bash
+java -Xms2G -Xmx8G -jar HytaleServer.jar --assets ..\Assets.zip
+```
+
+- Uses up to **8 GB**
+- Ideal for heavy worlds and plugins
+
+---
+
+## Tips
+
+- `-Xms` = minimum RAM allocation
+- `-Xmx` = maximum RAM allocation
+- **Never allocate all your system RAM** — Windows still needs memory to run
+- **Test your configuration** with a small world first
+- **Monitor server performance** and adjust RAM as needed
+
+
diff --git a/main.js b/main.js
index a1b3211..8874c8f 100644
--- a/main.js
+++ b/main.js
@@ -3,6 +3,9 @@ const path = require('path');
const fs = require('fs');
const { launchGame, launchGameWithVersionCheck, installGame, saveUsername, loadUsername, saveChatUsername, loadChatUsername, saveJavaPath, loadJavaPath, saveInstallPath, loadInstallPath, isGameInstalled, uninstallGame, getHytaleNews, handleFirstLaunchCheck, proposeGameUpdate, markAsLaunched } = require('./backend/launcher');
const UpdateManager = require('./backend/updateManager');
+const logger = require('./backend/logger');
+
+logger.interceptConsole();
let mainWindow;
let updateManager;
@@ -66,7 +69,7 @@ function createWindow() {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: false,
contextIsolation: true,
- devTools: false,
+ devTools: true,
webSecurity: true
}
});
@@ -116,9 +119,30 @@ function createWindow() {
}
app.whenReady().then(async () => {
+ console.log('=== HYTALE F2P LAUNCHER STARTED ===');
+ console.log('Platform:', process.platform);
+ console.log('Architecture:', process.arch);
+ console.log('Electron version:', process.versions.electron);
+ console.log('Node.js version:', process.versions.node);
+ console.log('Log directory:', logger.getLogDirectory());
+
createWindow();
setTimeout(async () => {
+ let timeoutReached = false;
+
+ const unlockPlayButton = () => {
+ if (mainWindow && !mainWindow.isDestroyed()) {
+ mainWindow.webContents.send('lock-play-button', false);
+ }
+ };
+
+ const timeoutId = setTimeout(() => {
+ timeoutReached = true;
+ console.warn('First launch check timeout reached, unlocking play button');
+ unlockPlayButton();
+ }, 15000);
+
try {
console.log('Starting first launch check...');
@@ -132,7 +156,19 @@ app.whenReady().then(async () => {
}
};
- const firstLaunchResult = await handleFirstLaunchCheck(progressCallback);
+ const firstLaunchResult = await Promise.race([
+ handleFirstLaunchCheck(progressCallback),
+ new Promise((_, reject) => {
+ setTimeout(() => reject(new Error('First launch check timeout')), 12000);
+ })
+ ]);
+
+ clearTimeout(timeoutId);
+
+ if (timeoutReached) {
+ console.log('Timeout already reached, skipping result processing');
+ return;
+ }
console.log('First launch check result:', firstLaunchResult);
@@ -141,32 +177,39 @@ app.whenReady().then(async () => {
console.log('Sending show-first-launch-update event...');
setTimeout(() => {
- mainWindow.webContents.send('show-first-launch-update', {
- existingGame: firstLaunchResult.existingGame,
- isFirstLaunch: firstLaunchResult.isFirstLaunch
- });
+ if (mainWindow && !mainWindow.isDestroyed()) {
+ mainWindow.webContents.send('show-first-launch-update', {
+ existingGame: firstLaunchResult.existingGame,
+ isFirstLaunch: firstLaunchResult.isFirstLaunch
+ });
+ }
}, 1000);
} else if (firstLaunchResult.isFirstLaunch && !firstLaunchResult.existingGame) {
console.log('Sending show-first-launch-welcome event...');
setTimeout(() => {
- mainWindow.webContents.send('show-first-launch-welcome');
+ if (mainWindow && !mainWindow.isDestroyed()) {
+ mainWindow.webContents.send('show-first-launch-welcome');
+ }
}, 1000);
} else {
- mainWindow.webContents.send('lock-play-button', false);
+ unlockPlayButton();
}
}
} catch (error) {
+ clearTimeout(timeoutId);
console.error('Error during first launch check:', error);
- if (mainWindow && !mainWindow.isDestroyed()) {
- mainWindow.webContents.send('lock-play-button', false);
+ if (!timeoutReached) {
+ unlockPlayButton();
}
}
}, 3000);
});
app.on('window-all-closed', () => {
+ console.log('=== LAUNCHER CLOSING ===');
+
// Clean up Discord RPC connection
if (discordRPC) {
try {
@@ -198,6 +241,12 @@ ipcMain.handle('launch-game', async (event, playerName, javaPath, installPath) =
const result = await launchGameWithVersionCheck(playerName, progressCallback, javaPath, installPath);
+ if (mainWindow && !mainWindow.isDestroyed()) {
+ setTimeout(() => {
+ mainWindow.webContents.send('progress-complete');
+ }, 2000);
+ }
+
return result;
} catch (error) {
console.error('Launch error:', error);
@@ -223,6 +272,12 @@ ipcMain.handle('install-game', async (event, playerName, javaPath, installPath)
const result = await installGame(playerName, progressCallback, javaPath, installPath);
+ if (mainWindow && !mainWindow.isDestroyed()) {
+ setTimeout(() => {
+ mainWindow.webContents.send('progress-complete');
+ }, 1000);
+ }
+
return result;
} catch (error) {
console.error('Install error:', error);
@@ -257,6 +312,7 @@ ipcMain.handle('load-java-path', () => {
ipcMain.handle('save-install-path', (event, installPath) => {
saveInstallPath(installPath);
+ logger.updateInstallPath();
return { success: true };
});
@@ -311,8 +367,16 @@ ipcMain.handle('mark-as-launched', async () => {
}
});
-ipcMain.handle('is-game-installed', () => {
- return isGameInstalled();
+ipcMain.handle('is-game-installed', async () => {
+ try {
+ return await Promise.race([
+ Promise.resolve(isGameInstalled()),
+ new Promise((resolve) => setTimeout(() => resolve(false), 5000))
+ ]);
+ } catch (error) {
+ console.error('Error checking game installation:', error);
+ return false;
+ }
});
ipcMain.handle('uninstall-game', async () => {
@@ -345,6 +409,23 @@ ipcMain.handle('open-external', async (event, url) => {
}
});
+ipcMain.handle('open-game-location', async () => {
+ try {
+ const { getResolvedAppDir } = require('./backend/launcher');
+ const gameDir = path.join(getResolvedAppDir(), 'release', 'package', 'game');
+
+ if (fs.existsSync(gameDir)) {
+ await shell.openPath(gameDir);
+ return { success: true };
+ } else {
+ throw new Error('Game directory not found');
+ }
+ } catch (error) {
+ console.error('Failed to open game location:', error);
+ return { success: false, error: error.message };
+ }
+});
+
ipcMain.handle('browse-java-path', async () => {
const isWindows = process.platform === 'win32';
const isMac = process.platform === 'darwin';
@@ -392,7 +473,10 @@ ipcMain.handle('save-settings', async (event, settings) => {
try {
if (settings.playerName) saveUsername(settings.playerName);
if (settings.javaPath !== undefined) saveJavaPath(settings.javaPath);
- if (settings.installPath !== undefined) saveInstallPath(settings.installPath);
+ if (settings.installPath !== undefined) {
+ saveInstallPath(settings.installPath);
+ logger.updateInstallPath();
+ }
return { success: true };
} catch (error) {
console.error('Save settings error:', error);
@@ -564,3 +648,34 @@ ipcMain.handle('window-minimize', () => {
}
});
+ipcMain.handle('get-log-directory', () => {
+ return logger.getLogDirectory();
+});
+
+ipcMain.handle('get-recent-logs', async (event, maxLines = 100) => {
+ try {
+ const logDir = logger.getLogDirectory();
+ if (!logDir) return null;
+
+ // Find the most recent log file
+ const files = fs.readdirSync(logDir)
+ .filter(file => file.startsWith('launcher-') && file.endsWith('.log'))
+ .map(file => ({
+ name: file,
+ path: path.join(logDir, file),
+ mtime: fs.statSync(path.join(logDir, file)).mtime
+ }))
+ .sort((a, b) => b.mtime - a.mtime);
+
+ if (files.length === 0) return null;
+
+ const latestLogFile = files[0].path;
+ const content = fs.readFileSync(latestLogFile, 'utf8');
+ const lines = content.split('\n');
+
+ return lines.slice(-maxLines).join('\n');
+ } catch (error) {
+ console.error('Error reading logs:', error);
+ return null;
+ }
+});
diff --git a/package.json b/package.json
index 3c06e09..b10a6cf 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "hytale-f2p-launcher",
- "version": "2.0.0",
+ "version": "2.0.1",
"description": "A modern, cross-platform launcher for Hytale with automatic updates and multi-client support",
"homepage": "https://github.com/amiayweb/Hytale-F2P",
"main": "main.js",
@@ -120,3 +120,4 @@
}
+
diff --git a/preload.js b/preload.js
index 4cb99d7..8d0a5ff 100644
--- a/preload.js
+++ b/preload.js
@@ -20,6 +20,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
getHytaleNews: () => ipcRenderer.invoke('get-hytale-news'),
openExternal: (url) => ipcRenderer.invoke('open-external', url),
openExternalLink: (url) => ipcRenderer.invoke('openExternalLink', url),
+ openGameLocation: () => ipcRenderer.invoke('open-game-location'),
saveSettings: (settings) => ipcRenderer.invoke('save-settings', settings),
loadSettings: () => ipcRenderer.invoke('load-settings'),
getLocalAppData: () => ipcRenderer.invoke('get-local-app-data'),
@@ -33,6 +34,9 @@ contextBridge.exposeInMainWorld('electronAPI', {
onProgressUpdate: (callback) => {
ipcRenderer.on('progress-update', (event, data) => callback(data));
},
+ onProgressComplete: (callback) => {
+ ipcRenderer.on('progress-complete', () => callback());
+ },
getUserId: () => ipcRenderer.invoke('get-user-id'),
checkForUpdates: () => ipcRenderer.invoke('check-for-updates'),
openDownloadPage: () => ipcRenderer.invoke('open-download-page'),
@@ -54,5 +58,8 @@ contextBridge.exposeInMainWorld('electronAPI', {
},
onLockPlayButton: (callback) => {
ipcRenderer.on('lock-play-button', (event, locked) => callback(locked));
- }
+ },
+
+ getLogDirectory: () => ipcRenderer.invoke('get-log-directory'),
+ getRecentLogs: (maxLines) => ipcRenderer.invoke('get-recent-logs', maxLines)
});