mirror of
https://git.sanhost.net/sanasol/hytale-f2p.git
synced 2026-02-27 15:21:47 -03:00
Compare commits
24 Commits
macos-nota
...
v2.3.0-tes
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4b5368538 | ||
|
|
e0ebf137fc | ||
|
|
5241a502e5 | ||
|
|
3e7c7ccff3 | ||
|
|
89d09f032f | ||
|
|
8bdb78d1e2 | ||
|
|
e9e66dbca7 | ||
|
|
92a0a26251 | ||
|
|
3abe885ab4 | ||
|
|
82f1dd2739 | ||
|
|
4502c11bd0 | ||
|
|
bcc7476322 | ||
|
|
ae6a7db80a | ||
|
|
48395fbff3 | ||
|
|
aae90a72e8 | ||
|
|
b93dc027e1 | ||
|
|
fdbca6b9da | ||
|
|
b2f65bd524 | ||
|
|
bd6b05d1e4 | ||
|
|
454ca7f075 | ||
|
|
e7324eb176 | ||
|
|
a6c61aef68 | ||
|
|
31653a37a7 | ||
|
|
1cb08f029a |
198
.github/workflows/release.yml
vendored
198
.github/workflows/release.yml
vendored
@@ -6,173 +6,117 @@ on:
|
|||||||
- 'v*'
|
- 'v*'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
# Domain for small API calls (goes through Cloudflare - fine for <100MB)
|
||||||
|
FORGEJO_API: https://git.sanhost.net/api/v1
|
||||||
|
# Direct to Forgejo port (bypasses Cloudflare + Traefik for large uploads)
|
||||||
|
FORGEJO_UPLOAD: http://208.69.78.130:3001/api/v1
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-windows:
|
create-release:
|
||||||
runs-on: windows-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
- name: Create Draft Release
|
||||||
|
run: |
|
||||||
|
curl -s -X POST "${FORGEJO_API}/repos/${GITHUB_REPOSITORY}/releases" \
|
||||||
|
-H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"tag_name\":\"${{ github.ref_name }}\",\"name\":\"${{ github.ref_name }}\",\"body\":\"Release ${{ github.ref_name }}\",\"draft\":true,\"prerelease\":false}" \
|
||||||
|
-o release.json
|
||||||
|
cat release.json
|
||||||
|
echo "RELEASE_ID=$(cat release.json | python3 -c 'import sys,json; print(json.load(sys.stdin)["id"])')" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
build-windows:
|
||||||
|
needs: [create-release]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install Wine for cross-compilation
|
||||||
|
run: |
|
||||||
|
sudo dpkg --add-architecture i386
|
||||||
|
sudo mkdir -pm755 /etc/apt/keyrings
|
||||||
|
sudo wget -O /etc/apt/keyrings/winehq-archive.key https://dl.winehq.org/wine-builds/winehq.key
|
||||||
|
sudo wget -NP /etc/apt/sources.list.d/ https://dl.winehq.org/wine-builds/ubuntu/dists/$(lsb_release -cs)/winehq-$(lsb_release -cs).sources
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y --install-recommends winehq-stable
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: '22'
|
node-version: '22'
|
||||||
cache: 'npm'
|
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
|
|
||||||
- name: Build Windows Packages
|
- name: Build Windows Packages
|
||||||
run: npx electron-builder --win --publish never
|
run: npx electron-builder --win --publish never --config.npmRebuild=false
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
- name: Upload to Release
|
||||||
name: windows-builds
|
run: |
|
||||||
path: |
|
RELEASE_ID=$(curl -s "${FORGEJO_API}/repos/${GITHUB_REPOSITORY}/releases/tags/${{ github.ref_name }}" \
|
||||||
dist/*.exe
|
-H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" | python3 -c 'import sys,json; print(json.load(sys.stdin)["id"])')
|
||||||
dist/*.exe.blockmap
|
for file in dist/*.exe dist/*.exe.blockmap dist/latest.yml; do
|
||||||
dist/latest.yml
|
[ -f "$file" ] || continue
|
||||||
|
echo "Uploading $file..."
|
||||||
|
curl -s --max-time 600 -X POST "${FORGEJO_UPLOAD}/repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}/assets?name=$(basename $file)" \
|
||||||
|
-H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \
|
||||||
|
-F "attachment=@${file}" || echo "Failed to upload $file"
|
||||||
|
done
|
||||||
|
|
||||||
build-macos:
|
build-macos:
|
||||||
|
needs: [create-release]
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: '22'
|
node-version: '22'
|
||||||
cache: 'npm'
|
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
|
|
||||||
- name: Build macOS Packages
|
- name: Build macOS Packages
|
||||||
env:
|
env:
|
||||||
# Code signing
|
|
||||||
CSC_LINK: ${{ secrets.CSC_LINK }}
|
CSC_LINK: ${{ secrets.CSC_LINK }}
|
||||||
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
||||||
# Notarization
|
|
||||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||||
run: npx electron-builder --mac --publish never
|
run: npx electron-builder --mac --publish never
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
- name: Upload to Release
|
||||||
name: macos-builds
|
run: |
|
||||||
path: |
|
RELEASE_ID=$(curl -s "${FORGEJO_API}/repos/${GITHUB_REPOSITORY}/releases/tags/${{ github.ref_name }}" \
|
||||||
dist/*.dmg
|
-H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" | python3 -c 'import sys,json; print(json.load(sys.stdin)["id"])')
|
||||||
dist/*.zip
|
for file in dist/*.dmg dist/*.zip dist/*.blockmap dist/latest-mac.yml; do
|
||||||
dist/*.blockmap
|
[ -f "$file" ] || continue
|
||||||
dist/latest-mac.yml
|
echo "Uploading $file..."
|
||||||
|
curl -s --max-time 600 -X POST "${FORGEJO_UPLOAD}/repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}/assets?name=$(basename $file)" \
|
||||||
|
-H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \
|
||||||
|
-F "attachment=@${file}" || echo "Failed to upload $file"
|
||||||
|
done
|
||||||
|
|
||||||
build-linux:
|
build-linux:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
needs: [create-release]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Install build dependencies
|
- name: Install build dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y libarchive-tools
|
sudo apt-get install -y libarchive-tools rpm
|
||||||
|
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: '22'
|
node-version: '22'
|
||||||
cache: 'npm'
|
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
|
|
||||||
- name: Build Linux Packages
|
- name: Build Linux Packages
|
||||||
|
run: npx electron-builder --linux AppImage deb rpm --publish never
|
||||||
|
|
||||||
|
- name: Upload to Release
|
||||||
run: |
|
run: |
|
||||||
npx electron-builder --linux AppImage deb rpm --publish never
|
RELEASE_ID=$(curl -s "${FORGEJO_API}/repos/${GITHUB_REPOSITORY}/releases/tags/${{ github.ref_name }}" \
|
||||||
- uses: actions/upload-artifact@v4
|
-H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" | python3 -c 'import sys,json; print(json.load(sys.stdin)["id"])')
|
||||||
with:
|
for file in dist/*.AppImage dist/*.AppImage.blockmap dist/*.deb dist/*.rpm dist/latest-linux.yml; do
|
||||||
name: linux-builds
|
[ -f "$file" ] || continue
|
||||||
path: |
|
echo "Uploading $file..."
|
||||||
dist/*.AppImage
|
curl -s --max-time 600 -X POST "${FORGEJO_UPLOAD}/repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}/assets?name=$(basename $file)" \
|
||||||
dist/*.AppImage.blockmap
|
-H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \
|
||||||
dist/*.deb
|
-F "attachment=@${file}" || echo "Failed to upload $file"
|
||||||
dist/*.rpm
|
done
|
||||||
dist/latest-linux.yml
|
|
||||||
|
|
||||||
build-arch:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container:
|
|
||||||
image: archlinux:latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Install base packages
|
|
||||||
run: |
|
|
||||||
pacman -Syu --noconfirm
|
|
||||||
pacman -S --noconfirm \
|
|
||||||
base-devel \
|
|
||||||
git \
|
|
||||||
nodejs \
|
|
||||||
npm \
|
|
||||||
rpm-tools \
|
|
||||||
libxcrypt-compat
|
|
||||||
|
|
||||||
- name: Create build user
|
|
||||||
run: |
|
|
||||||
useradd -m builder
|
|
||||||
echo "builder ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
|
||||||
|
|
||||||
- name: Fix Permissions
|
|
||||||
run: chown -R builder:builder .
|
|
||||||
|
|
||||||
- name: Build Arch Package
|
|
||||||
run: |
|
|
||||||
sudo -u builder bash << 'EOF'
|
|
||||||
set -e
|
|
||||||
makepkg --printsrcinfo > .SRCINFO
|
|
||||||
makepkg -s --noconfirm
|
|
||||||
EOF
|
|
||||||
|
|
||||||
- name: Fix permissions for upload
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
sudo chown -R $(id -u):$(id -g) .
|
|
||||||
|
|
||||||
- name: Upload Arch Package
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: arch-package
|
|
||||||
path: |
|
|
||||||
*.pkg.tar.zst
|
|
||||||
.SRCINFO
|
|
||||||
include-hidden-files: true
|
|
||||||
|
|
||||||
release:
|
|
||||||
needs: [build-windows, build-macos, build-linux, build-arch]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: |
|
|
||||||
startsWith(github.ref, 'refs/tags/v') ||
|
|
||||||
github.ref == 'refs/heads/main' ||
|
|
||||||
github.event_name == 'workflow_dispatch'
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Download all artifacts
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
path: artifacts
|
|
||||||
|
|
||||||
- name: Display structure of downloaded files
|
|
||||||
run: ls -R artifacts
|
|
||||||
|
|
||||||
- name: Get version from package.json
|
|
||||||
id: pkg_version
|
|
||||||
run: echo "VERSION=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Create Release
|
|
||||||
uses: softprops/action-gh-release@v2
|
|
||||||
with:
|
|
||||||
tag_name: ${{ github.ref_name }}
|
|
||||||
files: |
|
|
||||||
artifacts/arch-package/*.pkg.tar.zst
|
|
||||||
artifacts/arch-package/.SRCINFO
|
|
||||||
artifacts/linux-builds/**/*
|
|
||||||
artifacts/windows-builds/**/*
|
|
||||||
artifacts/macos-builds/**/*
|
|
||||||
generate_release_notes: true
|
|
||||||
draft: true
|
|
||||||
prerelease: false
|
|
||||||
|
|
||||||
|
|||||||
126
SERVER.md
126
SERVER.md
@@ -6,15 +6,16 @@ Play with friends online! This guide covers both easy in-game hosting and advanc
|
|||||||
|
|
||||||
**Table of Contents**
|
**Table of Contents**
|
||||||
|
|
||||||
|
* [\[NEW!\] Play Online with Official Accounts 🆕](#new-play-online-with-official-accounts-)
|
||||||
* ["Server" Term and Definition](#server-term-and-definiton)
|
* ["Server" Term and Definition](#server-term-and-definiton)
|
||||||
* [Server Directory Location](#server-directory-location)
|
* [Server Directory Location](#server-directory-location)
|
||||||
* [A. Online Play Feature](#a-online-play-feature)
|
* [A. Host Your Singleplayer World](#a-host-your-singleplayer-world)
|
||||||
* [1. Host Your Singleplayer World using In-Game Invite Code](#1-host-your-singleplayer-world-using-in-game-invite-code)
|
* [1. Using Online-Play Feature In-Game Invite Code](#1-using-online-play-feature--in-game-invite-code)
|
||||||
* [Common Issues (UPnP/NAT/STUN) on Online Play](#common-issues-upnpnatstun-on-online-play)
|
* [Common Issues (UPnP/NAT/STUN) on Online Play](#common-issues-upnpnatstun-on-online-play)
|
||||||
* [2. Host Your Singleplayer World using Tailscale](#2-host-your-singleplayer-world-using-tailscale)
|
* [2. Using Tailscale](#2-using-tailscale)
|
||||||
|
* [3. Using Radmin VPN](#3-using-radmin-vpn)
|
||||||
* [B. Local Dedicated Server](#b-local-dedicated-server)
|
* [B. Local Dedicated Server](#b-local-dedicated-server)
|
||||||
* [1. Using Playit.gg (Recommended) ✅](#1-using-playitgg-recommended-)
|
* [1. Using Playit.gg (Recommended) ✅](#1-using-playitgg-recommended-)
|
||||||
* [2. Using Radmin VPN](#2-using-radmin-vpn)
|
|
||||||
* [C. 24/7 Dedicated Server (Advanced)](#c-247-dedicated-server-advanced)
|
* [C. 24/7 Dedicated Server (Advanced)](#c-247-dedicated-server-advanced)
|
||||||
* [Step 1: Get the Files Ready](#step-1-get-the-files-ready)
|
* [Step 1: Get the Files Ready](#step-1-get-the-files-ready)
|
||||||
* [Step 2: Place HytaleServer.jar in the Server directory](#step-2-place-hytaleserverjar-in-the-server-directory)
|
* [Step 2: Place HytaleServer.jar in the Server directory](#step-2-place-hytaleserverjar-in-the-server-directory)
|
||||||
@@ -32,6 +33,69 @@ Play with friends online! This guide covers both easy in-game hosting and advanc
|
|||||||
* [10. Getting Help](#10-getting-help)
|
* [10. Getting Help](#10-getting-help)
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<div align='center'>
|
||||||
|
<h3>
|
||||||
|
<b>
|
||||||
|
Do you want to create Hytale Game Server with EASY SETUP, AFFORDABLE PRICE, AND 24/7 SUPPORT?
|
||||||
|
</b>
|
||||||
|
</h3>
|
||||||
|
<h2>
|
||||||
|
<b>
|
||||||
|
<a href="https://cloudnord.net/hytale-server-hosting">CLOUDNORD</a> is the ANSWER! HF2P Server is available!
|
||||||
|
</b>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
**CloudNord's Hytale, Minecraft, and Game Hosting** is at the core of our Server Hosting business. Join our Gaming community and experience our large choice of premium game servers, we’ve got you covered with super high-performance hardware, fantastic support options, and powerful server hosting to build and explore your worlds without limits!
|
||||||
|
|
||||||
|
**Order your Hytale, Minecraft, or other game servers today!**
|
||||||
|
Choose Java Edition, Bedrock Edition, Cross-Play, or any of our additional supported games.
|
||||||
|
Enjoy **20% OFF** all new game servers, **available now for a limited time!** Don’t miss out.
|
||||||
|
|
||||||
|
### **CloudNord key hosting features include:**
|
||||||
|
- Instant Server Setup ⚡
|
||||||
|
- High Performance Game Servers 🚀
|
||||||
|
- Game DDoS Protection 🛡️
|
||||||
|
- Intelligent Game Backups 🧠
|
||||||
|
- Quick Modpack Installer 🔧
|
||||||
|
- Quick Plugin & Mod Installer 🧰
|
||||||
|
- Full File Access 🗃️
|
||||||
|
- 24/7 Support 📞 🏪
|
||||||
|
- Powerful Game Control Server Panel 💪
|
||||||
|
|
||||||
|
### **Check Us Out:**
|
||||||
|
* 👉 CloudNord Website: https://cloudnord.net/hytalef2p
|
||||||
|
* 👉 CloudNord Discord: https://discord.gg/TYxGrmUz4Y
|
||||||
|
* 👉 CloudNord Reviews: https://www.trustpilot.com/review/cloudnord.net?page=2&stars=5
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### [NEW!] Play Online with Official Accounts 🆕
|
||||||
|
|
||||||
|
**Documentations:**
|
||||||
|
* [Hytale-Server-Docker by Sanasol](https://github.com/sanasol/hytale-server-docker/tree/main?tab=readme-ov-file#dual-authentication)
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
* Using the patched HytaleServer.jar
|
||||||
|
* Has Official Account with Purchased status on Official Hytale Website.
|
||||||
|
* This official account holder can be the server hoster or one of the players.
|
||||||
|
|
||||||
|
**Steps:**
|
||||||
|
1. Running the patched HytaleServer.jar with either [B. Local Dedicated Server](#b-local-dedicated-server) or [C. 24/7 Dedicated Server (Advanced)](#c-247-dedicated-server-advanced) successfully.
|
||||||
|
2. On the server's console/terminal/CMD, server admin **MUST RUN THIS EACH BOOT** to allow players with Official Hytale game license to connect on the server:
|
||||||
|
```
|
||||||
|
/auth logout
|
||||||
|
/auth persistence Encrypted
|
||||||
|
/auth login device
|
||||||
|
```
|
||||||
|
3. Server console will show instructions, an URL and a code; these will be revoked after 10 minutes if not authorized.
|
||||||
|
4. The server hoster can open the URL directly to browser by holding Ctrl then Click on it, or copy and send it to the player with official account.
|
||||||
|
5. Once it authorized, the official accounts can join server with F2P players.
|
||||||
|
6. If you want to modify anything, look at the [Hytale-Server-Docker](https://github.com/sanasol/hytale-server-docker/) above, give the repo a STAR too.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### "Server" Term and Definiton
|
### "Server" Term and Definiton
|
||||||
|
|
||||||
"HytaleServer.jar", which called as "Server", functions as the place of authentication of the client that supposed to go to Hytale Official Authentication System but we managed our way to redirect it on our service (Thanks to Sanasol), handling approximately thousands of players worldwide to play this game for free.
|
"HytaleServer.jar", which called as "Server", functions as the place of authentication of the client that supposed to go to Hytale Official Authentication System but we managed our way to redirect it on our service (Thanks to Sanasol), handling approximately thousands of players worldwide to play this game for free.
|
||||||
@@ -41,14 +105,15 @@ Kindly support us via [our Buy Me a Coffee link](https://buymeacoffee.com/hf2p)
|
|||||||
|
|
||||||
### Server Directory Location
|
### Server Directory Location
|
||||||
|
|
||||||
Here are the directory locations of Server folder if you have installed
|
Here are the directory locations of Server folder if you have installed it on default instalation location:
|
||||||
- **Windows:** `%localappdata%\HytaleF2P\release\package\game\latest\Server`
|
- **Windows:** `%localappdata%\HytaleF2P\release\package\game\latest\Server`
|
||||||
- **macOS:** `~/Library/Application Support/HytaleF2P/release/package/game/latest/Server`
|
- **macOS:** `~/Library/Application Support/HytaleF2P/release/package/game/latest/Server`
|
||||||
- **Linux:** `~/.hytalef2p/release/package/game/latest/Server`
|
- **Linux:** `~/.hytalef2p/release/package/game/latest/Server`
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> This location only exists if the user installed the game using our launcher. The `Server` folder needed to auth the HytaleClient to play Hytale online
|
> This location only exists if the user installed the game using our launcher.
|
||||||
> (for now; we planned to add offline mode in later version of our launcher).
|
> The `Server` folder needed to auth the HytaleClient to play Hytale in Singleplayer/Multiplayer for now.
|
||||||
|
> (We planned to add offline mode in later version of our launcher).
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> Hosting a dedicated Hytale server will not need the exact similar tree. You can put it anywhere, as long as the directory has `Assets.zip` which
|
> Hosting a dedicated Hytale server will not need the exact similar tree. You can put it anywhere, as long as the directory has `Assets.zip` which
|
||||||
@@ -64,6 +129,7 @@ Terms and conditions applies.
|
|||||||
## 1. Using Online-Play Feature / In-Game Invite Code
|
## 1. Using Online-Play Feature / In-Game Invite Code
|
||||||
|
|
||||||
The easiest way to play with friends - no manual server setup required!
|
The easiest way to play with friends - no manual server setup required!
|
||||||
|
|
||||||
*The game automatically handles networking using UPnP/STUN/NAT traversal.*
|
*The game automatically handles networking using UPnP/STUN/NAT traversal.*
|
||||||
|
|
||||||
**For Online Play to work, you need:**
|
**For Online Play to work, you need:**
|
||||||
@@ -112,6 +178,7 @@ Warning: Your network configuration may prevent other players from connecting.
|
|||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details><summary><b>b. "UPnP Failed" or "Port Mapping Failed" Warning</b></summary>
|
<details><summary><b>b. "UPnP Failed" or "Port Mapping Failed" Warning</b></summary>
|
||||||
|
|
||||||
**Check your router:**
|
**Check your router:**
|
||||||
1. Log into router admin panel (usually `192.168.1.1` or `192.168.0.1`)
|
1. Log into router admin panel (usually `192.168.1.1` or `192.168.0.1`)
|
||||||
2. Find UPnP settings (often under "Advanced" or "NAT")
|
2. Find UPnP settings (often under "Advanced" or "NAT")
|
||||||
@@ -123,7 +190,8 @@ Warning: Your network configuration may prevent other players from connecting.
|
|||||||
- See "Port Forwarding" or "Workarounds or NAT/CGNAT" sections below
|
- See "Port Forwarding" or "Workarounds or NAT/CGNAT" sections below
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details><summary><b>c. "Strict NAT" or "Symmetric NAT" Warning</b></summary>
|
<details><summary><b>c. "Connected via STUN", "Strict NAT" or "Symmetric NAT" Warning</b></summary>
|
||||||
|
|
||||||
Some routers have restrictive NAT that blocks peer connections.
|
Some routers have restrictive NAT that blocks peer connections.
|
||||||
|
|
||||||
**Try:**
|
**Try:**
|
||||||
@@ -133,6 +201,7 @@ Some routers have restrictive NAT that blocks peer connections.
|
|||||||
</details>
|
</details>
|
||||||
|
|
||||||
## 2. Using Tailscale
|
## 2. Using Tailscale
|
||||||
|
|
||||||
Tailscale creates mesh VPN service that streamlines connecting devices and services securely across different networks. And **works crossplatform!!**
|
Tailscale creates mesh VPN service that streamlines connecting devices and services securely across different networks. And **works crossplatform!!**
|
||||||
|
|
||||||
1. All members are required to download [Tailscale](https://tailscale.com/download) on your device.
|
1. All members are required to download [Tailscale](https://tailscale.com/download) on your device.
|
||||||
@@ -148,6 +217,17 @@ Tailscale creates mesh VPN service that streamlines connecting devices and servi
|
|||||||
* Use the new share code to connect
|
* Use the new share code to connect
|
||||||
* To test your connection, ping the host's ipv4 mentioned in Tailscale
|
* To test your connection, ping the host's ipv4 mentioned in Tailscale
|
||||||
|
|
||||||
|
## 3. Using Radmin VPN
|
||||||
|
|
||||||
|
Creates a virtual LAN - all players need to install it:
|
||||||
|
|
||||||
|
1. Download [Radmin VPN](https://www.radmin-vpn.com/) - All players install it
|
||||||
|
2. One person create a room/network, others join with network name/password
|
||||||
|
3. Host joined the world, others will connect to it.
|
||||||
|
4. Open Hytale Game > Servers > Add Servers > Direct Connect > Type IP Address of the Host from Radmin.
|
||||||
|
|
||||||
|
These options bypass all NAT/CGNAT issues. But for **Windows machines only!**
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# B. Local Dedicated Server
|
# B. Local Dedicated Server
|
||||||
@@ -167,11 +247,12 @@ Free tunneling service - only the host needs to install it:
|
|||||||
* Right-click file > Properties > Turn on 'Executable as a Program' | or `chmod +x playit-linux-amd64` on terminal
|
* Right-click file > Properties > Turn on 'Executable as a Program' | or `chmod +x playit-linux-amd64` on terminal
|
||||||
* Run by double-clicking the file or `./playit-linux-amd64` via terminal
|
* Run by double-clicking the file or `./playit-linux-amd64` via terminal
|
||||||
5. Open the URL/link by `Ctrl+Click` it. If unable, select the URL, then Right-Click to Copy (`Ctrl+Shift+C` for Linux) then Paste the URL into your browser to link it with your created account.
|
5. Open the URL/link by `Ctrl+Click` it. If unable, select the URL, then Right-Click to Copy (`Ctrl+Shift+C` for Linux) then Paste the URL into your browser to link it with your created account.
|
||||||
6. **WARNING: Do not close the terminal if you are still playing or hosting the server**
|
6. Once it done, download the `run_server_with_tokens (1)` script file (`.BAT` for Windows, `.SH` for Linux) from our Discord server > channel `#open-public-server`
|
||||||
7. Once it done, download the `run_server_with_tokens` script file (`.BAT` for Windows, `.SH` for Linux) from our Discord server > channel `#open-public-server`
|
7. Put the script file to the `Server` folder in `HytaleF2P` directory (`%localappdata%\HytaleF2P\release\package\game\latest\Server`)
|
||||||
8. Put the script file to the `Server` folder in `HytaleF2P` directory (`%localappdata%\HytaleF2P\release\package\game\latest\Server`)
|
8. Rename the script file to `run_server_with_tokens` to make it easier if you run it with Terminal, then do Method A or B.
|
||||||
9. Copy the `Assets.zip` from the `%localappdata%\HytaleF2P\release\package\game\latest\` folder to the `Server\` folder. (TIP: You can use Symlink of that file to reduce disk usage!)
|
9. If you put it in `Server` folder in `HytaleF2P` launcher, change `ASSETS_PATH="${ASSETS_PATH:-./Assets.zip}"` inside the script to be `ASSETS_PATH="${ASSETS_PATH:-../Assets.zip}"`. NOTICE THE `./` and `../` DIFFERENCE.
|
||||||
10. Double-click the .BAT file to host your server, wait until it shows:
|
10. Copy the `Assets.zip` from the `%localappdata%\HytaleF2P\release\package\game\latest\` folder to the `Server\` folder. (TIP: You can use Symlink of that file to reduce disk usage!)
|
||||||
|
11. Double-click the .BAT file to host your server, wait until it shows:
|
||||||
```
|
```
|
||||||
===================================================
|
===================================================
|
||||||
Hytale Server Booted! [Multiplayer, Fresh Universe]
|
Hytale Server Booted! [Multiplayer, Fresh Universe]
|
||||||
@@ -180,16 +261,12 @@ Hytale Server Booted! [Multiplayer, Fresh Universe]
|
|||||||
11. Connect to the server by go to `Servers` in your game client, press `Add Server`, type `localhost` in the address box, use any name for your server.
|
11. Connect to the server by go to `Servers` in your game client, press `Add Server`, type `localhost` in the address box, use any name for your server.
|
||||||
12. Send the public address in Step 3 to your friends.
|
12. Send the public address in Step 3 to your friends.
|
||||||
|
|
||||||
## 2. Using Radmin VPN
|
> [!CAUTION]
|
||||||
|
> Do not close the Playit.gg Terminal OR HytaleServer Terminal if you are still playing or hosting the server.
|
||||||
|
|
||||||
Creates a virtual LAN - all players need to install it:
|
## 2. Using Tailscale [DRAFT]
|
||||||
|
|
||||||
1. Download [Radmin VPN](https://www.radmin-vpn.com/) - All players install it
|
Tailscale
|
||||||
2. One person create a room/network, others join with network name/password
|
|
||||||
3. Host joined the world, others will connect to it.
|
|
||||||
4. Open Hytale Game > Servers > Add Servers > Direct Connect > Type IP Address of the Host from Radmin.
|
|
||||||
|
|
||||||
These options bypass all NAT/CGNAT issues. But for **Windows machines only!**
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -228,12 +305,12 @@ For 24/7 servers, custom configurations, or hosting on a VPS/dedicated machine.
|
|||||||
|
|
||||||
**Windows:**
|
**Windows:**
|
||||||
```batch
|
```batch
|
||||||
run_server.bat
|
run_server_with_token.bat
|
||||||
```
|
```
|
||||||
|
|
||||||
**macOS / Linux:**
|
**macOS / Linux:**
|
||||||
```bash
|
```bash
|
||||||
./run_server.sh
|
./run_server_with_token.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -503,3 +580,6 @@ See [Docker documentation](https://github.com/Hybrowse/hytale-server-docker) for
|
|||||||
- Auth Server: sanasol.ws
|
- Auth Server: sanasol.ws
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
const FORCE_CLEAN_INSTALL_VERSION = false;
|
const FORCE_CLEAN_INSTALL_VERSION = false;
|
||||||
const CLEAN_INSTALL_TEST_VERSION = '4.pwr';
|
const CLEAN_INSTALL_TEST_VERSION = 'v4';
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
FORCE_CLEAN_INSTALL_VERSION,
|
FORCE_CLEAN_INSTALL_VERSION,
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ async function safeRemoveDirectory(dirPath, maxRetries = 3) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function downloadPWR(branch = 'release', fileName = '7.pwr', progressCallback, cacheDir = CACHE_DIR, manualRetry = false) {
|
async function downloadPWR(branch = 'release', fileName = 'v8', progressCallback, cacheDir = CACHE_DIR, manualRetry = false) {
|
||||||
const osName = getOS();
|
const osName = getOS();
|
||||||
const arch = getArch();
|
const arch = getArch();
|
||||||
|
|
||||||
@@ -72,8 +72,23 @@ async function downloadPWR(branch = 'release', fileName = '7.pwr', progressCallb
|
|||||||
throw new Error('Hytale x86_64 Intel Mac Support has not been released yet. Please check back later.');
|
throw new Error('Hytale x86_64 Intel Mac Support has not been released yet. Please check back later.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = `https://game-patches.hytale.com/patches/${osName}/${arch}/${branch}/0/${fileName}`;
|
const { getPWRUrlFromNewAPI } = require('../services/versionManager');
|
||||||
const dest = path.join(cacheDir, `${branch}_${fileName}`);
|
|
||||||
|
let url;
|
||||||
|
let isUsingNewAPI = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(`[DownloadPWR] Fetching URL from new API for branch: ${branch}, version: ${fileName}`);
|
||||||
|
url = await getPWRUrlFromNewAPI(branch, fileName);
|
||||||
|
isUsingNewAPI = true;
|
||||||
|
console.log(`[DownloadPWR] Using new API URL: ${url}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`[DownloadPWR] Failed to get URL from new API: ${error.message}`);
|
||||||
|
console.log(`[DownloadPWR] Falling back to old URL format`);
|
||||||
|
url = `https://game-patches.hytale.com/patches/${osName}/${arch}/${branch}/0/${fileName}.pwr`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dest = path.join(cacheDir, `${branch}_${fileName}.pwr`);
|
||||||
|
|
||||||
// Check if file exists and validate it
|
// Check if file exists and validate it
|
||||||
if (fs.existsSync(dest) && !manualRetry) {
|
if (fs.existsSync(dest) && !manualRetry) {
|
||||||
@@ -93,7 +108,7 @@ async function downloadPWR(branch = 'release', fileName = '7.pwr', progressCallb
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Fetching PWR patch file:', url);
|
console.log(`Fetching PWR patch file from ${isUsingNewAPI ? 'NEW API' : 'old API'}:`, url);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (manualRetry) {
|
if (manualRetry) {
|
||||||
|
|||||||
@@ -6,10 +6,141 @@ const { smartRequest } = require('../utils/proxyClient');
|
|||||||
|
|
||||||
const BASE_PATCH_URL = 'https://game-patches.hytale.com/patches';
|
const BASE_PATCH_URL = 'https://game-patches.hytale.com/patches';
|
||||||
const MANIFEST_API = 'https://files.hytalef2p.com/api/patch_manifest';
|
const MANIFEST_API = 'https://files.hytalef2p.com/api/patch_manifest';
|
||||||
|
const NEW_API_URL = 'https://thecute.cloud/ShipOfYarn/api.php';
|
||||||
|
|
||||||
|
let apiCache = null;
|
||||||
|
let apiCacheTime = 0;
|
||||||
|
const API_CACHE_DURATION = 60000; // 1 minute
|
||||||
|
|
||||||
|
async function fetchNewAPI() {
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
if (apiCache && (now - apiCacheTime) < API_CACHE_DURATION) {
|
||||||
|
console.log('[NewAPI] Using cached API data');
|
||||||
|
return apiCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('[NewAPI] Fetching from:', NEW_API_URL);
|
||||||
|
const response = await axios.get(NEW_API_URL, {
|
||||||
|
timeout: 15000,
|
||||||
|
headers: {
|
||||||
|
'User-Agent': 'Hytale-F2P-Launcher'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.data && response.data.hytale) {
|
||||||
|
apiCache = response.data;
|
||||||
|
apiCacheTime = now;
|
||||||
|
console.log('[NewAPI] API data fetched and cached successfully');
|
||||||
|
return response.data;
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid API response structure');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[NewAPI] Error fetching API:', error.message);
|
||||||
|
if (apiCache) {
|
||||||
|
console.log('[NewAPI] Using expired cache due to error');
|
||||||
|
return apiCache;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getLatestVersionFromNewAPI(branch = 'release') {
|
||||||
|
try {
|
||||||
|
const apiData = await fetchNewAPI();
|
||||||
|
const osName = getOS();
|
||||||
|
const arch = getArch();
|
||||||
|
|
||||||
|
let osKey = osName;
|
||||||
|
if (osName === 'darwin') {
|
||||||
|
osKey = 'mac';
|
||||||
|
}
|
||||||
|
|
||||||
|
const branchData = apiData.hytale[branch];
|
||||||
|
if (!branchData || !branchData[osKey]) {
|
||||||
|
throw new Error(`No data found for branch: ${branch}, OS: ${osKey}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const osData = branchData[osKey];
|
||||||
|
|
||||||
|
const versions = Object.keys(osData).filter(key => key.endsWith('.pwr'));
|
||||||
|
|
||||||
|
if (versions.length === 0) {
|
||||||
|
throw new Error(`No .pwr files found for ${osKey}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const versionNumbers = versions.map(v => {
|
||||||
|
const match = v.match(/v(\d+)/);
|
||||||
|
return match ? parseInt(match[1]) : 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
const latestVersionNumber = Math.max(...versionNumbers);
|
||||||
|
console.log(`[NewAPI] Latest version number: ${latestVersionNumber} for branch ${branch}`);
|
||||||
|
|
||||||
|
return `v${latestVersionNumber}`;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[NewAPI] Error getting latest version:', error.message);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getPWRUrlFromNewAPI(branch = 'release', version = 'v8') {
|
||||||
|
try {
|
||||||
|
const apiData = await fetchNewAPI();
|
||||||
|
const osName = getOS();
|
||||||
|
const arch = getArch();
|
||||||
|
|
||||||
|
let osKey = osName;
|
||||||
|
if (osName === 'darwin') {
|
||||||
|
osKey = 'mac';
|
||||||
|
}
|
||||||
|
|
||||||
|
let fileName;
|
||||||
|
if (osName === 'windows') {
|
||||||
|
fileName = `${version}-windows-amd64.pwr`;
|
||||||
|
} else if (osName === 'linux') {
|
||||||
|
fileName = `${version}-linux-amd64.pwr`;
|
||||||
|
} else if (osName === 'darwin') {
|
||||||
|
fileName = `${version}-darwin-arm64.pwr`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const branchData = apiData.hytale[branch];
|
||||||
|
if (!branchData || !branchData[osKey]) {
|
||||||
|
throw new Error(`No data found for branch: ${branch}, OS: ${osKey}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const osData = branchData[osKey];
|
||||||
|
const url = osData[fileName];
|
||||||
|
|
||||||
|
if (!url) {
|
||||||
|
throw new Error(`No URL found for ${fileName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[NewAPI] URL for ${fileName}: ${url}`);
|
||||||
|
return url;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[NewAPI] Error getting PWR URL:', error.message);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function getLatestClientVersion(branch = 'release') {
|
async function getLatestClientVersion(branch = 'release') {
|
||||||
try {
|
try {
|
||||||
console.log(`Fetching latest client version from API (branch: ${branch})...`);
|
console.log(`[NewAPI] Fetching latest client version from new API (branch: ${branch})...`);
|
||||||
|
|
||||||
|
// Utiliser la nouvelle API
|
||||||
|
const latestVersion = await getLatestVersionFromNewAPI(branch);
|
||||||
|
console.log(`[NewAPI] Latest client version for ${branch}: ${latestVersion}`);
|
||||||
|
return latestVersion;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[NewAPI] Error fetching client version from new API:', error.message);
|
||||||
|
console.log('[NewAPI] Falling back to old API...');
|
||||||
|
|
||||||
|
// Fallback vers l'ancienne API si la nouvelle échoue
|
||||||
|
try {
|
||||||
const response = await smartRequest(`https://files.hytalef2p.com/api/version_client?branch=${branch}`, {
|
const response = await smartRequest(`https://files.hytalef2p.com/api/version_client?branch=${branch}`, {
|
||||||
timeout: 40000,
|
timeout: 40000,
|
||||||
headers: {
|
headers: {
|
||||||
@@ -19,18 +150,41 @@ async function getLatestClientVersion(branch = 'release') {
|
|||||||
|
|
||||||
if (response.data && response.data.client_version) {
|
if (response.data && response.data.client_version) {
|
||||||
const version = response.data.client_version;
|
const version = response.data.client_version;
|
||||||
console.log(`Latest client version for ${branch}: ${version}`);
|
console.log(`Latest client version for ${branch} (old API): ${version}`);
|
||||||
return version;
|
return version;
|
||||||
} else {
|
} else {
|
||||||
console.log('Warning: Invalid API response, falling back to latest known version (7.pwr)');
|
console.log('Warning: Invalid API response, falling back to latest known version (v8)');
|
||||||
return '7.pwr';
|
return 'v8';
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (fallbackError) {
|
||||||
console.error('Error fetching client version:', error.message);
|
console.error('Error fetching client version from old API:', fallbackError.message);
|
||||||
console.log('Warning: API unavailable, falling back to latest known version (7.pwr)');
|
console.log('Warning: Both APIs unavailable, falling back to latest known version (v8)');
|
||||||
return '7.pwr';
|
return 'v8';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction utilitaire pour extraire le numéro de version
|
||||||
|
// Supporte les formats: "7.pwr", "v8", "v8-windows-amd64.pwr", etc.
|
||||||
|
function extractVersionNumber(version) {
|
||||||
|
if (!version) return 0;
|
||||||
|
|
||||||
|
// Nouveau format: "v8" ou "v8-xxx.pwr"
|
||||||
|
const vMatch = version.match(/v(\d+)/);
|
||||||
|
if (vMatch) {
|
||||||
|
return parseInt(vMatch[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ancien format: "7.pwr"
|
||||||
|
const pwrMatch = version.match(/(\d+)\.pwr/);
|
||||||
|
if (pwrMatch) {
|
||||||
|
return parseInt(pwrMatch[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: essayer de parser directement
|
||||||
|
const num = parseInt(version);
|
||||||
|
return isNaN(num) ? 0 : num;
|
||||||
|
}
|
||||||
|
|
||||||
function buildArchiveUrl(buildNumber, branch = 'release') {
|
function buildArchiveUrl(buildNumber, branch = 'release') {
|
||||||
const os = getOS();
|
const os = getOS();
|
||||||
@@ -50,7 +204,7 @@ async function checkArchiveExists(buildNumber, branch = 'release') {
|
|||||||
|
|
||||||
async function discoverAvailableVersions(latestKnown, branch = 'release', maxProbe = 50) {
|
async function discoverAvailableVersions(latestKnown, branch = 'release', maxProbe = 50) {
|
||||||
const available = [];
|
const available = [];
|
||||||
const latest = parseInt(latestKnown.replace('.pwr', ''));
|
const latest = extractVersionNumber(latestKnown);
|
||||||
|
|
||||||
for (let i = latest; i >= Math.max(1, latest - maxProbe); i--) {
|
for (let i = latest; i >= Math.max(1, latest - maxProbe); i--) {
|
||||||
const exists = await checkArchiveExists(i, branch);
|
const exists = await checkArchiveExists(i, branch);
|
||||||
@@ -77,7 +231,7 @@ async function fetchPatchManifest(branch = 'release') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function extractVersionDetails(targetVersion, branch = 'release') {
|
async function extractVersionDetails(targetVersion, branch = 'release') {
|
||||||
const buildNumber = parseInt(targetVersion.replace('.pwr', ''));
|
const buildNumber = extractVersionNumber(targetVersion);
|
||||||
const previousBuild = buildNumber - 1;
|
const previousBuild = buildNumber - 1;
|
||||||
|
|
||||||
const manifest = await fetchPatchManifest(branch);
|
const manifest = await fetchPatchManifest(branch);
|
||||||
@@ -103,8 +257,8 @@ function canUseDifferentialUpdate(currentVersion, targetDetails) {
|
|||||||
|
|
||||||
if (!currentVersion) return false;
|
if (!currentVersion) return false;
|
||||||
|
|
||||||
const currentBuild = parseInt(currentVersion.replace('.pwr', ''));
|
const currentBuild = extractVersionNumber(currentVersion);
|
||||||
const expectedSource = parseInt(targetDetails.sourceVersion?.replace('.pwr', '') || '0');
|
const expectedSource = extractVersionNumber(targetDetails.sourceVersion);
|
||||||
|
|
||||||
return currentBuild === expectedSource;
|
return currentBuild === expectedSource;
|
||||||
}
|
}
|
||||||
@@ -112,8 +266,8 @@ function canUseDifferentialUpdate(currentVersion, targetDetails) {
|
|||||||
function needsIntermediatePatches(currentVersion, targetVersion) {
|
function needsIntermediatePatches(currentVersion, targetVersion) {
|
||||||
if (!currentVersion) return [];
|
if (!currentVersion) return [];
|
||||||
|
|
||||||
const current = parseInt(currentVersion.replace('.pwr', ''));
|
const current = extractVersionNumber(currentVersion);
|
||||||
const target = parseInt(targetVersion.replace('.pwr', ''));
|
const target = extractVersionNumber(targetVersion);
|
||||||
|
|
||||||
const intermediates = [];
|
const intermediates = [];
|
||||||
for (let i = current + 1; i <= target; i++) {
|
for (let i = current + 1; i <= target; i++) {
|
||||||
@@ -160,5 +314,9 @@ module.exports = {
|
|||||||
needsIntermediatePatches,
|
needsIntermediatePatches,
|
||||||
computeFileChecksum,
|
computeFileChecksum,
|
||||||
validateChecksum,
|
validateChecksum,
|
||||||
getInstalledClientVersion
|
getInstalledClientVersion,
|
||||||
|
fetchNewAPI,
|
||||||
|
getLatestVersionFromNewAPI,
|
||||||
|
getPWRUrlFromNewAPI,
|
||||||
|
extractVersionNumber
|
||||||
};
|
};
|
||||||
|
|||||||
8
main.js
8
main.js
@@ -627,7 +627,7 @@ ipcMain.handle('install-game', async (event, playerName, javaPath, installPath,
|
|||||||
console.log('[Main] Processing Butler error with retry context');
|
console.log('[Main] Processing Butler error with retry context');
|
||||||
errorData.retryData = {
|
errorData.retryData = {
|
||||||
branch: error.branch || 'release',
|
branch: error.branch || 'release',
|
||||||
fileName: error.fileName || '7.pwr',
|
fileName: error.fileName || 'v8',
|
||||||
cacheDir: error.cacheDir
|
cacheDir: error.cacheDir
|
||||||
};
|
};
|
||||||
errorData.canRetry = error.canRetry !== false;
|
errorData.canRetry = error.canRetry !== false;
|
||||||
@@ -647,7 +647,7 @@ ipcMain.handle('install-game', async (event, playerName, javaPath, installPath,
|
|||||||
console.log('[Main] Processing generic error, creating default retry data');
|
console.log('[Main] Processing generic error, creating default retry data');
|
||||||
errorData.retryData = {
|
errorData.retryData = {
|
||||||
branch: 'release',
|
branch: 'release',
|
||||||
fileName: '7.pwr'
|
fileName: 'v8'
|
||||||
};
|
};
|
||||||
// For generic errors, assume it's retryable unless specified
|
// For generic errors, assume it's retryable unless specified
|
||||||
errorData.canRetry = error.canRetry !== false;
|
errorData.canRetry = error.canRetry !== false;
|
||||||
@@ -887,7 +887,7 @@ ipcMain.handle('retry-download', async (event, retryData) => {
|
|||||||
console.log('[IPC] Invalid retry data, using PWR defaults');
|
console.log('[IPC] Invalid retry data, using PWR defaults');
|
||||||
retryData = {
|
retryData = {
|
||||||
branch: 'release',
|
branch: 'release',
|
||||||
fileName: '7.pwr'
|
fileName: 'v8'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -921,7 +921,7 @@ ipcMain.handle('retry-download', async (event, retryData) => {
|
|||||||
} :
|
} :
|
||||||
{
|
{
|
||||||
branch: retryData?.branch || 'release',
|
branch: retryData?.branch || 'release',
|
||||||
fileName: retryData?.fileName || '7.pwr',
|
fileName: retryData?.fileName || 'v8',
|
||||||
cacheDir: retryData?.cacheDir
|
cacheDir: retryData?.cacheDir
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hytale-f2p-launcher",
|
"name": "hytale-f2p-launcher",
|
||||||
"version": "2.2.1",
|
"version": "2.2.2",
|
||||||
"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",
|
||||||
|
|||||||
Reference in New Issue
Block a user