mirror of
https://git.sanhost.net/sanasol/hytale-f2p.git
synced 2026-02-26 06:41:47 -03:00
- Migrate patch downloads to auth server redirect gateway (302 -> CDN) Allows instant CDN switching via admin panel without launcher update - Fix identity token "Player" username mismatch on fresh install Add token username verification with retry in fetchAuthTokens - Refactor versionManager to use mirror manifest via auth.sanasol.ws/patches - Add optimal patch routing (BFS) for differential updates - Add PATCH_CDN_INFRASTRUCTURE.md documentation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
218 lines
7.1 KiB
Markdown
218 lines
7.1 KiB
Markdown
# Patch CDN Infrastructure & Game Update System
|
|
|
|
## Overview
|
|
|
|
The F2P launcher downloads game patches through a CDN redirect gateway hosted on the auth server. This allows instant CDN switching (e.g., for DMCA takedowns) without releasing a new launcher version.
|
|
|
|
## Architecture
|
|
|
|
```
|
|
Launcher --> GET auth.sanasol.ws/patches/manifest.json
|
|
--> 302 --> mega.io/.../manifest.json
|
|
|
|
Launcher --> GET auth.sanasol.ws/patches/windows/amd64/release/0_to_11.pwr
|
|
--> 302 --> mega.io/.../windows/amd64/release/0_to_11.pwr
|
|
```
|
|
|
|
The auth server acts as a pure redirect gateway (302). No bandwidth is consumed on the auth server - all actual file transfers happen directly between the launcher and the CDN.
|
|
|
|
## URLs
|
|
|
|
| URL | Purpose |
|
|
|-----|---------|
|
|
| `https://auth.sanasol.ws/patches/*` | Redirect gateway (302 -> CDN) |
|
|
| `https://auth.sanasol.ws/patches/manifest.json` | Patch manifest (redirects to CDN) |
|
|
| `https://auth.sanasol.ws/admin/page/settings` | Admin panel to change CDN URL |
|
|
| `https://auth.sanasol.ws/admin/api/settings/patches-cdn` | API to GET/POST CDN base URL |
|
|
|
|
### Default CDN (MEGA S4)
|
|
|
|
```
|
|
Base URL: https://s3.g.s4.mega.io/kcvismkrtfcalgwxzsazbq46l72dwsypqaham/hytale/patches
|
|
```
|
|
|
|
### Changing CDN (DMCA response)
|
|
|
|
1. Go to `https://auth.sanasol.ws/admin/page/settings`
|
|
2. Find "Patches CDN Base URL" section
|
|
3. Change URL to new CDN (e.g., `https://new-cdn.example.com/patches`)
|
|
4. Click "Save" - all launcher requests instantly redirect to new CDN
|
|
5. No launcher update needed
|
|
|
|
## Manifest Format
|
|
|
|
The manifest is a JSON file listing all available patch files:
|
|
|
|
```json
|
|
{
|
|
"updated": "2026-02-20T13:20:09.776Z",
|
|
"files": {
|
|
"windows/amd64/release/0_to_11.pwr": { "size": 1618804736 },
|
|
"windows/amd64/release/10_to_11.pwr": { "size": 62914560 },
|
|
"darwin/arm64/release/0_to_11.pwr": { "size": 1617100800 },
|
|
"server/release": { "version": "2026.02.19-1a311a592", "size": 1509949440, "sha256": "..." },
|
|
...
|
|
}
|
|
}
|
|
```
|
|
|
|
### Key Structure
|
|
|
|
File keys follow the pattern: `{os}/{arch}/{branch}/{from}_to_{to}.pwr`
|
|
|
|
- **OS**: `windows`, `linux`, `darwin`
|
|
- **Arch**: `amd64`, `arm64`
|
|
- **Branch**: `release`, `pre-release`
|
|
- **Patch**: `{from}_to_{to}.pwr` (e.g., `0_to_11.pwr` for full install, `10_to_11.pwr` for differential)
|
|
|
|
Server builds use: `server/{branch}` with `version`, `size`, `sha256` fields.
|
|
|
|
## Game Update Process
|
|
|
|
### 1. Version Check
|
|
|
|
```
|
|
Launcher calls: getLatestClientVersion(branch)
|
|
-> Fetches manifest from auth.sanasol.ws/patches/manifest.json
|
|
-> Finds highest build number for current platform/branch
|
|
-> Returns "v{buildNumber}" (e.g., "v11")
|
|
```
|
|
|
|
### 2. Update Plan (Optimal Patch Routing)
|
|
|
|
```
|
|
Launcher calls: getUpdatePlan(currentBuild, targetBuild, branch)
|
|
-> Fetches manifest
|
|
-> Finds available patches for platform
|
|
-> Uses BFS to find optimal path (minimizes total download size)
|
|
-> Example: build 5 -> 11 might use: 5->10 (148MB) + 10->11 (60MB)
|
|
instead of: 0->11 (1.5GB)
|
|
```
|
|
|
|
### 3. Download & Apply
|
|
|
|
```
|
|
For each step in the update plan:
|
|
1. Download .pwr file from auth.sanasol.ws/patches/{key}
|
|
(redirects to CDN, supports resume via Range headers)
|
|
2. Apply patch using butler tool:
|
|
butler apply --staging-dir <staging> <pwr_file> <game_dir>
|
|
3. Save version after each step
|
|
```
|
|
|
|
### 4. Fresh Install
|
|
|
|
For first-time installs (currentBuild = 0):
|
|
- Downloads `0_to_{target}.pwr` (full install, ~1.5GB)
|
|
- Applies with butler to create the full game directory
|
|
|
|
### 5. Differential Update
|
|
|
|
For existing installations:
|
|
- Finds optimal patch chain (e.g., `10_to_11.pwr` at ~60MB)
|
|
- Applies incrementally, saving progress after each step
|
|
- Falls back to full install if no patch path found
|
|
|
|
## Mirror Sync Script
|
|
|
|
The mirror script (`scripts/hytale-mirror.js`) downloads patches from the official Hytale API and uploads to MEGA S4.
|
|
|
|
### Usage
|
|
|
|
```bash
|
|
cd scripts
|
|
node hytale-mirror.js download # Download patches locally
|
|
node hytale-mirror.js upload # Upload to MEGA S4 via rclone
|
|
node hytale-mirror.js sync # Download + Upload in one step
|
|
```
|
|
|
|
### What It Does
|
|
|
|
1. **Discovery**: Calls Hytale API to find available patches for all platforms
|
|
2. **Download**: Downloads .pwr files to `scripts/mirror/` directory
|
|
3. **Manifest Generation**: Creates `manifest.json` with file sizes (no local paths)
|
|
4. **Upload**: Uses `rclone` to sync to MEGA S4
|
|
|
|
### SOCKS5 Proxy
|
|
|
|
- API discovery calls use SOCKS5 proxy rotation (for rate limiting)
|
|
- File downloads do NOT use proxy (too slow for large files)
|
|
- Proxy list in `proxies.json` (auto-refreshed from proxy service)
|
|
|
|
### Prerequisites
|
|
|
|
- `rclone` configured with `megas4` remote pointing to MEGA S4
|
|
- Node.js 20+
|
|
- Network access to Hytale API endpoints
|
|
|
|
## Launcher Configuration
|
|
|
|
### Environment Variables
|
|
|
|
| Variable | Default | Description |
|
|
|----------|---------|-------------|
|
|
| `HYTALE_AUTH_DOMAIN` | `auth.sanasol.ws` | Auth domain (used for patch redirects) |
|
|
|
|
### Key Files
|
|
|
|
| File | Description |
|
|
|------|-------------|
|
|
| `backend/services/versionManager.js` | Manifest fetching, version checking, update planning |
|
|
| `backend/managers/differentialUpdateManager.js` | Download orchestration, butler integration |
|
|
| `backend/utils/fileManager.js` | File download with retry, resume, stall detection |
|
|
| `backend/managers/gameLauncher.js` | Game launch with token fetch, patching, signing |
|
|
|
|
### Constants (versionManager.js)
|
|
|
|
```javascript
|
|
const AUTH_DOMAIN = process.env.HYTALE_AUTH_DOMAIN || 'auth.sanasol.ws';
|
|
const MIRROR_BASE_URL = `https://${AUTH_DOMAIN}/patches`;
|
|
const MIRROR_MANIFEST_URL = `${MIRROR_BASE_URL}/manifest.json`;
|
|
const MANIFEST_CACHE_DURATION = 60000; // 1 minute cache
|
|
const FALLBACK_LATEST_BUILD = 11; // If manifest unreachable
|
|
```
|
|
|
|
## Auth Server Implementation
|
|
|
|
### Routes
|
|
|
|
```
|
|
GET /patches/* -> handlePatchRedirect()
|
|
- Extracts path after /patches/
|
|
- Reads CDN base URL from Redis settings
|
|
- Returns 302 redirect to {baseUrl}/{path}
|
|
- Tracks download metrics
|
|
|
|
GET /admin/api/settings/patches-cdn -> getPatchesCdnBaseUrl()
|
|
POST /admin/api/settings/patches-cdn -> setPatchesCdnBaseUrl()
|
|
```
|
|
|
|
### Redis Storage
|
|
|
|
```
|
|
settings:global -> { patchesCdnBaseUrl: "https://s3.g.s4.mega.io/..." }
|
|
metrics:downloads -> { "patch:manifest.json": count, ... }
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### "Invalid manifest structure" error
|
|
- Check manifest.json is valid JSON with `files` object
|
|
- Verify CDN is accessible: `curl -sL https://auth.sanasol.ws/patches/manifest.json | python3 -m json.tool`
|
|
- Check admin settings for correct CDN URL
|
|
|
|
### 0-byte downloads
|
|
- Verify redirect works: `curl -sI https://auth.sanasol.ws/patches/darwin/arm64/release/0_to_11.pwr`
|
|
- Should show `302` with `Location` header
|
|
- Test actual download: `curl -sL -o /dev/null -w "%{size_download}" -r 0-1023 <url>`
|
|
|
|
### Manifest has local paths
|
|
- Regenerate manifest: `node scripts/hytale-mirror.js download` (re-scans files)
|
|
- Re-upload: `node scripts/hytale-mirror.js upload`
|
|
- Verify: entries should only have `{ size: <bytes> }`, no `path` field
|
|
|
|
### CDN switch not taking effect
|
|
- Check Redis: CDN URL stored in `settings:global`
|
|
- Verify via API: `curl https://auth.sanasol.ws/admin/api/settings/patches-cdn`
|
|
- Manifest is cached for 1 minute in launcher - wait or restart
|