mirror of
https://git.sanhost.net/sanasol/hytale-f2p.git
synced 2026-02-26 23:01:47 -03:00
Compare commits
4 Commits
552ec42d6c
...
v2.3.9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e14d56ef48 | ||
|
|
a649bf1fcc | ||
|
|
66faa1bb1e | ||
|
|
a63e026700 |
@@ -27,6 +27,7 @@ const { ensureGameInstalled } = require('./differentialUpdateManager');
|
|||||||
const { syncModsForCurrentProfile } = require('./modManager');
|
const { syncModsForCurrentProfile } = require('./modManager');
|
||||||
const { getUserDataPath } = require('../utils/userDataMigration');
|
const { getUserDataPath } = require('../utils/userDataMigration');
|
||||||
const { syncServerList } = require('../utils/serverListSync');
|
const { syncServerList } = require('../utils/serverListSync');
|
||||||
|
const { killGameProcesses } = require('./gameManager');
|
||||||
|
|
||||||
// Client patcher for custom auth server (sanasol.ws)
|
// Client patcher for custom auth server (sanasol.ws)
|
||||||
let clientPatcher = null;
|
let clientPatcher = null;
|
||||||
@@ -436,6 +437,22 @@ exec "$REAL_JAVA" "\${ARGS[@]}"
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Kill any stalled game processes from a previous launch to prevent file locks
|
||||||
|
// and "game already running" issues
|
||||||
|
await killGameProcesses();
|
||||||
|
|
||||||
|
// Remove AOT cache: generated by official Hytale JRE, incompatible with F2P JRE.
|
||||||
|
// Client adds -XX:AOTCache when this file exists, causing classloading failures.
|
||||||
|
const aotCache = path.join(gameLatest, 'Server', 'HytaleServer.aot');
|
||||||
|
if (fs.existsSync(aotCache)) {
|
||||||
|
try {
|
||||||
|
fs.unlinkSync(aotCache);
|
||||||
|
console.log('Removed incompatible AOT cache (HytaleServer.aot)');
|
||||||
|
} catch (aotErr) {
|
||||||
|
console.warn('Could not remove AOT cache:', aotErr.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// DualAuth Agent: Set JAVA_TOOL_OPTIONS so java picks up -javaagent: flag
|
// DualAuth Agent: Set JAVA_TOOL_OPTIONS so java picks up -javaagent: flag
|
||||||
// This enables runtime auth patching without modifying the server JAR
|
// This enables runtime auth patching without modifying the server JAR
|
||||||
const agentJar = path.join(gameLatest, 'Server', 'dualauth-agent.jar');
|
const agentJar = path.join(gameLatest, 'Server', 'dualauth-agent.jar');
|
||||||
|
|||||||
@@ -39,6 +39,41 @@ async function isGameRunning() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Force-kill stalled game processes to release file locks before repair/reinstall.
|
||||||
|
// Cross-platform: Windows (taskkill/PowerShell), macOS (pkill), Linux (pkill).
|
||||||
|
async function killGameProcesses() {
|
||||||
|
const killed = [];
|
||||||
|
|
||||||
|
async function tryKill(command, label) {
|
||||||
|
try {
|
||||||
|
await execAsync(command);
|
||||||
|
killed.push(label);
|
||||||
|
} catch (_) { /* process not found is expected */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
// Kill client
|
||||||
|
await tryKill('taskkill /F /IM "HytaleClient.exe" /T', 'HytaleClient.exe');
|
||||||
|
// Kill java.exe instances running HytaleServer.jar via PowerShell
|
||||||
|
// (Get-CimInstance replaces deprecated wmic, works on Windows 10+)
|
||||||
|
await tryKill(
|
||||||
|
'powershell -NoProfile -Command "Get-CimInstance Win32_Process -Filter \\"name=\'java.exe\'\\" | Where-Object { $_.CommandLine -like \'*HytaleServer*\' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force }"',
|
||||||
|
'java.exe(HytaleServer)'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// macOS and Linux
|
||||||
|
await tryKill('pkill -9 -f HytaleClient', 'HytaleClient');
|
||||||
|
await tryKill('pkill -9 -f HytaleServer', 'HytaleServer');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (killed.length > 0) {
|
||||||
|
console.log(`[GameManager] Force-killed stalled processes: ${killed.join(', ')}`);
|
||||||
|
// Wait for OS to release file handles
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||||
|
}
|
||||||
|
return killed;
|
||||||
|
}
|
||||||
|
|
||||||
// Helper function to safely remove directory with retry logic
|
// Helper function to safely remove directory with retry logic
|
||||||
async function safeRemoveDirectory(dirPath, maxRetries = 3) {
|
async function safeRemoveDirectory(dirPath, maxRetries = 3) {
|
||||||
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
||||||
@@ -52,6 +87,11 @@ async function safeRemoveDirectory(dirPath, maxRetries = 3) {
|
|||||||
console.warn(`Attempt ${attempt}/${maxRetries} failed to remove ${dirPath}: ${error.message}`);
|
console.warn(`Attempt ${attempt}/${maxRetries} failed to remove ${dirPath}: ${error.message}`);
|
||||||
|
|
||||||
if (attempt < maxRetries) {
|
if (attempt < maxRetries) {
|
||||||
|
// On EPERM/EBUSY, try killing stalled game processes that hold file locks
|
||||||
|
if (attempt === 1 && (error.code === 'EPERM' || error.code === 'EBUSY')) {
|
||||||
|
console.log('Permission error detected, killing stalled game processes...');
|
||||||
|
await killGameProcesses();
|
||||||
|
}
|
||||||
// Wait before retrying (exponential backoff)
|
// Wait before retrying (exponential backoff)
|
||||||
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000);
|
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000);
|
||||||
console.log(`Waiting ${delay}ms before retry...`);
|
console.log(`Waiting ${delay}ms before retry...`);
|
||||||
@@ -833,11 +873,14 @@ async function repairGame(progressCallback, branchOverride = null) {
|
|||||||
progressCallback('Removing old game files...', 30, null, null, null);
|
progressCallback('Removing old game files...', 30, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if game is running before attempting to delete files
|
// Kill stalled game processes before attempting to delete files
|
||||||
const gameRunning = await isGameRunning();
|
const gameRunning = await isGameRunning();
|
||||||
if (gameRunning) {
|
if (gameRunning) {
|
||||||
console.warn('[RepairGame] Game appears to be running. This may cause permission errors during repair.');
|
console.warn('[RepairGame] Game processes detected. Force-killing to release file locks...');
|
||||||
console.log('[RepairGame] Please close the game before repairing, or wait for the repair to complete.');
|
if (progressCallback) {
|
||||||
|
progressCallback('Stopping stalled game processes...', 20, null, null, null);
|
||||||
|
}
|
||||||
|
await killGameProcesses();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete Game and Cache Directory with retry logic
|
// Delete Game and Cache Directory with retry logic
|
||||||
@@ -964,5 +1007,6 @@ module.exports = {
|
|||||||
installGame,
|
installGame,
|
||||||
uninstallGame,
|
uninstallGame,
|
||||||
checkExistingGameInstallation,
|
checkExistingGameInstallation,
|
||||||
repairGame
|
repairGame,
|
||||||
|
killGameProcesses
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hytale-f2p-launcher",
|
"name": "hytale-f2p-launcher",
|
||||||
"version": "2.3.8",
|
"version": "2.3.9",
|
||||||
"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://git.sanhost.net/sanasol/hytale-f2p",
|
"homepage": "https://git.sanhost.net/sanasol/hytale-f2p",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
|
|||||||
@@ -9,19 +9,38 @@ Host your own Hytale server. The scripts handle everything automatically.
|
|||||||
- **Internet connection** for first launch (downloads ~3.5 GB of game files)
|
- **Internet connection** for first launch (downloads ~3.5 GB of game files)
|
||||||
- If you have the F2P launcher installed, game files are copied locally (no download needed)
|
- If you have the F2P launcher installed, game files are copied locally (no download needed)
|
||||||
|
|
||||||
|
## Video Guide
|
||||||
|
|
||||||
|
[](https://youtu.be/KvuXLH7SKvI)
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
### Windows
|
### Free Hosted Server (no PC required)
|
||||||
|
|
||||||
|
Use [play.hosting](https://play.hosting) to get a free Hytale server with F2P support:
|
||||||
|
|
||||||
|
1. Register at [play.hosting](https://play.hosting)
|
||||||
|
2. Create a **Hytale** server
|
||||||
|
3. Start the server once and wait for it to fully load
|
||||||
|
4. Go to **Files** → open the `mods` folder
|
||||||
|
5. Click **New** → **File via URL**
|
||||||
|
6. Paste: `https://github.com/sanasol/hytale-auth-server/releases/latest/download/dualauth-agent.jar`
|
||||||
|
7. Click **Query** and download the file
|
||||||
|
8. Go to **Console** and **Restart** the server
|
||||||
|
|
||||||
|
Done — your F2P server is ready to join.
|
||||||
|
|
||||||
|
### Windows (self-hosted)
|
||||||
|
|
||||||
1. Download `start.bat` to an empty folder
|
1. Download `start.bat` to an empty folder
|
||||||
2. Double-click `start.bat`
|
2. Double-click `start.bat`
|
||||||
3. Done — server starts on port **5520**
|
3. Done — server starts on port **5520**
|
||||||
|
|
||||||
### Linux / macOS
|
### Linux / macOS (self-hosted)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mkdir hytale-server && cd hytale-server
|
mkdir hytale-server && cd hytale-server
|
||||||
curl -O https://raw.githubusercontent.com/amiayweb/Hytale-F2P/develop/server/start.sh
|
curl -O https://git.sanhost.net/sanasol/hytale-f2p/raw/branch/develop/server/start.sh
|
||||||
chmod +x start.sh
|
chmod +x start.sh
|
||||||
./start.sh
|
./start.sh
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user