Add files via upload

This commit is contained in:
AMIAY
2026-01-18 15:42:20 +01:00
committed by GitHub
parent 4bc1661587
commit 9fcf603e08
5 changed files with 237 additions and 116 deletions

View File

@@ -2,10 +2,9 @@
<div align="center"> <div align="center">
![Version](https://img.shields.io/badge/Version-2.0.0-green?style=for-the-badge) ![Version](https://img.shields.io/badge/Version-2.0.1-green?style=for-the-badge)
![Platform](https://img.shields.io/badge/Platform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey?style=for-the-badge) ![Platform](https://img.shields.io/badge/Platform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey?style=for-the-badge)
![License](https://img.shields.io/badge/License-Educational-blue?style=for-the-badge) ![License](https://img.shields.io/badge/License-Educational-blue?style=for-the-badge)
[![Join Discord](https://img.shields.io/badge/Discord-Join%20Server-5865F2?logo=discord&logoColor=white)](https://discord.gg/MHkEjepMQ7)
**A modern, cross-platform offline launcher for Hytale with automatic updates and multiplayer support (windows users & non-premium only)** **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)? #### 🖥️ How to create server (Windows Only)?
See [SERVER.md](SERVER.md) 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 ## 📋 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 -**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. -**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 - 🛡️ **UserData Preservation** - Intelligent backup/restore of game saves during updates
@@ -184,11 +187,6 @@ This launcher is created for **educational purposes only**.
--- ---
## 📬 Contact
[![Discord](https://img.shields.io/badge/Discord-amiay3-5865F2?logo=discord&logoColor=white)](https://discord.com/users/1433515183606599873)
<div align="center"> <div align="center">
**⭐ Star this project if you found it helpful! ⭐** **⭐ Star this project if you found it helpful! ⭐**

174
SERVER.md
View File

@@ -1,87 +1,87 @@
# Hytale F2P Server Setup Guide # Hytale F2P Server Setup Guide
## Server File Setup ## Server File Setup
**Download server file:** **Download server file:**
``` ```
http://3.10.208.30:3002/server http://3.10.208.30:3002/server
``` ```
**Replace the file here:** **Replace the file here:**
`<your_path>\HytaleF2P\release\package\game\latest\Server` `<your_path>\HytaleF2P\release\package\game\latest\Server`
If you don't have any custom installation path: If you don't have any custom installation path:
1. Press **WIN + R** 1. Press **WIN + R**
2. Type: `%localappdata%\HytaleF2P\release\package\game\latest\Server` 2. Type: `%localappdata%\HytaleF2P\release\package\game\latest\Server`
3. Press **Enter** 3. Press **Enter**
You will be redirected to the correct folder automatically. You will be redirected to the correct folder automatically.
## Network Setup - Radmin VPN Required ## 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. **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/)** 1. **Download and install [Radmin VPN](https://www.radmin-vpn.com/)**
2. **Create or join a network** in Radmin VPN 2. **Create or join a network** in Radmin VPN
3. **All players must be connected** to the same Radmin network 3. **All players must be connected** to the same Radmin network
4. **Use the Radmin VPN IP address** to connect to the server 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. This creates a virtual LAN environment that allows the Hytale server to work properly with multiple players.
## RAM Allocation Guide (Windows) ## RAM Allocation Guide (Windows)
When you start a Hytale server using `start-server.bat`, Java will use very little memory by default. 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. This can cause slow startup, crashes, or the server not launching at all.
**You should always allocate RAM in your launch command.** **You should always allocate RAM in your launch command.**
Edit your `start-server.bat` file and use the version that matches your PC: Edit your `start-server.bat` file and use the version that matches your PC:
--- ---
### PC with 4 GB RAM ### PC with 4 GB RAM
*Best for small servers / testing* *Best for small servers / testing*
```bash ```bash
java -Xms512M -Xmx2G -jar HytaleServer.jar --assets ..\Assets.zip java -Xms512M -Xmx2G -jar HytaleServer.jar --assets ..\Assets.zip
``` ```
- Uses up to **2 GB** - Uses up to **2 GB**
- Leaves enough memory for Windows - Leaves enough memory for Windows
--- ---
### PC with 8 GB RAM ### PC with 8 GB RAM
*Good for small communities* *Good for small communities*
```bash ```bash
java -Xms1G -Xmx4G -jar HytaleServer.jar --assets ..\Assets.zip java -Xms1G -Xmx4G -jar HytaleServer.jar --assets ..\Assets.zip
``` ```
- Uses up to **4 GB** - Uses up to **4 GB**
- Stable for most setups - Stable for most setups
--- ---
### PC with 16 GB RAM ### PC with 16 GB RAM
*Perfect for large or modded servers* *Perfect for large or modded servers*
```bash ```bash
java -Xms2G -Xmx8G -jar HytaleServer.jar --assets ..\Assets.zip java -Xms2G -Xmx8G -jar HytaleServer.jar --assets ..\Assets.zip
``` ```
- Uses up to **8 GB** - Uses up to **8 GB**
- Ideal for heavy worlds and plugins - Ideal for heavy worlds and plugins
--- ---
## Tips ## Tips
- `-Xms` = minimum RAM allocation - `-Xms` = minimum RAM allocation
- `-Xmx` = maximum RAM allocation - `-Xmx` = maximum RAM allocation
- **Never allocate all your system RAM** — Windows still needs memory to run - **Never allocate all your system RAM** — Windows still needs memory to run
- **Test your configuration** with a small world first - **Test your configuration** with a small world first
- **Monitor server performance** and adjust RAM as needed - **Monitor server performance** and adjust RAM as needed

141
main.js
View File

@@ -3,6 +3,9 @@ const path = require('path');
const fs = require('fs'); 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 { 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 UpdateManager = require('./backend/updateManager');
const logger = require('./backend/logger');
logger.interceptConsole();
let mainWindow; let mainWindow;
let updateManager; let updateManager;
@@ -66,7 +69,7 @@ function createWindow() {
preload: path.join(__dirname, 'preload.js'), preload: path.join(__dirname, 'preload.js'),
nodeIntegration: false, nodeIntegration: false,
contextIsolation: true, contextIsolation: true,
devTools: false, devTools: true,
webSecurity: true webSecurity: true
} }
}); });
@@ -116,9 +119,30 @@ function createWindow() {
} }
app.whenReady().then(async () => { 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(); createWindow();
setTimeout(async () => { 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 { try {
console.log('Starting first launch check...'); 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); console.log('First launch check result:', firstLaunchResult);
@@ -141,32 +177,39 @@ app.whenReady().then(async () => {
console.log('Sending show-first-launch-update event...'); console.log('Sending show-first-launch-update event...');
setTimeout(() => { setTimeout(() => {
mainWindow.webContents.send('show-first-launch-update', { if (mainWindow && !mainWindow.isDestroyed()) {
existingGame: firstLaunchResult.existingGame, mainWindow.webContents.send('show-first-launch-update', {
isFirstLaunch: firstLaunchResult.isFirstLaunch existingGame: firstLaunchResult.existingGame,
}); isFirstLaunch: firstLaunchResult.isFirstLaunch
});
}
}, 1000); }, 1000);
} else if (firstLaunchResult.isFirstLaunch && !firstLaunchResult.existingGame) { } else if (firstLaunchResult.isFirstLaunch && !firstLaunchResult.existingGame) {
console.log('Sending show-first-launch-welcome event...'); console.log('Sending show-first-launch-welcome event...');
setTimeout(() => { setTimeout(() => {
mainWindow.webContents.send('show-first-launch-welcome'); if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.webContents.send('show-first-launch-welcome');
}
}, 1000); }, 1000);
} else { } else {
mainWindow.webContents.send('lock-play-button', false); unlockPlayButton();
} }
} }
} catch (error) { } catch (error) {
clearTimeout(timeoutId);
console.error('Error during first launch check:', error); console.error('Error during first launch check:', error);
if (mainWindow && !mainWindow.isDestroyed()) { if (!timeoutReached) {
mainWindow.webContents.send('lock-play-button', false); unlockPlayButton();
} }
} }
}, 3000); }, 3000);
}); });
app.on('window-all-closed', () => { app.on('window-all-closed', () => {
console.log('=== LAUNCHER CLOSING ===');
// Clean up Discord RPC connection // Clean up Discord RPC connection
if (discordRPC) { if (discordRPC) {
try { try {
@@ -198,6 +241,12 @@ ipcMain.handle('launch-game', async (event, playerName, javaPath, installPath) =
const result = await launchGameWithVersionCheck(playerName, progressCallback, javaPath, installPath); const result = await launchGameWithVersionCheck(playerName, progressCallback, javaPath, installPath);
if (mainWindow && !mainWindow.isDestroyed()) {
setTimeout(() => {
mainWindow.webContents.send('progress-complete');
}, 2000);
}
return result; return result;
} catch (error) { } catch (error) {
console.error('Launch error:', 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); const result = await installGame(playerName, progressCallback, javaPath, installPath);
if (mainWindow && !mainWindow.isDestroyed()) {
setTimeout(() => {
mainWindow.webContents.send('progress-complete');
}, 1000);
}
return result; return result;
} catch (error) { } catch (error) {
console.error('Install error:', error); console.error('Install error:', error);
@@ -257,6 +312,7 @@ ipcMain.handle('load-java-path', () => {
ipcMain.handle('save-install-path', (event, installPath) => { ipcMain.handle('save-install-path', (event, installPath) => {
saveInstallPath(installPath); saveInstallPath(installPath);
logger.updateInstallPath();
return { success: true }; return { success: true };
}); });
@@ -311,8 +367,16 @@ ipcMain.handle('mark-as-launched', async () => {
} }
}); });
ipcMain.handle('is-game-installed', () => { ipcMain.handle('is-game-installed', async () => {
return isGameInstalled(); 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 () => { 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 () => { ipcMain.handle('browse-java-path', async () => {
const isWindows = process.platform === 'win32'; const isWindows = process.platform === 'win32';
const isMac = process.platform === 'darwin'; const isMac = process.platform === 'darwin';
@@ -392,7 +473,10 @@ ipcMain.handle('save-settings', async (event, settings) => {
try { try {
if (settings.playerName) saveUsername(settings.playerName); if (settings.playerName) saveUsername(settings.playerName);
if (settings.javaPath !== undefined) saveJavaPath(settings.javaPath); 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 }; return { success: true };
} catch (error) { } catch (error) {
console.error('Save settings error:', 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;
}
});

View File

@@ -1,6 +1,6 @@
{ {
"name": "hytale-f2p-launcher", "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", "description": "A modern, cross-platform launcher for Hytale with automatic updates and multi-client support",
"homepage": "https://github.com/amiayweb/Hytale-F2P", "homepage": "https://github.com/amiayweb/Hytale-F2P",
"main": "main.js", "main": "main.js",
@@ -120,3 +120,4 @@
} }

View File

@@ -20,6 +20,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
getHytaleNews: () => ipcRenderer.invoke('get-hytale-news'), getHytaleNews: () => ipcRenderer.invoke('get-hytale-news'),
openExternal: (url) => ipcRenderer.invoke('open-external', url), openExternal: (url) => ipcRenderer.invoke('open-external', url),
openExternalLink: (url) => ipcRenderer.invoke('openExternalLink', url), openExternalLink: (url) => ipcRenderer.invoke('openExternalLink', url),
openGameLocation: () => ipcRenderer.invoke('open-game-location'),
saveSettings: (settings) => ipcRenderer.invoke('save-settings', settings), saveSettings: (settings) => ipcRenderer.invoke('save-settings', settings),
loadSettings: () => ipcRenderer.invoke('load-settings'), loadSettings: () => ipcRenderer.invoke('load-settings'),
getLocalAppData: () => ipcRenderer.invoke('get-local-app-data'), getLocalAppData: () => ipcRenderer.invoke('get-local-app-data'),
@@ -33,6 +34,9 @@ contextBridge.exposeInMainWorld('electronAPI', {
onProgressUpdate: (callback) => { onProgressUpdate: (callback) => {
ipcRenderer.on('progress-update', (event, data) => callback(data)); ipcRenderer.on('progress-update', (event, data) => callback(data));
}, },
onProgressComplete: (callback) => {
ipcRenderer.on('progress-complete', () => callback());
},
getUserId: () => ipcRenderer.invoke('get-user-id'), getUserId: () => ipcRenderer.invoke('get-user-id'),
checkForUpdates: () => ipcRenderer.invoke('check-for-updates'), checkForUpdates: () => ipcRenderer.invoke('check-for-updates'),
openDownloadPage: () => ipcRenderer.invoke('open-download-page'), openDownloadPage: () => ipcRenderer.invoke('open-download-page'),
@@ -54,5 +58,8 @@ contextBridge.exposeInMainWorld('electronAPI', {
}, },
onLockPlayButton: (callback) => { onLockPlayButton: (callback) => {
ipcRenderer.on('lock-play-button', (event, locked) => callback(locked)); ipcRenderer.on('lock-play-button', (event, locked) => callback(locked));
} },
getLogDirectory: () => ipcRenderer.invoke('get-log-directory'),
getRecentLogs: (maxLines) => ipcRenderer.invoke('get-recent-logs', maxLines)
}); });